瀏覽代碼

优化集群方案, 每个zlm一套ssrc;
优化集群下的docker接入逻辑;
更正sql脚本;
支持重启不设置设备离线。重启SIP事务不丢失

64850858 4 年之前
父節點
當前提交
3469271ec2
共有 57 個文件被更改,包括 1312 次插入1070 次删除
  1. 7 0
      pom.xml
  2. 107 97
      sql/mysql.sql
  3. 6 0
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  4. 15 173
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  5. 10 10
      src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
  6. 36 1
      src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
  7. 0 31
      src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java
  8. 13 15
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  9. 70 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
  10. 1 2
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
  11. 140 0
      src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java
  12. 0 101
      src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java
  13. 80 31
      src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
  14. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
  15. 4 6
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  16. 89 122
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  17. 1 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
  18. 1 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
  19. 1 4
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
  20. 13 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
  21. 14 10
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  22. 5 3
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
  23. 3 6
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
  24. 21 24
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  25. 39 41
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
  26. 20 14
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
  27. 0 92
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java
  28. 41 13
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
  29. 20 10
      src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
  30. 2 3
      src/main/java/com/genersoft/iot/vmp/service/IMediaService.java
  31. 4 5
      src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
  32. 1 2
      src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java
  33. 2 2
      src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
  34. 38 0
      src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java
  35. 221 164
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
  36. 3 5
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
  37. 43 22
      src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
  38. 4 5
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
  39. 2 3
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
  40. 0 2
      src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
  41. 0 1
      src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
  42. 5 5
      src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
  43. 0 3
      src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
  44. 8 0
      src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java
  45. 31 0
      src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java
  46. 97 0
      src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java
  47. 3 3
      src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
  48. 12 13
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
  49. 10 4
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java
  50. 18 4
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
  51. 23 0
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java
  52. 16 8
      src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
  53. 1 3
      src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
  54. 7 0
      src/main/resources/all-application.yml
  55. 2 0
      src/main/resources/application-dev.yml
  56. 二進制
      src/main/resources/wvp.sqlite
  57. 1 1
      web_src/src/components/service/MediaServer.js

+ 7 - 0
pom.xml

@@ -46,6 +46,7 @@
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<maven.build.timestamp.format>MMddHHmm</maven.build.timestamp.format>
 		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
+		<jedis-version>3.1.0</jedis-version>
 
 		<!-- 依赖版本 -->
 		<pagehelper.version>5.2.0</pagehelper.version>
@@ -80,6 +81,12 @@
 			<artifactId>spring-boot-starter-security</artifactId>
 		</dependency>
 
+		<dependency>
+			<groupId>redis.clients</groupId>
+			<artifactId>jedis</artifactId>
+			<version>${jedis-version}</version>
+		</dependency>
+
 		<!-- druid数据库连接池 -->
 		<dependency>
 			<groupId>com.alibaba</groupId>

+ 107 - 97
sql/mysql.sql

@@ -1,112 +1,145 @@
 -- auto-generated definition
-create schema wvp collate utf8_bin;
+
+
+CREATE DATABASE `wvp` /*!40100 DEFAULT CHARACTER SET utf8mb3 COLLATE utf8mb3_bin */;
+
+use wvp;
 
 create table device
 (
-    deviceId           varchar(50) not null
+    deviceId      varchar(50)  not null
         primary key,
-    name               varchar(255) null,
-    manufacturer       varchar(255) null,
-    model              varchar(255) null,
-    firmware           varchar(255) null,
-    transport          varchar(50) null,
-    streamMode         varchar(50) null,
-    online             varchar(50) null,
-    registerTime       varchar(50) null,
-    keepaliveTime      varchar(50) null,
-    ip                 varchar(50) not null,
-    createTime         varchar(50) not null,
-    updateTime         varchar(50) not null,
-    port               int          not null,
-    expires            int          not null,
-    hostAddress        varchar(50) not null
+    name          varchar(255) null,
+    manufacturer  varchar(255) null,
+    model         varchar(255) null,
+    firmware      varchar(255) null,
+    transport     varchar(50)  null,
+    streamMode    varchar(50)  null,
+    online        varchar(50)  null,
+    registerTime  varchar(50)  null,
+    keepaliveTime varchar(50)  null,
+    ip            varchar(50)  not null,
+    createTime    varchar(50)  not null,
+    updateTime    varchar(50)  not null,
+    port          int          not null,
+    expires       int          not null,
+    hostAddress   varchar(50)  not null
 );
 
 create table device_channel
 (
-    channelId   varchar(50) not null,
+    channelId   varchar(50)  not null,
     name        varchar(255) null,
-    manufacture varchar(50) null,
-    model       varchar(50) null,
-    owner       varchar(50) null,
-    civilCode   varchar(50) null,
-    block       varchar(50) null,
-    address     varchar(50) null,
-    parentId    varchar(50) null,
+    manufacture varchar(50)  null,
+    model       varchar(50)  null,
+    owner       varchar(50)  null,
+    civilCode   varchar(50)  null,
+    block       varchar(50)  null,
+    address     varchar(50)  null,
+    parentId    varchar(50)  null,
     safetyWay   int          null,
     registerWay int          null,
-    certNum     varchar(50) null,
+    certNum     varchar(50)  null,
     certifiable int          null,
     errCode     int          null,
-    endTime     varchar(50) null,
-    secrecy     varchar(50) null,
-    ipAddress   varchar(50) null,
+    endTime     varchar(50)  null,
+    secrecy     varchar(50)  null,
+    ipAddress   varchar(50)  null,
     port        int          null,
     password    varchar(255) null,
     PTZType     int          null,
     status      int          null,
     longitude   double       null,
     latitude    double       null,
-    streamId    varchar(50) null,
-    deviceId    varchar(50) not null,
-    parental    varchar(50) null,
-    hasAudio    bit(1)   null,
-    createTime  varchar(50) not null,
-    updateTime  varchar(50) not null,
+    streamId    varchar(50)  null,
+    deviceId    varchar(50)  not null,
+    parental    varchar(50)  null,
+    hasAudio    bit          null,
+    createTime  varchar(50)  not null,
+    updateTime  varchar(50)  not null,
     primary key (channelId, deviceId)
 );
 
 create table device_mobile_position
 (
-    deviceId       varchar(50) not null,
+    deviceId       varchar(50)  not null,
     deviceName     varchar(255) null,
-    time           varchar(50) not null,
+    time           varchar(50)  not null,
     longitude      double       not null,
     latitude       double       not null,
     altitude       double       null,
     speed          double       null,
     direction      double       null,
-    reportSource   varchar(50) null,
-    geodeticSystem varchar(50) null,
-    cnLng          varchar(50) null,
-    cnLat          varchar(50) null,
+    reportSource   varchar(50)  null,
+    geodeticSystem varchar(50)  null,
+    cnLng          varchar(50)  null,
+    cnLat          varchar(50)  null,
     primary key (deviceId, time)
 );
 
 create table gb_stream
 (
-    app        varchar(255) not null,
-    stream     varchar(255) not null,
-    gbId       varchar(50) not null,
-    name       varchar(255) null,
-    longitude  double       null,
-    latitude   double       null,
-    streamType varchar(50) null,
-    status     int          null,
+    app           varchar(255) not null,
+    stream        varchar(255) not null,
+    gbId          varchar(50)  not null,
+    name          varchar(255) null,
+    longitude     double       null,
+    latitude      double       null,
+    streamType    varchar(50)  null,
+    mediaServerId varchar(50)  null,
+    status        int          null,
     primary key (app, stream, gbId)
 );
 
+create table media_server
+(
+    id                      varchar(255) not null
+        primary key,
+    ip                      varchar(50)  not null,
+    hookIp                  varchar(50)  not null,
+    sdpIp                   varchar(50)  not null,
+    streamIp                varchar(50)  not null,
+    httpPort                int          not null,
+    httpSSlPort             int          not null,
+    rtmpPort                int          not null,
+    rtmpSSlPort             int          not null,
+    rtpProxyPort            int          not null,
+    rtspPort                int          not null,
+    rtspSSLPort             int          not null,
+    autoConfig              int          not null,
+    secret                  varchar(50)  not null,
+    streamNoneReaderDelayMS int          not null,
+    rtpEnable               int          not null,
+    rtpPortRange            varchar(50)  not null,
+    recordAssistPort        int          not null,
+    defaultServer           int          not null,
+    createTime              varchar(50)  not null,
+    updateTime              varchar(50)  not null,
+    constraint media_server_i
+        unique (ip, httpPort)
+);
+
 create table parent_platform
 (
-    id          int auto_increment,
+    id             int auto_increment,
     enable         int          null,
     name           varchar(255) null,
-    serverGBId     varchar(50) not null,
-    serverGBDomain varchar(50) null,
-    serverIP       varchar(50) null,
+    serverGBId     varchar(50)  not null,
+    serverGBDomain varchar(50)  null,
+    serverIP       varchar(50)  null,
     serverPort     int          null,
-    deviceGBId     varchar(50) not null,
-    deviceIp       varchar(50) null,
-    devicePort     varchar(50) null,
+    deviceGBId     varchar(50)  not null,
+    deviceIp       varchar(50)  null,
+    devicePort     varchar(50)  null,
     username       varchar(255) null,
-    password       varchar(50) null,
-    expires        varchar(50) null,
-    keepTimeout    varchar(50) null,
-    transport      varchar(50) null,
-    characterSet   varchar(50) null,
+    password       varchar(50)  null,
+    expires        varchar(50)  null,
+    keepTimeout    varchar(50)  null,
+    transport      varchar(50)  null,
+    characterSet   varchar(50)  null,
     ptz            int          null,
     rtcp           int          null,
-    status         bit(1)   null,
+    status         bit          null,
     primary key (id, serverGBId)
 );
 
@@ -121,7 +154,7 @@ create table platform_gb_channel
 
 create table platform_gb_stream
 (
-    platformId varchar(50) not null,
+    platformId varchar(50)  not null,
     app        varchar(255) not null,
     stream     varchar(255) not null,
     primary key (platformId, app, stream)
@@ -129,7 +162,7 @@ create table platform_gb_stream
 
 create table stream_proxy
 (
-    type           varchar(50) not null,
+    type           varchar(50)  not null,
     app            varchar(255) not null,
     stream         varchar(255) not null,
     url            varchar(255) null,
@@ -137,11 +170,12 @@ create table stream_proxy
     dst_url        varchar(255) null,
     timeout_ms     int          null,
     ffmpeg_cmd_key varchar(255) null,
-    rtp_type       varchar(50) null,
-    mediaServerId       varchar(50) null,
-    enable_hls     bit(1)   null,
-    enable_mp4     bit(1)   null,
-    enable         bit(1)   not null,
+    rtp_type       varchar(50)  null,
+    mediaServerId  varchar(50)  null,
+    enable_hls     bit          null,
+    enable_mp4     bit          null,
+    enable         bit          not null,
+    createTime     varchar(50)  not null,
     primary key (app, stream)
 );
 
@@ -149,11 +183,12 @@ create table stream_push
 (
     app              varchar(255) not null,
     stream           varchar(255) not null,
-    totalReaderCount varchar(50) null,
+    totalReaderCount varchar(50)  null,
     originType       int          null,
-    originTypeStr    varchar(50) null,
+    originTypeStr    varchar(50)  null,
     createStamp      int          null,
     aliveSecond      int          null,
+    mediaServerId    varchar(50)  null,
     primary key (app, stream)
 );
 
@@ -164,31 +199,6 @@ create table user
     username    varchar(255) not null,
     password    varchar(255) not null,
     roleId      int          not null,
-    create_time varchar(50) not null
+    create_time varchar(50)  not null
 );
 
-insert into user (username, password, roleId, create_time) values ('admin', '21232f297a57a5a743894a0e4a801fc3', '0', '2021-04-13 14:14:57');
-
-create table media_server (
-      id          varchar(255)
-          primary key,
-      ip varchar(50) NOT NULL,
-      hookIp varchar(50) NOT NULL,
-      sdpIp varchar(50) NOT NULL,
-      streamIp varchar(50) NOT NULL,
-      httpPort int NOT NULL,
-      httpSSlPort int NOT NULL,
-      rtmpPort int NOT NULL,
-      rtmpSSlPort int NOT NULL,
-      rtpProxyPort int NOT NULL,
-      rtspPort int NOT NULL,
-      rtspSSLPort int NOT NULL,
-      autoConfig int NOT NULL,
-      secret varchar(50) NOT NULL,
-      streamNoneReaderDelayMS int NOT NULL,
-      rtpEnable int NOT NULL,
-      rtpPortRange varchar(50) NOT NULL,
-      recordAssistPort int NOT NULL,
-      createTime  varchar(50) not null,
-      updateTime  varchar(50) not null
-);

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

