From 3469271ec25c69e4528b085ba3be7d9d85ec519e Mon Sep 17 00:00:00 2001 From: 64850858 <648540858@qq.com> Date: Mon, 26 Jul 2021 11:40:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9B=86=E7=BE=A4=E6=96=B9?= =?UTF-8?q?=E6=A1=88=EF=BC=8C=20=E6=AF=8F=E4=B8=AAzlm=E4=B8=80=E5=A5=97ssr?= =?UTF-8?q?c=EF=BC=9B=20=E4=BC=98=E5=8C=96=E9=9B=86=E7=BE=A4=E4=B8=8B?= =?UTF-8?q?=E7=9A=84docker=E6=8E=A5=E5=85=A5=E9=80=BB=E8=BE=91=EF=BC=9B=20?= =?UTF-8?q?=E6=9B=B4=E6=AD=A3sql=E8=84=9A=E6=9C=AC=EF=BC=9B=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=87=8D=E5=90=AF=E4=B8=8D=E8=AE=BE=E7=BD=AE=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E7=A6=BB=E7=BA=BF=E3=80=82=E9=87=8D=E5=90=AFSIP?= =?UTF-8?q?=E4=BA=8B=E5=8A=A1=E4=B8=8D=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 + sql/mysql.sql | 204 ++++----- .../iot/vmp/common/VideoManagerConstants.java | 6 + .../genersoft/iot/vmp/conf/MediaConfig.java | 190 +-------- .../iot/vmp/conf/ProxyServletConfig.java | 20 +- .../genersoft/iot/vmp/conf/RedisConfig.java | 37 +- .../iot/vmp/conf/SipDeviceRunner.java | 31 -- .../genersoft/iot/vmp/gb28181/SipLayer.java | 28 +- .../iot/vmp/gb28181/bean/SsrcTransaction.java | 70 ++++ .../PlatformNotRegisterEventLister.java | 3 +- .../iot/vmp/gb28181/session/SsrcConfig.java | 140 +++++++ .../iot/vmp/gb28181/session/SsrcUtil.java | 101 ----- .../session/VideoStreamSessionManager.java | 115 ++++-- .../gb28181/transmit/SIPProcessorFactory.java | 2 +- .../gb28181/transmit/cmd/ISIPCommander.java | 10 +- .../transmit/cmd/impl/SIPCommander.java | 211 ++++------ .../request/impl/AckRequestProcessor.java | 3 +- .../request/impl/ByeRequestProcessor.java | 3 +- .../request/impl/InviteRequestProcessor.java | 5 +- .../impl/InviteResponseProcessor.java | 14 +- .../vmp/media/zlm/ZLMHttpHookListener.java | 24 +- .../vmp/media/zlm/ZLMHttpHookSubscribe.java | 8 +- .../vmp/media/zlm/ZLMMediaListManager.java | 9 +- .../iot/vmp/media/zlm/ZLMRESTfulUtils.java | 45 +- .../vmp/media/zlm/ZLMRTPServerFactory.java | 80 ++-- .../iot/vmp/media/zlm/ZLMRunner.java | 34 +- .../vmp/media/zlm/dto/IMediaServerItem.java | 92 ----- .../vmp/media/zlm/dto/MediaServerItem.java | 54 ++- .../iot/vmp/service/IMediaServerService.java | 30 +- .../iot/vmp/service/IMediaService.java | 5 +- .../iot/vmp/service/IPlayService.java | 9 +- .../iot/vmp/service/IStreamProxyService.java | 3 +- .../iot/vmp/service/IStreamPushService.java | 4 +- .../iot/vmp/service/bean/SSRCInfo.java | 38 ++ .../service/impl/MediaServerServiceImpl.java | 391 ++++++++++-------- .../vmp/service/impl/MediaServiceImpl.java | 8 +- .../iot/vmp/service/impl/PlayServiceImpl.java | 65 ++- .../service/impl/StreamProxyServiceImpl.java | 9 +- .../service/impl/StreamPushServiceImpl.java | 5 +- .../iot/vmp/storager/IRedisCatchStorage.java | 2 - .../vmp/storager/IVideoManagerStorager.java | 1 - .../vmp/storager/dao/MediaServerMapper.java | 10 +- .../storager/impl/RedisCatchStorageImpl.java | 3 - .../genersoft/iot/vmp/utils/ConfigConst.java | 8 + .../iot/vmp/utils/SerializeUtils.java | 31 ++ .../iot/vmp/utils/redis/JedisUtil.java | 97 +++++ .../iot/vmp/utils/redis/RedisUtil.java | 6 +- .../vmanager/gb28181/play/PlayController.java | 25 +- .../gb28181/playback/DownloadController.java | 14 +- .../gb28181/playback/PlaybackController.java | 22 +- .../gb28181/session/PlayTypeEnum.java | 23 ++ .../vmp/vmanager/server/ServerController.java | 24 +- .../streamProxy/StreamProxyController.java | 4 +- src/main/resources/all-application.yml | 7 + src/main/resources/application-dev.yml | 2 + src/main/resources/wvp.sqlite | Bin 126976 -> 126976 bytes web_src/src/components/service/MediaServer.js | 2 +- 57 files changed, 1318 insertions(+), 1076 deletions(-) delete mode 100644 src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java delete mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java delete mode 100644 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java create mode 100644 src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java create mode 100644 src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java create mode 100644 src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java create mode 100644 src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java create mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java diff --git a/pom.xml b/pom.xml index 93b42579..077f8f13 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ UTF-8 MMddHHmm 3.1.1 + 3.1.0 5.2.0 @@ -80,6 +81,12 @@ spring-boot-starter-security + + redis.clients + jedis + ${jedis-version} + + com.alibaba diff --git a/sql/mysql.sql b/sql/mysql.sql index 0bb4c03a..5d8bbfc8 100644 --- a/sql/mysql.sql +++ b/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 -); \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java index 57493143..cb2b1732 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/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_"; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java index f2a0bcd0..744e56f0 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java +++ b/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); + + 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()))); + return mediaServerItem; } - @Override - public String getUpdateTime() { - return updateTime; - } - - @Override - public void setUpdateTime(String updateTime) { - this.updateTime = updateTime; - } - - @Override - public String getCreateTime() { - return createTime; - } - - @Override - public void setCreateTime(String createTime) { - this.createTime = createTime; - } - - @Override - public int getCount() { - return count; - } - - @Override - public void setCount(int count) { - this.count = count; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java index c2fb3262..ab327b0e 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java +++ b/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; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java index 0b3e23be..ee907882 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java +++ b/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 redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate 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; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java deleted file mode 100644 index 2b7ab1a9..00000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java +++ /dev/null @@ -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(); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java index 0a76f698..e71ac054 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/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 processQueue = new LinkedBlockingQueue(10000); + LinkedBlockingQueue 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 - } /** diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java new file mode 100644 index 00000000..47d4d8ae --- /dev/null +++ b/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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java index aed21009..0b4bc886 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java +++ b/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 param = new HashMap<>(); param.put("vhost", "__defaultVhost__"); param.put("app", app.toString()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java new file mode 100644 index 00000000..e96e6a5b --- /dev/null +++ b/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 isUsed; + /** + * zlm流媒体服务器可用会话句柄 + */ + private List notUsed; + + public SsrcConfig() { + } + + public SsrcConfig(String mediaServerId, Set 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 getIsUsed() { + return isUsed; + } + + public void setIsUsed(List isUsed) { + this.isUsed = isUsed; + } + + public List getNotUsed() { + return notUsed; + } + + public void setNotUsed(List notUsed) { + this.notUsed = notUsed; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java deleted file mode 100644 index 73779979..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java +++ /dev/null @@ -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 isUsed; - - private static List notUsed; - - private static void init() { - SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig"); - ssrcPrefix = sipConfig.getSipDomain().substring(3, 8); - isUsed = new ArrayList(); - notUsed = new ArrayList(); - 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; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java index 9e402e1e..f9d3a694 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java +++ b/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 sessionMap = new ConcurrentHashMap<>(); - private ConcurrentHashMap ssrcMap = new ConcurrentHashMap<>(); - private ConcurrentHashMap streamIdMap = new ConcurrentHashMap<>(); + @Autowired + private RedisUtil redisUtil; - public String createPlaySsrc(){ - return SsrcUtil.getPlaySsrc(); + 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 String createPlayBackSsrc(){ - return SsrcUtil.getPlayBackSsrc(); - } - - 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 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 SsrcTransaction getSsrcTransaction(String deviceId, String channelId){ + SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId); + return ssrcTransaction; } public String getStreamId(String deviceId, String channelId){ - return streamIdMap.get(deviceId + "_" + 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 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) { - sessionMap.remove(deviceId + "_" + channelId); - if (ssrcMap.get(deviceId + "_" + channelId) != null) { - SsrcUtil.releaseSsrc(ssrcMap.get(deviceId + "_" + channelId)); + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); + if (ssrcTransaction == null) return; + redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId); + } + + public List getAllSsrc() { + List ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX)); + List 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); } - ssrcMap.remove(deviceId + "_" + channelId); - streamIdMap.remove(deviceId + "_" + channelId); - } - - public ConcurrentHashMap getSessionMap() { - return sessionMap; - } - - public ConcurrentHashMap getSsrcMap() { - return ssrcMap; - } - - public ConcurrentHashMap getStreamIdMap() { - return streamIdMap; + return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java index e2ff0f4f..30c9e95c 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java +++ b/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; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index 99da23d6..10611dc4 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/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); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index d2318dcd..f1223d50 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/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(); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java index 6f809718..7d9e5f7b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java +++ b/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("已获取设备推流[{}/{}],开始向上级推流[{}:{}]", diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java index 50e8c323..ffa53778 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java +++ b/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) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java index 7219dbb3..1aa00794 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java +++ b/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) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java index 7368c367..bcc76269 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java +++ b/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(); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index b7aebf42..478772aa 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/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); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java index 62b18f5b..be69df1f 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java +++ b/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> 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(); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java index 38763094..7fc0a088 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java +++ b/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->{ diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java index 8e12e574..af8793a2 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/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 param, RequestCallback callback) { + public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map 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 param, String targetPath, String fileName) { + public void sendPostForImg(MediaServerItem mediaServerItem, String api, Map 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 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 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 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 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 param){ + public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map param){ return sendPost(mediaServerItem,"setServerConfig",param, null); } - public JSONObject openRtpServer(IMediaServerItem mediaServerItem,Map param){ + public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map param){ return sendPost(mediaServerItem, "openRtpServer",param, null); } - public JSONObject closeRtpServer(IMediaServerItem mediaServerItem,Map param) { + public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map 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 param) { + public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map param) { return sendPost(mediaServerItem, "startSendRtp",param, null); } - public JSONObject stopSendRtp(IMediaServerItem mediaServerItem,Map param) { + public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map 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 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 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 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 param = new HashMap<>(); param.put("url", flvUrl); param.put("timeout_sec", timeout_sec); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java index 5919c9f7..0ddd9047 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java +++ b/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 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 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 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 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 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, Mapparam) { + public Boolean startSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { 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,Mapparam) { + public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { Boolean result = false; JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param); if (jsonObject == null) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index d06c6c32..67b605c3 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/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 all = mediaServerService.getAll(); + List 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; diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java deleted file mode 100644 index 64971628..00000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java +++ /dev/null @@ -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); -} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java index b56881f7..d1578d17 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java +++ b/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 int count; + private SsrcConfig ssrcConfig; + + private int currentPort; + + + /** + * 每一台ZLM都有一套独立的SSRC列表 + * 在ApplicationCheckRunner里对mediaServerSsrcMap进行初始化 + */ + private HashMap 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 getMediaServerSsrcMap() { + return mediaServerSsrcMap; } - public void setCount(int count) { - this.count = count; + public void setMediaServerSsrcMap(HashMap mediaServerSsrcMap) { + this.mediaServerSsrcMap = mediaServerSsrcMap; } + + public SsrcConfig getSsrcConfig() { + return ssrcConfig; + } + + public void setSsrcConfig(SsrcConfig ssrcConfig) { + this.ssrcConfig = ssrcConfig; + } + + public int getCurrentPort() { + return currentPort; + } + + public void setCurrentPort(int currentPort) { + this.currentPort = currentPort; + } + + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java index 7a57bed7..be9959a8 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java +++ b/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 getAll(); + List getAll(); - IMediaServerItem getOne(String generalMediaServerId); + List 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); + MediaServerItem getMediaServerForMinimumLoad(); - IMediaServerItem getMediaServerForMinimumLoad(); + void setZLMConfig(MediaServerItem mediaServerItem); - void setZLMConfig(IMediaServerItem 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); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java index 868a8310..54f8315e 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java +++ b/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); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java index fdc86d6e..9e5c4442 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java +++ b/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); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java index fa79d696..12e48983 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java +++ b/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); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java index eaf24904..899da98b 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java +++ b/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 handleJSON(String json, IMediaServerItem mediaServerItem); + List handleJSON(String json, MediaServerItem mediaServerItem); /** * 将应用名和流ID加入国标关联 diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java new file mode 100644 index 00000000..faab1c80 --- /dev/null +++ b/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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java index 52c9f857..3d666658 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java +++ b/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 zlmServers = new HashMap<>(); // 所有数据库的zlm的缓存 - private Map 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 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 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() + ) + ); + } + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItemInDataBase.getId(); + redisUtil.set(key, mediaServerItemInDataBase); } @Override - public List getAll() { - if (zlmServers.size() == 0) { - init(); - } - List 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); + public List getAll() { + List result = new ArrayList<>(); + List 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 getAllOnline() { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX; + Set mediaServerIdSet = redisUtil.zRevRange(key, 0, -1); + List 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); + }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 { + 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); } - }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); - mediaServerMapper.add(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 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 (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) { + logger.info("获取负载最低的节点时无在线节点"); } - if (key == null) { - logger.info("获取负载最低的节点时无在线节点"); - return null; - }else{ - return zlmServers.get(key); - } + // 获取分数最低的,及并发最低的 + Set objects = redisUtil.ZRange(key, 0, -1); + ArrayList 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()); } } + + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java index 465ca88a..fb958616 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java +++ b/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); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index 85a4f48c..c21f599b 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/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); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java index fe49c3b9..bbcad1c3 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java +++ b/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 diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java index 3692e549..aabf35f3 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java +++ b/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 handleJSON(String jsonData, IMediaServerItem mediaServerItem) { + public List handleJSON(String jsonData, MediaServerItem mediaServerItem) { if (jsonData == null) return null; Map 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()); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java index a4edb088..f5b2b1b8 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/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; diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java index 97c37944..223a5e88 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ b/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; diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java index 475ec6d2..16ed4016 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java +++ b/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 = {" "}) - 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 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); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java index eafb8a0b..08eb9d13 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/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; diff --git a/src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java b/src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java new file mode 100644 index 00000000..125d8180 --- /dev/null +++ b/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; +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java b/src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java new file mode 100644 index 00000000..ae91ad59 --- /dev/null +++ b/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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java new file mode 100644 index 00000000..54e5422f --- /dev/null +++ b/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 smembers(String key) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + Set 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(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java index 5db3c7f4..dd5614f0 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java +++ b/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); } /** diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java index aae9cce9..d6545b5f 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java +++ b/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("未找到视频流信息, 视频流可能已经停止", 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( 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 entry: streamSession.getSsrcMap().entrySet()) { - System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); + List 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 result = new WVPResult<>(); result.setCode(0); result.setMsg("success"); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java index 218debfa..2ca4abaa 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java +++ b/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 -> { diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java index d2a30fa7..9fd05b2e 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java +++ b/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> result = new DeferredResult>(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 -> { diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java new file mode 100644 index 00000000..58fbb26f --- /dev/null +++ b/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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java index 45eeac84..76ad86c9 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java +++ b/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> getMediaServerList(){ - WVPResult> result = new WVPResult<>(); + public WVPResult> getMediaServerList(){ + WVPResult> 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> getOnlineMediaServerList(){ + WVPResult> 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 getMediaServer(@PathVariable String id){ - WVPResult result = new WVPResult<>(); + public WVPResult getMediaServer(@PathVariable String id){ + WVPResult result = new WVPResult<>(); result.setCode(0); result.setMsg("success"); result.setData(mediaServerService.getOne(id)); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java index f0996311..f8c01dd7 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java +++ b/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 result = new WVPResult<>(); result.setCode(0); diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml index 2552e01e..c4bd7ca2 100644 --- a/src/main/resources/all-application.yml +++ b/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 diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index b756be9b..dd2e0f76 100644 --- a/src/main/resources/application-dev.yml +++ b/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: # 推流直播是否录制 diff --git a/src/main/resources/wvp.sqlite b/src/main/resources/wvp.sqlite index 8e41983611287cf85bfed5a65ee7b373becf9a9f..500607cd2268575b3a762aa6a59e5256d11c560c 100644 GIT binary patch literal 126976 zcmeI)?Qa{$odwg-#lGtMow|2SPi@QIdFZ!ZrilFGbLtpgGy}LJu{s9N{#o=DuyL(%peRDHAOLAtp zl+wExO^B}|!Q7eM*_qG$c6Jt=P5Z&FONr49W8b32M(I*X(@Otf7^Tv=-;_$FW%jqo z{?4)A|6%{mvA^MuHvBOCUslG~@BZ@zwu4sxE8p#M{jas(FNW1W)!wfBqWaO?hZTEn zQ~SgDzi3pWbN@2Eq*=;oy;9a6ZD=JoaLCi-9glT@Y4vHCZkv&3QQjS9@6fc5tRNts z*(^wW`MvMdEBe}+_VaI0>(C>S6_bFD4$p1Aw0+Rp>a>i`)~(%^k;|;D)(rMT0$z@> zv)5_eX&tPmX@TVvqtkk;BQ_HfKPK^=TRTpgXapfOg1+aa`|>jSf%{IMs5{>deaj8h z1LQq+_NV9VhcO-2XmmDlutOfZHkqu8oVybh6)lcTi{?$smD^{H95p+a7g87;}10~g1b)$9Th|-96Q{(yO z#frYNqJ3~9jdl5W`SXGj<#J9I;%;F)9EIUAixhT+?B@DAaiZc7N@z?hzc_DL>tS!? z%H-MYws%To$J9@oJ*5DieO3X{ejGj>oO+Jr^m8Qa9J{9!z{6=f47zS_N;MP0Voii~ zPC(%8g#kGrmP6v(#IpwX+EdDjY1C3ird(+(pI7t13VLFw%I6cCU9nq9;wJQz_OPpm zUzkOgj<^U}Yb;9mg`Mu0J4-)u3Uh0Xn~P=r?#i?aO%77CDQ{hTwot3+%gfpcNrRr< z4+Pxk!@LsiqvR~stw;pBsJ%E;8hd(&*&r_RM3IN*mTr?;iPkhbUYgsa9Fl*9}qjy1^^js5P1kWxcgLjd9d?vpM=$JE>JG`sSwg zT-+hWuy6X|q3aPd3KN&QA-f0j+ekjOqR;^q`o6Q-= zcQd66hbrp^Z!YV{b#lmK@IKBG(Mw#7y8rlad>7|kFl}(q_tqVmxneCbN zV767us~0q_jgXw2!52%W>E| zLY_#7d#-YsCRUfw!JF1#I9Q7F#iYkZHxf@|*d(TVeWe#!iraN3r+Org(;6!CKN2{j z5ig4@KCIgVsp4+5HCc5&(NRh&m^$ruev}R!%6>&N8}-T%^-wN|tZL$>EYHqFEEM;w z;I%xdDMwba)py)b>dM@(FV2)3?M0#z|D%{%H->d!7s0nHW&M8RjEf*UbQJvAcKPIo za}|AQNqaw(f?gDUt{})y9iFD&=BCHH9MB@*$Jc(>jr}KViYQqYO?btaeows+!oYI_ z`N&~3=OV9o^2W!oVWj28#iKBxa%kqV`8?h7+{fd4y6DH@j9v-LTo~kv%Ed!2ck<>} zYph);>)&5GBNDRV?>u|@t@0<}{qtr1#P=xe|Ln;zA?$%Gut{!nPKk(I<|^-GR^BUh z{&3G8`tbt+2tWV=5P$##AOHafKmY;|fWQn3)Og$Q{6E7zjHW>V0uX=z1Rwwb2tWV= z5P$###NvLf{^wHt&-MSR|2sq=009U<00Izz00bZa0SG_<0ucBN2&@&?@zrYX_1%hg z{_;G3(HZOii^Y%%0SG_<0uX=z1Rwwb2tWV=5SS$atpA^--b7m<009U<00Izz00bZa z0SG_<0>uLO{=e7-sStnw1Rwwb2tWV=5P$##AOL|`62SNWv(%et3j`nl0SG_<0uX=z z1Rwwb2tc4%0N?)?yC4+;5P$##AOHafKmY;|fB*y_FiQgX{(qKw6K#P21Rwwb2tWV= z5P$##AOHaf6bs<_zt{z-5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwx`&!B+D>U@#?Eqn&+jw(X{0uX=z1Rwwb2tWV=5P$##AaI5N{{Mex03jO!5P$## zAOHafKmY;|fB*y_FarX3|9=KL5lw&q1Rwwb2tWV=5P$##AOHafoFRbk|IYwIHUuC5 z0SG_<0uX=z1Rwwb2tZ&41n~X;40Ixz009U<00Izz00bZa0SG_<0uVSuU~z7-RMY-g zs{d~BxAj5o_lsfmPqnuzzo>qs{ZagP{x2m@qxD~(QP9W8*8ZSX^^Fbfq)Dwqk3^nD zyJ75`y+hMJvVwqk1xc5-4_aHDmeJX|wc9cZcBrk^4EB?iu;Un=)>|DT2&obDJ#RhD zbjV}ZCdGNfB8zjykz0YYEoxZ2R}{Ox6%UMKGB8#r4_P18d&gqHME-9d+5+k;UwE2=M zt7c}ZB;Y+|?Cf<~cUlJmGq8M;B8$z0#E(gQ=T=@Oh37rf58QY9MBVvz=v!`3)K#Lm zo&D)~`(aFnH7e}q$+{>}5ET_IIxQ`_Ab$F+R--;!-zc_h3FZ$fs5m+TwjOXOUUlDv`IR-Z3e=5u_cFyOXHq`AiuygkQ)V14#gc4th7(3Vvdg`{N+@mIEK|9X6 z!JEta@9unAH#evCU$L;+EKaXJzfrE}jfVEYN_uHzdu4M#b?ZV=PiA|5oVjD}!tkx2 z-(@%Gek_~$jLb7`?cTp7qB(pLkXWX2AG_W*+eu10^g{bs+O`~rO&a7`gP5=>muX^k z2_3v?4Tkr1alV-JT-IChM21aby4P2Fk)^m@cXD1w;yA6LGT9@6!{;qhH50<-3-&-t zaiguts!Nl!>;j*hC+&BBlnx!r8H=Vd>XjkNV^0soqmkkzVxjor6ug!vHRZ@kw)&16 zN?plXYf+Lz=ts_EqB4YK5ymfoVI9~-@a;-jzu!3HBFGLM1%I|(KKbEXMPFLd-Vddq z7lofI2=Y^hr=sbgaGf6SazKlKA5UAmZtOo{j}Ve&(d<@?>G#wNAq+e>kdGWja}M1t zoVlm-=PZJ9)ZG4cZ|JJOIXPn+l2>FKsHwbV%3gdsHm! zovf{KyY+*uhr1o)Dm%^o!Op#{gGa{Q)}z%c?DT9o+`IR`RM9VA)=soEtodse{O`+3 z81s~DEt~r{yv`t2v+;vRE823)vXnPBa(Qv+@%M#>L9D(`B)m+%ur*!pHafP}c=pO&{{XkxV8^&(W4JH>= zj?>#zUdAU{(x#T5dzmC%7uRNy6pRoA=;T_qQR~GH$|^`{Avf#p8(%EzJ8w)|^Cmf% zO=;sxCu?6w2fz216jOeo4|n6AmkL#RPR2@y%hYo<9&+cBMcHZxXF(OqYetjUHv3A7 zlh8C}ydIc`3VZj*^mFg$bh~~;dZz6=CVzKqyq=N`0p_T_;;iCbEc3|Am64_U(e+`t z%KHz~a*aE)8^>cpk-#aPd~>O+-(5MYlXT zbU@>uKCg7~IQ{=TJWO=`XkD!G)>?WMs}6-b{y$aI4lf>ZhHHqW5E-p7meNK;U9GX9 zm-RcZPwR)Voq28i`3oDL|Ck3g#|P=}-uqldZ#K1Ot+eChcNN7oG@0@_r6VWvvko0* z6tCZ&=sXek)2~}4YR6@-8|pB%{c2f%vpG#}cpQH}GaG)C*E2=htF2ec`X}&`=d%kh zSMbhkt?}JUW&Oe0)IL#PUS;pgstNT{>D=2dYHp5lAutx8%k0IZ746-xrf+l0PhcjS z1F)Civd6ZCYy^C`xAWj(i`}Jfx85?8CpOc)V%*2urgS*zB#`IXady7GC= zro6R`_5b6p!zTzp00Izz00bZa0SG_<0uX?}tO{WL|E%^b+64g!KmY;|fB*y_009U< z00Iyg3*h;GY=KV@fB*y_009U<00Izz00bZafms#6`u|z&S+olR5P$##AOHafKmY;| zfB*y_Fc!e~|6>b$f&c^{009U<00Izz00bZa0SL^h0KWg9)t*JWAOHafKmY;|fB*y_ z009U<00LtHeE&bTz$XYm00Izz00bZa0SG_<0uX?}tP1e&|F!zRme?-QZgD+Qzl+wauH>4fbo@yuR_x&Fj`xa^3po z)n?b;xOVkg^J`bHf33M;G_QY){oc5V_5YvJjzzT~009U<00Izz00bZa0SG_<0-vA& g*8hKkm?#PY5P$##AOHafKmY;|fB*y_@F^AeU+}X&8~^|S literal 126976 zcmeI)O>^7E8NhK;v`9&=qBN{<9`FDAh>?T<0tg_000IagfB*srAb`MEOkhg(hoArdij6AW zK>z^+5I_I{1Q0*~0R#|0AT7Z2f7*jB1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**j-!D5`Ty+9UozsGF9Z-k009ILKmY**5I_I{1P~Zefgfh`8SUCc#i%ze zd$GJ&S=?S+zGS>AzKu&umDiS+jEm-y@!G}mcCE5_ak2dJ#if_am1246b@Bb`CI0`v z5gk~Xg#ZEwAbt=$K0$o_DbgO;@8uBt?a*>*KWU(%~*Eb{K)^HVFjjcv;rsE)&4p}Yd3FK)qSowO~bN#_DgnHyD>29hT{d@MsyD$i`LBtR?X~l{3LVD?Ne`DF0NPh zB~@ul^eX9QD$6x||3k-X_f{Un{K&OD(~plK6YiU)yJa;^rzP6laSA-c_T8u(out~X z;Tg5S^fpapM>qIox2Li;FQ|$3EB=091TAqmCCL)aX>S&9+*-S~a_esKo$B4<%I&S| zYa3#iuT?j;rsf`A31|ZqMsI2^)E=HGXv@plM{7|(;<415&ONJP>aOEkf#rzngq#_@spF}> zzSn=J_b*Hpi|xF%`uoN71Suw&8{%AuvlpUN+1$RnHynG%3R-oMD^9xggdr*>&dOKd9_q4cTA^l23Boz&kxL|+WwloZtwIY z7=ebXG|bI}uVg00!xj$TeaKezvK_}zkb z?p*eh*=W?0tmMxDzH#e4ly@1m&s(OKhFO3 zv8Nh4jI=-{yl})wxR}JUZ;8QwaiqKTB{pt#|7UsaPWdQd#*gB7I?BXp&puq=zg^JE zee^Q;c8y1o*mTMZt&B~ z-H$Ksp9n7(w6kZk;hiYnbaT2N&ZJ^Yshb{0$ZinqjeT-^=Uyjp%B|#fS8jD5vPzTD zlaWknKUpOyard^O+w9Jhc%Mh=$K1rH?$=V$b|G$}2)ZYKnkn{I?jE$@{xYr6d8Wq?Yklp!}QJC4FY%Y5{%5<+-wZq z!3&y)6qmV=Dem5oQ}eI`_=g_A7YA4$TwJaruR8X&wbOqs!Z*cgM0<%wz5cc@up5qT z-ZG84>0L1!#@@Bf{DZ@jc_G=v!IfOIY2tx29O`Xe684me>4H ztc)fpeHQ29v*DU3ne67+4a-)xJ#$BlhUq1Y^28HF2kz8L?!X1iUB?er>UFV#*j_?R zbx*D(8V&1#GF0*y-8DyL5%B*W?^2&wCk~RD2Hg=5L;h;`MqXPxKWNB1*N>LJn_Nu9 z#xwiRhp!j3^XIc+ygIGysC=AGPXi_A2v>)|35-U3F0MK)TUl0hJZs0YOPPn{& z{jwwr;uUhenO@4?-fp_)j$Uil_50>ta_))ns3+}=&VeYH>bs3}1@RjTz3DEcMy}^Y zMygAX8NK#ebaIxfg%2ItH^Qa7c71NpnG&B=$#PeEyYT3ASSe_8bJ^d-=Sw;}y)~8| z<8)>`%7-m&4UD460JMW>XZV~CZ8gN!tOr(OH5D!IHJsZ0q`aZVI5~dta$_f`%M{<( zHiNx8h8T9`oa;o{-t?O31skpQI;<=%6Rd6PzzZwW^Wv5%m6~=v8PDk0@)}=LCY%BtA zF7)0Ah_uv{PG-$QvH~rl7XSYL$Z?t-5kLR|1Q0*~0R#|0009ILIJN>j{~z0-qu~f3 zfB*srAbjMq#)(!U wfB*srAb