@@ -12,6 +12,8 @@ public class VideoManagerConstants {
 
 	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 MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM";
 
 	public static final String DEVICE_PREFIX = "VMP_DEVICE_";
@@ -45,4 +47,8 @@ public class VideoManagerConstants {
 	public static final String EVENT_OUTLINE_UNREGISTER = "1";
 	
 	public static final String EVENT_OUTLINE_TIMEOUT = "2";
+
+	public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
+
+	public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_";
 }

+ 15 - 173
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java

@@ -1,13 +1,16 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
+import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.StringUtils;
 
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
 @Configuration("mediaConfig")
-public class MediaConfig implements IMediaServerItem {
+public class MediaConfig{
 
     @Value("${media.id:}")
     private String id;
@@ -21,6 +24,9 @@ public class MediaConfig implements IMediaServerItem {
     @Value("${sip.ip}")
     private String sipIp;
 
+    @Value("${sip.domain}")
+    private String sipDomain;
+
     @Value("${media.sdp-ip:${media.ip}}")
     private String sdpIp;
 
@@ -66,31 +72,14 @@ public class MediaConfig implements IMediaServerItem {
     @Value("${media.record-assist-port:0}")
     private Integer recordAssistPort = 0;
 
-    private String updateTime;
-
-    private String createTime;
-
-    private boolean docker = false;
-
-    private int count;
-
-
     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() {
         if (StringUtils.isEmpty(hookIp)){
             return sipIp;
@@ -100,78 +89,26 @@ public class MediaConfig implements IMediaServerItem {
 
     }
 
-    public void setHookIp(String hookIp) {
-        this.hookIp = hookIp;
-    }
-
     public String getSipIp() {
         return sipIp;
     }
 
-    public void setSipIp(String sipIp) {
-        this.sipIp = sipIp;
-    }
-
-    public void setSdpIp(String sdpIp) {
-        this.sdpIp = sdpIp;
-    }
-
-    public void setStreamIp(String streamIp) {
-        this.streamIp = streamIp;
-    }
-
     public int getHttpPort() {
         return httpPort;
     }
 
-    @Override
-    public void setHttpPort(int httpPort) {
-
-    }
-
-    public void setHttpPort(Integer httpPort) {
-        this.httpPort = httpPort;
-    }
-
     public int getHttpSSlPort() {
         return httpSSlPort;
     }
 
-    @Override
-    public void setHttpSSlPort(int httpSSlPort) {
-
-    }
-
-    public void setHttpSSlPort(Integer httpSSlPort) {
-        this.httpSSlPort = httpSSlPort;
-    }
-
     public int getRtmpPort() {
         return rtmpPort;
     }
-
-    @Override
-    public void setRtmpPort(int rtmpPort) {
-
-    }
-
-    public void setRtmpPort(Integer rtmpPort) {
-        this.rtmpPort = rtmpPort;
-    }
-
+    
     public int getRtmpSSlPort() {
         return rtmpSSlPort;
     }
 
-    @Override
-    public void setRtmpSSlPort(int rtmpSSlPort) {
-
-    }
-
-    public void setRtmpSSlPort(Integer rtmpSSlPort) {
-        this.rtmpSSlPort = rtmpSSlPort;
-    }
-
     public int getRtpProxyPort() {
         if (rtpProxyPort == null) {
             return 0;
@@ -181,104 +118,38 @@ public class MediaConfig implements IMediaServerItem {
 
     }
 
-    @Override
-    public void setRtpProxyPort(int rtpProxyPort) {
-
-    }
-
-    public void setRtpProxyPort(Integer rtpProxyPort) {
-        this.rtpProxyPort = rtpProxyPort;
-    }
-
     public int getRtspPort() {
         return rtspPort;
     }
 
-    @Override
-    public void setRtspPort(int rtspPort) {
-
-    }
-
-    public void setRtspPort(Integer rtspPort) {
-        this.rtspPort = rtspPort;
-    }
-
     public int getRtspSSLPort() {
         return rtspSSLPort;
     }
 
-    @Override
-    public void setRtspSSLPort(int rtspSSLPort) {
-
-    }
-
-    public void setRtspSSLPort(Integer rtspSSLPort) {
-        this.rtspSSLPort = rtspSSLPort;
-    }
-
     public boolean isAutoConfig() {
         return autoConfig;
     }
 
-    public void setAutoConfig(boolean autoConfig) {
-        this.autoConfig = autoConfig;
-    }
-
     public String getSecret() {
         return secret;
     }
 
-    public void setSecret(String secret) {
-        this.secret = secret;
-    }
-
     public String getStreamNoneReaderDelayMS() {
         return streamNoneReaderDelayMS;
     }
 
-    public void setStreamNoneReaderDelayMS(String streamNoneReaderDelayMS) {
-        this.streamNoneReaderDelayMS = streamNoneReaderDelayMS;
-    }
-
     public boolean isRtpEnable() {
         return rtpEnable;
     }
 
-    public void setRtpEnable(boolean rtpEnable) {
-        this.rtpEnable = rtpEnable;
-    }
-
     public String getRtpPortRange() {
         return rtpPortRange;
     }
-
-    public void setRtpPortRange(String rtpPortRange) {
-        this.rtpPortRange = rtpPortRange;
-    }
-
+    
     public int getRecordAssistPort() {
         return recordAssistPort;
     }
 
-    @Override
-    public void setRecordAssistPort(int recordAssistPort) {
-
-    }
-
-    public void setRecordAssistPort(Integer recordAssistPort) {
-        this.recordAssistPort = recordAssistPort;
-    }
-
-    @Override
-    public boolean isDocker() {
-        return docker;
-    }
-
-    @Override
-    public void setDocker(boolean docker) {
-        this.docker = docker;
-    }
-
     public String getSdpIp() {
         if (StringUtils.isEmpty(sdpIp)){
             return ip;
@@ -295,13 +166,11 @@ public class MediaConfig implements IMediaServerItem {
         }
     }
 
-
-
     public MediaServerItem getMediaSerItem(){
         MediaServerItem mediaServerItem = new MediaServerItem();
         mediaServerItem.setId(id);
         mediaServerItem.setIp(ip);
-        mediaServerItem.setDocker(true);
+        mediaServerItem.setDefaultServer(true);
         mediaServerItem.setHookIp(hookIp);
         mediaServerItem.setSdpIp(sdpIp);
         mediaServerItem.setStreamIp(streamIp);
@@ -318,39 +187,12 @@ public class MediaConfig implements IMediaServerItem {
         mediaServerItem.setRtpEnable(rtpEnable);
         mediaServerItem.setRtpPortRange(rtpPortRange);
         mediaServerItem.setRecordAssistPort(recordAssistPort);
-        mediaServerItem.setCreateTime(createTime);
-        mediaServerItem.setUpdateTime(updateTime);
-        mediaServerItem.setCount(count);
-        return mediaServerItem;
-    }
-
-    @Override
-    public String getUpdateTime() {
-        return updateTime;
-    }
 
-    @Override
-    public void setUpdateTime(String updateTime) {
-        this.updateTime = updateTime;
-    }
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        mediaServerItem.setCreateTime(format.format(new Date(System.currentTimeMillis())));
+        mediaServerItem.setUpdateTime(format.format(new Date(System.currentTimeMillis())));
 
-    @Override
-    public String getCreateTime() {
-        return createTime;
-    }
-
-    @Override
-    public void setCreateTime(String createTime) {
-        this.createTime = createTime;
-    }
-
-    @Override
-    public int getCount() {
-        return count;
+        return mediaServerItem;
     }
 
-    @Override
-    public void setCount(int count) {
-        this.count = count;
-    }
 }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import org.apache.catalina.connector.ClientAbortException;
 import org.apache.http.HttpHost;
@@ -49,7 +49,7 @@ public class ProxyServletConfig {
         @Override
         protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
             String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
-            IMediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
+            MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
             if (mediaInfo != null) {
                 if (!StringUtils.isEmpty(queryStr)) {
                     queryStr += "&secret=" + mediaInfo.getSecret();
@@ -88,7 +88,7 @@ public class ProxyServletConfig {
         @Override
         protected String getTargetUri(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
 
             String uri = null;
             if (mediaInfo != null) {
@@ -106,7 +106,7 @@ public class ProxyServletConfig {
         @Override
         protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
             HttpHost host;
             if (mediaInfo != null) {
                 host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
@@ -120,7 +120,7 @@ public class ProxyServletConfig {
         /**
          * 根据uri获取流媒体信息
          */
-        IMediaServerItem getMediaInfoByUri(String uri){
+        MediaServerItem getMediaInfoByUri(String uri){
             String[] split = uri.split("/");
             String mediaServerId = split[2];
             return mediaServerService.getOne(mediaServerId);
@@ -132,7 +132,7 @@ public class ProxyServletConfig {
         @Override
         protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
             String url = super.rewriteUrlFromRequest(servletRequest);
             if (mediaInfo == null) {
                 return  url;
@@ -186,7 +186,7 @@ public class ProxyServletConfig {
         @Override
         protected String getTargetUri(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
 
             String uri = null;
             if (mediaInfo != null) {
@@ -204,7 +204,7 @@ public class ProxyServletConfig {
         @Override
         protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
             HttpHost host;
             if (mediaInfo != null) {
                 host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
@@ -218,7 +218,7 @@ public class ProxyServletConfig {
         /**
          * 根据uri获取流媒体信息
          */
-        IMediaServerItem getMediaInfoByUri(String uri){
+        MediaServerItem getMediaInfoByUri(String uri){
             String[] split = uri.split("/");
             String mediaServerId = split[2];
             return mediaServerService.getOne(mediaServerId);
@@ -230,7 +230,7 @@ public class ProxyServletConfig {
         @Override
         protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
             String requestURI = servletRequest.getRequestURI();
-            IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
             String url = super.rewriteUrlFromRequest(servletRequest);
             if (mediaInfo == null) {
                 return  url;

+ 36 - 1
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java

@@ -1,5 +1,7 @@
 package com.genersoft.iot.vmp.conf;
 
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cache.annotation.CachingConfigurerSupport;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -10,6 +12,8 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
 
 import com.alibaba.fastjson.parser.ParserConfig;
 import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
 
 /**
  * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置
@@ -20,6 +24,37 @@ import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
 @Configuration
 public class RedisConfig extends CachingConfigurerSupport {
 
+	@Value("${spring.redis.host}")
+	private String host;
+	@Value("${spring.redis.port}")
+	private int port;
+	@Value("${spring.redis.database}")
+	private int database;
+	@Value("${spring.redis.password}")
+	private String password;
+	@Value("${spring.redis.timeout}")
+	private int timeout;
+	@Value("${spring.redis.poolMaxTotal:1000}")
+	private int poolMaxTotal;
+	@Value("${spring.redis.poolMaxIdle:500}")
+	private int poolMaxIdle;
+	@Value("${spring.redis.poolMaxWait:5}")
+	private int poolMaxWait;
+
+	@Bean
+	public JedisPool jedisPool() {
+		if (StringUtils.isBlank(password)) {
+			password = null;
+		}
+		JedisPoolConfig poolConfig = new JedisPoolConfig();
+		poolConfig.setMaxIdle(poolMaxIdle);
+		poolConfig.setMaxTotal(poolMaxTotal);
+		// 秒转毫秒
+		poolConfig.setMaxWaitMillis(poolMaxWait * 1000L);
+		JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database);
+		return jp;
+	}
+
 	@Bean("redisTemplate")
 	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
 		RedisTemplate<Object, Object> template = new RedisTemplate<>();
@@ -34,7 +69,7 @@ public class RedisConfig extends CachingConfigurerSupport {
 		template.setHashKeySerializer(new StringRedisSerializer());
 		template.setConnectionFactory(redisConnectionFactory);
 		// 使用fastjson时需设置此项,否则会报异常not support type
-		ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 
+		ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
 		return template;
 	}
 

+ 0 - 31
src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java

@@ -1,31 +0,0 @@
-package com.genersoft.iot.vmp.conf;
-
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-
-
-/**
- * 系统启动时控制设备离线
- */
-@Component
-@Order(value=4)
-public class SipDeviceRunner implements CommandLineRunner {
-
-    @Autowired
-    private IVideoManagerStorager storager;
-
-    @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
-    @Override
-    public void run(String... args) throws Exception {
-        // 设置所有设备离线
-        storager.outlineForAll();
-        // 设置所有设备离线
-        redisCatchStorage.outlineForAll();
-    }
-}

+ 13 - 15
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@@ -12,6 +12,7 @@ import javax.sip.header.CallIdHeader;
 import javax.sip.message.Response;
 
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import gov.nist.javax.sip.SipProviderImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -39,7 +40,7 @@ public class SipLayer implements SipListener {
 	@Autowired
 	private SipSubscribe sipSubscribe;
 
-	private SipStack sipStack;
+	private SipStackImpl sipStack;
 
 	private SipFactory sipFactory;
 
@@ -52,7 +53,7 @@ public class SipLayer implements SipListener {
 	private ThreadPoolExecutor initSipServer() {
 		
 		int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;
-		LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<Runnable>(10000);
+		LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);
 		processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum,
 				0L,TimeUnit.MILLISECONDS,processQueue,
 				new ThreadPoolExecutor.CallerRunsPolicy());
@@ -88,17 +89,14 @@ public class SipLayer implements SipListener {
 
 	@Bean("tcpSipProvider")
 	@DependsOn("sipStack")
-	private SipProvider startTcpListener() {
+	private SipProviderImpl startTcpListener() {
 		ListeningPoint tcpListeningPoint = null;
-		SipProvider tcpSipProvider  = null;
+		SipProviderImpl tcpSipProvider  = null;
 		try {
 			tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "TCP");
-			tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
+			tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
 			tcpSipProvider.addSipListener(this);
 			logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getSipPort() + "}");
-//		} catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
-//			logger.error(String.format("创建SIP服务失败: %s", e.getMessage()));
-//		}
 		} catch (TransportNotSupportedException e) {
 			e.printStackTrace();
 		} catch (InvalidArgumentException e) {
@@ -114,13 +112,14 @@ public class SipLayer implements SipListener {
 	
 	@Bean("udpSipProvider")
 	@DependsOn("sipStack")
-	private SipProvider startUdpListener() {
+	private SipProviderImpl startUdpListener() {
 		ListeningPoint udpListeningPoint = null;
-		SipProvider udpSipProvider = null;
+		SipProviderImpl udpSipProvider = null;
 		try {
 			udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP");
-			udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
+			udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
 			udpSipProvider.addSipListener(this);
+//			udpSipProvider.setAutomaticDialogSupportEnabled(false);
 		} catch (TransportNotSupportedException e) {
 			e.printStackTrace();
 		} catch (InvalidArgumentException e) {
@@ -141,7 +140,7 @@ public class SipLayer implements SipListener {
 	 */
 	@Override
 	public void processRequest(RequestEvent evt) {
-//		logger.debug(evt.getRequest().toString());
+		logger.debug(evt.getRequest().toString());
 		// 由于jainsip是单线程程序,为提高性能并发处理
 		processThreadPool.execute(() -> {
 			if (processorFactory != null) {
@@ -153,7 +152,7 @@ public class SipLayer implements SipListener {
 	@Override
 	public void processResponse(ResponseEvent evt) {
 		Response response = evt.getResponse();
-//		logger.debug(evt.getResponse().toString());
+		logger.debug(evt.getResponse().toString());
 		int status = response.getStatusCode();
 		if (((status >= 200) && (status < 300)) || status == 401) { // Success!
 			ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
@@ -163,6 +162,7 @@ public class SipLayer implements SipListener {
 				// TODO Auto-generated catch block
 				e.printStackTrace();
 			}
+
 			if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
 				CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
 				if (callIdHeader != null) {
@@ -220,7 +220,6 @@ public class SipLayer implements SipListener {
 	@Override
 	public void processIOException(IOExceptionEvent exceptionEvent) {
 		// TODO Auto-generated method stub
-
 	}
 
 	/**
@@ -236,7 +235,6 @@ public class SipLayer implements SipListener {
 	@Override
 	public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
 		// TODO Auto-generated method stub
-
 	}
 
 	/**

+ 70 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java

@@ -0,0 +1,70 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import javax.sip.message.Request;
+
+public class SsrcTransaction {
+
+    private String deviceId;
+    private String channelId;
+    private String ssrc;
+    private String streamId;
+    private byte[] transaction;
+    private byte[] dialog;
+    private String mediaServerId;
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public String getStreamId() {
+        return streamId;
+    }
+
+    public void setStreamId(String streamId) {
+        this.streamId = streamId;
+    }
+
+    public byte[] getTransaction() {
+        return transaction;
+    }
+
+    public void setTransaction(byte[] transaction) {
+        this.transaction = transaction;
+    }
+
+    public byte[] getDialog() {
+        return dialog;
+    }
+
+    public void setDialog(byte[] dialog) {
+        this.dialog = dialog;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+}

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java

@@ -6,7 +6,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -77,7 +76,7 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
                 }
                 stream.append(sendRtpItem.getStreamId());
                 redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId());
-                IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
                 Map<String, Object> param = new HashMap<>();
                 param.put("vhost", "__defaultVhost__");
                 param.put("app", app.toString());

+ 140 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java

@@ -0,0 +1,140 @@
+package com.genersoft.iot.vmp.gb28181.session;
+
+import com.genersoft.iot.vmp.utils.ConfigConst;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+public class SsrcConfig {
+
+    /**
+     * zlm流媒体服务器Id
+     */
+    private String mediaServerId;
+
+    private String ssrcPrefix;
+    /**
+     * zlm流媒体服务器已用会话句柄
+     */
+    private List<String> isUsed;
+    /**
+     * zlm流媒体服务器可用会话句柄
+     */
+    private List<String> notUsed;
+
+    public SsrcConfig() {
+    }
+
+    public SsrcConfig(String mediaServerId, Set<String> usedSet, String sipDomain) {
+        this.mediaServerId = mediaServerId;
+        this.isUsed = new ArrayList<>();
+        this.ssrcPrefix = sipDomain.substring(3, 8);
+        this.notUsed = new ArrayList<>();
+        for (int i = 1; i < ConfigConst.MAX_STRTEAM_COUNT; i++) {
+            String ssrc;
+            if (i < 10) {
+                ssrc = "000" + i;
+            } else if (i < 100) {
+                ssrc = "00" + i;
+            } else if (i < 1000) {
+                ssrc = "0" + i;
+            } else {
+                ssrc = String.valueOf(i);
+            }
+            if (null == usedSet || !usedSet.contains(ssrc)) {
+                this.notUsed.add(ssrc);
+            } else {
+                this.isUsed.add(ssrc);
+            }
+        }
+    }
+
+
+    /**
+     * 获取视频预览的SSRC值,第一位固定为0
+     * @return ssrc
+     */
+    public String getPlaySsrc() {
+        return "0" + getSsrcPrefix() + getSN();
+    }
+
+    /**
+     * 获取录像回放的SSRC值,第一位固定为1
+     *
+     */
+    public String getPlayBackSsrc() {
+        return "1" + getSsrcPrefix() + getSN();
+    }
+
+    /**
+     * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
+     * @param ssrc 需要重置的ssrc
+     */
+    public void releaseSsrc(String ssrc) {
+        if (ssrc == null) {
+            return;
+        }
+        String sn = ssrc.substring(6);
+        try {
+            isUsed.remove(sn);
+            notUsed.add(sn);
+        }catch (NullPointerException e){
+            System.out.printf("11111");
+        }
+    }
+
+    /**
+     * 获取后四位数SN,随机数
+     *
+     */
+    private String getSN() {
+        String sn = null;
+        int index = 0;
+        if (notUsed.size() == 0) {
+            throw new RuntimeException("ssrc已经用完");
+        } else if (notUsed.size() == 1) {
+            sn = notUsed.get(0);
+        } else {
+            index = new Random().nextInt(notUsed.size() - 1);
+            sn = notUsed.get(index);
+        }
+        notUsed.remove(index);
+        isUsed.add(sn);
+        return sn;
+    }
+
+    public String getSsrcPrefix() {
+        return ssrcPrefix;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public void setSsrcPrefix(String ssrcPrefix) {
+        this.ssrcPrefix = ssrcPrefix;
+    }
+
+    public List<String> getIsUsed() {
+        return isUsed;
+    }
+
+    public void setIsUsed(List<String> isUsed) {
+        this.isUsed = isUsed;
+    }
+
+    public List<String> getNotUsed() {
+        return notUsed;
+    }
+
+    public void setNotUsed(List<String> notUsed) {
+        this.notUsed = notUsed;
+    }
+
+}

+ 0 - 101
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java

@@ -1,101 +0,0 @@
-package com.genersoft.iot.vmp.gb28181.session;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.utils.SpringBeanFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @Description:SIP信令中的SSRC工具类。SSRC值由10位十进制整数组成的字符串,第一位为0代表实况,为1则代表回放;第二位至第六位由监控域ID的第4位到第8位组成;最后4位为不重复的4个整数
- * @author: swwheihei
- * @date: 2020年5月10日 上午11:57:57
- */
-public class SsrcUtil {
-
-	private final static Logger logger = LoggerFactory.getLogger(SsrcUtil.class);
-
-	private static String ssrcPrefix;
-
-	private static List<String> isUsed;
-
-	private static List<String> notUsed;
-
-	private static void init() {
-		SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig");
-		ssrcPrefix = sipConfig.getSipDomain().substring(3, 8);
-		isUsed = new ArrayList<String>();
-		notUsed = new ArrayList<String>();
-		for (int i = 1; i < 10000; i++) {
-			if (i < 10) {
-				notUsed.add("000" + i);
-			} else if (i < 100) {
-				notUsed.add("00" + i);
-			} else if (i < 1000) {
-				notUsed.add("0" + i);
-			} else {
-				notUsed.add(String.valueOf(i));
-			}
-		}
-	}
-
-	/**
-	 * 获取视频预览的SSRC值,第一位固定为0
-	 * 
-	 */
-	public static String getPlaySsrc() {
-		return "0" + getSsrcPrefix() + getSN();
-	}
-
-	/**
-	 * 获取录像回放的SSRC值,第一位固定为1
-	 * 
-	 */
-	public static String getPlayBackSsrc() {
-		return "1" + getSsrcPrefix() + getSN();
-	}
-
-	/**
-	 * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
-	 * 
-	 */
-	public static void releaseSsrc(String ssrc) {
-		if (ssrc == null) {
-			logger.error("要释放ssrc为null");
-			return;
-		}
-		String sn = ssrc.substring(6);
-		isUsed.remove(sn);
-		notUsed.add(sn);
-	}
-
-	/**
-	 * 获取后四位数SN,随机数
-	 * 
-	 */
-	private static String getSN() {
-		String sn = null;
-		int index = 0;
-		if (notUsed.size() == 0) {
-			throw new RuntimeException("ssrc已经用完");
-		} else if (notUsed.size() == 1) {
-			sn = notUsed.get(0);
-		} else {
-			index = new Random().nextInt(notUsed.size() - 1);
-			sn = notUsed.get(index);
-		}
-		notUsed.remove(index);
-		isUsed.add(sn);
-		return sn;
-	}
-
-	private static String getSsrcPrefix() {
-		if (ssrcPrefix == null) {
-			init();
-		}
-		return ssrcPrefix;
-	}
-}

+ 80 - 31
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java

@@ -1,9 +1,23 @@
 package com.genersoft.iot.vmp.gb28181.session;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.sip.ClientTransaction;
+import javax.sip.Dialog;
+import javax.sip.message.Request;
 
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.utils.SerializeUtils;
+import com.genersoft.iot.vmp.utils.redis.JedisUtil;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import gov.nist.javax.sip.stack.SIPDialog;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
 /**    
@@ -14,50 +28,85 @@ import org.springframework.stereotype.Component;
 @Component
 public class VideoStreamSessionManager {
 
-	private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
-	private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>();
-	private ConcurrentHashMap<String, String> streamIdMap = new ConcurrentHashMap<>();
+	@Autowired
+	private RedisUtil redisUtil;
 
-	public String createPlaySsrc(){
-		return SsrcUtil.getPlaySsrc();
-	}
-	
-	public String createPlayBackSsrc(){
-		return SsrcUtil.getPlayBackSsrc();
+	public void put(String deviceId, String channelId ,String ssrc, String streamId, String mediaServerId, ClientTransaction transaction){
+		SsrcTransaction ssrcTransaction = new SsrcTransaction();
+		ssrcTransaction.setDeviceId(deviceId);
+		ssrcTransaction.setChannelId(channelId);
+		ssrcTransaction.setStreamId(streamId);
+		byte[] transactionByteArray = SerializeUtils.serialize(transaction);
+		ssrcTransaction.setTransaction(transactionByteArray);
+		ssrcTransaction.setSsrc(ssrc);
+		ssrcTransaction.setMediaServerId(mediaServerId);
+
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX +  deviceId + "_" + channelId, ssrcTransaction);
 	}
-	
-	public void put(String deviceId, String channelId ,String ssrc, String streamId, ClientTransaction transaction){
-		sessionMap.put(deviceId + "_" + channelId, transaction);
-		ssrcMap.put(deviceId + "_" + channelId, ssrc);
-		streamIdMap.put(deviceId + "_" + channelId, streamId);
+
+	public void put(String deviceId, String channelId , Dialog dialog){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction != null) {
+			byte[] dialogByteArray = SerializeUtils.serialize(dialog);
+			ssrcTransaction.setDialog(dialogByteArray);
+		}
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX +  deviceId + "_" + channelId, ssrcTransaction);
 	}
+
 	
 	public ClientTransaction getTransaction(String deviceId, String channelId){
-		return sessionMap.get(deviceId + "_" + channelId);
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		byte[] transactionByteArray = ssrcTransaction.getTransaction();
+		ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray);
+		return clientTransaction;
 	}
 
-	public String getStreamId(String deviceId, String channelId){
-		return streamIdMap.get(deviceId + "_" + channelId);
+	public SIPDialog getDialog(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		byte[] dialogByteArray = ssrcTransaction.getDialog();
+		if (dialogByteArray == null) return null;
+		SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
+		return dialog;
 	}
-	
-	public void remove(String deviceId, String channelId) {
-		sessionMap.remove(deviceId + "_" + channelId);
-		if (ssrcMap.get(deviceId + "_" + channelId) != null) {
-			SsrcUtil.releaseSsrc(ssrcMap.get(deviceId + "_" + channelId));
-		}
-		ssrcMap.remove(deviceId + "_" + channelId);
-		streamIdMap.remove(deviceId + "_" + channelId);
+
+	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId);
+		return ssrcTransaction;
 	}
 
-	public ConcurrentHashMap<String, ClientTransaction> getSessionMap() {
-		return sessionMap;
+	public String getStreamId(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getStreamId();
+	}
+	public String getMediaServerId(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getMediaServerId();
 	}
 
-	public ConcurrentHashMap<String, String> getSsrcMap() {
-		return ssrcMap;
+	public String getSSRC(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getSsrc();
+	}
+	
+	public void remove(String deviceId, String channelId) {
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return;
+		redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX +  deviceId + "_" + channelId);
 	}
 
-	public ConcurrentHashMap<String, String> getStreamIdMap() {
-		return streamIdMap;
+	public List<SsrcTransaction> getAllSsrc() {
+		List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX));
+		List<SsrcTransaction> result= new ArrayList<>();
+		for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
+			String key = (String)ssrcTransactionKeys.get(i);
+			SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(key);
+			result.add(ssrcTransaction);
+		}
+		return result;
 	}
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java

@@ -7,6 +7,7 @@ import javax.sip.header.CSeqHeader;
 import javax.sip.message.Request;
 import javax.sip.message.Response;
 
+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.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.service.IMediaServerService;
@@ -108,7 +109,6 @@ public class SIPProcessorFactory {
 	@Autowired
 	private IMediaServerService mediaServerService;
 
-
 	// 注:这里使用注解会导致循环依赖注入,暂用springBean
 	private SipProvider tcpSipProvider;
 		

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

@@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 
 /**    
  * @Description:设备能力接口,用于定义设备的控制、查询能力   
@@ -92,7 +92,7 @@ public interface ISIPCommander {
 	 * @param device  视频设备
 	 * @param channelId  预览通道
 	 */
-	void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
+	void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
 	
 	/**
 	 * 请求回放视频流
@@ -102,7 +102,7 @@ public interface ISIPCommander {
 	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 */
-	void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
+	void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
 
 	/**
 	 * 请求历史媒体下载
@@ -113,12 +113,10 @@ public interface ISIPCommander {
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 * @param downloadSpeed 下载倍速参数
 	 */ 
-	void downloadStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
+	void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
 
 	/**
 	 * 视频流停止
-	 * 
-	 * @param ssrc  ssrc
 	 */
 	void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
 	void streamByeCmd(String deviceId, String channelId);

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

@@ -1,6 +1,8 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
 
+import java.lang.reflect.Field;
 import java.text.ParseException;
+import java.util.HashSet;
 
 import javax.sip.*;
 import javax.sip.address.SipURI;
@@ -8,18 +10,21 @@ import javax.sip.header.CallIdHeader;
 import javax.sip.header.ViaHeader;
 import javax.sip.message.Request;
 
-import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.conf.MediaConfig;
 import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.media.zlm.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import gov.nist.javax.sip.SipProviderImpl;
+import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.stack.SIPDialog;
+import gov.nist.javax.sip.stack.SIPTransaction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -35,7 +40,6 @@ 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.DateUtil;
 import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
-import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import org.springframework.util.StringUtils;
 
 /**    
@@ -55,12 +59,12 @@ public class SIPCommander implements ISIPCommander {
 	@Lazy
 	@Autowired
 	@Qualifier(value="tcpSipProvider")
-	private SipProvider tcpSipProvider;
+	private SipProviderImpl tcpSipProvider;
 
 	@Lazy
 	@Autowired
 	@Qualifier(value="udpSipProvider")
-	private SipProvider udpSipProvider;
+	private SipProviderImpl udpSipProvider;
 
 	@Autowired
 	private SIPRequestHeaderProvider headerProvider;
@@ -74,9 +78,6 @@ public class SIPCommander implements ISIPCommander {
 	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 
-	@Autowired
-	private ZLMRTPServerFactory zlmrtpServerFactory;
-
 	@Autowired
 	private UserSetup userSetup;
 
@@ -86,6 +87,11 @@ public class SIPCommander implements ISIPCommander {
 	@Autowired
 	private SipSubscribe sipSubscribe;
 
+	@Autowired
+	private IMediaServerService mediaServerService;
+
+	private SIPDialog dialog;
+
 	public SipConfig getSipConfig() {
 		return sipConfig;
 	}
@@ -334,26 +340,13 @@ public class SIPCommander implements ISIPCommander {
 	  * @param errorEvent sip错误订阅
 	  */
 	@Override
-	public void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
-		String streamId = null;
+	public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
+		String streamId = ssrcInfo.getStreamId();
 		try {
 			if (device == null) return;
 			String streamMode = device.getStreamMode().toUpperCase();
 
-			String ssrc = streamSession.createPlaySsrc();
-			if (mediaServerItem.isRtpEnable()) {
-				streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
-			}else {
-				streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
-			}
-			Integer mediaPort = null;
-			// 使用动态udp端口
-			if (mediaServerItem.isRtpEnable()) {
-				mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
-			}else {
-				mediaPort = mediaServerItem.getRtpProxyPort();
-			}
-			logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
+			logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
 			// 添加订阅
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");
@@ -361,7 +354,7 @@ public class SIPCommander implements ISIPCommander {
 			subscribeKey.put("regist", true);
 			subscribeKey.put("mediaServerId", mediaServerItem.getId());
 			subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
-					(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
+					(MediaServerItem mediaServerItemInUse, JSONObject json)->{
 				if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
 				event.response(mediaServerItemInUse, json);
 				subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
@@ -369,7 +362,6 @@ public class SIPCommander implements ISIPCommander {
 			//
 			StringBuffer content = new StringBuffer(200);
 			content.append("v=0\r\n");
-//			content.append("o=" + sipConfig.getSipId() + " 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
 			content.append("o="+"00000"+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
 			content.append("s=Play\r\n");
 			content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
@@ -377,11 +369,11 @@ public class SIPCommander implements ISIPCommander {
 
 			if (userSetup.isSeniorSdp()) {
 				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}
 				content.append("a=recvonly\r\n");
 				content.append("a=rtpmap:96 PS/90000\r\n");
@@ -402,11 +394,11 @@ public class SIPCommander implements ISIPCommander {
 				}
 			}else {
 				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
 				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
 				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
 				}
 				content.append("a=recvonly\r\n");
 				content.append("a=rtpmap:96 PS/90000\r\n");
@@ -421,20 +413,25 @@ public class SIPCommander implements ISIPCommander {
 				}
 			}
 
-			content.append("y="+ssrc+"\r\n");//ssrc
+			content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc
 
 			String tm = Long.toString(System.currentTimeMillis());
 
 			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
 					: udpSipProvider.getNewCallId();
 
-			Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc, callIdHeader);
+			Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrcInfo.getSsrc(), callIdHeader);
 
-			ClientTransaction transaction = transmitRequest(device, request, (e -> {
+			String finalStreamId = streamId;
+			transmitRequest(device, request, (e -> {
 				streamSession.remove(device.getDeviceId(), channelId);
+				mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc());
 				errorEvent.response(e);
-			}));
-			streamSession.put(device.getDeviceId(), channelId ,ssrc,streamId, transaction);
+			}), e ->{
+				streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(),e.getClientTransaction());
+				streamSession.put(device.getDeviceId(), channelId , e.getDialog());
+			});
+
 			
 		} catch ( SipException | ParseException | InvalidArgumentException e) {
 			e.printStackTrace();
@@ -450,30 +447,21 @@ public class SIPCommander implements ISIPCommander {
 	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
 	 */ 
 	@Override
-	public void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event
+	public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event
 			, SipSubscribe.Event errorEvent) {
 		try {
-			String ssrc = streamSession.createPlayBackSsrc();
-			String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
 
-			Integer mediaPort = null;
-			// 使用动态udp端口
-			if (mediaServerItem.isRtpEnable()) {
-				mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
-			}else {
-				mediaPort = mediaServerItem.getRtpProxyPort();
-			}
-			logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
+			logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
 
 			// 添加订阅
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");
-			subscribeKey.put("stream", streamId);
+			subscribeKey.put("stream", ssrcInfo.getStreamId());
 			subscribeKey.put("regist", true);
 			subscribeKey.put("mediaServerId", mediaServerItem.getId());
 			logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
 			subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
-					(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
+					(MediaServerItem mediaServerItemInUse, JSONObject json)->{
 				if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
 				event.response(mediaServerItemInUse, json);
 				subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
@@ -494,11 +482,11 @@ public class SIPCommander implements ISIPCommander {
 
 			if (userSetup.isSeniorSdp()) {
 				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}
 				content.append("a=recvonly\r\n");
 				content.append("a=rtpmap:96 PS/90000\r\n");
@@ -519,11 +507,11 @@ public class SIPCommander implements ISIPCommander {
 				}
 			}else {
 				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
 				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
 				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
 				}
 				content.append("a=recvonly\r\n");
 				content.append("a=rtpmap:96 PS/90000\r\n");
@@ -538,7 +526,7 @@ public class SIPCommander implements ISIPCommander {
 				}
 			}
 
-	        content.append("y="+ssrc+"\r\n");//ssrc
+	        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
 	        
 			String tm = Long.toString(System.currentTimeMillis());
 
@@ -547,9 +535,11 @@ public class SIPCommander implements ISIPCommander {
 
 	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
 
-	        ClientTransaction transaction = transmitRequest(device, request, errorEvent);
-	        streamSession.put(device.getDeviceId(), channelId, ssrc, streamId, transaction);
-
+	        transmitRequest(device, request, errorEvent, okEvent -> {
+				Dialog dialog = okEvent.getClientTransaction().getDialog();
+	        	streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), okEvent.getClientTransaction());
+				streamSession.put(device.getDeviceId(), channelId, dialog);
+			});
 		} catch ( SipException | ParseException | InvalidArgumentException e) {
 			e.printStackTrace();
 		}
@@ -565,30 +555,20 @@ public class SIPCommander implements ISIPCommander {
 	 * @param downloadSpeed 下载倍速参数
 	 */ 
 	@Override
-	public void downloadStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event
+	public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event
 			, SipSubscribe.Event errorEvent) {
 		try {
-			String ssrc = streamSession.createPlayBackSsrc();
-			String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
-
-			Integer mediaPort = null;
-			// 使用动态udp端口
-			if (mediaServerItem.isRtpEnable()) {
-				mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
-			}else {
-				mediaPort = mediaServerItem.getRtpProxyPort();
-			}
-			logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
+			logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
 
 			// 添加订阅
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");
-			subscribeKey.put("stream", streamId);
+			subscribeKey.put("stream", ssrcInfo.getStreamId());
 			subscribeKey.put("regist", true);
 			subscribeKey.put("mediaServerId", mediaServerItem.getId());
 			logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
 			subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
-					(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
+					(MediaServerItem mediaServerItemInUse, JSONObject json)->{
 				if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
 				event.response(mediaServerItemInUse, json);
 				subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
@@ -609,11 +589,11 @@ public class SIPCommander implements ISIPCommander {
 
 			if (userSetup.isSeniorSdp()) {
 				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
 				}
 				content.append("a=recvonly\r\n");
 				content.append("a=rtpmap:96 PS/90000\r\n");
@@ -634,11 +614,11 @@ public class SIPCommander implements ISIPCommander {
 				}
 			}else {
 				if("TCP-PASSIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
 				}else if ("TCP-ACTIVE".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
 				}else if("UDP".equals(streamMode)) {
-					content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
+					content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
 				}
 				content.append("a=recvonly\r\n");
 				content.append("a=rtpmap:96 PS/90000\r\n");
@@ -654,7 +634,7 @@ public class SIPCommander implements ISIPCommander {
 			}
 			content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
 
-	        content.append("y="+ssrc+"\r\n");//ssrc
+	        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
 	        
 			String tm = Long.toString(System.currentTimeMillis());
 
@@ -664,7 +644,7 @@ public class SIPCommander implements ISIPCommander {
 	        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
 
 	        ClientTransaction transaction = transmitRequest(device, request, errorEvent);
-	        streamSession.put(device.getDeviceId(), channelId, ssrc, streamId, transaction);
+	        streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction);
 
 		} catch ( SipException | ParseException | InvalidArgumentException e) {
 			e.printStackTrace();
@@ -684,53 +664,35 @@ public class SIPCommander implements ISIPCommander {
 	 */
 	@Override
 	public void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent) {
-		StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
 		try {
 			ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
-			// 服务重启后, 无法直接发送bye, 通过手动构建发送
-//			if (transaction == null) {
-//
-//				if (streamInfo != null) {
-//					MediaServerItem mediaServerItem = redisCatchStorage.getMediaInfo(streamInfo.getMediaServerId());
-//					JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaServerItem,streamInfo.getApp(), streamInfo.getStreamId());
-//					if (mediaList != null) { // 仍在推流才发送
-//						if (mediaList.getInteger("code") == 0) {
-//							JSONArray data = mediaList.getJSONArray("data");
-//							if (data != null && data.size() > 0) {
-//								Device device = storager.queryVideoDevice(deviceId);
-//								if (device != null) {
-//									StreamInfo.TransactionInfo transactionInfo = streamInfo.getTransactionInfo();
-//									try {
-//										Request byteRequest = headerProvider.createByteRequest(device, channelId,
-//												transactionInfo.branch,
-//												transactionInfo.localTag,
-//												transactionInfo.remoteTag,
-//												transactionInfo.callId);
-//										transmitRequest(device, byteRequest);
-//									} catch (InvalidArgumentException e) {
-//										e.printStackTrace();
-//									}
-//								}
-//							}
-//						}
-//					}
-//					redisCatchStorage.stopPlay(streamInfo);
-//				}
-//
-//				if (okEvent != null) {
-//					okEvent.response(null);
-//				}
-//				return;
-//			}
 			if (transaction == null) {
 				logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
 				return;
 			}
-			Dialog dialog = transaction.getDialog();
+			SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
 			if (dialog == null) {
 				logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId);
 				return;
 			}
+			SipStack sipStack = udpSipProvider.getSipStack();
+			SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
+			if (dialog != sipDialog) {
+				dialog = sipDialog;
+			}else {
+				dialog.setSipProvider(udpSipProvider);
+				try {
+					Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
+					sipStackField.setAccessible(true);
+					sipStackField.set(dialog, sipStack);
+					Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
+					eventListenersField.setAccessible(true);
+					eventListenersField.set(dialog, new HashSet<>());
+				} catch (NoSuchFieldException | IllegalAccessException e) {
+					e.printStackTrace();
+				}
+			}
+
 			Request byeRequest = dialog.createRequest(Request.BYE);
 			SipURI byeURI = (SipURI) byeRequest.getRequestURI();
 			SIPRequest request = (SIPRequest)transaction.getRequest();
@@ -752,7 +714,12 @@ public class SIPCommander implements ISIPCommander {
 
 			dialog.sendRequest(clientTransaction);
 
-			streamSession.remove(deviceId, channelId);
+			SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId);
+			if (ssrcTransaction != null) {
+				MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
+				mediaServerService.releaseSsrc(mediaServerItem, ssrcTransaction.getSsrc());
+				streamSession.remove(deviceId, channelId);
+			}
 		} catch (SipException | ParseException e) {
 			e.printStackTrace();
 		}

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java

@@ -13,7 +13,6 @@ import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -81,7 +80,7 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor {
 			while (!rtpPushed) {
 				try {
 					if (System.currentTimeMillis() - startTime < 30 * 1000) {
-						IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+						MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
 						if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
 							rtpPushed = true;
 							logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java

@@ -15,7 +15,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -65,7 +64,7 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
 				param.put("stream",streamId);
 				param.put("ssrc",sendRtpItem.getSsrc());
 				logger.info("停止向上级推流:" + streamId);
-				IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+				MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
 				zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
 				redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
 				if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {

+ 1 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java

@@ -11,14 +11,11 @@ import javax.sip.header.*;
 import javax.sip.message.Request;
 import javax.sip.message.Response;
 
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -97,7 +94,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
 				// 查询平台下是否有该通道
 				DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
 				GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
-				IMediaServerItem mediaServerItem = null;
+				MediaServerItem mediaServerItem = null;
 				// 不是通道可能是直播流
 				if (channel != null && gbStream == null ) {
 					if (channel.getStatus() == 0) {

+ 13 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java

@@ -3,14 +3,18 @@ package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
 import java.text.ParseException;
 
 import javax.sip.*;
+import javax.sip.address.Address;
 import javax.sip.address.SipURI;
 import javax.sip.header.CSeqHeader;
 import javax.sip.message.Request;
 import javax.sip.message.Response;
 
+import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import gov.nist.javax.sip.ResponseEventExt;
+import gov.nist.javax.sip.stack.SIPDialog;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import com.genersoft.iot.vmp.conf.SipConfig;
@@ -28,6 +32,9 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
 
 	 private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
 
+	@Autowired
+	private VideoStreamSessionManager streamSession;
+
 	/**
 	 * 处理invite响应
 	 * 
@@ -46,7 +53,7 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
 			// 下发ack
 			if (statusCode == Response.OK) {
 				ResponseEventExt event = (ResponseEventExt)evt;
-				Dialog dialog = evt.getDialog();
+				SIPDialog dialog = (SIPDialog)evt.getDialog();
 				CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
 				Request reqAck = dialog.createAck(cseq.getSeqNumber());
 				SipURI requestURI = (SipURI) reqAck.getRequestURI();
@@ -54,7 +61,12 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
 				requestURI.setPort(event.getRemotePort());
 				reqAck.setRequestURI(requestURI);
 				logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack");
+				SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI();
+				String deviceId = requestURI.getUser();
+				String channelId = sipURI.getUser();
+
 				dialog.sendAck(reqAck);
+
 			}
 		} catch (InvalidArgumentException | SipException e) {
 			e.printStackTrace();

+ 14 - 10
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -3,15 +3,14 @@ package com.genersoft.iot.vmp.media.zlm;
 import java.util.List;
 import java.util.UUID;
 
-import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.MediaConfig;
 import com.genersoft.iot.vmp.conf.UserSetup;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import com.genersoft.iot.vmp.service.IPlayService;
@@ -42,7 +41,6 @@ public class ZLMHttpHookListener {
 
 	private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
 
-
 	@Autowired
 	private SIPCommander cmder;
 
@@ -125,7 +123,7 @@ public class ZLMHttpHookListener {
 		String mediaServerId = json.getString("mediaServerId");
 		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json);
 		if (subscribe != null ) {
-			IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 			if (mediaInfo != null) {
 				subscribe.response(mediaInfo, json);
 			}
@@ -150,7 +148,7 @@ public class ZLMHttpHookListener {
 		String mediaServerId = json.getString("mediaServerId");
 		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
 		if (subscribe != null) {
-			IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 			if (mediaInfo != null) {
 				subscribe.response(mediaInfo, json);
 			}
@@ -237,7 +235,7 @@ public class ZLMHttpHookListener {
 		String mediaServerId = json.getString("mediaServerId");
 		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json);
 		if (subscribe != null ) {
-			IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 			if (mediaInfo != null) {
 				subscribe.response(mediaInfo, json);
 			}
@@ -264,7 +262,7 @@ public class ZLMHttpHookListener {
 		String mediaServerId = json.getString("mediaServerId");
 		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json);
 		if (subscribe != null ) {
-			IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 			if (mediaInfo != null) {
 				subscribe.response(mediaInfo, json);
 			}
@@ -297,7 +295,7 @@ public class ZLMHttpHookListener {
 				}
 			}else {
 				if (!"rtp".equals(app) ){
-					IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+					MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
 					if (regist) {
 						zlmMediaListManager.addMedia(mediaServerItem, app, streamId);
 					}else {
@@ -369,7 +367,7 @@ public class ZLMHttpHookListener {
 			logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString());
 		}
 		String mediaServerId = json.getString("mediaServerId");
-		IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+		MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 		if (userSetup.isAutoApplyPlay() && mediaInfo != null) {
 			String app = json.getString("app");
 			String streamId = json.getString("stream");
@@ -381,7 +379,13 @@ public class ZLMHttpHookListener {
 					Device device = storager.queryVideoDevice(deviceId);
 					if (device != null) {
 						UUID uuid = UUID.randomUUID();
-						cmder.playStreamCmd(mediaInfo, device, channelId, (IMediaServerItem mediaServerItemInuse, JSONObject response) -> {
+						SSRCInfo ssrcInfo;
+						String streamId2 = null;
+						if (mediaInfo.isRtpEnable()) {
+							streamId2 = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
+						}
+						ssrcInfo = mediaServerService.openRTPServer(mediaInfo, streamId2);
+						cmder.playStreamCmd(mediaInfo, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
 							logger.info("收到订阅消息: " + response.toJSONString());
 							playService.onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
 						}, null);

+ 5 - 3
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.media.zlm;
 
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import org.springframework.stereotype.Component;
 
@@ -32,7 +31,7 @@ public class ZLMHttpHookSubscribe {
     }
 
     public interface Event{
-        void response(IMediaServerItem mediaServerItem, JSONObject response);
+        void response(MediaServerItem mediaServerItem, JSONObject response);
     }
 
     private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();
@@ -58,6 +57,9 @@ public class ZLMHttpHookSubscribe {
                 if (result == null) {
                     result = key.getString(s).equals(hookResponse.getString(s));
                 }else {
+                    if (key.getString(s) == null) {
+                        continue;
+                    }
                     result = result && key.getString(s).equals(hookResponse.getString(s));
                 }
 
@@ -83,9 +85,9 @@ public class ZLMHttpHookSubscribe {
                 if (result == null) {
                     result = key.getString(s).equals(hookResponse.getString(s));
                 }else {
+                    if (key.getString(s) == null) continue;
                     result = result && key.getString(s).equals(hookResponse.getString(s));
                 }
-
             }
             if (null != result && result){
                 iterator.remove();

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

@@ -1,12 +1,9 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
-import com.genersoft.iot.vmp.gb28181.bean.GbStream;
 import com.genersoft.iot.vmp.service.IStreamPushService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
@@ -76,7 +73,7 @@ public class ZLMMediaListManager {
                     jsonObject.put("stream", streamPushItem.getStream());
                     jsonObject.put("mediaServerId", mediaServerItem.getId());
                     subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,
-                            (IMediaServerItem mediaServerItemInuse, JSONObject response)->{
+                            (MediaServerItem mediaServerItemInuse, JSONObject response)->{
                                 updateMedia(mediaServerItem, response.getString("app"), response.getString("stream"));
                             }
                     );
@@ -86,13 +83,13 @@ public class ZLMMediaListManager {
 
     }
 
-    public void addMedia(IMediaServerItem mediaServerItem, String app, String streamId) {
+    public void addMedia(MediaServerItem mediaServerItem, String app, String streamId) {
         //使用异步更新推流
         updateMedia(mediaServerItem, app, streamId);
     }
 
 
-    public void updateMedia(IMediaServerItem mediaServerItem, String app, String streamId) {
+    public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) {
         //使用异步更新推流
         zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{
 

+ 21 - 24
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java

@@ -2,14 +2,11 @@ package com.genersoft.iot.vmp.media.zlm;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.conf.MediaConfig;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import okhttp3.*;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import java.io.*;
@@ -27,7 +24,7 @@ public class ZLMRESTfulUtils {
         void run(JSONObject response);
     }
 
-    public JSONObject sendPost(IMediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
+    public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
         OkHttpClient client = new OkHttpClient();
         String url = String.format("http://%s:%s/index/api/%s",  mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
         JSONObject responseJSON = null;
@@ -93,7 +90,7 @@ public class ZLMRESTfulUtils {
     }
 
 
-    public void sendPostForImg(IMediaServerItem mediaServerItem, String api, Map<String, Object> param, String targetPath, String fileName) {
+    public void sendPostForImg(MediaServerItem mediaServerItem, String api, Map<String, Object> param, String targetPath, String fileName) {
         OkHttpClient client = new OkHttpClient();
         String url = String.format("http://%s:%s/index/api/%s",  mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
         JSONObject responseJSON = null;
@@ -139,7 +136,7 @@ public class ZLMRESTfulUtils {
     }
 
 
-    public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream, String schema, RequestCallback callback){
+    public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){
         Map<String, Object> param = new HashMap<>();
         if (app != null) param.put("app",app);
         if (stream != null) param.put("stream",stream);
@@ -148,15 +145,15 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "getMediaList",param, callback);
     }
 
-    public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream){
+    public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream){
         return getMediaList(mediaServerItem, app, stream,null,  null);
     }
 
-    public JSONObject getMediaList(IMediaServerItem mediaServerItem,RequestCallback callback){
+    public JSONObject getMediaList(MediaServerItem mediaServerItem, RequestCallback callback){
         return sendPost(mediaServerItem, "getMediaList",null, callback);
     }
 
-    public JSONObject getMediaInfo(IMediaServerItem mediaServerItem,String app, String schema, String stream){
+    public JSONObject getMediaInfo(MediaServerItem mediaServerItem, String app, String schema, String stream){
         Map<String, Object> param = new HashMap<>();
         param.put("app",app);
         param.put("schema",schema);
@@ -165,13 +162,13 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "getMediaInfo",param, null);
     }
 
-    public JSONObject getRtpInfo(IMediaServerItem mediaServerItem,String stream_id){
+    public JSONObject getRtpInfo(MediaServerItem 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(IMediaServerItem mediaServerItem,String src_url, String dst_url, String timeout_ms,
+    public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms,
                                       boolean enable_hls, boolean enable_mp4, String ffmpeg_cmd_key){
         logger.info(src_url);
         logger.info(dst_url);
@@ -185,41 +182,41 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "addFFmpegSource",param, null);
     }
 
-    public JSONObject delFFmpegSource(IMediaServerItem mediaServerItem,String key){
+    public JSONObject delFFmpegSource(MediaServerItem mediaServerItem, String key){
         Map<String, Object> param = new HashMap<>();
         param.put("key", key);
         return sendPost(mediaServerItem, "delFFmpegSource",param, null);
     }
 
-    public JSONObject getMediaServerConfig(IMediaServerItem mediaServerItem){
+    public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){
         return sendPost(mediaServerItem, "getServerConfig",null, null);
     }
 
-    public JSONObject setServerConfig(IMediaServerItem mediaServerItem, Map<String, Object> param){
+    public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map<String, Object> param){
         return sendPost(mediaServerItem,"setServerConfig",param, null);
     }
 
-    public JSONObject openRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param){
+    public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param){
         return sendPost(mediaServerItem, "openRtpServer",param, null);
     }
 
-    public JSONObject closeRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param) {
+    public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param) {
         return sendPost(mediaServerItem, "closeRtpServer",param, null);
     }
 
-    public JSONObject listRtpServer(IMediaServerItem mediaServerItem) {
+    public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
         return sendPost(mediaServerItem, "listRtpServer",null, null);
     }
 
-    public JSONObject startSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
+    public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
         return sendPost(mediaServerItem, "startSendRtp",param, null);
     }
 
-    public JSONObject stopSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
+    public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
         return sendPost(mediaServerItem, "stopSendRtp",param, null);
     }
 
-    public JSONObject addStreamProxy(IMediaServerItem mediaServerItem,String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
+    public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
         Map<String, Object> param = new HashMap<>();
         param.put("vhost", "__defaultVhost__");
         param.put("app", app);
@@ -231,7 +228,7 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "addStreamProxy",param, null);
     }
 
-    public JSONObject closeStreams(IMediaServerItem mediaServerItem,String app, String stream) {
+    public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) {
         Map<String, Object> param = new HashMap<>();
         param.put("vhost", "__defaultVhost__");
         param.put("app", app);
@@ -240,17 +237,17 @@ public class ZLMRESTfulUtils {
         return sendPost(mediaServerItem, "close_streams",param, null);
     }
 
-    public JSONObject getAllSession(IMediaServerItem mediaServerItem) {
+    public JSONObject getAllSession(MediaServerItem mediaServerItem) {
         return sendPost(mediaServerItem, "getAllSession",null, null);
     }
 
-    public void kickSessions(IMediaServerItem mediaServerItem, String localPortSStr) {
+    public void kickSessions(MediaServerItem mediaServerItem, String localPortSStr) {
         Map<String, Object> param = new HashMap<>();
         param.put("local_port", localPortSStr);
         sendPost(mediaServerItem, "kick_sessions",param, null);
     }
 
-    public void getSnap(IMediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
+    public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
         Map<String, Object> param = new HashMap<>();
         param.put("url", flvUrl);
         param.put("timeout_sec", timeout_sec);

+ 39 - 41
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java

@@ -2,10 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.conf.MediaConfig;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.gb28181.session.SsrcUtil;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -20,29 +17,20 @@ public class ZLMRTPServerFactory {
 
     private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory");
 
-    @Autowired
-    private MediaConfig mediaConfig;
-
     @Autowired
     private ZLMRESTfulUtils zlmresTfulUtils;
 
     private int[] portRangeArray = new int[2];
 
-    private int currentPort = 0;
-
-    private Map<String, Integer> currentStreams = null;
-
-    public int createRTPServer(IMediaServerItem mediaServerItem, String streamId) {
-        if (currentStreams == null) {
-            currentStreams = new HashMap<>();
-            JSONObject jsonObject = zlmresTfulUtils.listRtpServer(mediaServerItem);
-            if (jsonObject != null) {
-                JSONArray data = jsonObject.getJSONArray("data");
-                if (data != null) {
-                    for (int i = 0; i < data.size(); i++) {
-                        JSONObject dataItem = data.getJSONObject(i);
-                        currentStreams.put(dataItem.getString("stream_id"), dataItem.getInteger("port"));
-                    }
+    public int createRTPServer(MediaServerItem mediaServerItem, String streamId) {
+        Map<String, Integer> currentStreams = new HashMap<>();
+        JSONObject listRtpServerJsonResult = zlmresTfulUtils.listRtpServer(mediaServerItem);
+        if (listRtpServerJsonResult != null) {
+            JSONArray data = listRtpServerJsonResult.getJSONArray("data");
+            if (data != null) {
+                for (int i = 0; i < data.size(); i++) {
+                    JSONObject dataItem = data.getJSONObject(i);
+                    currentStreams.put(dataItem.getString("stream_id"), dataItem.getInteger("port"));
                 }
             }
         }
@@ -56,18 +44,18 @@ public class ZLMRTPServerFactory {
 
         Map<String, Object> param = new HashMap<>();
         int result = -1;
-        int newPort = getPortFromportRange();
+        int newPort = getPortFromportRange(mediaServerItem);
         param.put("port", newPort);
         param.put("enable_tcp", 1);
         param.put("stream_id", streamId);
-        JSONObject jsonObject = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
+        JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
 
-        if (jsonObject != null) {
-            switch (jsonObject.getInteger("code")){
+        if (openRtpServerResultJson != null) {
+            switch (openRtpServerResultJson.getInteger("code")){
                 case 0:
                     result= newPort;
                     break;
-                case -300: // id已经存在, 可能已经在其他端口推流
+                case -300: // id已经存在, 可能已经在其他端口推流, TODO 也可能是设备不等ack就直接推流了, 需要查询与设置的推流ip端口是否一致
                     Map<String, Object> closeRtpServerParam = new HashMap<>();
                     closeRtpServerParam.put("stream_id", streamId);
                     zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);
@@ -77,7 +65,7 @@ public class ZLMRTPServerFactory {
                     result= createRTPServer(mediaServerItem, streamId);
                     break;
                 default:
-                    logger.error("创建RTP Server 失败 {}: " + jsonObject.getString("msg"), newPort);
+                    logger.error("创建RTP Server 失败 {}: " + openRtpServerResultJson.getString("msg"), newPort);
                     break;
             }
         }else {
@@ -87,7 +75,7 @@ public class ZLMRTPServerFactory {
         return result;
     }
 
-    public boolean closeRTPServer(IMediaServerItem serverItem, String streamId) {
+    public boolean closeRTPServer(MediaServerItem serverItem, String streamId) {
         boolean result = false;
         if (serverItem !=null){
             Map<String, Object> param = new HashMap<>();
@@ -107,21 +95,25 @@ public class ZLMRTPServerFactory {
         return result;
     }
 
-    private int getPortFromportRange() {
+    private int getPortFromportRange(MediaServerItem mediaServerItem) {
+        int currentPort = mediaServerItem.getCurrentPort();
         if (currentPort == 0) {
-            String[] portRangeStrArray = mediaConfig.getRtpPortRange().split(",");
+            String[] portRangeStrArray = mediaServerItem.getRtpPortRange().split(",");
             portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]);
             portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]);
         }
 
         if (currentPort == 0 || currentPort++ > portRangeArray[1]) {
             currentPort = portRangeArray[0];
+            mediaServerItem.setCurrentPort(currentPort);
             return portRangeArray[0];
         } else {
             if (currentPort % 2 == 1) {
                 currentPort++;
             }
-            return currentPort++;
+            currentPort++;
+            mediaServerItem.setCurrentPort(currentPort);
+            return currentPort;
         }
     }
 
@@ -135,10 +127,14 @@ public class ZLMRTPServerFactory {
      * @param tcp 是否为tcp
      * @return SendRtpItem
      */
-    public SendRtpItem createSendRtpItem(IMediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
-        String playSsrc = SsrcUtil.getPlaySsrc();
-        int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
+    public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
+
+        // 使用RTPServer 功能找一个可用的端口
+        String playSsrc = serverItem.getSsrcConfig().getPlaySsrc();
+        int localPort = createRTPServer(serverItem, playSsrc);
         if (localPort != -1) {
+            // TODO 高并发时可能因为未放入缓存而ssrc冲突
+            serverItem.getSsrcConfig().releaseSsrc(playSsrc);
             closeRTPServer(serverItem, playSsrc);
         }else {
             logger.error("没有可用的端口");
@@ -168,10 +164,12 @@ public class ZLMRTPServerFactory {
      * @param tcp 是否为tcp
      * @return SendRtpItem
      */
-    public SendRtpItem createSendRtpItem(IMediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
-        String playSsrc = SsrcUtil.getPlaySsrc();
-        int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
+    public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
+        String playSsrc = serverItem.getSsrcConfig().getPlaySsrc();
+        int localPort = createRTPServer(serverItem, playSsrc);
         if (localPort != -1) {
+            // TODO 高并发时可能因为未放入缓存而ssrc冲突
+            serverItem.getSsrcConfig().releaseSsrc(ssrc);
             closeRTPServer(serverItem, playSsrc);
         }else {
             logger.error("没有可用的端口");
@@ -194,7 +192,7 @@ public class ZLMRTPServerFactory {
     /**
      * 调用zlm RESTful API —— startSendRtp
      */
-    public Boolean startSendRtpStream(IMediaServerItem mediaServerItem, Map<String, Object>param) {
+    public Boolean startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
         Boolean result = false;
         JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServerItem, param);
         if (jsonObject == null) {
@@ -219,7 +217,7 @@ public class ZLMRTPServerFactory {
     /**
      * 查询待转推的流是否就绪
      */
-    public Boolean isStreamReady(IMediaServerItem mediaServerItem, String app, String streamId) {
+    public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
         return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
     }
@@ -229,7 +227,7 @@ public class ZLMRTPServerFactory {
      * @param streamId
      * @return
      */
-    public int totalReaderCount(IMediaServerItem mediaServerItem, String app, String streamId) {
+    public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
         return mediaInfo.getInteger("totalReaderCount");
     }
@@ -237,7 +235,7 @@ public class ZLMRTPServerFactory {
     /**
      * 调用zlm RESTful API —— stopSendRtp
      */
-    public Boolean stopSendRtpStream(IMediaServerItem mediaServerItem,Map<String, Object>param) {
+    public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
         Boolean result = false;
         JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
         if (jsonObject == null) {

+ 20 - 14
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java

@@ -4,22 +4,16 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.conf.MediaConfig;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 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 com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import com.genersoft.iot.vmp.service.IStreamProxyService;
 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.boot.CommandLineRunner;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
 
-import javax.print.attribute.standard.Media;
 import java.util.*;
 
 @Component
@@ -47,16 +41,26 @@ public class ZLMRunner implements CommandLineRunner {
 
     @Override
     public void run(String... strings) throws Exception {
-        IMediaServerItem presetMediaServer = mediaServerService.getOneByHostAndPort(
+        // 清楚redis缓存的在线zlm信息
+        mediaServerService.clearMediaServerForOnline();
+
+        // 将配置文件的meida配置写入数据库
+        MediaServerItem presetMediaServer = mediaServerService.getOneByHostAndPort(
                 mediaConfig.getIp(), mediaConfig.getHttpPort());
         if (presetMediaServer  != null) {
-            mediaConfig.setId(presetMediaServer.getId());
-            mediaServerService.update(mediaConfig);
+            MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
+            mediaSerItem.setId(presetMediaServer.getId());
+            mediaServerService.update(mediaSerItem);
+        }else {
+            if (mediaConfig.getId() != null) {
+                MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
+                mediaServerService.add(mediaSerItem);
+            }
         }
 
         // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
         hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null,
-                (IMediaServerItem mediaServerItem, JSONObject response)->{
+                (MediaServerItem mediaServerItem, JSONObject response)->{
             ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class);
             if (zlmServerConfig !=null ) {
                 startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId());
@@ -69,23 +73,25 @@ public class ZLMRunner implements CommandLineRunner {
         logger.info("等待默认zlm接入...");
 
         // 获取所有的zlm, 并开启主动连接
-        List<IMediaServerItem> all = mediaServerService.getAll();
+        List<MediaServerItem> all = mediaServerService.getAll();
         if (presetMediaServer == null) {
             all.add(mediaConfig.getMediaSerItem());
         }
-        for (IMediaServerItem mediaServerItem : all) {
+        for (MediaServerItem mediaServerItem : all) {
             if (startGetMedia == null) startGetMedia = new HashMap<>();
             startGetMedia.put(mediaServerItem.getId(), true);
             new Thread(() -> {
                 ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem);
                 if (zlmServerConfig != null) {
+                    zlmServerConfig.setIp(mediaServerItem.getIp());
+                    zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort());
                     startGetMedia.remove(mediaServerItem.getId());
                     mediaServerService.handLeZLMServerConfig(zlmServerConfig);
                 }
             }).start();
         }
         Timer timer = new Timer();
-        // 1分钟后未连接到则不再去主动连接
+        // 2分钟后未连接到则不再去主动连接, TODO 并对重启前使用此在zlm的通道发送bye
         timer.schedule(new TimerTask() {
             @Override
             public void run() {
@@ -100,7 +106,7 @@ public class ZLMRunner implements CommandLineRunner {
         }, 60 * 1000 * 2);
     }
 
-    public ZLMServerConfig getMediaServerConfig(IMediaServerItem mediaServerItem) {
+    public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) {
         if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) return null;
         JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
         ZLMServerConfig ZLMServerConfig = null;

+ 0 - 92
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java

@@ -1,92 +0,0 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
-
-public interface IMediaServerItem {
-
-    String getId();
-
-    void setId(String id);
-
-    String getIp();
-
-    void setIp(String ip);
-
-    String getHookIp();
-
-    void setHookIp(String hookIp);
-
-    String getSdpIp();
-
-    void setSdpIp(String sdpIp);
-
-    String getStreamIp();
-
-    void setStreamIp(String streamIp);
-
-    int getHttpPort();
-
-    void setHttpPort(int httpPort);
-
-    int getHttpSSlPort();
-
-    void setHttpSSlPort(int httpSSlPort);
-
-    int getRtmpPort();
-
-    void setRtmpPort(int rtmpPort);
-
-    int getRtmpSSlPort();
-
-    void setRtmpSSlPort(int rtmpSSlPort);
-
-    int getRtpProxyPort();
-
-    void setRtpProxyPort(int rtpProxyPort);
-
-    int getRtspPort();
-
-    void setRtspPort(int rtspPort);
-
-    int getRtspSSLPort();
-
-    void setRtspSSLPort(int rtspSSLPort);
-
-    boolean isAutoConfig();
-
-    void setAutoConfig(boolean autoConfig);
-
-    String getSecret();
-
-    void setSecret(String secret);
-
-    String getStreamNoneReaderDelayMS();
-
-    void setStreamNoneReaderDelayMS(String streamNoneReaderDelayMS);
-
-    boolean isRtpEnable();
-
-    void setRtpEnable(boolean rtpEnable);
-
-    String getRtpPortRange();
-
-    void setRtpPortRange(String rtpPortRange);
-
-    int getRecordAssistPort();
-
-    void setRecordAssistPort(int recordAssistPort);
-
-    boolean isDocker();
-
-    void setDocker(boolean docker);
-
-    String getUpdateTime();
-
-    void setUpdateTime(String updateTime);
-
-    String getCreateTime();
-
-    void setCreateTime(String createTime);
-
-    int getCount();
-
-    void setCount(int count);
-}

+ 41 - 13
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java

@@ -1,10 +1,13 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
 
+import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import org.springframework.util.StringUtils;
 
-public class MediaServerItem implements IMediaServerItem{
+import java.util.HashMap;
+
+public class MediaServerItem{
 
     private String id;
 
@@ -46,9 +49,18 @@ public class MediaServerItem implements IMediaServerItem{
 
     private String updateTime;
 
-    private boolean docker;
+    private boolean defaultServer;
+
+    private SsrcConfig ssrcConfig;
+
+    private int currentPort;
+
 
-    private int count;
+    /**
+     * 每一台ZLM都有一套独立的SSRC列表
+     * 在ApplicationCheckRunner里对mediaServerSsrcMap进行初始化
+     */
+    private HashMap<String, SsrcConfig> mediaServerSsrcMap;
 
     public MediaServerItem() {
     }
@@ -218,14 +230,12 @@ public class MediaServerItem implements IMediaServerItem{
         this.recordAssistPort = recordAssistPort;
     }
 
-    @Override
-    public boolean isDocker() {
-        return docker;
+    public boolean isDefaultServer() {
+        return defaultServer;
     }
 
-    @Override
-    public void setDocker(boolean docker) {
-        this.docker = docker;
+    public void setDefaultServer(boolean defaultServer) {
+        this.defaultServer = defaultServer;
     }
 
     public String getCreateTime() {
@@ -244,11 +254,29 @@ public class MediaServerItem implements IMediaServerItem{
         this.updateTime = updateTime;
     }
 
-    public int getCount() {
-        return count;
+    public HashMap<String, SsrcConfig> getMediaServerSsrcMap() {
+        return mediaServerSsrcMap;
+    }
+
+    public void setMediaServerSsrcMap(HashMap<String, SsrcConfig> mediaServerSsrcMap) {
+        this.mediaServerSsrcMap = mediaServerSsrcMap;
+    }
+
+    public SsrcConfig getSsrcConfig() {
+        return ssrcConfig;
+    }
+
+    public void setSsrcConfig(SsrcConfig ssrcConfig) {
+        this.ssrcConfig = ssrcConfig;
     }
 
-    public void setCount(int count) {
-        this.count = count;
+    public int getCurrentPort() {
+        return currentPort;
     }
+
+    public void setCurrentPort(int currentPort) {
+        this.currentPort = currentPort;
+    }
+
+
 }

+ 20 - 10
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java

@@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.service;
 import com.genersoft.iot.vmp.conf.MediaConfig;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 
 import java.util.List;
 
@@ -13,11 +13,13 @@ import java.util.List;
  */
 public interface IMediaServerService {
 
-    List<IMediaServerItem> getAll();
+    List<MediaServerItem> getAll();
 
-    IMediaServerItem getOne(String generalMediaServerId);
+    List<MediaServerItem> getAllOnline();
 
-    IMediaServerItem getOneByHostAndPort(String host, int port);
+    MediaServerItem getOne(String generalMediaServerId);
+
+    MediaServerItem getOneByHostAndPort(String host, int port);
 
     /**
      * 新的节点加入
@@ -26,19 +28,27 @@ public interface IMediaServerService {
      */
     void handLeZLMServerConfig(ZLMServerConfig zlmServerConfig);
 
-    void updateServerCatch(IMediaServerItem mediaServerItem, Integer count, Boolean b);
-
-    IMediaServerItem getMediaServerForMinimumLoad();
+    MediaServerItem getMediaServerForMinimumLoad();
 
-    void setZLMConfig(IMediaServerItem mediaServerItem);
+    void setZLMConfig(MediaServerItem mediaServerItem);
 
-    void init();
+    SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId);
 
     void closeRTPServer(Device device, String channelId);
 
-    void update(MediaConfig mediaConfig);
+    void clearRTPServer(MediaServerItem mediaServerItem);
+
+    void update(MediaServerItem mediaSerItem);
 
     void addCount(String mediaServerId);
 
     void removeCount(String mediaServerId);
+
+    void releaseSsrc(MediaServerItem mediaServerItem, String ssrc);
+
+    void clearMediaServerForOnline();
+
+    void add(MediaServerItem mediaSerItem);
+
+    void resetOnlineServerItem(MediaServerItem serverItem);
 }

+ 2 - 3
src/main/java/com/genersoft/iot/vmp/service/IMediaService.java

@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.service;
 
 import com.alibaba.fastjson.JSONArray;
 import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 
 /**
@@ -33,7 +32,7 @@ public interface IMediaService {
      * @param stream
      * @return
      */
-    StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaServerItem, String app, String stream, JSONArray tracks);
+    StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, JSONArray tracks);
 
     /**
      * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况
@@ -41,5 +40,5 @@ public interface IMediaService {
      * @param stream
      * @return
      */
-    StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr);
+    StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr);
 }

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

@@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
 
@@ -13,10 +12,10 @@ import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
  */
 public interface IPlayService {
 
-    void onPublishHandlerForPlayBack(IMediaServerItem mediaServerItem,JSONObject resonse, String deviceId, String channelId, String uuid);
-    void onPublishHandlerForPlay(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
+    void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
+    void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
 
-    PlayResult play(IMediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
+    PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
 
-    IMediaServerItem getNewMediaServerItem(Device device);
+    MediaServerItem getNewMediaServerItem(Device device);
 }

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.service;
 
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
 import com.github.pagehelper.PageInfo;
@@ -63,5 +62,5 @@ public interface IStreamProxyService {
      * 获取ffmpeg.cmd模板
      * @return
      */
-    JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem);
+    JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem);
 }

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

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

+ 38 - 0
src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java

@@ -0,0 +1,38 @@
+package com.genersoft.iot.vmp.service.bean;
+
+public class SSRCInfo {
+
+    private int port;
+    private String ssrc;
+    private String StreamId;
+
+    public SSRCInfo(int port, String ssrc, String streamId) {
+        this.port = port;
+        this.ssrc = ssrc;
+        StreamId = streamId;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public String getStreamId() {
+        return StreamId;
+    }
+
+    public void setStreamId(String streamId) {
+        StreamId = streamId;
+    }
+}

+ 221 - 164
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java

@@ -1,29 +1,32 @@
 package com.genersoft.iot.vmp.service.impl;
 
-import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-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.ProxyServletConfig;
+import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.ZLMRunInfo;
 import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
-import org.mitre.dsmiley.httpproxy.ProxyServlet;
+import com.genersoft.iot.vmp.utils.redis.JedisUtil;
+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.beans.factory.annotation.Value;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
 
+import java.sql.Array;
 import java.text.SimpleDateFormat;
 import java.util.*;
 
@@ -31,15 +34,13 @@ import java.util.*;
  * 媒体服务器节点管理
  */
 @Service
-public class MediaServerServiceImpl implements IMediaServerService {
+@Order(value=2)
+public class MediaServerServiceImpl implements IMediaServerService, CommandLineRunner {
 
     private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class);
 
-    private Map<String, IMediaServerItem> zlmServers = new HashMap<>(); // 所有数据库的zlm的缓存
-    private Map<String, Integer> zlmServerStatus = new LinkedHashMap<>(); // 所有上线的zlm的缓存以及负载
-
-    @Value("${sip.ip}")
-    private String sipIp;
+    @Autowired
+    private SipConfig sipConfig;
 
     @Value("${server.ssl.enabled:false}")
     private boolean sslEnabled;
@@ -56,7 +57,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
     @Autowired
     private MediaServerMapper mediaServerMapper;
 
-
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
@@ -66,53 +66,134 @@ public class MediaServerServiceImpl implements IMediaServerService {
     @Autowired
     private ZLMRTPServerFactory zlmrtpServerFactory;
 
-    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @Autowired
+    JedisUtil jedisUtil;
+
+    private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
     /**
      * 初始化
      */
     @Override
-    public void init() {
-        zlmServers.clear();
-        zlmServerStatus.clear();
+    public void run(String... args) throws Exception {
+        logger.info("Media Server 缓存初始化");
         List<MediaServerItem> mediaServerItemList = mediaServerMapper.queryAll();
-        for (IMediaServerItem mediaServerItem : mediaServerItemList) {
-            zlmServers.put(mediaServerItem.getId(), mediaServerItem);
+        for (MediaServerItem mediaServerItem : mediaServerItemList) {
+            // 更新
+            if (mediaServerItem.getSsrcConfig() == null) {
+                SsrcConfig ssrcConfig = new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getSipDomain());
+                mediaServerItem.setSsrcConfig(ssrcConfig);
+                redisUtil.set(VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId(), mediaServerItem);
+            }
+            // 查询redis是否存在此mediaServer
+            String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId();
+            if (!redisUtil.hasKey(key)) {
+                redisUtil.set(key, mediaServerItem);
+            }
+        }
+    }
+
+    @Override
+    public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId) {
+        if (mediaServerItem == null || mediaServerItem.getId() == null) return null;
+        // 获取mediaServer可用的ssrc
+        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId();
+
+        SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
+        if (ssrcConfig == null) {
+            logger.info("media server [ {} ] ssrcConfig is null", mediaServerItem.getId());
+            return null;
+        }else {
+            String ssrc = ssrcConfig.getPlaySsrc();
+            if (streamId == null) streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
+            int rtpServerPort = mediaServerItem.getRtpProxyPort();
+            if (mediaServerItem.isRtpEnable()) {
+                rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
+            }
+            redisUtil.set(key, mediaServerItem);
+            return new SSRCInfo(rtpServerPort, ssrc, streamId);
         }
     }
 
     @Override
     public void closeRTPServer(Device device, String channelId) {
-        StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
-        IMediaServerItem mediaServerItem = null;
-        if (streamInfo != null) {
-            mediaServerItem = this.getOne (streamInfo.getMediaServerId());
+        String mediaServerId = streamSession.getMediaServerId(device.getDeviceId(), channelId);
+        MediaServerItem mediaServerItem = this.getOne(mediaServerId);
+        if (mediaServerItem != null) {
+            String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
+            zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId);
+            releaseSsrc(mediaServerItem, streamSession.getSSRC(device.getDeviceId(), channelId));
         }
-        String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
-        zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId);
         streamSession.remove(device.getDeviceId(), channelId);
     }
 
     @Override
-    public void update(MediaConfig mediaConfig) {
+    public void releaseSsrc(MediaServerItem mediaServerItem, String ssrc) {
+        if (mediaServerItem == null || ssrc == null) return;
+        SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
+        ssrcConfig.releaseSsrc(ssrc);
+        mediaServerItem.setSsrcConfig(ssrcConfig);
+        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId();
+        redisUtil.set(key, mediaServerItem);
+    }
 
+    /**
+     * zlm 重启后重置他的推流信息, TODO 给正在使用的设备发送停止命令
+     * @param mediaServerItem
+     */
+    @Override
+    public void clearRTPServer(MediaServerItem mediaServerItem) {
+        mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getSipDomain()));
+        redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX, mediaServerItem.getId(), 0);
     }
 
+
     @Override
-    public List<IMediaServerItem> getAll() {
-        if (zlmServers.size() == 0) {
-            init();
+    public void update(MediaServerItem mediaSerItem) {
+        mediaServerMapper.update(mediaSerItem);
+        MediaServerItem mediaServerItemInRedis = getOne(mediaSerItem.getId());
+        MediaServerItem mediaServerItemInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId());
+        if (mediaServerItemInRedis != null && mediaServerItemInRedis.getSsrcConfig() != null) {
+            mediaServerItemInDataBase.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
+        }else {
+            mediaServerItemInDataBase.setSsrcConfig(
+                    new SsrcConfig(
+                            mediaServerItemInDataBase.getId(),
+                            null,
+                            sipConfig.getSipDomain()
+                    )
+            );
         }
-        List<IMediaServerItem> result = new ArrayList<>();
-        for (String id : zlmServers.keySet()) {
-            IMediaServerItem mediaServerItem = zlmServers.get(id);
-            mediaServerItem.setCount(zlmServerStatus.get(id) == null ? 0 : zlmServerStatus.get(id));
-            result.add(mediaServerItem);
+        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItemInDataBase.getId();
+        redisUtil.set(key, mediaServerItemInDataBase);
+    }
+
+    @Override
+    public List<MediaServerItem> getAll() {
+        List<MediaServerItem> result = new ArrayList<>();
+        List<Object> mediaServerKeys = redisUtil.scan(String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX));
+        for (int i = 0; i < mediaServerKeys.size(); i++) {
+            String key = (String) mediaServerKeys.get(i);
+            result.add((MediaServerItem)redisUtil.get(key));
         }
         return result;
+    }
 
-
-//        return mediaServerMapper.queryAll();
+    @Override
+    public List<MediaServerItem> getAllOnline() {
+        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
+        Set<String> mediaServerIdSet = redisUtil.zRevRange(key, 0, -1);
+        List<MediaServerItem> result = new ArrayList<>();
+        if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) {
+            for (String mediaServerId : mediaServerIdSet) {
+                String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerId;
+                result.add((MediaServerItem) redisUtil.get(serverKey));
+            }
+        }
+        return result;
     }
 
     /**
@@ -121,26 +202,28 @@ public class MediaServerServiceImpl implements IMediaServerService {
      * @return MediaServerItem
      */
     @Override
-    public IMediaServerItem getOne(String mediaServerId) {
-        if (mediaServerId ==null) return null;
-        IMediaServerItem mediaServerItem = zlmServers.get(mediaServerId);
-        if (mediaServerItem != null) {
-            mediaServerItem.setCount(zlmServerStatus.get(mediaServerId) == null ? 0 : zlmServerStatus.get(mediaServerId));
-            return mediaServerItem;
-        }else {
-            IMediaServerItem item = mediaServerMapper.queryOne(mediaServerId);
-            if (item != null) {
-                zlmServers.put(item.getId(), item);
-            }
-            return item;
-        }
+    public MediaServerItem getOne(String mediaServerId) {
+        if (mediaServerId == null) return null;
+        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerId;
+        return (MediaServerItem)redisUtil.get(key);
     }
 
     @Override
-    public IMediaServerItem getOneByHostAndPort(String host, int port) {
+    public MediaServerItem getOneByHostAndPort(String host, int port) {
         return mediaServerMapper.queryOneByHostAndPort(host, port);
     }
 
+    @Override
+    public void clearMediaServerForOnline() {
+        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
+        redisUtil.del(key);
+    }
+
+    @Override
+    public void add(MediaServerItem mediaSerItem) {
+        mediaServerMapper.add(mediaSerItem);
+    }
+
     /**
      * 处理zlm上线
      * @param zlmServerConfig zlm上线携带的参数
@@ -150,111 +233,100 @@ public class MediaServerServiceImpl implements IMediaServerService {
         logger.info("[ {} ]-[ {}:{} ]已连接",
                 zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
 
-        IMediaServerItem serverItem = getOne(zlmServerConfig.getGeneralMediaServerId());
-        String now = this.format.format(new Date(System.currentTimeMillis()));
-        if (serverItem != null) {
-            serverItem.setSecret(zlmServerConfig.getApiSecret());
-            serverItem.setIp(zlmServerConfig.getIp());
+        MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId());
+        if (serverItem == null) {
+            serverItem = mediaServerMapper.queryOneByHostAndPort(zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
+        }
+        if (zlmServerConfig.getGeneralMediaServerId().equals(mediaConfig.getId())
+                || (zlmServerConfig.getIp().equals(mediaConfig.getIp()) && zlmServerConfig.getHttpPort() == mediaConfig.getHttpPort())) {
+            // 配置文件的zlm
             // 如果是配置文件中的zlm。 也就是默认zlm。 一切以配置文件内容为准
-            // docker部署不会使用zlm配置的端口号;
-            // 直接编译部署的使用配置文件的端口号,如果zlm修改配改了配置,wvp自动修改
-
-            if (serverItem.getId().equals(mediaConfig.getId())
-                    || (serverItem.getIp().equals(mediaConfig.getIp()) && serverItem.getHttpPort() == mediaConfig.getHttpPort())) {
-                // 配置文件的zlm
-                mediaConfig.setId(zlmServerConfig.getGeneralMediaServerId());
-                mediaConfig.setUpdateTime(now);
-                if (mediaConfig.getHttpPort() == 0) mediaConfig.setHttpPort(zlmServerConfig.getHttpPort());
-                if (mediaConfig.getHttpSSlPort() == 0) mediaConfig.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
-                if (mediaConfig.getRtmpPort() == 0) mediaConfig.setRtmpPort(zlmServerConfig.getRtmpPort());
-                if (mediaConfig.getRtmpSSlPort() == 0) mediaConfig.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
-                if (mediaConfig.getRtspPort() == 0) mediaConfig.setRtspPort(zlmServerConfig.getRtspPort());
-                if (mediaConfig.getRtspSSLPort() == 0) mediaConfig.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
-                if (mediaConfig.getRtpProxyPort() == 0) mediaConfig.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
-                mediaServerMapper.update(mediaConfig);
-                serverItem = mediaConfig.getMediaSerItem();
-                setZLMConfig(mediaConfig);
-            }else {
-                if (!serverItem.isDocker()) {
-                    serverItem.setHttpPort(zlmServerConfig.getHttpPort());
-                    serverItem.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
-                    serverItem.setRtmpPort(zlmServerConfig.getRtmpPort());
-                    serverItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
-                    serverItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
-                    serverItem.setRtspPort(zlmServerConfig.getRtspPort());
-
+            // docker部署不会使用zlm配置的端口号不是默认的则不做更新, 配置修改需要自行修改server配置;
+            MediaServerItem serverItemFromConfig = mediaConfig.getMediaSerItem();
+            serverItemFromConfig.setId(zlmServerConfig.getGeneralMediaServerId());
+            if (mediaConfig.getHttpPort() == 0) serverItemFromConfig.setHttpPort(zlmServerConfig.getHttpPort());
+            if (mediaConfig.getHttpSSlPort() == 0) serverItemFromConfig.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
+            if (mediaConfig.getRtmpPort() == 0) serverItemFromConfig.setRtmpPort(zlmServerConfig.getRtmpPort());
+            if (mediaConfig.getRtmpSSlPort() == 0) serverItemFromConfig.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
+            if (mediaConfig.getRtspPort() == 0) serverItemFromConfig.setRtspPort(zlmServerConfig.getRtspPort());
+            if (mediaConfig.getRtspSSLPort() == 0) serverItemFromConfig.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
+            if (mediaConfig.getRtpProxyPort() == 0) serverItemFromConfig.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
+            if (serverItem != null){
+                // 可能是同一个zlm但id发生了变化
+                if (!serverItem.getId().equals(zlmServerConfig.getGeneralMediaServerId())) {
+                    mediaServerMapper.delOne(serverItem.getId());
+                    redisUtil.del(VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItem.getId());
+
+                    String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItemFromConfig.getId();
+                    serverItemFromConfig.setSsrcConfig(new SsrcConfig(serverItemFromConfig.getId(), null, sipConfig.getSipDomain()));
+                    redisUtil.set(key, serverItemFromConfig);
+                    mediaServerMapper.add(serverItemFromConfig);
+                }else {
+                    mediaServerMapper.update(serverItemFromConfig);
                 }
-                serverItem.setUpdateTime(now);
-                mediaServerMapper.update(serverItem);
-                setZLMConfig(serverItem);
+            }else {
+                String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItemFromConfig.getId();
+                serverItemFromConfig.setSsrcConfig(new SsrcConfig(serverItemFromConfig.getId(), null, sipConfig.getSipDomain()));
+                redisUtil.set(key, serverItemFromConfig);
+                mediaServerMapper.add(serverItemFromConfig);
             }
+            resetOnlineServerItem(serverItemFromConfig);
+            setZLMConfig(serverItemFromConfig);
         }else {
-            if (zlmServerConfig.getGeneralMediaServerId().equals(mediaConfig.getId())
-                    || (zlmServerConfig.getIp().equals(mediaConfig.getIp()) && zlmServerConfig.getHttpPort() == mediaConfig.getHttpPort())) {
-                mediaConfig.setId(zlmServerConfig.getGeneralMediaServerId());
-                mediaConfig.setCreateTime(now);
-                mediaConfig.setUpdateTime(now);
-                serverItem = mediaConfig.getMediaSerItem();
-                mediaServerMapper.add(mediaConfig);
-            }else {
-                // 一个新的zlm接入wvp
-                serverItem = new MediaServerItem(zlmServerConfig, sipIp);
-                serverItem.setCreateTime(now);
-                serverItem.setUpdateTime(now);
+            String now = this.format.format(new Date(System.currentTimeMillis()));
+            if (serverItem == null){
+                    // 一个新的zlm接入wvp
+                    serverItem = new MediaServerItem(zlmServerConfig, sipConfig.getSipIp());
+                    serverItem.setCreateTime(now);
+                    serverItem.setUpdateTime(now);
+                String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItem.getId();
+                serverItem.setSsrcConfig(new SsrcConfig(serverItem.getId(), null, sipConfig.getSipDomain()));
+                redisUtil.set(key, serverItem);
+                // 存入数据库
                 mediaServerMapper.add(serverItem);
+                setZLMConfig(serverItem);
             }
+            resetOnlineServerItem(serverItem);
         }
-        // 更新缓存
-        if (zlmServerStatus.get(serverItem.getId()) == null) {
-            zlmServers.put(serverItem.getId(), serverItem);
-            zlmServerStatus.put(serverItem.getId(),0);
-        }
-        // 查询服务流数量
-        IMediaServerItem finalServerItem = serverItem;
-        zlmresTfulUtils.getMediaList(serverItem, null, null, "rtmp",(mediaList ->{
-            Integer code = mediaList.getInteger("code");
-            if (code == 0) {
-                JSONArray data = mediaList.getJSONArray("data");
-                if (data != null) {
-                    zlmServerStatus.put(finalServerItem.getId(),data.size());
-                }else {
-                    zlmServerStatus.put(finalServerItem.getId(),0);
-                }
-
-            }
-        }));
-
     }
 
-    /**
-     * 更新缓存
-     * @param mediaServerItem zlm服务
-     * @param count 在线数
-     * @param online 在线状态
-     */
     @Override
-    public void updateServerCatch(IMediaServerItem mediaServerItem, Integer count, Boolean online) {
-        if (mediaServerItem != null) {
-            zlmServers.put(mediaServerItem.getId(), mediaServerItem);
-            Collection<Integer> values = zlmServerStatus.values();
-            if (online != null && count != null) {
-                zlmServerStatus.put(mediaServerItem.getId(), count);
-            }
+    public void resetOnlineServerItem(MediaServerItem serverItem) {
+        // 更新缓存
+        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
+        // 使用zset的分数作为当前并发量, 默认值设置为0
+        if (redisUtil.zScore(key, serverItem.getId()) == null) {  // 不存在则设置默认值 已存在则重置
+            redisUtil.zAdd(key, serverItem.getId(), 0L);
+            // 查询服务流数量
+            zlmresTfulUtils.getMediaList(serverItem, null, null, "rtmp",(mediaList ->{
+                Integer code = mediaList.getInteger("code");
+                if (code == 0) {
+                    JSONArray data = mediaList.getJSONArray("data");
+                    if (data != null) {
+                        redisUtil.zAdd(key, serverItem.getId(), data.size());
+                    }
+                }
+            }));
+        }else {
+            clearRTPServer(serverItem);
         }
+
     }
 
+
     @Override
     public void addCount(String mediaServerId) {
-        if (zlmServerStatus.get(mediaServerId) != null) {
-            zlmServerStatus.put(mediaServerId, zlmServerStatus.get(mediaServerId) + 1);
-        }
+        if (mediaServerId == null) return;
+        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
+        Double aDouble = redisUtil.zScore(key, mediaServerId);
+        redisUtil.zIncrScore(key, mediaServerId, 1);
+
     }
 
     @Override
     public void removeCount(String mediaServerId) {
-        if (zlmServerStatus.get(mediaServerId) != null) {
-            zlmServerStatus.put(mediaServerId, zlmServerStatus.get(mediaServerId) - 1);
-        }
+        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
+        redisUtil.zIncrScore(key, mediaServerId, - 1);
     }
 
     /**
@@ -262,35 +334,18 @@ public class MediaServerServiceImpl implements IMediaServerService {
      * @return MediaServerItem
      */
     @Override
-    public IMediaServerItem getMediaServerForMinimumLoad() {
-        int mediaCount = -1;
-        String key = null;
-        System.out.println(JSON.toJSONString(zlmServerStatus));
-        if (zlmServerStatus.size() == 1) {
-            Map.Entry entry = zlmServerStatus.entrySet().iterator().next();
-            key= (String) entry.getKey();
-        }else {
-            for (String id : zlmServerStatus.keySet()) {
-                if (key == null) {
-                    key = id;
-                    mediaCount = zlmServerStatus.get(id);
-                }
-                if (zlmServerStatus.get(id) == 0) {
-                    key = id;
-                    break;
-                }else if (mediaCount >= zlmServerStatus.get(id)){
-                    mediaCount = zlmServerStatus.get(id);
-                    key = id;
-                }
-            }
-        }
+    public MediaServerItem getMediaServerForMinimumLoad() {
+        String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
 
-        if (key == null) {
+        if (redisUtil.zSize(key)  == null || redisUtil.zSize(key) == 0) {
             logger.info("获取负载最低的节点时无在线节点");
-            return null;
-        }else{
-            return  zlmServers.get(key);
         }
+
+        // 获取分数最低的,及并发最低的
+        Set<Object> objects = redisUtil.ZRange(key, 0, -1);
+        ArrayList<Object> MediaServerObjectS = new ArrayList<>(objects);
+        String mediaServerId = (String)MediaServerObjectS.get(0);
+        return getOne(mediaServerId);
     }
 
     /**
@@ -298,7 +353,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
      * @param mediaServerItem 服务ID
      */
     @Override
-    public void setZLMConfig(IMediaServerItem mediaServerItem) {
+    public void setZLMConfig(MediaServerItem mediaServerItem) {
         logger.info("[ {} ]-[ {}:{} ]设置zlm",
                 mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
         String protocol = sslEnabled ? "https" : "http";
@@ -333,8 +388,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
             logger.info("[ {} ]-[ {}:{} ]设置zlm成功",
                     mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
         }else {
-            logger.info("[ {} ]-[ {}:{} ]设置zlm失败" + responseJSON.getString("msg"),
+            logger.info("[ {} ]-[ {}:{} ]设置zlm失败",
                     mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
         }
     }
+
+
 }

+ 3 - 5
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java

@@ -4,9 +4,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -33,14 +31,14 @@ public class MediaServiceImpl implements IMediaService {
 
 
     @Override
-    public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks) {
+    public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks) {
         return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null);
     }
 
     @Override
     public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) {
         StreamInfo streamInfo = null;
-        IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+        MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
         if (mediaInfo == null) {
             return streamInfo;
         }
@@ -63,7 +61,7 @@ public class MediaServiceImpl implements IMediaService {
     }
 
     @Override
-    public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) {
+    public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) {
         StreamInfo streamInfoResult = new StreamInfo();
         streamInfoResult.setStreamId(stream);
         streamInfoResult.setApp(app);

+ 43 - 22
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -14,11 +14,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
 import com.genersoft.iot.vmp.service.IMediaService;
@@ -53,6 +54,9 @@ public class PlayServiceImpl implements IPlayService {
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
+    @Autowired
+    private RedisUtil redis;
+
     @Autowired
     private DeferredResultHolder resultHolder;
 
@@ -73,7 +77,7 @@ public class PlayServiceImpl implements IPlayService {
 
 
     @Override
-    public PlayResult play(IMediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) {
+    public PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) {
         PlayResult playResult = new PlayResult();
         if (mediaServerItem == null) {
             RequestMessage msg = new RequestMessage();
@@ -97,14 +101,21 @@ public class PlayServiceImpl implements IPlayService {
         // 超时处理
         result.onTimeout(()->{
             logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId));
-            // 释放rtpserver
-            mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
             RequestMessage msg = new RequestMessage();
             msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid());
             WVPResult wvpResult = new WVPResult();
             wvpResult.setCode(-1);
-            wvpResult.setMsg("Timeout");
+            SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
+            if (dialog != null) {
+                wvpResult.setMsg("收流超时,请稍候重试");
+            }else {
+                wvpResult.setMsg("点播超时,请稍候重试");
+            }
             msg.setData(wvpResult);
+            // 点播超时回复BYE
+            cmder.streamByeCmd(device.getDeviceId(), channelId);
+            // 释放rtpserver
+            mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
             resultHolder.invokeResult(msg);
         });
         result.onCompletion(()->{
@@ -131,7 +142,7 @@ public class PlayServiceImpl implements IPlayService {
                     WVPResult wvpResult = (WVPResult)responseEntity.getBody();
                     if (wvpResult.getCode() == 0) {
                         StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
-                        IMediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
+                        MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
                         String streamUrl = streamInfoForSuccess.getFmp4();
                         // 请求截图
                         zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
@@ -142,14 +153,23 @@ public class PlayServiceImpl implements IPlayService {
             }
         });
         if (streamInfo == null) {
+            SSRCInfo ssrcInfo;
+            String streamId = null;
+            if (mediaServerItem.isRtpEnable()) {
+                streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
+            }
+
+            ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId);
+
             // 发送点播消息
-            cmder.playStreamCmd(mediaServerItem, device, channelId, (IMediaServerItem mediaServerItemInUse, JSONObject response) -> {
+            cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
                 logger.info("收到订阅消息: " + response.toJSONString());
                 onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid.toString());
                 if (hookEvent != null) {
                     hookEvent.response(mediaServerItem, response);
                 }
             }, (event) -> {
+                // 点播返回sip错误
                 RequestMessage msg = new RequestMessage();
                 msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
                 Response response = event.getResponse();
@@ -162,6 +182,7 @@ public class PlayServiceImpl implements IPlayService {
                 if (errorEvent != null) {
                     errorEvent.response(event);
                 }
+
             });
         } else {
             String streamId = streamInfo.getStreamId();
@@ -176,7 +197,7 @@ public class PlayServiceImpl implements IPlayService {
                 return playResult;
             }
             String mediaServerId = streamInfo.getMediaServerId();
-            IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+            MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 
             JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
             if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
@@ -194,9 +215,17 @@ public class PlayServiceImpl implements IPlayService {
                     hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
                 }
             } else {
+                // TODO 点播前是否重置状态
                 redisCatchStorage.stopPlay(streamInfo);
                 storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
-                cmder.playStreamCmd(mediaServerItem, device, channelId, (IMediaServerItem mediaServerItemInuse, JSONObject response) -> {
+                SSRCInfo ssrcInfo;
+                String streamId2 = null;
+                if (mediaServerItem.isRtpEnable()) {
+                    streamId2 = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
+                }
+                ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId2);
+
+                cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
                     logger.info("收到订阅消息: " + response.toJSONString());
                     onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
                 }, (event) -> {
@@ -218,7 +247,7 @@ public class PlayServiceImpl implements IPlayService {
     }
 
     @Override
-    public void onPublishHandlerForPlay(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
+    public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
         RequestMessage msg = new RequestMessage();
         msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
         StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
@@ -228,14 +257,6 @@ public class PlayServiceImpl implements IPlayService {
                 deviceChannel.setStreamId(streamInfo.getStreamId());
                 storager.startPlay(deviceId, channelId, streamInfo.getStreamId());
             }
-            ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
-            SIPDialog dialog = (SIPDialog)transaction.getDialog();
-            StreamInfo.TransactionInfo transactionInfo = new StreamInfo.TransactionInfo();
-            transactionInfo.callId = dialog.getCallId().getCallId();
-            transactionInfo.localTag = dialog.getLocalTag();
-            transactionInfo.remoteTag = dialog.getRemoteTag();
-            transactionInfo.branch = dialog.getFirstTransactionInt().getBranchId();
-            streamInfo.setTransactionInfo(transactionInfo);
             redisCatchStorage.startPlay(streamInfo);
             msg.setData(JSON.toJSONString(streamInfo));
 
@@ -254,10 +275,10 @@ public class PlayServiceImpl implements IPlayService {
     }
 
     @Override
-    public IMediaServerItem getNewMediaServerItem(Device device) {
+    public MediaServerItem getNewMediaServerItem(Device device) {
         if (device == null) return null;
         String mediaServerId = device.getMediaServerId();
-        IMediaServerItem mediaServerItem = null;
+        MediaServerItem mediaServerItem = null;
         if (mediaServerId == null) {
             mediaServerItem = mediaServerService.getMediaServerForMinimumLoad();
         }else {
@@ -270,7 +291,7 @@ public class PlayServiceImpl implements IPlayService {
     }
 
     @Override
-    public void onPublishHandlerForPlayBack(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
+    public void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
         RequestMessage msg = new RequestMessage();
         msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
         StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
@@ -285,7 +306,7 @@ public class PlayServiceImpl implements IPlayService {
         }
     }
 
-    public StreamInfo onPublishHandler(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
+    public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
         String streamId = resonse.getString("stream");
         JSONArray tracks = resonse.getJSONArray("tracks");
         StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks);

+ 4 - 5
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java

@@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.service.impl;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
 import com.genersoft.iot.vmp.service.IGbStreamService;
@@ -58,7 +57,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
 
     @Override
     public String save(StreamProxyItem param) {
-        IMediaServerItem mediaInfo;
+        MediaServerItem mediaInfo;
         if ("auto".equals(param.getMediaServerId())){
             mediaInfo = mediaServerService.getMediaServerForMinimumLoad();
         }else {
@@ -120,7 +119,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     @Override
     public JSONObject addStreamProxyToZlm(StreamProxyItem param) {
         JSONObject result = null;
-        IMediaServerItem mediaServerItem = null;
+        MediaServerItem mediaServerItem = null;
         if (param.getMediaServerId() == null) {
             logger.warn("添加代理时MediaServerId 为null");
             return null;
@@ -141,7 +140,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     @Override
     public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) {
         if (param ==null) return null;
-        IMediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
+        MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
         JSONObject result = zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream());
         return result;
     }
@@ -198,7 +197,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
     }
 
     @Override
-    public JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem) {
+    public JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem) {
         JSONObject result = new JSONObject();
         JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
         if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0

+ 2 - 3
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java

@@ -5,7 +5,6 @@ import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.TypeReference;
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
@@ -43,7 +42,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
     private IMediaServerService mediaServerService;
 
     @Override
-    public List<StreamPushItem> handleJSON(String jsonData, IMediaServerItem mediaServerItem) {
+    public List<StreamPushItem> handleJSON(String jsonData, MediaServerItem mediaServerItem) {
         if (jsonData == null) return null;
 
         Map<String, StreamPushItem> result = new HashMap<>();
@@ -98,7 +97,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
     @Override
     public boolean removeFromGB(GbStream stream) {
         int del = gbStreamMapper.del(stream.getApp(), stream.getStream());
-        IMediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId());
+        MediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId());
         JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, stream.getApp(), stream.getStream());
         if (mediaList == null) {
             streamPushMapper.del(stream.getApp(), stream.getStream());

+ 0 - 2
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java

@@ -5,8 +5,6 @@ import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 
 import java.util.List;
 import java.util.Map;

+ 0 - 1
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java

@@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.storager;
 import java.util.List;
 
 import com.genersoft.iot.vmp.gb28181.bean.*;
-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.StreamProxyItem;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;

+ 5 - 5
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java

@@ -1,7 +1,5 @@
 package com.genersoft.iot.vmp.storager.dao;
 
-import com.genersoft.iot.vmp.conf.MediaConfig;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Mapper;
@@ -35,6 +33,7 @@ public interface MediaServerMapper {
             "rtpEnable, " +
             "rtpPortRange, " +
             "recordAssistPort, " +
+            "defaultServer, " +
             "createTime, " +
             "updateTime" +
             ") VALUES " +
@@ -57,9 +56,10 @@ public interface MediaServerMapper {
             "${rtpEnable}, " +
             "'${rtpPortRange}', " +
             "${recordAssistPort}, " +
+            "${defaultServer}, " +
             "'${createTime}', " +
             "'${updateTime}')")
-    int add(IMediaServerItem mediaServerItem);
+    int add(MediaServerItem mediaServerItem);
 
     @Update(value = {" <script>" +
             "UPDATE media_server " +
@@ -83,7 +83,7 @@ public interface MediaServerMapper {
             "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
             "WHERE id='${id}'"+
             " </script>"})
-    int update(IMediaServerItem mediaServerItem);
+    int update(MediaServerItem mediaServerItem);
 
     @Select("SELECT * FROM media_server WHERE id='${id}'")
     MediaServerItem queryOne(String id);
@@ -92,7 +92,7 @@ public interface MediaServerMapper {
     List<MediaServerItem> queryAll();
 
     @Select("DELETE FROM media_server WHERE id='${id}'")
-    int delOne(String secret);
+    void delOne(String id);
 
     @Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}")
     MediaServerItem queryOneByHostAndPort(String host, int port);

+ 0 - 3
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java

@@ -3,10 +3,7 @@ package com.genersoft.iot.vmp.storager.impl;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import com.genersoft.iot.vmp.gb28181.bean.*;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
-import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;

+ 8 - 0
src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java

@@ -0,0 +1,8 @@
+package com.genersoft.iot.vmp.utils;
+
+public class ConfigConst {
+    /**
+     * 播流最大并发个数
+     */
+    public static final Integer MAX_STRTEAM_COUNT = 10000;
+}

+ 31 - 0
src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java

@@ -0,0 +1,31 @@
+package com.genersoft.iot.vmp.utils;
+
+import java.io.*;
+
+public class SerializeUtils {
+    public static byte[] serialize(Object obj){
+        byte[] bytes = null;
+        try {
+            ByteArrayOutputStream baos=new ByteArrayOutputStream();;
+            ObjectOutputStream oos=new ObjectOutputStream(baos);
+            oos.writeObject(obj);
+            bytes=baos.toByteArray();
+            baos.close();
+            oos.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return bytes;
+    }
+    public static Object deSerialize(byte[] bytes){
+        Object obj=null;
+        try {
+            ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
+            ObjectInputStream ois=new ObjectInputStream(bais);
+            obj=ois.readObject();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return obj;
+    }
+}

+ 97 - 0
src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java

@@ -0,0 +1,97 @@
+package com.genersoft.iot.vmp.utils.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+import java.util.Set;
+
+/**
+ * @Description:Jedis工具类
+ * @author: wangshaopeng@sunnybs.com
+ * @date: 2021年03月22日 下午8:27:29
+ */
+@Component
+public class JedisUtil {
+
+    @Autowired
+    private JedisPool jedisPool;
+
+    //    ============================== Key ==============================
+
+    /**
+     * 检查给定 key 是否存在。
+     *
+     * @param key
+     * @return
+     */
+    public Boolean exists(String key) {
+        Jedis jedis = null;
+        try {
+            jedis = jedisPool.getResource();
+            Boolean exists = jedis.exists(key);
+            return exists;
+        } finally {
+            returnToPool(jedis);
+        }
+    }
+
+
+    //    ============================== Set ==============================
+
+    /**
+     * SADD key member [member ...]
+     * 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
+     * 假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
+     * 当 key 不是集合类型时,返回一个错误。
+     */
+    public Long sadd(String key, String... members) {
+        Jedis jedis = null;
+        try {
+            jedis = jedisPool.getResource();
+            Long smove = jedis.sadd(key, members);
+            return smove;
+        } finally {
+            returnToPool(jedis);
+        }
+    }
+
+    /**
+     * SMEMBERS key
+     * 返回集合 key 中的所有成员。
+     * 不存在的 key 被视为空集合。
+     */
+    public Set<String> smembers(String key) {
+        Jedis jedis = null;
+        try {
+            jedis = jedisPool.getResource();
+            Set<String> smembers = jedis.smembers(key);
+            return smembers;
+        } finally {
+            returnToPool(jedis);
+        }
+    }
+
+
+    /**
+     * SREM key member1 [member2]
+     * 移除集合中一个或多个成员
+     */
+    public Long srem(String key, String... member) {
+        Jedis jedis = null;
+        try {
+            jedis = jedisPool.getResource();
+            Long srem = jedis.srem(key, member);
+            return srem;
+        } finally {
+            returnToPool(jedis);
+        }
+    }
+
+    private void returnToPool(Jedis jedis) {
+        if (jedis != null) {
+            jedis.close();
+        }
+    }
+}

+ 3 - 3
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java

@@ -415,10 +415,10 @@ public class RedisUtil {
      *
      * @param key
      * @param value
-     * @param score
+     * @param delta -1 表示减 1 表示加1
      */
-    public Double zIncrScore(Object key, Object value, double score) {
-        return redisTemplate.opsForZSet().incrementScore(key, value, score);
+    public Double zIncrScore(Object key, Object value, double delta) {
+        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
     }
 
     /**

+ 12 - 13
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@@ -2,13 +2,12 @@ package com.genersoft.iot.vmp.vmanager.gb28181.play;
 
 import com.alibaba.fastjson.JSONArray;
 import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -37,7 +36,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import org.springframework.web.context.request.async.DeferredResult;
 
-import java.util.Enumeration;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -89,7 +88,7 @@ public class PlayController {
 
 		// 获取可用的zlm
 		Device device = storager.queryVideoDevice(deviceId);
-		IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
+		MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
 		PlayResult playResult = playService.play(newMediaServerItem, deviceId, channelId, null, null);
 
 		return playResult.getResult();
@@ -174,7 +173,7 @@ public class PlayController {
 			logger.warn("视频转码API调用失败!, 视频流已经停止!");
 			return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK);
 		}
-		IMediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
+		MediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
 		JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
 		if (!rtpInfo.getBoolean("exist")) {
 			logger.warn("视频转码API调用失败!, 视频流已停止推流!");
@@ -219,7 +218,7 @@ public class PlayController {
 			result.put("msg", "mediaServerId is null");
 			return new ResponseEntity<String>( result.toJSONString(), HttpStatus.BAD_REQUEST);
 		}
-		IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+		MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 		if (mediaInfo == null) {
 			result.put("code", 0);
 			result.put("msg", "使用的流媒体已经停止运行");
@@ -307,16 +306,16 @@ public class PlayController {
 			logger.debug("获取所有的ssrc");
 		}
 		JSONArray objects = new JSONArray();
-		for(Map.Entry<String, String> entry: streamSession.getSsrcMap().entrySet()) {
-			System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
+		List<SsrcTransaction> allSsrc = streamSession.getAllSsrc();
+		for (SsrcTransaction transaction : allSsrc) {
 			JSONObject jsonObject = new JSONObject();
-			String[] keyArray = entry.getKey().split("_");
-			jsonObject.put("deviceId", keyArray[0]);
-			jsonObject.put("channelId", keyArray[1]);
-			jsonObject.put("ssrc", entry.getValue());
-			jsonObject.put("streamId", streamSession.getStreamIdMap().get(entry.getKey()));
+			jsonObject.put("deviceId", transaction.getDeviceId());
+			jsonObject.put("channelId", transaction.getChannelId());
+			jsonObject.put("ssrc", transaction.getSsrc());
+			jsonObject.put("streamId", transaction.getStreamId());
 			objects.add(jsonObject);
 		}
+
 		WVPResult<JSONObject> result = new WVPResult<>();
 		result.setCode(0);
 		result.setMsg("success");

+ 10 - 4
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java

@@ -3,9 +3,9 @@ package com.genersoft.iot.vmp.vmanager.gb28181.playback;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
-//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.service.IPlayService;
 import io.swagger.annotations.Api;
@@ -58,6 +58,9 @@ public class DownloadController {
 	@Autowired
 	private DeferredResultHolder resultHolder;
 
+	@Autowired
+	private IMediaServerService mediaServerService;
+
 	@ApiOperation("开始历史媒体下载")
 	@ApiImplicitParams({
 			@ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
@@ -90,7 +93,7 @@ public class DownloadController {
 			cmder.streamByeCmd(deviceId, channelId);
 		}
 		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
-		IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
+		MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
 		if (newMediaServerItem == null) {
 			logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId));
 			RequestMessage msg = new RequestMessage();
@@ -99,7 +102,10 @@ public class DownloadController {
 			resultHolder.invokeResult(msg);
 			return result;
 		}
-		cmder.downloadStreamCmd(newMediaServerItem, device, channelId, startTime, endTime, downloadSpeed, (IMediaServerItem mediaServerItem, JSONObject response) -> {
+
+		SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null);
+
+		cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (MediaServerItem mediaServerItem, JSONObject response) -> {
 			logger.info("收到订阅消息: " + response.toJSONString());
 			playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
 		}, event -> {

+ 18 - 4
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java

@@ -4,8 +4,9 @@ import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 //import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.service.IPlayService;
 import io.swagger.annotations.Api;
@@ -58,6 +59,9 @@ public class PlaybackController {
 	@Autowired
 	private DeferredResultHolder resultHolder;
 
+	@Autowired
+	private IMediaServerService mediaServerService;
+
 	@ApiOperation("开始视频回放")
 	@ApiImplicitParams({
 			@ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
@@ -74,6 +78,15 @@ public class PlaybackController {
 		}
 		UUID uuid = UUID.randomUUID();
 		DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
+
+		Device device = storager.queryVideoDevice(deviceId);
+		if (device == null) {
+			result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
+			return result;
+		}
+		MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
+		SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null);
+
 		// 超时处理
 		result.onTimeout(()->{
 			logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
@@ -82,14 +95,14 @@ public class PlaybackController {
 			msg.setData("Timeout");
 			resultHolder.invokeResult(msg);
 		});
-		Device device = storager.queryVideoDevice(deviceId);
+
 		StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId);
 		if (streamInfo != null) {
 			// 停止之前的回放
 			cmder.streamByeCmd(deviceId, channelId);
 		}
 		resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
-		IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
+
 		if (newMediaServerItem == null) {
 			logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
 			RequestMessage msg = new RequestMessage();
@@ -98,7 +111,8 @@ public class PlaybackController {
 			resultHolder.invokeResult(msg);
 			return result;
 		}
-		cmder.playbackStreamCmd(newMediaServerItem, device, channelId, startTime, endTime, (IMediaServerItem mediaServerItem, JSONObject response) -> {
+
+		cmder.playbackStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, (MediaServerItem mediaServerItem, JSONObject response) -> {
 			logger.info("收到订阅消息: " + response.toJSONString());
 			playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
 		}, event -> {

+ 23 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java

@@ -0,0 +1,23 @@
+package com.genersoft.iot.vmp.vmanager.gb28181.session;
+
+public enum PlayTypeEnum {
+
+    PLAY("0", "直播"),
+    PLAY_BACK("1", "回放");
+
+    private String value;
+    private String name;
+
+    PlayTypeEnum(String value, String name) {
+        this.value = value;
+        this.name = name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 16 - 8
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java

@@ -1,10 +1,8 @@
 package com.genersoft.iot.vmp.vmanager.server;
 
 import com.genersoft.iot.vmp.VManageBootstrap;
-import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import gov.nist.javax.sip.SipStackImpl;
@@ -17,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
 import javax.sip.ListeningPoint;
 import javax.sip.ObjectInUseException;
 import javax.sip.SipProvider;
-import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
@@ -38,19 +35,30 @@ public class ServerController {
     @ApiOperation("流媒体服务列表")
     @GetMapping(value = "/media_server/list")
     @ResponseBody
-    public WVPResult<List<IMediaServerItem>> getMediaServerList(){
-        WVPResult<List<IMediaServerItem>> result = new WVPResult<>();
+    public WVPResult<List<MediaServerItem>> getMediaServerList(){
+        WVPResult<List<MediaServerItem>> result = new WVPResult<>();
         result.setCode(0);
         result.setMsg("success");
         result.setData(mediaServerService.getAll());
         return result;
     }
 
+    @ApiOperation("在线流媒体服务列表")
+    @GetMapping(value = "/media_server/online/list")
+    @ResponseBody
+    public WVPResult<List<MediaServerItem>> getOnlineMediaServerList(){
+        WVPResult<List<MediaServerItem>> result = new WVPResult<>();
+        result.setCode(0);
+        result.setMsg("success");
+        result.setData(mediaServerService.getAllOnline());
+        return result;
+    }
+
     @ApiOperation("获取流媒体服务")
     @GetMapping(value = "/media_server/one/{id}")
     @ResponseBody
-    public WVPResult<IMediaServerItem> getMediaServer(@PathVariable String id){
-        WVPResult<IMediaServerItem> result = new WVPResult<>();
+    public WVPResult<MediaServerItem> getMediaServer(@PathVariable String id){
+        WVPResult<MediaServerItem> result = new WVPResult<>();
         result.setCode(0);
         result.setMsg("success");
         result.setData(mediaServerService.getOne(id));

+ 1 - 3
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.vmanager.streamProxy;
 
 import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
 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;
@@ -9,7 +8,6 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.service.IStreamProxyService;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.github.pagehelper.PageInfo;
-import io.netty.util.internal.StringUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
@@ -86,7 +84,7 @@ public class StreamProxyController {
     public WVPResult getFFmpegCMDs(@RequestParam String mediaServerId){
         logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId );
 
-        IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+        MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
         JSONObject data = streamProxyService.getFFmpegCMDs(mediaServerItem);
         WVPResult<JSONObject> result = new WVPResult<>();
         result.setCode(0);

+ 7 - 0
src/main/resources/all-application.yml

@@ -17,6 +17,12 @@ spring:
         password:
         # [可选] 超时时间
         timeout: 10000
+        # [可选] 一个pool最多可分配多少个jedis实例
+        poolMaxTotal: 1000
+        # [可选] 一个pool最多有多少个状态为idle(空闲)的jedis实例
+        poolMaxIdle: 500
+        # [可选] 最大的等待时间(秒)
+        poolMaxWait: 5
     # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置
     datasource:
         # 使用mysql 打开23-28行注释, 删除29-36行
@@ -124,6 +130,7 @@ logging:
     level:
         com.genersoft.iot: debug
         com.genersoft.iot.vmp.storager.dao: info
+        com.genersoft.iot.vmp.gb28181: info
 # [根据业务需求配置]
 user-settings:
     # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true

+ 2 - 0
src/main/resources/application-dev.yml

@@ -76,6 +76,8 @@ logging:
     level:
         com.genersoft.iot: debug
         com.genersoft.iot.vmp.storager.dao: info
+        com.genersoft.iot.vmp.gb28181: info
+
 # [根据业务需求配置]
 user-settings:
     # 推流直播是否录制

二進制
src/main/resources/wvp.sqlite


+ 1 - 1
web_src/src/components/service/MediaServer.js

@@ -9,7 +9,7 @@ class MediaServer{
   getMediaServerList(callback){
     this.$axios({
       method: 'get',
-      url:`/api/server/media_server/list`,
+      url:`/api/server/media_server/online/list`,
     }).then(function (res) {
       if (typeof (callback) == "function") callback(res.data)
     }).catch(function (error) {