diff --git a/pom.xml b/pom.xml index 074f9fe4..62239457 100644 --- a/pom.xml +++ b/pom.xml @@ -196,6 +196,16 @@ 1.12 + + + + + + + + + + be.teletask diff --git a/sql/mysql.sql b/sql/mysql.sql index 21d7a52e..0bb4c03a 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -138,6 +138,7 @@ create table stream_proxy 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, @@ -166,4 +167,28 @@ create table user create_time varchar(50) not null ); -insert into user (username, password, roleId, create_time) values ('admin', '21232f297a57a5a743894a0e4a801fc3', '0', '2021-04-13 14:14:57'); \ No newline at end of file +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/StreamInfo.java b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java index 1336d051..771838d6 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -19,6 +19,7 @@ public class StreamInfo { private String rtmp; private String rtsp; private String rtc; + private String mediaServerId; private JSONArray tracks; public static class TransactionInfo{ @@ -165,4 +166,12 @@ public class StreamInfo { public void setTransactionInfo(TransactionInfo transactionInfo) { this.transactionInfo = transactionInfo; } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } } 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 f4e895f0..57493143 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -10,33 +10,31 @@ public class VideoManagerConstants { public static final String WVP_SERVER_PREFIX = "VMP_wvp_server"; - public static final String MEDIA_SERVER_PREFIX = "VMP_media_server"; + public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_"; - public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream"; + public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM"; - public static final String DEVICE_PREFIX = "VMP_device_"; + public static final String DEVICE_PREFIX = "VMP_DEVICE_"; - public static final String CACHEKEY_PREFIX = "VMP_channel_"; + public static final String CACHEKEY_PREFIX = "VMP_CHANNEL_"; public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_"; - public static final String PLAYER_PREFIX = "VMP_player_"; + public static final String PLAYER_PREFIX = "VMP_PLAYER_"; - public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; + public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_"; - public static final String PLATFORM_PREFIX = "VMP_platform"; + public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_"; - public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_platform_keeplive_"; + public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_"; - public static final String PLATFORM_CATCH_PREFIX = "VMP_platform_catch_"; + public static final String PLATFORM_REGISTER_PREFIX = "VMP_PLATFORM_REGISTER_"; - public static final String PLATFORM_REGISTER_PREFIX = "VMP_platform_register_"; + public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_"; - public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_platform_register_info_"; + public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_"; - public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_platform_send_rtp_info_"; - - public static final String Pattern_Topic = "VMP_keeplive_platform_"; + public static final String Pattern_Topic = "VMP_KEEPLIVE_PLATFORM_"; public static final String EVENT_ONLINE_REGISTER = "1"; 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 52292a57..88e8a64d 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java @@ -1,11 +1,16 @@ 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 org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; @Configuration("mediaConfig") -public class MediaConfig { +public class MediaConfig implements IMediaServerItem { + + @Value("${media.id:}") + private String id; @Value("${media.ip}") private String ip; @@ -25,22 +30,22 @@ public class MediaConfig { @Value("${media.http-port}") private Integer httpPort; - @Value("${media.http-ssl-port:}") + @Value("${media.http-ssl-port:0}") private Integer httpSSlPort; - @Value("${media.rtmp-port:}") + @Value("${media.rtmp-port:0}") private Integer rtmpPort; - @Value("${media.rtmp-ssl-port:}") + @Value("${media.rtmp-ssl-port:0}") private Integer rtmpSSlPort; - @Value("${media.rtp-proxy-port:}") + @Value("${media.rtp-proxy-port:0}") private Integer rtpProxyPort; - @Value("${media.rtsp-port:}") + @Value("${media.rtsp-port:0}") private Integer rtspPort; - @Value("${media.rtsp-ssl-port:}") + @Value("${media.rtsp-ssl-port:0}") private Integer rtspSSLPort; @Value("${media.auto-config:true}") @@ -61,6 +66,23 @@ public class MediaConfig { @Value("${media.record-assist-port:0}") private Integer recordAssistPort; + 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; } @@ -82,82 +104,114 @@ public class MediaConfig { this.hookIp = hookIp; } - public String getSdpIp() { - if (StringUtils.isEmpty(sdpIp)){ - return ip; - }else { - return sdpIp; - } + public String getSipIp() { + return sipIp; + } + + public void setSipIp(String sipIp) { + this.sipIp = sipIp; } public void setSdpIp(String sdpIp) { this.sdpIp = sdpIp; } - public String getStreamIp() { - if (StringUtils.isEmpty(streamIp)){ - return ip; - }else { - return streamIp; - } - } - public void setStreamIp(String streamIp) { this.streamIp = streamIp; } - public Integer getHttpPort() { + public int getHttpPort() { return httpPort; } + @Override + public void setHttpPort(int httpPort) { + + } + public void setHttpPort(Integer httpPort) { this.httpPort = httpPort; } - public Integer getHttpSSlPort() { + public int getHttpSSlPort() { return httpSSlPort; } + @Override + public void setHttpSSlPort(int httpSSlPort) { + + } + public void setHttpSSlPort(Integer httpSSlPort) { this.httpSSlPort = httpSSlPort; } - public Integer getRtmpPort() { + public int getRtmpPort() { return rtmpPort; } + @Override + public void setRtmpPort(int rtmpPort) { + + } + public void setRtmpPort(Integer rtmpPort) { this.rtmpPort = rtmpPort; } - public Integer getRtmpSSlPort() { + public int getRtmpSSlPort() { return rtmpSSlPort; } + @Override + public void setRtmpSSlPort(int rtmpSSlPort) { + + } + public void setRtmpSSlPort(Integer rtmpSSlPort) { this.rtmpSSlPort = rtmpSSlPort; } - public Integer getRtpProxyPort() { - return rtpProxyPort; + public int getRtpProxyPort() { + if (rtpProxyPort == null) { + return 0; + }else { + return rtpProxyPort; + } + + } + + @Override + public void setRtpProxyPort(int rtpProxyPort) { + } public void setRtpProxyPort(Integer rtpProxyPort) { this.rtpProxyPort = rtpProxyPort; } - public Integer getRtspPort() { + public int getRtspPort() { return rtspPort; } + @Override + public void setRtspPort(int rtspPort) { + + } + public void setRtspPort(Integer rtspPort) { this.rtspPort = rtspPort; } - public Integer getRtspSSLPort() { + public int getRtspSSLPort() { return rtspSSLPort; } + @Override + public void setRtspSSLPort(int rtspSSLPort) { + + } + public void setRtspSSLPort(Integer rtspSSLPort) { this.rtspSSLPort = rtspSSLPort; } @@ -202,11 +256,101 @@ public class MediaConfig { this.rtpPortRange = rtpPortRange; } - public Integer getRecordAssistPort() { + 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; + }else { + return sdpIp; + } + } + + public String getStreamIp() { + if (StringUtils.isEmpty(streamIp)){ + return ip; + }else { + return streamIp; + } + } + + + + public MediaServerItem getMediaSerItem(){ + MediaServerItem mediaServerItem = new MediaServerItem(); + mediaServerItem.setId(id); + mediaServerItem.setIp(ip); + mediaServerItem.setDocker(true); + mediaServerItem.setHookIp(hookIp); + mediaServerItem.setSdpIp(sdpIp); + mediaServerItem.setStreamIp(streamIp); + mediaServerItem.setHttpPort(httpPort); + mediaServerItem.setHttpSSlPort(httpSSlPort); + mediaServerItem.setRtmpPort(rtmpPort); + mediaServerItem.setRtmpSSlPort(rtmpSSlPort); + mediaServerItem.setRtpProxyPort(rtpProxyPort); + mediaServerItem.setRtspPort(rtspPort); + mediaServerItem.setRtspSSLPort(rtspSSLPort); + mediaServerItem.setAutoConfig(autoConfig); + mediaServerItem.setSecret(secret); + mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS); + mediaServerItem.setRtpEnable(rtpEnable); + mediaServerItem.setRtpPortRange(rtpPortRange); + mediaServerItem.setRecordAssistPort(recordAssistPort); + mediaServerItem.setCreateTime(createTime); + mediaServerItem.setUpdateTime(updateTime); + mediaServerItem.setCount(count); + return mediaServerItem; + } + + @Override + public String getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + @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 3c4ea172..c543e6ec 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java @@ -1,12 +1,16 @@ package com.genersoft.iot.vmp.conf; +import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import org.apache.catalina.connector.ClientAbortException; +import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; -import org.apache.catalina.connector.ClientAbortException; import org.mitre.dsmiley.httpproxy.ProxyServlet; 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.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -24,13 +28,16 @@ public class ProxyServletConfig { private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class); @Autowired - private MediaConfig mediaConfig; + private IMediaServerService mediaServerService; + + @Value("${server.port}") + private int serverPort; @Bean public ServletRegistrationBean zlmServletRegistrationBean(){ ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZLMProxySerlet(),"/zlm/*"); servletRegistrationBean.setName("zlm_Proxy"); - servletRegistrationBean.addInitParameter("targetUri", String.format("http://%s:%s", mediaConfig.getIp(), mediaConfig.getHttpPort())); + servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080"); servletRegistrationBean.addUrlMappings(); if (logger.isDebugEnabled()) { servletRegistrationBean.addInitParameter("log", "true"); @@ -38,24 +45,26 @@ public class ProxyServletConfig { return servletRegistrationBean; } - class ZLMProxySerlet extends ProxyServlet{ - - - + class ZLMProxySerlet extends ProxyServlet{ @Override protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) { String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString); - if (!StringUtils.isEmpty(queryStr)) { - queryStr += "&secret=" + mediaConfig.getSecret(); - }else { - queryStr = "secret=" + mediaConfig.getSecret(); + IMediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI()); + if (mediaInfo != null) { + if (!StringUtils.isEmpty(queryStr)) { + queryStr += "&secret=" + mediaInfo.getSecret(); + }else { + queryStr = "secret=" + mediaInfo.getSecret(); + } } return queryStr; } + /** + * 异常处理 + */ @Override protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){ - //System.out.println(e.getMessage()); try { super.handleRequestException(proxyRequest, proxyResonse, e); } catch (ServletException servletException) { @@ -72,6 +81,64 @@ public class ProxyServletConfig { logger.error("zlm 代理失败: ", e); } } + + /** + * 对于为按照格式请求的可以直接返回404 + */ + @Override + protected String getTargetUri(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + + String uri = null; + if (mediaInfo != null) { +// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length()); + uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort()); + }else { + uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以 + } + return uri; + } + + /** + * 动态替换请求目标 + */ + @Override + protected HttpHost getTargetHost(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + HttpHost host; + if (mediaInfo != null) { + host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort()); + }else { + host = new HttpHost("127.0.0.1", serverPort); + } + return host; + + } + + /** + * 根据uri获取流媒体信息 + */ + IMediaServerItem getMediaInfoByUri(String uri){ + String[] split = uri.split("/"); + String mediaServerId = split[2]; + return mediaServerService.getOne(mediaServerId); + } + + /** + * 去掉url中的标志信息 + */ + @Override + protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + String url = super.rewriteUrlFromRequest(servletRequest); + if (mediaInfo == null) { + return url; + } + return url.replace(mediaInfo.getId() + "/", ""); + } } } 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 0dca4af6..0a76f698 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -95,20 +95,43 @@ public class SipLayer implements SipListener { tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "TCP"); tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); tcpSipProvider.addSipListener(this); - logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); - } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) { - logger.error(String.format("创建SIP服务失败: %s", e.getMessage())); + 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) { + logger.error("无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用" + , sipConfig.getMonitorIp(), sipConfig.getSipPort()); + } catch (TooManyListenersException e) { + e.printStackTrace(); + } catch (ObjectInUseException e) { + e.printStackTrace(); } return tcpSipProvider; } @Bean("udpSipProvider") @DependsOn("sipStack") - private SipProvider startUdpListener() throws Exception { - ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP"); - SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); - udpSipProvider.addSipListener(this); - logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); + private SipProvider startUdpListener() { + ListeningPoint udpListeningPoint = null; + SipProvider udpSipProvider = null; + try { + udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP"); + udpSipProvider = sipStack.createSipProvider(udpListeningPoint); + udpSipProvider.addSipListener(this); + } catch (TransportNotSupportedException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + logger.error("无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用" + , sipConfig.getMonitorIp(), sipConfig.getSipPort()); + } catch (TooManyListenersException e) { + e.printStackTrace(); + } catch (ObjectInUseException e) { + e.printStackTrace(); + } + logger.info("Sip Server UDP 启动成功 port [" + sipConfig.getMonitorIp() + ":" + sipConfig.getSipPort() + "]"); return udpSipProvider; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java index 6f3d4d73..cf939cfe 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java @@ -94,6 +94,11 @@ public class Device { */ private String updateTime; + /** + * 设备使用的媒体id, 默认为null + */ + private String mediaServerId; + public String getDeviceId() { return deviceId; } @@ -229,4 +234,12 @@ public class Device { public void setUpdateTime(String updateTime) { this.updateTime = updateTime; } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java index fd469474..a1ce8ca9 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java @@ -9,6 +9,7 @@ public class GbStream extends PlatformGbStream{ private String stream; private String gbId; private String name; + private String mediaServerId; private double longitude; private double latitude; private String streamType; @@ -77,4 +78,12 @@ public class GbStream extends PlatformGbStream{ public void setStatus(boolean status) { this.status = status; } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java index 15e6f1e9..2c9c494c 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java @@ -66,6 +66,11 @@ public class SendRtpItem { */ private int localPort; + /** + * 使用的流媒体 + */ + private String mediaServerId; + public String getIp() { return ip; } @@ -161,4 +166,12 @@ public class SendRtpItem { public void setTcpActive(boolean tcpActive) { this.tcpActive = tcpActive; } + + 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 a475a1ba..aed21009 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,6 +6,9 @@ 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; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import org.slf4j.Logger; @@ -32,6 +35,8 @@ public class PlatformNotRegisterEventLister implements ApplicationListener param = new HashMap<>(); + param.put("vhost", "__defaultVhost__"); + param.put("app", app.toString()); + param.put("stream", stream.toString()); + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); } - Map param = new HashMap<>(); - param.put("vhost","__defaultVhost__"); - param.put("app", app.toString()); - param.put("stream", stream.toString()); - zlmrtpServerFactory.stopSendRtpStream(param); + } 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 dcfbf576..e2ff0f4f 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 @@ -9,6 +9,7 @@ import javax.sip.message.Response; 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; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*; import com.genersoft.iot.vmp.service.IPlayService; @@ -104,6 +105,9 @@ public class SIPProcessorFactory { @Autowired private ZLMRTPServerFactory zlmrtpServerFactory; + @Autowired + private IMediaServerService mediaServerService; + // 注:这里使用注解会导致循环依赖注入,暂用springBean private SipProvider tcpSipProvider; @@ -128,6 +132,7 @@ public class SIPProcessorFactory { processor.setStorager(storager); processor.setRedisCatchStorage(redisCatchStorage); processor.setZlmrtpServerFactory(zlmrtpServerFactory); + processor.setMediaServerService(mediaServerService); return processor; } else if (Request.REGISTER.equals(method)) { RegisterRequestProcessor processor = new RegisterRequestProcessor(); @@ -148,6 +153,7 @@ public class SIPProcessorFactory { processor.setRequestEvent(evt); processor.setRedisCatchStorage(redisCatchStorage); processor.setZlmrtpServerFactory(zlmrtpServerFactory); + processor.setMediaServerService(mediaServerService); return processor; } else if (Request.BYE.equals(method)) { ByeRequestProcessor processor = new ByeRequestProcessor(); @@ -155,6 +161,7 @@ public class SIPProcessorFactory { processor.setRedisCatchStorage(redisCatchStorage); processor.setZlmrtpServerFactory(zlmrtpServerFactory); processor.setSIPCommander(cmder); + processor.setMediaServerService(mediaServerService); return processor; } else if (Request.CANCEL.equals(method)) { CancelRequestProcessor processor = new CancelRequestProcessor(); 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 4e111d64..b06fa773 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,6 +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; /** * @Description:设备能力接口,用于定义设备的控制、查询能力 @@ -90,7 +92,7 @@ public interface ISIPCommander { * @param device 视频设备 * @param channelId 预览通道 */ - void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); /** * 请求回放视频流 @@ -100,7 +102,7 @@ public interface ISIPCommander { * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ - void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); /** * 视频流停止 @@ -288,12 +290,4 @@ public interface ISIPCommander { * @return true = 命令发送成功 */ boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); - - - /** - * 释放rtpserver - * @param device - * @param channelId - */ - void closeRTPServer(Device device, 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 2c9339cc..5c2fa32d 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 @@ -13,11 +13,10 @@ 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.media.zlm.ZLMServerConfig; +import com.genersoft.iot.vmp.media.zlm.*; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; -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.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import gov.nist.javax.sip.message.SIPRequest; @@ -37,6 +36,7 @@ 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; /** * @Description:设备能力接口,用于定义设备的控制、查询能力 @@ -77,12 +77,6 @@ public class SIPCommander implements ISIPCommander { @Autowired private ZLMRTPServerFactory zlmrtpServerFactory; - @Autowired - private ZLMRESTfulUtils zlmresTfulUtils; - - @Autowired - private MediaConfig mediaConfig; - @Autowired private UserSetup userSetup; @@ -340,48 +334,45 @@ public class SIPCommander implements ISIPCommander { * @param errorEvent sip错误订阅 */ @Override - public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { + public void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { String streamId = null; try { if (device == null) return; + String streamMode = device.getStreamMode().toUpperCase(); + String ssrc = streamSession.createPlaySsrc(); - if (mediaConfig.isRtpEnable()) { + if (mediaServerItem.isRtpEnable()) { streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); }else { streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); } - String streamMode = device.getStreamMode().toUpperCase(); - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - if (mediaInfo == null) { - logger.warn("点播时发现ZLM尚未连接..."); - return; - } Integer mediaPort = null; // 使用动态udp端口 - if (mediaConfig.isRtpEnable()) { - mediaPort = zlmrtpServerFactory.createRTPServer(streamId); + if (mediaServerItem.isRtpEnable()) { + mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId); }else { - mediaPort = mediaInfo.getRtpProxyPort(); + mediaPort = mediaServerItem.getRtpProxyPort(); } - + logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort); // 添加订阅 JSONObject subscribeKey = new JSONObject(); subscribeKey.put("app", "rtp"); subscribeKey.put("stream", streamId); subscribeKey.put("regist", true); - - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, json->{ + subscribeKey.put("mediaServerId", mediaServerItem.getId()); + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, + (IMediaServerItem mediaServerItemInUse, JSONObject json)->{ if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return; - event.response(json); + event.response(mediaServerItemInUse, json); subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); }); // 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 "+mediaInfo.getSdpIp()+"\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 "+mediaInfo.getSdpIp()+"\r\n"); + content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n"); content.append("t=0 0\r\n"); if (userSetup.isSeniorSdp()) { @@ -459,21 +450,32 @@ public class SIPCommander implements ISIPCommander { * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ @Override - public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event + public void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event , SipSubscribe.Event errorEvent) { try { - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); 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); + // 添加订阅 JSONObject subscribeKey = new JSONObject(); subscribeKey.put("app", "rtp"); subscribeKey.put("stream", streamId); subscribeKey.put("regist", true); + subscribeKey.put("mediaServerId", mediaServerItem.getId()); logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, json->{ + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, + (IMediaServerItem mediaServerItemInUse, JSONObject json)->{ if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return; - event.response(json); + event.response(mediaServerItemInUse, json); subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); }); @@ -482,16 +484,12 @@ public class SIPCommander implements ISIPCommander { content.append("o="+sipConfig.getSipId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n"); content.append("s=Playback\r\n"); content.append("u="+channelId+":0\r\n"); - content.append("c=IN IP4 "+mediaInfo.getSdpIp()+"\r\n"); + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" " +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); - Integer mediaPort = null; - // 使用动态udp端口 - if (mediaConfig.isRtpEnable()) { - mediaPort = zlmrtpServerFactory.createRTPServer(streamId); - }else { - mediaPort = mediaInfo.getRtpProxyPort(); - } + + + String streamMode = device.getStreamMode().toUpperCase(); if (userSetup.isSeniorSdp()) { @@ -560,56 +558,63 @@ public class SIPCommander implements ISIPCommander { /** - * 视频流停止 - * + * 视频流停止, 不使用回调 */ @Override public void streamByeCmd(String deviceId, String channelId) { streamByeCmd(deviceId, channelId, null); } + + /** + * 视频流停止 + */ @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) { - - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); - if (streamInfo != null) { - JSONObject mediaList = zlmresTfulUtils.getMediaList(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); - } + logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId); return; } - Dialog dialog = transaction.getDialog(); if (dialog == null) { + logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId); return; } Request byeRequest = dialog.createRequest(Request.BYE); @@ -632,7 +637,7 @@ public class SIPCommander implements ISIPCommander { } dialog.sendRequest(clientTransaction); - zlmrtpServerFactory.closeRTPServer(streamSession.getStreamId(deviceId, channelId)); + streamSession.remove(deviceId, channelId); } catch (SipException | ParseException e) { e.printStackTrace(); @@ -721,7 +726,7 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("\r\n"); cmdXml.append("DeviceControl\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { + if (StringUtils.isEmpty(channelId)) { cmdXml.append("" + device.getDeviceId() + "\r\n"); } else { cmdXml.append("" + channelId + "\r\n"); @@ -821,16 +826,16 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); cmdXml.append("" + device.getDeviceId() + "\r\n"); cmdXml.append("ResetAlarm\r\n"); - if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { + if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) { cmdXml.append("\r\n"); } - if (!XmlUtil.isEmpty(alarmMethod)) { + if (!StringUtils.isEmpty(alarmMethod)) { cmdXml.append("" + alarmMethod + "\r\n"); } - if (!XmlUtil.isEmpty(alarmType)) { + if (!StringUtils.isEmpty(alarmType)) { cmdXml.append("" + alarmType + "\r\n"); } - if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { + if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) { cmdXml.append("\r\n"); } cmdXml.append("\r\n"); @@ -863,7 +868,7 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("\r\n"); cmdXml.append("DeviceControl\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { + if (StringUtils.isEmpty(channelId)) { cmdXml.append("" + device.getDeviceId() + "\r\n"); } else { cmdXml.append("" + channelId + "\r\n"); @@ -901,7 +906,7 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("\r\n"); cmdXml.append("DeviceControl\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { + if (StringUtils.isEmpty(channelId)) { cmdXml.append("" + device.getDeviceId() + "\r\n"); } else { cmdXml.append("" + channelId + "\r\n"); @@ -969,13 +974,13 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("\r\n"); cmdXml.append("DeviceConfig\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { + if (StringUtils.isEmpty(channelId)) { cmdXml.append("" + device.getDeviceId() + "\r\n"); } else { cmdXml.append("" + channelId + "\r\n"); } cmdXml.append("\r\n"); - if (!XmlUtil.isEmpty(name)) { + if (!StringUtils.isEmpty(name)) { cmdXml.append("" + name + "\r\n"); } if (NumericUtil.isInteger(expiration)) { @@ -1169,22 +1174,22 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("Alarm\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); cmdXml.append("" + device.getDeviceId() + "\r\n"); - if (!XmlUtil.isEmpty(startPriority)) { + if (!StringUtils.isEmpty(startPriority)) { cmdXml.append("" + startPriority + "\r\n"); } - if (!XmlUtil.isEmpty(endPriority)) { + if (!StringUtils.isEmpty(endPriority)) { cmdXml.append("" + endPriority + "\r\n"); } - if (!XmlUtil.isEmpty(alarmMethod)) { + if (!StringUtils.isEmpty(alarmMethod)) { cmdXml.append("" + alarmMethod + "\r\n"); } - if (!XmlUtil.isEmpty(alarmType)) { + if (!StringUtils.isEmpty(alarmType)) { cmdXml.append("" + alarmType + "\r\n"); } - if (!XmlUtil.isEmpty(startTime)) { + if (!StringUtils.isEmpty(startTime)) { cmdXml.append("" + startTime + "\r\n"); } - if (!XmlUtil.isEmpty(endTime)) { + if (!StringUtils.isEmpty(endTime)) { cmdXml.append("" + endTime + "\r\n"); } cmdXml.append("\r\n"); @@ -1218,7 +1223,7 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("\r\n"); cmdXml.append("ConfigDownload\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { + if (StringUtils.isEmpty(channelId)) { cmdXml.append("" + device.getDeviceId() + "\r\n"); } else { cmdXml.append("" + channelId + "\r\n"); @@ -1253,7 +1258,7 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("\r\n"); cmdXml.append("PresetQuery\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { + if (StringUtils.isEmpty(channelId)) { cmdXml.append("" + device.getDeviceId() + "\r\n"); } else { cmdXml.append("" + channelId + "\r\n"); @@ -1365,22 +1370,22 @@ public class SIPCommander implements ISIPCommander { cmdXml.append("Alarm\r\n"); cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); cmdXml.append("" + device.getDeviceId() + "\r\n"); - if (!XmlUtil.isEmpty(startPriority)) { + if (!StringUtils.isEmpty(startPriority)) { cmdXml.append("" + startPriority + "\r\n"); } - if (!XmlUtil.isEmpty(endPriority)) { + if (!StringUtils.isEmpty(endPriority)) { cmdXml.append("" + endPriority + "\r\n"); } - if (!XmlUtil.isEmpty(alarmMethod)) { + if (!StringUtils.isEmpty(alarmMethod)) { cmdXml.append("" + alarmMethod + "\r\n"); } - if (!XmlUtil.isEmpty(alarmType)) { + if (!StringUtils.isEmpty(alarmType)) { cmdXml.append("" + alarmType + "\r\n"); } - if (!XmlUtil.isEmpty(startTime)) { + if (!StringUtils.isEmpty(startTime)) { cmdXml.append("" + startTime + "\r\n"); } - if (!XmlUtil.isEmpty(endTime)) { + if (!StringUtils.isEmpty(endTime)) { cmdXml.append("" + endTime + "\r\n"); } cmdXml.append("\r\n"); @@ -1431,16 +1436,4 @@ public class SIPCommander implements ISIPCommander { clientTransaction.sendRequest(); return clientTransaction; } - - - - - @Override - public void closeRTPServer(Device device, String channelId) { - if (mediaConfig.isRtpEnable()) { - String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); - zlmrtpServerFactory.closeRTPServer(streamId); - } - streamSession.remove(device.getDeviceId(), channelId); - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java index 54108208..12290d2a 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java @@ -16,6 +16,8 @@ import javax.sip.message.Request; import gov.nist.javax.sip.SipStackImpl; import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.stack.SIPServerTransaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @Description:处理接收IPCamera发来的SIP协议请求消息 @@ -24,6 +26,8 @@ import gov.nist.javax.sip.stack.SIPServerTransaction; */ public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcessor { + private final static Logger logger = LoggerFactory.getLogger(SIPRequestAbstractProcessor.class); + protected RequestEvent evt; private SipProvider tcpSipProvider; @@ -64,9 +68,9 @@ public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcesso } } } catch (TransactionAlreadyExistsException e) { - e.printStackTrace(); + logger.error(e.getMessage()); } catch (TransactionUnavailableException e) { - e.printStackTrace(); + logger.error(e.getMessage()); } } return serverTransaction; 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 413f0d14..6f809718 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,6 +13,9 @@ 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +33,8 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor { private ZLMRTPServerFactory zlmrtpServerFactory; + private IMediaServerService mediaServerService; + /** * 处理 ACK请求 * @@ -76,18 +81,22 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor { while (!rtpPushed) { try { if (System.currentTimeMillis() - startTime < 30 * 1000) { - if (zlmrtpServerFactory.isStreamReady(streamInfo.getApp(), streamInfo.getStreamId())) { + IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) { rtpPushed = true; - logger.info("已获取设备推流,开始向上级推流"); - zlmrtpServerFactory.startSendRtpStream(param); + logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]", + streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort()); + zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); } else { - logger.info("等待设备推流......."); + logger.info("等待设备推流[{}/{}].......", + streamInfo.getApp() ,streamInfo.getStreamId()); Thread.sleep(1000); continue; } } else { rtpPushed = true; - logger.info("设备推流超时,终止向上级推流"); + logger.info("设备推流[{}/{}]超时,终止向上级推流", + streamInfo.getApp() ,streamInfo.getStreamId()); } } catch (InterruptedException e) { e.printStackTrace(); @@ -123,4 +132,12 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor { public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) { this.zlmrtpServerFactory = zlmrtpServerFactory; } + + public IMediaServerService getMediaServerService() { + return mediaServerService; + } + + public void setMediaServerService(IMediaServerService mediaServerService) { + this.mediaServerService = mediaServerService; + } } 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 fdb7c0b2..50e8c323 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,6 +15,9 @@ 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,6 +41,8 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { private ZLMRTPServerFactory zlmrtpServerFactory; + private IMediaServerService mediaServerService; + /** * 处理BYE请求 * @param evt @@ -60,9 +65,10 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { param.put("stream",streamId); param.put("ssrc",sendRtpItem.getSsrc()); logger.info("停止向上级推流:" + streamId); - zlmrtpServerFactory.stopSendRtpStream(param); + IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); redisCatchStorage.deleteSendRTPServer(platformGbId, channelId); - if (zlmrtpServerFactory.totalReaderCount(sendRtpItem.getApp(), streamId) == 0) { + if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) { logger.info(streamId + "无其它观看者,通知设备停止推流"); cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId); } @@ -112,4 +118,11 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor { this.cmder = cmder; } + public IMediaServerService getMediaServerService() { + return mediaServerService; + } + + public void setMediaServerService(IMediaServerService mediaServerService) { + this.mediaServerService = mediaServerService; + } } 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 5f356b0d..7219dbb3 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,12 +11,16 @@ 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; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; @@ -51,6 +55,8 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { private ZLMRTPServerFactory zlmrtpServerFactory; + private IMediaServerService mediaServerService; + public ZLMRTPServerFactory getZlmrtpServerFactory() { return zlmrtpServerFactory; } @@ -91,6 +97,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { // 查询平台下是否有该通道 DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId); + IMediaServerItem mediaServerItem = null; // 不是通道可能是直播流 if (channel != null && gbStream == null ) { if (channel.getStatus() == 0) { @@ -100,8 +107,15 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { } responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中 }else if(channel == null && gbStream != null){ - Boolean streamReady = zlmrtpServerFactory.isStreamReady(gbStream.getApp(), gbStream.getStream()); - if (!streamReady) { + String mediaServerId = gbStream.getMediaServerId(); + mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + logger.info("[ app={}, stream={} ]zlm找不到,返回410",gbStream.getApp(), gbStream.getStream()); + responseAck(evt, Response.GONE, "media server not found"); + return; + } + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (!streamReady ) { logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream()); responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline"); return; @@ -130,8 +144,8 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { //boolean recvonly = false; boolean mediaTransmissionTCP = false; Boolean tcpActive = null; - for (int i = 0; i < mediaDescriptions.size(); i++) { - MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; Media media = mediaDescription.getMedia(); Vector mediaFormats = media.getMediaFormats(false); @@ -147,7 +161,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { mediaTransmissionTCP = true; if ("active".equals(setup)) { tcpActive = true; - }else if ("passive".equals(setup)) { + } else if ("passive".equals(setup)) { tcpActive = false; } } @@ -174,7 +188,13 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { responseAck(evt, Response.SERVER_INTERNAL_ERROR); return; } - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, requesterId, + mediaServerItem = playService.getNewMediaServerItem(device); + if (mediaServerItem == null) { + logger.warn("未找到可用的zlm"); + responseAck(evt, Response.BUSY_HERE); + return; + } + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, device.getDeviceId(), channelId, mediaTransmissionTCP); if (tcpActive != null) { @@ -189,18 +209,18 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { // 写入redis, 超时时回复 redisCatchStorage.updateSendRTPSever(sendRtpItem); // 通知下级推流, - PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{ + PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{ // 收到推流, 回复200OK, 等待ack // if (sendRtpItem == null) return; sendRtpItem.setStatus(1); redisCatchStorage.updateSendRTPSever(sendRtpItem); // TODO 添加对tcp的支持 - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); + StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getSdpIp()+"\r\n"); + content.append("o="+"00000"+" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n"); content.append("s=Play\r\n"); - content.append("c=IN IP4 "+mediaInfo.getSdpIp()+"\r\n"); + content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n"); content.append("t=0 0\r\n"); content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); content.append("a=sendonly\r\n"); @@ -217,7 +237,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { } catch (ParseException e) { e.printStackTrace(); } - } ,(event -> { + } ,((event) -> { // 未知错误。直接转发设备点播的错误 Response response = null; try { @@ -232,7 +252,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { } }else if (gbStream != null) { - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, requesterId, + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP); @@ -251,12 +271,11 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { sendRtpItem.setStatus(1); redisCatchStorage.updateSendRTPSever(sendRtpItem); // TODO 添加对tcp的支持 - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); - content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getSdpIp()+"\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 "+mediaInfo.getSdpIp()+"\r\n"); + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); content.append("t=0 0\r\n"); content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); content.append("a=sendonly\r\n"); @@ -444,4 +463,12 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor { public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { this.redisCatchStorage = redisCatchStorage; } + + public IMediaServerService getMediaServerService() { + return mediaServerService; + } + + public void setMediaServerService(IMediaServerService mediaServerService) { + this.mediaServerService = mediaServerService; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java index 9d282c68..70e64a5e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java @@ -282,7 +282,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { //String result = XmlUtil.getText(rootElement, "Result"); // 回复200 OK responseAck(evt); - if (rootElement.getName().equals("Response")) {//} !XmlUtil.isEmpty(result)) { + if (rootElement.getName().equals("Response")) {//} !StringUtils.isEmpty(result)) { // 此处是对本平台发出DeviceControl指令的应答 JSONObject json = new JSONObject(); XmlUtil.node2Json(rootElement, json); @@ -299,7 +299,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { String platformId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); String targetGBId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); // 远程启动功能 - if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement, "TeleBoot"))) { + if (!StringUtils.isEmpty(XmlUtil.getText(rootElement, "TeleBoot"))) { if (deviceId.equals(targetGBId)) { // 远程启动本平台:需要在重新启动程序后先对SipStack解绑 logger.info("执行远程启动本平台命令"); @@ -337,7 +337,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { } } // 云台/前端控制命令 - if (!XmlUtil.isEmpty(XmlUtil.getText(rootElement,"PTZCmd")) && !deviceId.equals(targetGBId)) { + if (!StringUtils.isEmpty(XmlUtil.getText(rootElement,"PTZCmd")) && !deviceId.equals(targetGBId)) { String cmdString = XmlUtil.getText(rootElement,"PTZCmd"); Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId); cmder.fronEndCmd(device, deviceId, cmdString); @@ -421,7 +421,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { String deviceId = XmlUtil.getText(rootElement, "DeviceID"); // 回复200 OK responseAck(evt); - if (rootElement.getName().equals("Response")) {// !XmlUtil.isEmpty(result)) { + if (rootElement.getName().equals("Response")) {// !StringUtils.isEmpty(result)) { // 此处是对本平台发出DeviceControl指令的应答 JSONObject json = new JSONObject(); XmlUtil.node2Json(rootElement, json); @@ -718,7 +718,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { deviceAlarm.setLatitude(0.00); } - if (!XmlUtil.isEmpty(deviceAlarm.getAlarmMethod())) { + if (!StringUtils.isEmpty(deviceAlarm.getAlarmMethod())) { if ( deviceAlarm.getAlarmMethod().equals("4")) { MobilePosition mobilePosition = new MobilePosition(); mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java index c82ba600..4220d816 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java @@ -17,6 +17,7 @@ import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; /** * 基于dom4j的工具包 @@ -114,12 +115,12 @@ public class XmlUtil { // 如果是属性 for (Object o : element.attributes()) { Attribute attr = (Attribute) o; - if (!isEmpty(attr.getValue())) { + if (!StringUtils.isEmpty(attr.getValue())) { json.put("@" + attr.getName(), attr.getValue()); } } List chdEl = element.elements(); - if (chdEl.isEmpty() && !isEmpty(element.getText())) {// 如果没有子元素,只有一个值 + if (chdEl.isEmpty() && !StringUtils.isEmpty(element.getText())) {// 如果没有子元素,只有一个值 json.put(element.getName(), element.getText()); } @@ -150,7 +151,7 @@ public class XmlUtil { } else { // 子元素没有子元素 for (Object o : element.attributes()) { Attribute attr = (Attribute) o; - if (!isEmpty(attr.getValue())) { + if (!StringUtils.isEmpty(attr.getValue())) { json.put("@" + attr.getName(), attr.getValue()); } } @@ -160,11 +161,4 @@ public class XmlUtil { } } } - - public static boolean isEmpty(String str) { - if (str == null || str.trim().isEmpty() || "null".equals(str)) { - return true; - } - return false; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java deleted file mode 100644 index a00bc2a5..00000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java +++ /dev/null @@ -1,54 +0,0 @@ -//package com.genersoft.iot.vmp.media.zlm; -// -//import com.genersoft.iot.vmp.conf.MediaConfig; -//import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.beans.factory.annotation.Value; -//import org.springframework.web.bind.annotation.*; -//import org.springframework.web.client.HttpClientErrorException; -//import org.springframework.web.client.RestTemplate; -// -//import javax.servlet.http.HttpServletRequest; -//import javax.servlet.http.HttpServletResponse; -// -//@RestController -//@RequestMapping("/zlm") -//public class ZLMHTTPProxyController { -// -// -// // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class); -// -// @Autowired -// private IRedisCatchStorage redisCatchStorage; -// -// @Autowired -// private MediaConfig mediaConfig; -// -// @ResponseBody -// @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8") -// public Object proxy(HttpServletRequest request, HttpServletResponse response){ -// -// if (redisCatchStorage.getMediaInfo() == null) { -// return "未接入流媒体"; -// } -// ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); -// String requestURI = String.format("http://%s:%s%s?%s&%s", -// mediaInfo.getLocalIP(), -// mediaConfig.getHttpPort(), -// request.getRequestURI().replace("/zlm",""), -// mediaInfo.getHookAdminParams(), -// request.getQueryString() -// ); -// // 发送请求 -// RestTemplate restTemplate = new RestTemplate(); -// //将指定的url返回的参数自动封装到自定义好的对应类对象中 -// Object result = null; -// try { -// result = restTemplate.getForObject(requestURI,Object.class); -// -// }catch (HttpClientErrorException httpClientErrorException) { -// response.setStatus(httpClientErrorException.getStatusCode().value()); -// } -// return result; -// } -//} 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 bd8485d1..b7aebf42 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 @@ -9,6 +9,9 @@ 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.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.service.IPlayService; @@ -53,10 +56,10 @@ public class ZLMHttpHookListener { private IRedisCatchStorage redisCatchStorage; @Autowired - private ZLMRESTfulUtils zlmresTfulUtils; + private IMediaServerService mediaServerService; @Autowired - private ZLMServerManger zlmServerManger; + private ZLMRESTfulUtils zlmresTfulUtils; @Autowired private ZLMMediaListManager zlmMediaListManager; @@ -81,6 +84,7 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString()); } + String mediaServerId = json.getString("mediaServerId"); JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("msg", "success"); @@ -98,6 +102,7 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); } + String mediaServerId = json.getString("mediaServerId"); JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("err", ""); @@ -117,9 +122,14 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString()); } + String mediaServerId = json.getString("mediaServerId"); ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json); if (subscribe != null ) { - subscribe.response(json); + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + } JSONObject ret = new JSONObject(); ret.put("code", 0); @@ -133,20 +143,25 @@ public class ZLMHttpHookListener { */ @ResponseBody @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") - public ResponseEntity onPublish(@RequestBody JSONObject json){ + public ResponseEntity onPublish(@RequestBody JSONObject json) { logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); + String mediaServerId = json.getString("mediaServerId"); ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); - if (subscribe != null) subscribe.response(json); - + if (subscribe != null) { + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + } JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("msg", "success"); ret.put("enableHls", true); ret.put("enableMP4", userSetup.isRecordPushLive()); ret.put("enableRtxp", true); - return new ResponseEntity(ret.toString(),HttpStatus.OK); + return new ResponseEntity(ret.toString(), HttpStatus.OK); } /** @@ -160,6 +175,7 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString()); } + String mediaServerId = json.getString("mediaServerId"); JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("msg", "success"); @@ -177,6 +193,7 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString()); } + String mediaServerId = json.getString("mediaServerId"); JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("realm", ""); @@ -195,6 +212,7 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString()); } + String mediaServerId = json.getString("mediaServerId"); JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("encrypted", false); @@ -216,9 +234,15 @@ public class ZLMHttpHookListener { // TODO 如果是带有rtpstream则开启按需拉流 // String app = json.getString("app"); // String stream = json.getString("stream"); - + String mediaServerId = json.getString("mediaServerId"); ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json); - if (subscribe != null) subscribe.response(json); + if (subscribe != null ) { + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + + } JSONObject ret = new JSONObject(); ret.put("code", 0); @@ -237,9 +261,15 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); } - + String mediaServerId = json.getString("mediaServerId"); ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json); - if (subscribe != null) subscribe.response(json); + if (subscribe != null ) { + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + + } // 流消失移除redis play String app = json.getString("app"); @@ -251,6 +281,11 @@ public class ZLMHttpHookListener { logger.info("[stream: " + streamId + "] on_stream_changed->>" + schema); } if ("rtmp".equals(schema)){ + if (regist) { + mediaServerService.addCount(mediaServerId); + }else { + mediaServerService.removeCount(mediaServerId); + } if ("rtp".equals(app) && !regist ) { StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); if (streamInfo!=null){ @@ -262,10 +297,11 @@ public class ZLMHttpHookListener { } }else { if (!"rtp".equals(app) ){ + IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); if (regist) { - zlmMediaListManager.addMedia(app, streamId); + zlmMediaListManager.addMedia(mediaServerItem, app, streamId); }else { - zlmMediaListManager.removeMedia(app, streamId); + zlmMediaListManager.removeMedia( app, streamId); } } } @@ -288,7 +324,7 @@ public class ZLMHttpHookListener { if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString()); } - + String mediaServerId = json.getString("mediaServerId"); String streamId = json.getString("stream"); String app = json.getString("app"); @@ -329,11 +365,12 @@ public class ZLMHttpHookListener { @ResponseBody @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") public ResponseEntity onStreamNotFound(@RequestBody JSONObject json){ - if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); } - if (userSetup.isAutoApplyPlay()) { + String mediaServerId = json.getString("mediaServerId"); + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (userSetup.isAutoApplyPlay() && mediaInfo != null) { String app = json.getString("app"); String streamId = json.getString("stream"); if ("rtp".equals(app) && streamId.contains("gb_play") ) { @@ -344,9 +381,9 @@ public class ZLMHttpHookListener { Device device = storager.queryVideoDevice(deviceId); if (device != null) { UUID uuid = UUID.randomUUID(); - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { + cmder.playStreamCmd(mediaInfo, device, channelId, (IMediaServerItem mediaServerItemInuse, JSONObject response) -> { logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); + playService.onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString()); }, null); } @@ -367,26 +404,19 @@ public class ZLMHttpHookListener { */ @ResponseBody @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") - public ResponseEntity onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){ + public ResponseEntity onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){ if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); + logger.debug("ZLM HOOK on_server_started API调用,参数:" + jsonObject.toString()); } - -// String data = json.getString("data"); -// List mediaServerConfigs = JSON.parseArray(JSON.toJSONString(json), MediaServerConfig.class); -// MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0); - + String remoteAddr = request.getRemoteAddr(); + jsonObject.put("ip", remoteAddr); List subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_started); - if (subscribes != null && subscribes.size() > 0) { + if (subscribes != null && subscribes.size() > 0) { for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { - subscribe.response(json); + subscribe.response(null, jsonObject); } } - ZLMServerConfig ZLMServerConfig = JSON.toJavaObject(json, ZLMServerConfig.class); - zlmServerManger.updateServerCatch(ZLMServerConfig); - // 重新发起代理 - JSONObject ret = new JSONObject(); ret.put("code", 0); ret.put("msg", "success"); 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 4f832aee..62b18f5b 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,6 +1,8 @@ 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; import java.util.*; @@ -30,7 +32,7 @@ public class ZLMHttpHookSubscribe { } public interface Event{ - void response(JSONObject response); + void response(IMediaServerItem mediaServerItem, JSONObject response); } private Map> allSubscribes = new ConcurrentHashMap<>(); 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 ed407d29..38763094 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 @@ -2,6 +2,8 @@ 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; @@ -45,11 +47,11 @@ public class ZLMMediaListManager { private ZLMHttpHookSubscribe subscribe; - public void updateMediaList() { + public void updateMediaList(MediaServerItem mediaServerItem) { storager.clearMediaList(); // 使用异步的当时更新媒体流列表 - zlmresTfulUtils.getMediaList((mediaList ->{ + zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{ if (mediaList == null) return; String dataStr = mediaList.getString("data"); @@ -57,10 +59,10 @@ public class ZLMMediaListManager { Map result = new HashMap<>(); List streamPushItems = null; // 获取所有的国标关联 - List gbStreams = gbStreamMapper.selectAll(); +// List gbStreams = gbStreamMapper.selectAllByMediaServerId(mediaServerItem.getId()); if (code == 0 ) { if (dataStr != null) { - streamPushItems = streamPushService.handleJSON(dataStr); + streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem); } }else { logger.warn("更新视频流失败,错误code: " + code); @@ -72,24 +74,27 @@ public class ZLMMediaListManager { JSONObject jsonObject = new JSONObject(); jsonObject.put("app", streamPushItem.getApp()); jsonObject.put("stream", streamPushItem.getStream()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,(response)->{ - updateMedia(response.getString("app"), response.getString("stream")); - }); + jsonObject.put("mediaServerId", mediaServerItem.getId()); + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject, + (IMediaServerItem mediaServerItemInuse, JSONObject response)->{ + updateMedia(mediaServerItem, response.getString("app"), response.getString("stream")); + } + ); } } })); } - public void addMedia(String app, String streamId) { + public void addMedia(IMediaServerItem mediaServerItem, String app, String streamId) { //使用异步更新推流 - updateMedia(app, streamId); + updateMedia(mediaServerItem, app, streamId); } - public void updateMedia(String app, String streamId) { + public void updateMedia(IMediaServerItem mediaServerItem, String app, String streamId) { //使用异步更新推流 - zlmresTfulUtils.getMediaList(app, streamId, "rtmp", json->{ + zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{ if (json == null) return; String dataStr = json.getString("data"); @@ -99,7 +104,7 @@ public class ZLMMediaListManager { List streamPushItems = null; if (code == 0 ) { if (dataStr != null) { - streamPushItems = streamPushService.handleJSON(dataStr); + streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem); } }else { logger.warn("更新视频流失败,错误code: " + code); @@ -122,32 +127,32 @@ public class ZLMMediaListManager { } } - public void clearAllSessions() { - logger.info("清空所有国标相关的session"); - JSONObject allSessionJSON = zlmresTfulUtils.getAllSession(); - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - HashSet allLocalPorts = new HashSet(); - if (allSessionJSON.getInteger("code") == 0) { - JSONArray data = allSessionJSON.getJSONArray("data"); - if (data.size() > 0) { - for (int i = 0; i < data.size(); i++) { - JSONObject sessionJOSN = data.getJSONObject(i); - Integer local_port = sessionJOSN.getInteger("local_port"); - if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) && - !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) && - !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) && - !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) && - !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) && - !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){ - allLocalPorts.add(sessionJOSN.getInteger("local_port") + ""); - } - } - } - } - if (allLocalPorts.size() > 0) { - List result = new ArrayList<>(allLocalPorts); - String localPortSStr = String.join(",", result); - zlmresTfulUtils.kickSessions(localPortSStr); - } - } +// public void clearAllSessions() { +// logger.info("清空所有国标相关的session"); +// JSONObject allSessionJSON = zlmresTfulUtils.getAllSession(); +// ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); +// HashSet allLocalPorts = new HashSet(); +// if (allSessionJSON.getInteger("code") == 0) { +// JSONArray data = allSessionJSON.getJSONArray("data"); +// if (data.size() > 0) { +// for (int i = 0; i < data.size(); i++) { +// JSONObject sessionJOSN = data.getJSONObject(i); +// Integer local_port = sessionJOSN.getInteger("local_port"); +// if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) && +// !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) && +// !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) && +// !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) && +// !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) && +// !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){ +// allLocalPorts.add(sessionJOSN.getInteger("local_port") + ""); +// } +// } +// } +// } +// if (allLocalPorts.size() > 0) { +// List result = new ArrayList<>(allLocalPorts); +// String localPortSStr = String.join(",", result); +// zlmresTfulUtils.kickSessions(localPortSStr); +// } +// } } 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 8425d039..8e12e574 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 @@ -3,6 +3,8 @@ 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; @@ -21,23 +23,18 @@ public class ZLMRESTfulUtils { private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); - @Autowired - private MediaConfig mediaConfig; - - - public interface RequestCallback{ void run(JSONObject response); } - public JSONObject sendPost(String api, Map param, RequestCallback callback) { + public JSONObject sendPost(IMediaServerItem mediaServerItem, String api, Map param, RequestCallback callback) { OkHttpClient client = new OkHttpClient(); - String url = String.format("http://%s:%s/index/api/%s", mediaConfig.getIp(), mediaConfig.getHttpPort(), api); + String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); JSONObject responseJSON = null; logger.debug(url); FormBody.Builder builder = new FormBody.Builder(); - builder.add("secret",mediaConfig.getSecret()); + builder.add("secret",mediaServerItem.getSecret()); if (param != null && param.keySet().size() > 0) { for (String key : param.keySet()){ if (param.get(key) != null) { @@ -96,14 +93,14 @@ public class ZLMRESTfulUtils { } - public void sendPostForImg(String api, Map param, String targetPath, String fileName) { + public void sendPostForImg(IMediaServerItem mediaServerItem, String api, Map param, String targetPath, String fileName) { OkHttpClient client = new OkHttpClient(); - String url = String.format("http://%s:%s/index/api/%s", mediaConfig.getIp(), mediaConfig.getHttpPort(), api); + String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); JSONObject responseJSON = null; logger.debug(url); FormBody.Builder builder = new FormBody.Builder(); - builder.add("secret",mediaConfig.getSecret()); + builder.add("secret",mediaServerItem.getSecret()); if (param != null && param.keySet().size() > 0) { for (String key : param.keySet()){ if (param.get(key) != null) { @@ -142,39 +139,39 @@ public class ZLMRESTfulUtils { } - public JSONObject getMediaList(String app, String stream, String schema, RequestCallback callback){ + public JSONObject getMediaList(IMediaServerItem 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); if (schema != null) param.put("schema",schema); param.put("vhost","__defaultVhost__"); - return sendPost("getMediaList",param, callback); + return sendPost(mediaServerItem, "getMediaList",param, callback); } - public JSONObject getMediaList(String app, String stream){ - return getMediaList(app, stream,null, null); + public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream){ + return getMediaList(mediaServerItem, app, stream,null, null); } - public JSONObject getMediaList(RequestCallback callback){ - return sendPost("getMediaList",null, callback); + public JSONObject getMediaList(IMediaServerItem mediaServerItem,RequestCallback callback){ + return sendPost(mediaServerItem, "getMediaList",null, callback); } - public JSONObject getMediaInfo(String app, String schema, String stream){ + public JSONObject getMediaInfo(IMediaServerItem mediaServerItem,String app, String schema, String stream){ Map param = new HashMap<>(); param.put("app",app); param.put("schema",schema); param.put("stream",stream); param.put("vhost","__defaultVhost__"); - return sendPost("getMediaInfo",param, null); + return sendPost(mediaServerItem, "getMediaInfo",param, null); } - public JSONObject getRtpInfo(String stream_id){ + public JSONObject getRtpInfo(IMediaServerItem mediaServerItem,String stream_id){ Map param = new HashMap<>(); param.put("stream_id",stream_id); - return sendPost("getRtpInfo",param, null); + return sendPost(mediaServerItem, "getRtpInfo",param, null); } - public JSONObject addFFmpegSource(String src_url, String dst_url, String timeout_ms, + public JSONObject addFFmpegSource(IMediaServerItem 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,44 +182,44 @@ public class ZLMRESTfulUtils { param.put("enable_hls", enable_hls); param.put("enable_mp4", enable_mp4); param.put("ffmpeg_cmd_key", ffmpeg_cmd_key); - return sendPost("addFFmpegSource",param, null); + return sendPost(mediaServerItem, "addFFmpegSource",param, null); } - public JSONObject delFFmpegSource(String key){ + public JSONObject delFFmpegSource(IMediaServerItem mediaServerItem,String key){ Map param = new HashMap<>(); param.put("key", key); - return sendPost("delFFmpegSource",param, null); + return sendPost(mediaServerItem, "delFFmpegSource",param, null); } - public JSONObject getMediaServerConfig(){ - return sendPost("getServerConfig",null, null); + public JSONObject getMediaServerConfig(IMediaServerItem mediaServerItem){ + return sendPost(mediaServerItem, "getServerConfig",null, null); } - public JSONObject setServerConfig(Map param){ - return sendPost("setServerConfig",param, null); + public JSONObject setServerConfig(IMediaServerItem mediaServerItem, Map param){ + return sendPost(mediaServerItem,"setServerConfig",param, null); } - public JSONObject openRtpServer(Map param){ - return sendPost("openRtpServer",param, null); + public JSONObject openRtpServer(IMediaServerItem mediaServerItem,Map param){ + return sendPost(mediaServerItem, "openRtpServer",param, null); } - public JSONObject closeRtpServer(Map param) { - return sendPost("closeRtpServer",param, null); + public JSONObject closeRtpServer(IMediaServerItem mediaServerItem,Map param) { + return sendPost(mediaServerItem, "closeRtpServer",param, null); } - public JSONObject listRtpServer() { - return sendPost("listRtpServer",null, null); + public JSONObject listRtpServer(IMediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "listRtpServer",null, null); } - public JSONObject startSendRtp(Map param) { - return sendPost("startSendRtp",param, null); + public JSONObject startSendRtp(IMediaServerItem mediaServerItem,Map param) { + return sendPost(mediaServerItem, "startSendRtp",param, null); } - public JSONObject stopSendRtp(Map param) { - return sendPost("stopSendRtp",param, null); + public JSONObject stopSendRtp(IMediaServerItem mediaServerItem,Map param) { + return sendPost(mediaServerItem, "stopSendRtp",param, null); } - public JSONObject addStreamProxy(String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) { + public JSONObject addStreamProxy(IMediaServerItem 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,33 +228,33 @@ public class ZLMRESTfulUtils { param.put("enable_hls", enable_hls?1:0); param.put("enable_mp4", enable_mp4?1:0); param.put("rtp_type", rtp_type); - return sendPost("addStreamProxy",param, null); + return sendPost(mediaServerItem, "addStreamProxy",param, null); } - public JSONObject closeStreams(String app, String stream) { + public JSONObject closeStreams(IMediaServerItem mediaServerItem,String app, String stream) { Map param = new HashMap<>(); param.put("vhost", "__defaultVhost__"); param.put("app", app); param.put("stream", stream); param.put("force", 1); - return sendPost("close_streams",param, null); + return sendPost(mediaServerItem, "close_streams",param, null); } - public JSONObject getAllSession() { - return sendPost("getAllSession",null, null); + public JSONObject getAllSession(IMediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "getAllSession",null, null); } - public void kickSessions(String localPortSStr) { + public void kickSessions(IMediaServerItem mediaServerItem, String localPortSStr) { Map param = new HashMap<>(); param.put("local_port", localPortSStr); - sendPost("kick_sessions",param, null); + sendPost(mediaServerItem, "kick_sessions",param, null); } - public void getSnap(String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) { + public void getSnap(IMediaServerItem 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); param.put("expire_sec", expire_sec); - sendPostForImg("getSnap",param, targetPath, fileName); + sendPostForImg(mediaServerItem, "getSnap",param, targetPath, fileName); } } 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 ed069f88..5919c9f7 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 @@ -5,6 +5,8 @@ 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; import org.springframework.beans.factory.annotation.Autowired; @@ -30,10 +32,10 @@ public class ZLMRTPServerFactory { private Map currentStreams = null; - public int createRTPServer(String streamId) { + public int createRTPServer(IMediaServerItem mediaServerItem, String streamId) { if (currentStreams == null) { currentStreams = new HashMap<>(); - JSONObject jsonObject = zlmresTfulUtils.listRtpServer(); + JSONObject jsonObject = zlmresTfulUtils.listRtpServer(mediaServerItem); if (jsonObject != null) { JSONArray data = jsonObject.getJSONArray("data"); if (data != null) { @@ -48,7 +50,7 @@ public class ZLMRTPServerFactory { if (currentStreams.get(streamId) != null) { Map closeRtpServerParam = new HashMap<>(); closeRtpServerParam.put("stream_id", streamId); - zlmresTfulUtils.closeRtpServer(closeRtpServerParam); + zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam); currentStreams.remove(streamId); } @@ -58,7 +60,7 @@ public class ZLMRTPServerFactory { param.put("port", newPort); param.put("enable_tcp", 1); param.put("stream_id", streamId); - JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); + JSONObject jsonObject = zlmresTfulUtils.openRtpServer(mediaServerItem, param); if (jsonObject != null) { switch (jsonObject.getInteger("code")){ @@ -68,11 +70,11 @@ public class ZLMRTPServerFactory { case -300: // id已经存在, 可能已经在其他端口推流 Map closeRtpServerParam = new HashMap<>(); closeRtpServerParam.put("stream_id", streamId); - zlmresTfulUtils.closeRtpServer(closeRtpServerParam); + zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam); result = newPort; break; case -400: // 端口占用 - result= createRTPServer(streamId); + result= createRTPServer(mediaServerItem, streamId); break; default: logger.error("创建RTP Server 失败 {}: " + jsonObject.getString("msg"), newPort); @@ -85,20 +87,22 @@ public class ZLMRTPServerFactory { return result; } - public boolean closeRTPServer(String streamId) { + public boolean closeRTPServer(IMediaServerItem serverItem, String streamId) { boolean result = false; - Map param = new HashMap<>(); - param.put("stream_id", streamId); - JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(param); - if (jsonObject != null ) { - if (jsonObject.getInteger("code") == 0) { - result = jsonObject.getInteger("hit") == 1; + if (serverItem !=null){ + Map param = new HashMap<>(); + param.put("stream_id", streamId); + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param); + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + result = jsonObject.getInteger("hit") == 1; + }else { + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + } }else { - logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + // 检查ZLM状态 + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); } - }else { - // 检查ZLM状态 - logger.error("关闭RTP Server 失败: 请检查ZLM服务"); } return result; } @@ -131,11 +135,11 @@ public class ZLMRTPServerFactory { * @param tcp 是否为tcp * @return SendRtpItem */ - public SendRtpItem createSendRtpItem(String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){ + 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(SsrcUtil.getPlaySsrc()); + int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc()); if (localPort != -1) { - closeRTPServer(playSsrc); + closeRTPServer(serverItem, playSsrc); }else { logger.error("没有可用的端口"); return null; @@ -150,6 +154,7 @@ public class ZLMRTPServerFactory { sendRtpItem.setTcp(tcp); sendRtpItem.setApp("rtp"); sendRtpItem.setLocalPort(localPort); + sendRtpItem.setMediaServerId(serverItem.getId()); return sendRtpItem; } @@ -163,11 +168,11 @@ public class ZLMRTPServerFactory { * @param tcp 是否为tcp * @return SendRtpItem */ - public SendRtpItem createSendRtpItem(String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){ + 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(SsrcUtil.getPlaySsrc()); + int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc()); if (localPort != -1) { - closeRTPServer(playSsrc); + closeRTPServer(serverItem, playSsrc); }else { logger.error("没有可用的端口"); return null; @@ -182,21 +187,21 @@ public class ZLMRTPServerFactory { sendRtpItem.setChannelId(channelId); sendRtpItem.setTcp(tcp); sendRtpItem.setLocalPort(localPort); + sendRtpItem.setMediaServerId(serverItem.getId()); return sendRtpItem; } /** * 调用zlm RESTful API —— startSendRtp */ - public Boolean startSendRtpStream(Mapparam) { + public Boolean startSendRtpStream(IMediaServerItem mediaServerItem, Mapparam) { Boolean result = false; - JSONObject jsonObject = zlmresTfulUtils.startSendRtp(param); - logger.info(jsonObject.toJSONString()); + JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServerItem, param); if (jsonObject == null) { logger.error("RTP推流失败: 请检查ZLM服务"); } else if (jsonObject.getInteger("code") == 0) { result= true; - logger.info("RTP推流请求成功,本地推流端口:" + jsonObject.getString("local_port")); + logger.info("RTP推流[ {}/{} ]请求成功,本地推流端口:{}" ,param.get("app"), param.get("stream"), jsonObject.getString("local_port")); } else { logger.error("RTP推流失败: " + jsonObject.getString("msg")); } @@ -206,16 +211,16 @@ public class ZLMRTPServerFactory { /** * 查询待转推的流是否就绪 */ - public Boolean isRtpReady(String streamId) { - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); + public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtmp", streamId); return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); } /** * 查询待转推的流是否就绪 */ - public Boolean isStreamReady(String app, String streamId) { - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(app, "rtmp", streamId); + public Boolean isStreamReady(IMediaServerItem mediaServerItem, String app, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId); return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); } @@ -224,18 +229,17 @@ public class ZLMRTPServerFactory { * @param streamId * @return */ - public int totalReaderCount(String app, String streamId) { - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(app, "rtmp", streamId); + public int totalReaderCount(IMediaServerItem mediaServerItem, String app, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId); return mediaInfo.getInteger("totalReaderCount"); } /** * 调用zlm RESTful API —— stopSendRtp */ - public Boolean stopSendRtpStream(Mapparam) { + public Boolean stopSendRtpStream(IMediaServerItem mediaServerItem,Mapparam) { Boolean result = false; - JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(param); - logger.info(jsonObject.toJSONString()); + JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param); if (jsonObject == null) { logger.error("停止RTP推流失败: 请检查ZLM服务"); } else if (jsonObject.getInteger("code") == 0) { 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 956db144..d06c6c32 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,7 +4,10 @@ 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; @@ -14,10 +17,10 @@ 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 java.util.HashMap; -import java.util.List; -import java.util.Map; +import javax.print.attribute.standard.Media; +import java.util.*; @Component @Order(value=1) @@ -25,140 +28,131 @@ public class ZLMRunner implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private MediaConfig mediaConfig; - - @Value("${server.port}") - private String serverPort; - - @Value("${server.ssl.enabled:false}") - private boolean sslEnabled; - - private boolean startGetMedia = false; + private Map startGetMedia; @Autowired private ZLMRESTfulUtils zlmresTfulUtils; - @Autowired - private ZLMMediaListManager zlmMediaListManager; - @Autowired private ZLMHttpHookSubscribe hookSubscribe; - @Autowired - private ZLMServerManger zlmServerManger; - @Autowired private IStreamProxyService streamProxyService; + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private MediaConfig mediaConfig; + @Override public void run(String... strings) throws Exception { - // 订阅 zlm启动事件 - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null,(response)->{ - ZLMServerConfig ZLMServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class); - zLmRunning(ZLMServerConfig); + IMediaServerItem presetMediaServer = mediaServerService.getOneByHostAndPort( + mediaConfig.getIp(), mediaConfig.getHttpPort()); + if (presetMediaServer != null) { + mediaConfig.setId(presetMediaServer.getId()); + mediaServerService.update(mediaConfig); + } + + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null, + (IMediaServerItem mediaServerItem, JSONObject response)->{ + ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class); + if (zlmServerConfig !=null ) { + startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId()); + mediaServerService.handLeZLMServerConfig(zlmServerConfig); +// zLmRunning(zlmServerConfig); + } }); // 获取zlm信息 - logger.info("等待zlm接入..."); - startGetMedia = true; - ZLMServerConfig ZLMServerConfig = getMediaServerConfig(); + logger.info("等待默认zlm接入..."); - if (ZLMServerConfig != null) { - zLmRunning(ZLMServerConfig); + // 获取所有的zlm, 并开启主动连接 + List all = mediaServerService.getAll(); + if (presetMediaServer == null) { + all.add(mediaConfig.getMediaSerItem()); } + for (IMediaServerItem mediaServerItem : all) { + if (startGetMedia == null) startGetMedia = new HashMap<>(); + startGetMedia.put(mediaServerItem.getId(), true); + new Thread(() -> { + ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem); + if (zlmServerConfig != null) { + startGetMedia.remove(mediaServerItem.getId()); + mediaServerService.handLeZLMServerConfig(zlmServerConfig); + } + }).start(); + } + Timer timer = new Timer(); + // 1分钟后未连接到则不再去主动连接 + timer.schedule(new TimerTask() { + @Override + public void run() { + if (startGetMedia != null) { + Set allZlmId = startGetMedia.keySet(); + for (String id : allZlmId) { + logger.error("[ {} ]]主动连接失败,不再主动连接", id); + startGetMedia.put(id, false); + } + } + } + }, 60 * 1000 * 2); } - public ZLMServerConfig getMediaServerConfig() { - if (!startGetMedia) return null; - JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(); + public ZLMServerConfig getMediaServerConfig(IMediaServerItem mediaServerItem) { + if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) return null; + JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); ZLMServerConfig ZLMServerConfig = null; if (responseJSON != null) { JSONArray data = responseJSON.getJSONArray("data"); if (data != null && data.size() > 0) { ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); - + ZLMServerConfig.setIp(mediaServerItem.getIp()); } } else { - logger.error("getMediaServerConfig失败, 1s后重试"); + logger.error("[ {} ]-[ {}:{} ]主动连接失败失败, 2s后重试", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); try { - Thread.sleep(1000); + Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } - ZLMServerConfig = getMediaServerConfig(); + ZLMServerConfig = getMediaServerConfig(mediaServerItem); } return ZLMServerConfig; - } - private void saveZLMConfig() { - logger.info("设置zlm..."); - String protocol = sslEnabled ? "https" : "http"; - String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaConfig.getHookIp(), serverPort); - String recordHookPrex = null; - if (mediaConfig.getRecordAssistPort() != 0) { - recordHookPrex = String.format("http://127.0.0.1:%s/api/record", mediaConfig.getRecordAssistPort()); - } - Map param = new HashMap<>(); - param.put("api.secret",mediaConfig.getSecret()); // -profile:v Baseline - param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); - param.put("hook.enable","1"); - param.put("hook.on_flow_report",""); - param.put("hook.on_play",String.format("%s/on_play", hookPrex)); - param.put("hook.on_http_access",""); - param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); - param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): ""); - param.put("hook.on_record_ts",""); - param.put("hook.on_rtsp_auth",""); - param.put("hook.on_rtsp_realm",""); - param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); - param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex)); - param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex)); - param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); - param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); - param.put("hook.timeoutSec","20"); - param.put("general.streamNoneReaderDelayMS",mediaConfig.getStreamNoneReaderDelayMS()); - - JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param); - - if (responseJSON != null && responseJSON.getInteger("code") == 0) { - logger.info("设置zlm成功"); - }else { - logger.info("设置zlm失败"); - } } /** * zlm 连接成功或者zlm重启后 */ - private void zLmRunning(ZLMServerConfig zlmServerConfig){ - logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功..."); - // 关闭循环获取zlm配置 - startGetMedia = false; - if (mediaConfig.isAutoConfig()) saveZLMConfig(); - zlmServerManger.updateServerCatch(zlmServerConfig); - - // 清空所有session -// zlmMediaListManager.clearAllSessions(); - - // 更新流列表 - zlmMediaListManager.updateMediaList(); - // 恢复流代理 - List streamProxyListForEnable = storager.getStreamProxyListForEnable(true); - for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { - logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); - JSONObject jsonObject = streamProxyService.addStreamProxyToZlm(streamProxyDto); - if (jsonObject == null) { - // 设置为未启用 - logger.info("恢复流代理失败,请检查流地址后重新启用" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); - streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream()); - }else if (jsonObject.getInteger("code") != 0){ // TODO 将错误信息存入数据库, 前端展示 - logger.info("恢复流代理失败:" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream() + "[ " + JSONObject.toJSONString(jsonObject) + " ]"); - streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream()); - } - } - } +// private void zLmRunning(ZLMServerConfig zlmServerConfig){ +// logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功..."); +// // 关闭循环获取zlm配置 +// startGetMedia = false; +// MediaServerItem mediaServerItem = new MediaServerItem(zlmServerConfig, sipIp); +// storager.updateMediaServer(mediaServerItem); +// +// if (mediaServerItem.isAutoConfig()) setZLMConfig(mediaServerItem); +// zlmServerManger.updateServerCatchFromHook(zlmServerConfig); +// +// // 清空所有session +//// zlmMediaListManager.clearAllSessions(); +// +// // 更新流列表 +// zlmMediaListManager.updateMediaList(mediaServerItem); +// // 恢复流代理, 只查找这个这个流媒体 +// List streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer( +// mediaServerItem.getId(), true); +// for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { +// logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); +// JSONObject jsonObject = streamProxyService.addStreamProxyToZlm(streamProxyDto); +// if (jsonObject == null) { +// // 设置为未启用 +// logger.info("恢复流代理失败,请检查流地址后重新启用" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); +// streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream()); +// } +// } +// } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java index 21fcebf1..c1a9bd20 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java @@ -34,12 +34,15 @@ public class ZLMServerConfig { @JSONField(name = "general.streamNoneReaderDelayMS") private String generalStreamNoneReaderDelayMS; + @JSONField(name = "ip") private String ip; private String sdpIp; private String streamIp; + private String hookIp; + private String updateTime; private String createTime; @@ -66,7 +69,7 @@ public class ZLMServerConfig { private String hookEnable; @JSONField(name = "hook.on_flow_report") - private Integer hookOnFlowReport; + private String hookOnFlowReport; @JSONField(name = "hook.on_http_access") private String hookOnHttpAccess; @@ -117,7 +120,7 @@ public class ZLMServerConfig { private String httpNotFound; @JSONField(name = "http.port") - private Integer httpPort; + private int httpPort; @JSONField(name = "http.rootPath") private String httpRootPath; @@ -126,7 +129,7 @@ public class ZLMServerConfig { private String httpSendBufSize; @JSONField(name = "http.sslport") - private Integer httpSSLport; + private int httpSSLport; @JSONField(name = "multicast.addrMax") private String multicastAddrMax; @@ -159,10 +162,10 @@ public class ZLMServerConfig { private String rtmpModifyStamp; @JSONField(name = "rtmp.port") - private Integer rtmpPort; + private int rtmpPort; @JSONField(name = "rtmp.sslport") - private Integer rtmpSslPort; + private int rtmpSslPort; @JSONField(name = "rtp.audioMtuSize") private String rtpAudioMtuSize; @@ -186,7 +189,7 @@ public class ZLMServerConfig { private String rtpProxyDumpDir; @JSONField(name = "rtp_proxy.port") - private Integer rtpProxyPort; + private int rtpProxyPort; @JSONField(name = "rtp_proxy.timeoutSec") private String rtpProxyTimeoutSec; @@ -201,10 +204,10 @@ public class ZLMServerConfig { private String rtspKeepAliveSecond; @JSONField(name = "rtsp.port") - private Integer rtspPort; + private int rtspPort; @JSONField(name = "rtsp.sslport") - private Integer rtspSSlport; + private int rtspSSlport; @JSONField(name = "shell.maxReqSize") private String shellMaxReqSize; @@ -212,6 +215,15 @@ public class ZLMServerConfig { @JSONField(name = "shell.shell") private String shellPhell; + + public String getHookIp() { + return hookIp; + } + + public void setHookIp(String hookIp) { + this.hookIp = hookIp; + } + public String getApiDebug() { return apiDebug; } @@ -388,11 +400,11 @@ public class ZLMServerConfig { this.hookEnable = hookEnable; } - public Integer getHookOnFlowReport() { + public String getHookOnFlowReport() { return hookOnFlowReport; } - public void setHookOnFlowReport(Integer hookOnFlowReport) { + public void setHookOnFlowReport(String hookOnFlowReport) { this.hookOnFlowReport = hookOnFlowReport; } @@ -524,11 +536,11 @@ public class ZLMServerConfig { this.httpNotFound = httpNotFound; } - public Integer getHttpPort() { + public int getHttpPort() { return httpPort; } - public void setHttpPort(Integer httpPort) { + public void setHttpPort(int httpPort) { this.httpPort = httpPort; } @@ -548,11 +560,11 @@ public class ZLMServerConfig { this.httpSendBufSize = httpSendBufSize; } - public Integer getHttpSSLport() { + public int getHttpSSLport() { return httpSSLport; } - public void setHttpSSLport(Integer httpSSLport) { + public void setHttpSSLport(int httpSSLport) { this.httpSSLport = httpSSLport; } @@ -636,19 +648,19 @@ public class ZLMServerConfig { this.rtmpModifyStamp = rtmpModifyStamp; } - public Integer getRtmpPort() { + public int getRtmpPort() { return rtmpPort; } - public void setRtmpPort(Integer rtmpPort) { + public void setRtmpPort(int rtmpPort) { this.rtmpPort = rtmpPort; } - public Integer getRtmpSslPort() { + public int getRtmpSslPort() { return rtmpSslPort; } - public void setRtmpSslPort(Integer rtmpSslPort) { + public void setRtmpSslPort(int rtmpSslPort) { this.rtmpSslPort = rtmpSslPort; } @@ -708,11 +720,11 @@ public class ZLMServerConfig { this.rtpProxyDumpDir = rtpProxyDumpDir; } - public Integer getRtpProxyPort() { + public int getRtpProxyPort() { return rtpProxyPort; } - public void setRtpProxyPort(Integer rtpProxyPort) { + public void setRtpProxyPort(int rtpProxyPort) { this.rtpProxyPort = rtpProxyPort; } @@ -748,19 +760,19 @@ public class ZLMServerConfig { this.rtspKeepAliveSecond = rtspKeepAliveSecond; } - public Integer getRtspPort() { + public int getRtspPort() { return rtspPort; } - public void setRtspPort(Integer rtspPort) { + public void setRtspPort(int rtspPort) { this.rtspPort = rtspPort; } - public Integer getRtspSSlport() { + public int getRtspSSlport() { return rtspSSlport; } - public void setRtspSSlport(Integer rtspSSlport) { + public void setRtspSSlport(int rtspSSlport) { this.rtspSSlport = rtspSSlport; } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerManger.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerManger.java deleted file mode 100644 index fde3b447..00000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerManger.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm; - -import com.genersoft.iot.vmp.conf.MediaConfig; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -@Component -public class ZLMServerManger { - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private MediaConfig mediaConfig; - - public void updateServerCatch(ZLMServerConfig zlmServerConfig) { - - zlmServerConfig.setIp(mediaConfig.getIp()); - zlmServerConfig.setStreamIp(mediaConfig.getStreamIp()); - zlmServerConfig.setSdpIp(mediaConfig.getSdpIp()); - zlmServerConfig.setHttpPort(mediaConfig.getHttpPort()); - - if(!StringUtils.isEmpty(mediaConfig.getHttpSSlPort())) - zlmServerConfig.setHttpSSLport(mediaConfig.getHttpSSlPort()); - - if(!StringUtils.isEmpty(mediaConfig.getRtspPort())) - zlmServerConfig.setRtspPort(mediaConfig.getRtspPort()); - - if(!StringUtils.isEmpty(mediaConfig.getRtspSSLPort())) - zlmServerConfig.setRtspSSlport(mediaConfig.getRtspSSLPort()); - - if(!StringUtils.isEmpty(mediaConfig.getRtmpPort())) - zlmServerConfig.setRtmpPort(mediaConfig.getRtmpPort()); - - if(!StringUtils.isEmpty(mediaConfig.getRtmpSSlPort())) - zlmServerConfig.setRtmpSslPort(mediaConfig.getRtmpSSlPort()); - - if(!StringUtils.isEmpty(mediaConfig.getRtpProxyPort())) - zlmServerConfig.setRtpProxyPort(mediaConfig.getRtpProxyPort()); - - redisCatchStorage.updateMediaInfo(zlmServerConfig); - } -} 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 new file mode 100644 index 00000000..64971628 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java @@ -0,0 +1,92 @@ +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/MediaItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java index 24bfcc17..6d9ceee9 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java @@ -78,6 +78,10 @@ public class MediaItem { */ private String vhost; + /** + * 是否是docker部署, docker部署不会自动更新zlm使用的端口,需要自己手动修改 + */ + private boolean docker; public static class MediaTrack { /** @@ -364,4 +368,12 @@ public class MediaItem { public OriginSock getOriginSock() { return originSock; } + + public boolean isDocker() { + return docker; + } + + public void setDocker(boolean docker) { + this.docker = docker; + } } 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 new file mode 100644 index 00000000..b56881f7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java @@ -0,0 +1,254 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + + +import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; +import org.springframework.util.StringUtils; + +public class MediaServerItem implements IMediaServerItem{ + + private String id; + + private String ip; + + private String hookIp; + + private String sdpIp; + + private String streamIp; + + private int httpPort; + + private int httpSSlPort; + + private int rtmpPort; + + private int rtmpSSlPort; + + private int rtpProxyPort; + + private int rtspPort; + + private int rtspSSLPort; + + private boolean autoConfig; + + private String secret; + + private String streamNoneReaderDelayMS; + + private boolean rtpEnable; + + private String rtpPortRange; + + private int recordAssistPort; + + private String createTime; + + private String updateTime; + + private boolean docker; + + private int count; + + public MediaServerItem() { + } + + public MediaServerItem(ZLMServerConfig zlmServerConfig, String sipIp) { + id = zlmServerConfig.getGeneralMediaServerId(); + ip = zlmServerConfig.getIp(); + hookIp = StringUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp(); + sdpIp = StringUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp(); + streamIp = StringUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp(); + httpPort = zlmServerConfig.getHttpPort(); + httpSSlPort = zlmServerConfig.getHttpSSLport(); + rtmpPort = zlmServerConfig.getRtmpPort(); + rtmpSSlPort = zlmServerConfig.getRtmpSslPort(); + rtpProxyPort = zlmServerConfig.getRtpProxyPort(); + rtspPort = zlmServerConfig.getRtspPort(); + rtspSSLPort = zlmServerConfig.getRtspSSlport(); + autoConfig = true; // 默认值true; + secret = zlmServerConfig.getApiSecret(); + streamNoneReaderDelayMS = zlmServerConfig.getGeneralStreamNoneReaderDelayMS(); + rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 + recordAssistPort = 0; // 默认关闭 + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getHookIp() { + return hookIp; + } + + public void setHookIp(String hookIp) { + this.hookIp = hookIp; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getStreamIp() { + return streamIp; + } + + public void setStreamIp(String streamIp) { + this.streamIp = streamIp; + } + + public int getHttpPort() { + return httpPort; + } + + public void setHttpPort(int httpPort) { + this.httpPort = httpPort; + } + + public int getHttpSSlPort() { + return httpSSlPort; + } + + public void setHttpSSlPort(int httpSSlPort) { + this.httpSSlPort = httpSSlPort; + } + + public int getRtmpPort() { + return rtmpPort; + } + + public void setRtmpPort(int rtmpPort) { + this.rtmpPort = rtmpPort; + } + + public int getRtmpSSlPort() { + return rtmpSSlPort; + } + + public void setRtmpSSlPort(int rtmpSSlPort) { + this.rtmpSSlPort = rtmpSSlPort; + } + + public int getRtpProxyPort() { + return rtpProxyPort; + } + + public void setRtpProxyPort(int rtpProxyPort) { + this.rtpProxyPort = rtpProxyPort; + } + + public int getRtspPort() { + return rtspPort; + } + + public void setRtspPort(int rtspPort) { + this.rtspPort = rtspPort; + } + + public int getRtspSSLPort() { + return rtspSSLPort; + } + + public void setRtspSSLPort(int rtspSSLPort) { + this.rtspSSLPort = rtspSSLPort; + } + + public 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; + } + + public void setRecordAssistPort(int recordAssistPort) { + this.recordAssistPort = recordAssistPort; + } + + @Override + public boolean isDocker() { + return docker; + } + + @Override + public void setDocker(boolean docker) { + this.docker = docker; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java index 3f997a66..40ba215f 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java @@ -7,6 +7,7 @@ public class StreamProxyItem extends GbStream { private String type; private String app; private String stream; + private String mediaServerId; private String url; private String src_url; private String dst_url; @@ -17,6 +18,7 @@ public class StreamProxyItem extends GbStream { private boolean enable_hls; private boolean enable_mp4; private String platformGbId; + private String createTime; public String getType() { return type; @@ -42,6 +44,16 @@ public class StreamProxyItem extends GbStream { this.stream = stream; } + @Override + public String getMediaServerId() { + return mediaServerId; + } + + @Override + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + public String getUrl() { return url; } @@ -122,4 +134,12 @@ public class StreamProxyItem extends GbStream { public void setPlatformGbId(String platformGbId) { this.platformGbId = platformGbId; } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java index 7a1c49ba..73e162af 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java @@ -76,6 +76,11 @@ public class StreamPushItem extends GbStream implements Comparable getAll(); + + IMediaServerItem getOne(String generalMediaServerId); + + IMediaServerItem getOneByHostAndPort(String host, int port); + + /** + * 新的节点加入 + * @param zlmServerConfig + * @return + */ + void handLeZLMServerConfig(ZLMServerConfig zlmServerConfig); + + void updateServerCatch(IMediaServerItem mediaServerItem, Integer count, Boolean b); + + IMediaServerItem getMediaServerForMinimumLoad(); + + void setZLMConfig(IMediaServerItem mediaServerItem); + + void init(); + + void closeRTPServer(Device device, String channelId); + + void update(MediaConfig mediaConfig); + + void addCount(String mediaServerId); + + void removeCount(String mediaServerId); +} 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 010870a7..868a8310 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java @@ -2,6 +2,8 @@ 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; /** * 媒体信息业务 @@ -14,23 +16,8 @@ public interface IMediaService { * @param stream * @return */ - StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream); + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr); - /** - * 根据应用名和流ID获取播放地址, 只是地址拼接 - * @param app - * @param stream - * @return - */ - StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks); - - /** - * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 - * @param app - * @param stream - * @return - */ - StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks, String addr); /** * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 @@ -38,5 +25,21 @@ public interface IMediaService { * @param stream * @return */ - StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String addr); + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaServerItem, String app, String stream, JSONArray tracks); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(IMediaServerItem 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 a6492677..fdc86d6e 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java @@ -1,8 +1,11 @@ package com.genersoft.iot.vmp.service; 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; /** @@ -10,8 +13,10 @@ import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; */ public interface IPlayService { - void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid); - void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid); + void onPublishHandlerForPlayBack(IMediaServerItem mediaServerItem,JSONObject resonse, String deviceId, String channelId, String uuid); + void onPublishHandlerForPlay(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid); - PlayResult play(String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + PlayResult play(IMediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + + IMediaServerItem 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 2f7388a9..fa79d696 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java @@ -1,6 +1,8 @@ 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; @@ -61,5 +63,5 @@ public interface IStreamProxyService { * 获取ffmpeg.cmd模板 * @return */ - JSONObject getFFmpegCMDs(); + JSONObject getFFmpegCMDs(IMediaServerItem 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 e66480a0..eaf24904 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java @@ -1,6 +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.StreamPushItem; import com.github.pagehelper.PageInfo; @@ -8,7 +9,7 @@ import java.util.List; public interface IStreamPushService { - List handleJSON(String json); + List handleJSON(String json, IMediaServerItem mediaServerItem); /** * 将应用名和流ID加入国标关联 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 new file mode 100644 index 00000000..a90225a8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java @@ -0,0 +1,340 @@ +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.conf.MediaConfig; +import com.genersoft.iot.vmp.conf.ProxyServletConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +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.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; +import org.mitre.dsmiley.httpproxy.ProxyServlet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 媒体服务器节点管理 + */ +@Service +public class MediaServerServiceImpl implements IMediaServerService { + + 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; + + @Value("${server.ssl.enabled:false}") + private boolean sslEnabled; + + @Value("${server.port}") + private String serverPort; + + @Autowired + private MediaConfig mediaConfig; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private MediaServerMapper mediaServerMapper; + + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + /** + * 初始化 + */ + @Override + public void init() { + zlmServers.clear(); + zlmServerStatus.clear(); + List mediaServerItemList = mediaServerMapper.queryAll(); + for (IMediaServerItem mediaServerItem : mediaServerItemList) { + zlmServers.put(mediaServerItem.getId(), mediaServerItem); + } + } + + @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 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) { + + } + + @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); + } + return result; + + +// return mediaServerMapper.queryAll(); + } + + /** + * 获取单个zlm服务器 + * @param mediaServerId 服务id + * @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; + } + } + + @Override + public IMediaServerItem getOneByHostAndPort(String host, int port) { + return mediaServerMapper.queryOneByHostAndPort(host, port); + } + + /** + * 处理zlm上线 + * @param zlmServerConfig zlm上线携带的参数 + */ + @Override + public void handLeZLMServerConfig(ZLMServerConfig zlmServerConfig) { + 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()); + // 如果是配置文件中的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()); + + } + serverItem.setUpdateTime(now); + mediaServerMapper.update(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; + mediaServerMapper.add(mediaConfig); + }else { + // 一个新的zlm接入wvp + serverItem = new MediaServerItem(zlmServerConfig, sipIp); + serverItem.setCreateTime(now); + serverItem.setUpdateTime(now); + mediaServerMapper.add(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); + } + } + } + + @Override + public void addCount(String mediaServerId) { + if (zlmServerStatus.get(mediaServerId) != null) { + zlmServerStatus.put(mediaServerId, zlmServerStatus.get(mediaServerId) + 1); + } + } + + @Override + public void removeCount(String mediaServerId) { + if (zlmServerStatus.get(mediaServerId) != null) { + zlmServerStatus.put(mediaServerId, zlmServerStatus.get(mediaServerId) - 1); + } + } + + /** + * 获取负载最低的节点 + * @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; + } + } + } + + if (key == null) { + logger.info("获取负载最低的节点时无在线节点"); + return null; + }else{ + return zlmServers.get(key); + } + } + + /** + * 对zlm服务器进行基础配置 + * @param mediaServerItem 服务ID + */ + @Override + public void setZLMConfig(IMediaServerItem mediaServerItem) { + logger.info("[ {} ]-[ {}:{} ]设置zlm", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + String protocol = sslEnabled ? "https" : "http"; + String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); + String recordHookPrex = null; + if (mediaServerItem.getRecordAssistPort() != 0) { + recordHookPrex = String.format("http://127.0.0.1:%s/api/record", mediaServerItem.getRecordAssistPort()); + } + Map param = new HashMap<>(); + param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline + param.put("ffmpeg.cmd","%s -fflags nobuffer -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); + param.put("hook.enable","1"); + param.put("hook.on_flow_report",""); + param.put("hook.on_play",String.format("%s/on_play", hookPrex)); + param.put("hook.on_http_access",""); + param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); + param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): ""); + param.put("hook.on_record_ts",""); + param.put("hook.on_rtsp_auth",""); + param.put("hook.on_rtsp_realm",""); + param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); + param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex)); + param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex)); + param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); + param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); + param.put("hook.timeoutSec","20"); + param.put("general.streamNoneReaderDelayMS",mediaServerItem.getStreamNoneReaderDelayMS()); + + JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param); + + if (responseJSON != null && responseJSON.getInteger("code") == 0) { + logger.info("[ {} ]-[ {}:{} ]设置zlm成功", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + }else { + logger.info("[ {} ]-[ {}:{} ]设置zlm失败" + responseJSON.getString("msg"), + 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 e40f4b17..465ca88a 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 @@ -6,6 +6,9 @@ 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; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.service.IMediaService; @@ -21,30 +24,53 @@ public class MediaServiceImpl implements IMediaService { @Autowired private IVideoManagerStorager storager; + @Autowired + private IMediaServerService mediaServerService; + @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @Override - public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks) { - return getStreamInfoByAppAndStream(app, stream, tracks, null); + public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks) { + return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null); } @Override - public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream) { - return getStreamInfoByAppAndStreamWithCheck(app, stream, null); + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) { + StreamInfo streamInfo = null; + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo == null) { + return streamInfo; + } + JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, app, stream); + if (mediaList != null) { + if (mediaList.getInteger("code") == 0) { + JSONArray data = mediaList.getJSONArray("data"); + if (data == null) return null; + JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); + JSONArray tracks = mediaJSON.getJSONArray("tracks"); + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks); + } + } + return streamInfo; } @Override - public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks, String addr) { - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId) { + return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null); + } + + @Override + public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) { StreamInfo streamInfoResult = new StreamInfo(); streamInfoResult.setStreamId(stream); streamInfoResult.setApp(app); if (addr == null) { addr = mediaInfo.getStreamIp(); } + streamInfoResult.setMediaServerId(mediaInfo.getId()); streamInfoResult.setRtmp(String.format("rtmp://%s:%s/%s/%s", addr, mediaInfo.getRtmpPort(), app, stream)); streamInfoResult.setRtsp(String.format("rtsp://%s:%s/%s/%s", addr, mediaInfo.getRtspPort(), app, stream)); streamInfoResult.setFlv(String.format("http://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpPort(), app, stream)); @@ -60,19 +86,4 @@ public class MediaServiceImpl implements IMediaService { return streamInfoResult; } - @Override - public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String addr) { - StreamInfo streamInfo = null; - JSONObject mediaList = zlmresTfulUtils.getMediaList(app, stream); - if (mediaList != null) { - if (mediaList.getInteger("code") == 0) { - JSONArray data = mediaList.getJSONArray("data"); - if (data == null) return null; - JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); - JSONArray tracks = mediaJSON.getJSONArray("tracks"); - streamInfo = getStreamInfoByAppAndStream(app, stream, tracks, addr); - } - } - return streamInfo; - } } 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 172d9803..85a4f48c 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,6 +14,9 @@ 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.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; @@ -59,6 +62,9 @@ public class PlayServiceImpl implements IPlayService { @Autowired private IMediaService mediaService; + @Autowired + private IMediaServerService mediaServerService; + @Autowired private VideoStreamSessionManager streamSession; @@ -67,8 +73,18 @@ public class PlayServiceImpl implements IPlayService { @Override - public PlayResult play(String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) { + public PlayResult play(IMediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) { PlayResult playResult = new PlayResult(); + if (mediaServerItem == null) { + RequestMessage msg = new RequestMessage(); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid()); + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(-1); + wvpResult.setMsg("未找到可用的zlm"); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + return playResult; + } Device device = storager.queryVideoDevice(deviceId); StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); playResult.setDevice(device); @@ -82,7 +98,7 @@ public class PlayServiceImpl implements IPlayService { result.onTimeout(()->{ logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); // 释放rtpserver - cmder.closeRTPServer(playResult.getDevice(), channelId); + mediaServerService.closeRTPServer(playResult.getDevice(), channelId); RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid()); WVPResult wvpResult = new WVPResult(); @@ -115,9 +131,10 @@ 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()); String streamUrl = streamInfoForSuccess.getFmp4(); // 请求截图 - zlmresTfulUtils.getSnap(streamUrl, 15, 1, path, fileName); + zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName); } } } catch (FileNotFoundException e) { @@ -126,17 +143,17 @@ public class PlayServiceImpl implements IPlayService { }); if (streamInfo == null) { // 发送点播消息 - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { + cmder.playStreamCmd(mediaServerItem, device, channelId, (IMediaServerItem mediaServerItemInUse, JSONObject response) -> { logger.info("收到订阅消息: " + response.toJSONString()); - onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); + onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid.toString()); if (hookEvent != null) { - hookEvent.response(response); + hookEvent.response(mediaServerItem, response); } - }, event -> { + }, (event) -> { RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); Response response = event.getResponse(); - cmder.closeRTPServer(playResult.getDevice(), channelId); + mediaServerService.closeRTPServer(playResult.getDevice(), channelId); WVPResult wvpResult = new WVPResult(); wvpResult.setCode(-1); wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); @@ -158,7 +175,10 @@ public class PlayServiceImpl implements IPlayService { resultHolder.invokeResult(msg); return playResult; } - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); + String mediaServerId = streamInfo.getMediaServerId(); + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); if (rtpInfo != null && rtpInfo.getBoolean("exist")) { RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); @@ -171,16 +191,16 @@ public class PlayServiceImpl implements IPlayService { resultHolder.invokeResult(msg); if (hookEvent != null) { - hookEvent.response(JSONObject.parseObject(JSON.toJSONString(streamInfo))); + hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo))); } } else { redisCatchStorage.stopPlay(streamInfo); storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { + cmder.playStreamCmd(mediaServerItem, device, channelId, (IMediaServerItem mediaServerItemInuse, JSONObject response) -> { logger.info("收到订阅消息: " + response.toJSONString()); - onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); - }, event -> { - cmder.closeRTPServer(playResult.getDevice(), channelId); + onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString()); + }, (event) -> { + mediaServerService.closeRTPServer(playResult.getDevice(), channelId); RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); Response response = event.getResponse(); @@ -198,10 +218,10 @@ public class PlayServiceImpl implements IPlayService { } @Override - public void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid) { + public void onPublishHandlerForPlay(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) { RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); + StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid); if (streamInfo != null) { DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); if (deviceChannel != null) { @@ -234,10 +254,26 @@ public class PlayServiceImpl implements IPlayService { } @Override - public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid) { + public IMediaServerItem getNewMediaServerItem(Device device) { + if (device == null) return null; + String mediaServerId = device.getMediaServerId(); + IMediaServerItem mediaServerItem = null; + if (mediaServerId == null) { + mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(); + }else { + mediaServerItem = mediaServerService.getOne(mediaServerId); + } + if (mediaServerItem == null) { + logger.warn("点播时未找到可使用的ZLM..."); + } + return mediaServerItem; + } + + @Override + public void onPublishHandlerForPlayBack(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) { RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); + StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid); if (streamInfo != null) { redisCatchStorage.startPlayback(streamInfo); msg.setData(JSON.toJSONString(streamInfo)); @@ -249,10 +285,10 @@ public class PlayServiceImpl implements IPlayService { } } - public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) { + public StreamInfo onPublishHandler(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) { String streamId = resonse.getString("stream"); JSONArray tracks = resonse.getJSONArray("tracks"); - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream("rtp", streamId, tracks); + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks); streamInfo.setDeviceID(deviceId); streamInfo.setChannelId(channelId); return streamInfo; 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 2eb99b82..fe49c3b9 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 @@ -2,10 +2,12 @@ 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.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.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.service.IGbStreamService; +import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; @@ -13,6 +15,8 @@ import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper; import com.genersoft.iot.vmp.service.IStreamProxyService; import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -25,6 +29,8 @@ import java.util.List; @Service public class StreamProxyServiceImpl implements IStreamProxyService { + private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class); + @Autowired private IVideoManagerStorager videoManagerStorager; @@ -32,7 +38,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { private IRedisCatchStorage redisCatchStorage; @Autowired - private ZLMRESTfulUtils zlmresTfulUtils; + private ZLMRESTfulUtils zlmresTfulUtils;; @Autowired private StreamProxyMapper streamProxyMapper; @@ -46,15 +52,28 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Autowired private IGbStreamService gbStreamService; + @Autowired + private IMediaServerService mediaServerService; + @Override public String save(StreamProxyItem param) { - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); + IMediaServerItem mediaInfo; + if ("auto".equals(param.getMediaServerId())){ + mediaInfo = mediaServerService.getMediaServerForMinimumLoad(); + }else { + mediaInfo = mediaServerService.getOne(param.getMediaServerId()); + } + if (mediaInfo == null) { + logger.warn("保存代理未找到在线的ZLM..."); + return "保存失败"; + } String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(), param.getStream() ); param.setDst_url(dstUrl); StringBuffer result = new StringBuffer(); boolean streamLive = false; + param.setMediaServerId(mediaInfo.getId()); // 更新 if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { if (videoManagerStorager.updateStreamProxy(param)) { @@ -81,6 +100,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService { videoManagerStorager.updateStreamProxy(param); } } + }else { + result.append("保存失败"); } } @@ -99,11 +120,18 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Override public JSONObject addStreamProxyToZlm(StreamProxyItem param) { JSONObject result = null; + IMediaServerItem mediaServerItem = null; + if (param.getMediaServerId() == null) { + logger.warn("添加代理时MediaServerId 为null"); + return null; + }else { + mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + } if ("default".equals(param.getType())){ - result = zlmresTfulUtils.addStreamProxy(param.getApp(), param.getStream(), param.getUrl(), + result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(), param.isEnable_hls(), param.isEnable_mp4(), param.getRtp_type()); }else if ("ffmpeg".equals(param.getType())) { - result = zlmresTfulUtils.addFFmpegSource(param.getSrc_url(), param.getDst_url(), + result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrc_url(), param.getDst_url(), param.getTimeout_ms() + "", param.isEnable_hls(), param.isEnable_mp4(), param.getFfmpeg_cmd_key()); } @@ -112,8 +140,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Override public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) { - JSONObject result = zlmresTfulUtils.closeStreams(param.getApp(), param.getStream()); - + if (param ==null) return null; + IMediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + JSONObject result = zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream()); return result; } @@ -124,17 +153,18 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Override public void del(String app, String stream) { - StreamProxyItem streamProxyItem = new StreamProxyItem(); - streamProxyItem.setApp(app); - streamProxyItem.setStream(stream); - JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem); - if (jsonObject.getInteger("code") == 0) { + StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream); + if (streamProxyItem != null) { videoManagerStorager.deleteStreamProxy(app, stream); - // 如果关联了国标那么移除关联 - gbStreamMapper.del(app, stream); - platformGbStreamMapper.delByAppAndStream(app, stream); - // TODO 如果关联的推流, 那么状态设置为离线 + JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + // 如果关联了国标那么移除关联 + gbStreamMapper.del(app, stream); + platformGbStreamMapper.delByAppAndStream(app, stream); + // TODO 如果关联的推流, 那么状态设置为离线 + } } + } @Override @@ -168,9 +198,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { } @Override - public JSONObject getFFmpegCMDs() { + public JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem) { JSONObject result = new JSONObject(); - JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(); + JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0 && mediaServerConfigResuly.getJSONArray("data").size() > 0){ JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(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 7ebfb54a..3692e549 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,9 +5,13 @@ 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; +import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; import com.genersoft.iot.vmp.storager.dao.StreamPushMapper; import com.github.pagehelper.PageHelper; @@ -32,8 +36,14 @@ public class StreamPushServiceImpl implements IStreamPushService { @Autowired private ZLMRESTfulUtils zlmresTfulUtils; + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaServerService mediaServerService; + @Override - public List handleJSON(String jsonData) { + public List handleJSON(String jsonData, IMediaServerItem mediaServerItem) { if (jsonData == null) return null; Map result = new HashMap<>(); @@ -50,6 +60,7 @@ public class StreamPushServiceImpl implements IStreamPushService { if (streamPushItem == null) { streamPushItem = new StreamPushItem(); streamPushItem.setApp(item.getApp()); + streamPushItem.setMediaServerId(mediaServerItem.getId()); streamPushItem.setStream(item.getStream()); streamPushItem.setAliveSecond(item.getAliveSecond()); streamPushItem.setCreateStamp(item.getCreateStamp()); @@ -87,7 +98,8 @@ public class StreamPushServiceImpl implements IStreamPushService { @Override public boolean removeFromGB(GbStream stream) { int del = gbStreamMapper.del(stream.getApp(), stream.getStream()); - JSONObject mediaList = zlmresTfulUtils.getMediaList(stream.getApp(), stream.getStream()); + IMediaServerItem 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 56c31c18..a4edb088 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -2,10 +2,11 @@ package com.genersoft.iot.vmp.storager; 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.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; @@ -40,19 +41,6 @@ public interface IRedisCatchStorage { StreamInfo queryPlayByDevice(String deviceId, String channelId); - /** - * 更新流媒体信息 - * @param ZLMServerConfig - * @return - */ - boolean updateMediaInfo(ZLMServerConfig ZLMServerConfig); - - /** - * 获取流媒体信息 - * @return - */ - ZLMServerConfig getMediaInfo(); - Map queryPlayByDeviceId(String deviceId); boolean startPlayback(StreamInfo stream); @@ -114,6 +102,13 @@ public interface IRedisCatchStorage { */ void clearCatchByDeviceId(String deviceId); + /** + * 获取mediaServer节点 + * @param mediaServerId + * @return + */ +// MediaServerItem getMediaInfo(String mediaServerId); + /** * 设置所有设备离线 */ 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 5d838c5d..97c37944 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java @@ -3,6 +3,8 @@ 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; import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; @@ -365,4 +367,12 @@ public interface IVideoManagerStorager { * @param online */ void updateParentPlatformStatus(String platformGbID, boolean online); + + /** + * 更新媒体节点 + * @param mediaServerItem + */ + void updateMediaServer(MediaServerItem mediaServerItem); + + List getStreamProxyListForEnableInMediaServer(String id, boolean b); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java index 80397d2c..230afbc9 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java @@ -12,9 +12,10 @@ import java.util.List; public interface GbStreamMapper { @Insert("INSERT INTO gb_stream (app, stream, gbId, name, " + - "longitude, latitude, streamType, status) VALUES" + + "longitude, latitude, streamType, mediaServerId, status) VALUES" + "('${app}', '${stream}', '${gbId}', '${name}', " + - "'${longitude}', '${latitude}', '${streamType}', ${status})") + "'${longitude}', '${latitude}', '${streamType}', " + + "'${mediaServerId}', ${status})") int add(GbStream gbStream); @Update("UPDATE gb_stream " + @@ -25,6 +26,7 @@ public interface GbStreamMapper { "streamType=#{streamType}," + "longitude=#{longitude}, " + "latitude=#{latitude}," + + "mediaServerId=#{mediaServerId}," + "status=${status} " + "WHERE app=#{app} AND stream=#{stream} AND gbId=#{gbId}") int update(GbStream gbStream); @@ -52,4 +54,7 @@ public interface GbStreamMapper { "SET status=${status} " + "WHERE app=#{app} AND stream=#{stream}") void setStatus(String app, String stream, boolean status); + + @Select("SELECT gs.*, pgs.platformId FROM gb_stream gs LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream WHERE mediaServerId=#{mediaServerId} ") + List selectAllByMediaServerId(String mediaServerId); } 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 new file mode 100644 index 00000000..475ec6d2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java @@ -0,0 +1,99 @@ +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; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface MediaServerMapper { + + @Insert("INSERT INTO media_server (" + + "id, " + + "ip, " + + "hookIp, " + + "sdpIp, " + + "streamIp, " + + "httpPort, " + + "httpSSlPort, " + + "rtmpPort, " + + "rtmpSSlPort, " + + "rtpProxyPort, " + + "rtspPort, " + + "rtspSSLPort, " + + "autoConfig, " + + "secret, " + + "streamNoneReaderDelayMS, " + + "rtpEnable, " + + "rtpPortRange, " + + "recordAssistPort, " + + "createTime, " + + "updateTime" + + ") VALUES " + + "(" + + "'${id}', " + + "'${ip}', " + + "'${hookIp}', " + + "'${sdpIp}', " + + "'${streamIp}', " + + "${httpPort}, " + + "${httpSSlPort}, " + + "${rtmpPort}, " + + "${rtmpSSlPort}, " + + "${rtpProxyPort}, " + + "${rtspPort}, " + + "${rtspSSLPort}, " + + "${autoConfig}, " + + "'${secret}', " + + "${streamNoneReaderDelayMS}, " + + "${rtpEnable}, " + + "'${rtpPortRange}', " + + "${recordAssistPort}, " + + "'${createTime}', " + + "'${updateTime}')") + int add(IMediaServerItem mediaServerItem); + + @Update(value = {" "}) + int update(IMediaServerItem mediaServerItem); + + @Select("SELECT * FROM media_server WHERE id='${id}'") + MediaServerItem queryOne(String id); + + @Select("SELECT * FROM media_server") + List queryAll(); + + @Select("DELETE FROM media_server WHERE id='${id}'") + int delOne(String secret); + + @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/dao/StreamProxyMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java index 3f53c362..7346da5c 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java @@ -10,10 +10,10 @@ import java.util.List; @Repository public interface StreamProxyMapper { - @Insert("INSERT INTO stream_proxy (type, app, stream, url, src_url, dst_url, " + - "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable) VALUES" + - "('${type}','${app}', '${stream}', '${url}', '${src_url}', '${dst_url}', " + - "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable} )") + @Insert("INSERT INTO stream_proxy (type, app, stream,mediaServerId, url, src_url, dst_url, " + + "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, createTime) VALUES" + + "('${type}','${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " + + "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, '${createTime}' )") int add(StreamProxyItem streamProxyDto); @Update("UPDATE stream_proxy " + @@ -21,6 +21,7 @@ public interface StreamProxyMapper { "app=#{app}," + "stream=#{stream}," + "url=#{url}, " + + "mediaServerId=#{mediaServerId}, " + "src_url=#{src_url}," + "dst_url=#{dst_url}, " + "timeout_ms=#{timeout_ms}, " + @@ -35,12 +36,17 @@ public interface StreamProxyMapper { @Delete("DELETE FROM stream_proxy WHERE app=#{app} AND stream=#{stream}") int del(String app, String stream); - @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream") + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.createTime desc") List selectAll(); - @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=${enable}") + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=${enable} order by st.createTime desc") List selectForEnable(boolean enable); - @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream}") + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.createTime desc") StreamProxyItem selectOne(String app, String stream); + + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st " + + "LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + + "WHERE st.enable=${enable} and st.mediaServerId = '${id}' order by st.createTime desc") + List selectForEnableInMediaServer(String id, boolean enable); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java index 455aa2cf..41e4c44e 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java @@ -11,14 +11,15 @@ import java.util.List; public interface StreamPushMapper { @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + - "createStamp, aliveSecond) VALUES" + + "createStamp, aliveSecond, mediaServerId) VALUES" + "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " + - "'${createStamp}', '${aliveSecond}' )") + "'${createStamp}', '${aliveSecond}', '${mediaServerId}' )") int add(StreamPushItem streamPushItem); @Update("UPDATE stream_push " + "SET app=#{app}," + "stream=#{stream}," + + "mediaServerId=#{mediaServerId}," + "totalReaderCount=#{totalReaderCount}, " + "originType=#{originType}," + "originTypeStr=#{originTypeStr}, " + @@ -41,10 +42,10 @@ public interface StreamPushMapper { @Insert("") void addAll(List streamPushItems); 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 86909c76..eafb8a0b 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 @@ -5,6 +5,8 @@ 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; @@ -87,26 +89,6 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { return (StreamInfo)redis.get(playLeys.get(0).toString()); } - /** - * 更新流媒体信息 - * @param ZLMServerConfig - * @return - */ - @Override - public boolean updateMediaInfo(ZLMServerConfig ZLMServerConfig) { - ZLMServerConfig.setUpdateTime(format.format(new Date(System.currentTimeMillis()))); - return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX, ZLMServerConfig); - } - - /** - * 获取流媒体信息 - * @return - */ - @Override - public ZLMServerConfig getMediaInfo() { - return (ZLMServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); - } - @Override public Map queryPlayByDeviceId(String deviceId) { Map streamInfos = new HashMap<>(); @@ -297,7 +279,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void outlineForAll() { - List onlineDevices = redis.scan(String.format("%S*", VideoManagerConstants.KEEPLIVEKEY_PREFIX)); + List onlineDevices = redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX + "*" ); for (int i = 0; i < onlineDevices.size(); i++) { String key = (String) onlineDevices.get(i); redis.del(key); @@ -308,4 +290,5 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { public void updateWVPInfo(JSONObject jsonObject) { } + } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java index bbe8c2bc..4e247531 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java @@ -5,6 +5,7 @@ import java.util.*; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +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.storager.IRedisCatchStorage; @@ -70,6 +71,9 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { @Autowired private VideoStreamSessionManager streamSession; + @Autowired + private MediaServerMapper mediaServerMapper; + private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -459,6 +463,8 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { boolean result = false; streamProxyItem.setStreamType("proxy"); streamProxyItem.setStatus(true); + String now = this.format.format(new Date(System.currentTimeMillis())); + streamProxyItem.setCreateTime(now); try { if (gbStreamMapper.add(streamProxyItem)<0 || streamProxyMapper.add(streamProxyItem) < 0) { //事务回滚 @@ -467,6 +473,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { result = true; dataSourceTransactionManager.commit(transactionStatus); //手动提交 }catch (Exception e) { + logger.error("向数据库添加流代理失败:", e); dataSourceTransactionManager.rollback(transactionStatus); } return result; @@ -599,4 +606,21 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { public void updateParentPlatformStatus(String platformGbID, boolean online) { platformMapper.updateParentPlatformStatus(platformGbID, online); } + + @Override + public void updateMediaServer(MediaServerItem mediaServerItem) { + String now = this.format.format(new Date(System.currentTimeMillis())); + mediaServerItem.setUpdateTime(now); + if (mediaServerMapper.queryOne(mediaServerItem.getId()) != null) { + mediaServerMapper.update(mediaServerItem); + }else { + mediaServerItem.setCreateTime(now); + mediaServerMapper.add(mediaServerItem); + } + } + + @Override + public List getStreamProxyListForEnableInMediaServer(String id, boolean enable) { + return streamProxyMapper.selectForEnableInMediaServer(id, enable); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java index eab00bcf..6caf8d9d 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java @@ -25,6 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -78,7 +79,7 @@ public class DeviceConfig { cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); resultHolder.invokeResult(msg); }); @@ -87,7 +88,7 @@ public class DeviceConfig { logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); JSONObject json = new JSONObject(); json.put("DeviceID", deviceId); json.put("Status", "Timeout"); @@ -95,7 +96,7 @@ public class DeviceConfig { msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); resultHolder.invokeResult(msg); }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (StringUtils.isEmpty(channelId) ? deviceId : channelId), result); return result; } @@ -123,7 +124,7 @@ public class DeviceConfig { cmder.deviceConfigQuery(device, channelId, configType, event -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); resultHolder.invokeResult(msg); }); @@ -132,11 +133,11 @@ public class DeviceConfig { logger.warn(String.format("获取设备配置超时")); // 释放rtpserver RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData("Timeout. Device did not response to this command."); resultHolder.invokeResult(msg); }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (StringUtils.isEmpty(channelId) ? deviceId : channelId), result); return result; } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java index 45059aa1..874d9a92 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -97,7 +98,7 @@ public class DeviceControl { cmder.recordCmd(device, channelId, recordCmdStr, event -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); resultHolder.invokeResult(msg); }); @@ -106,11 +107,11 @@ public class DeviceControl { logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData("Timeout. Device did not response to this command."); resultHolder.invokeResult(msg); }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (StringUtils.isEmpty(channelId) ? deviceId : channelId), result); return result; } @@ -254,7 +255,7 @@ public class DeviceControl { cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); resultHolder.invokeResult(msg); }); @@ -263,7 +264,7 @@ public class DeviceControl { logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); JSONObject json = new JSONObject(); json.put("DeviceID", deviceId); json.put("Status", "Timeout"); @@ -271,7 +272,7 @@ public class DeviceControl { msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); resultHolder.invokeResult(msg); }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (StringUtils.isEmpty(channelId) ? deviceId : channelId), result); return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java index 5db94788..450c54a0 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java @@ -43,11 +43,13 @@ public class MediaController { @ApiImplicitParams({ @ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class), @ApiImplicitParam(name = "stream", value = "流id", dataTypeClass = String.class), + @ApiImplicitParam(name = "mediaServerId", value = "媒体服务器id", dataTypeClass = String.class), }) @GetMapping(value = "/stream_info_by_app_and_stream") @ResponseBody - public StreamInfo getStreamInfoByAppAndStream(String app, String stream){ - return mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream); + public StreamInfo getStreamInfoByAppAndStream(@RequestParam String app, @RequestParam String stream, @RequestParam String mediaServerId){ + + return mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream,mediaServerId); } 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 4297bb2b..aae9cce9 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 @@ -8,6 +8,9 @@ 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; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; @@ -72,6 +75,9 @@ public class PlayController { @Autowired private IMediaService mediaService; + @Autowired + private IMediaServerService mediaServerService; + @ApiOperation("开始点播") @ApiImplicitParams({ @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), @@ -81,8 +87,10 @@ public class PlayController { public DeferredResult> play(@PathVariable String deviceId, @PathVariable String channelId) { - PlayResult playResult = playService.play(deviceId, channelId, null, null); - + // 获取可用的zlm + Device device = storager.queryVideoDevice(deviceId); + IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); + PlayResult playResult = playService.play(newMediaServerItem, deviceId, channelId, null, null); return playResult.getResult(); } @@ -102,8 +110,8 @@ public class PlayController { // 录像查询以channelId作为deviceId查询 resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result); - - cmder.streamByeCmd(deviceId, channelId, event -> { + Device device = storager.queryVideoDevice(deviceId); + cmder.streamByeCmd(deviceId, channelId, (event) -> { StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); if (streamInfo == null) { RequestMessage msg = new RequestMessage(); @@ -120,6 +128,7 @@ public class PlayController { msg.setData(String.format("success")); resultHolder.invokeResult(msg); } + mediaServerService.closeRTPServer(device, channelId); }); if (deviceId != null || channelId != null) { @@ -165,16 +174,16 @@ public class PlayController { logger.warn("视频转码API调用失败!, 视频流已经停止!"); return new ResponseEntity("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); } - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); + IMediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId()); + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); if (!rtpInfo.getBoolean("exist")) { logger.warn("视频转码API调用失败!, 视频流已停止推流!"); return new ResponseEntity("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); } else { - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), streamId ); String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); - JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(srcUrl, dstUrl, "1000000", true, false, null); + JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaInfo, srcUrl, dstUrl, "1000000", true, false, null); logger.info(jsonObject.toJSONString()); JSONObject result = new JSONObject(); if (jsonObject != null && jsonObject.getInteger("code") == 0) { @@ -182,7 +191,7 @@ public class PlayController { JSONObject data = jsonObject.getJSONObject("data"); if (data != null) { result.put("key", data.getString("key")); - StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId); + StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId, mediaInfo.getId()); result.put("data", streamInfoResult); } }else { @@ -203,25 +212,38 @@ public class PlayController { @ApiImplicitParam(name = "key", value = "视频流key", dataTypeClass = String.class), }) @PostMapping("/convertStop/{key}") - public ResponseEntity playConvertStop(@PathVariable String key) { - - JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(key); - logger.info(jsonObject.toJSONString()); + public ResponseEntity playConvertStop(@PathVariable String key, String mediaServerId) { JSONObject result = new JSONObject(); - if (jsonObject != null && jsonObject.getInteger("code") == 0) { - result.put("code", 0); - JSONObject data = jsonObject.getJSONObject("data"); - if (data != null && data.getBoolean("flag")) { - result.put("code", "0"); - result.put("msg", "success"); - }else { - - } - }else { - result.put("code", 1); - result.put("msg", "delFFmpegSource fail"); + if (mediaServerId == null) { + result.put("code", 400); + result.put("msg", "mediaServerId is null"); + return new ResponseEntity( result.toJSONString(), HttpStatus.BAD_REQUEST); } - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo == null) { + result.put("code", 0); + result.put("msg", "使用的流媒体已经停止运行"); + return new ResponseEntity( result.toJSONString(), HttpStatus.OK); + }else { + JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaInfo, key); + logger.info(jsonObject.toJSONString()); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + result.put("code", 0); + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null && data.getBoolean("flag")) { + result.put("code", "0"); + result.put("msg", "success"); + }else { + + } + }else { + result.put("code", 1); + result.put("msg", "delFFmpegSource fail"); + } + return new ResponseEntity( result.toJSONString(), HttpStatus.OK); + } + + } @ApiOperation("语音广播命令") @@ -249,7 +271,7 @@ public class PlayController { resultHolder.invokeResult(msg); return result; } - cmder.audioBroadcastCmd(device, event -> { + cmder.audioBroadcastCmd(device, (event) -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); msg.setId(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId); 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 11c210ed..d2a30fa7 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,6 +4,8 @@ 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.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.service.IPlayService; import io.swagger.annotations.Api; @@ -87,9 +89,18 @@ public class PlaybackController { cmder.streamByeCmd(deviceId, channelId); } resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); - cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { + IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); + if (newMediaServerItem == null) { + logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + RequestMessage msg = new RequestMessage(); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + return result; + } + cmder.playbackStreamCmd(newMediaServerItem, device, channelId, startTime, endTime, (IMediaServerItem mediaServerItem, JSONObject response) -> { logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); + playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString()); }, event -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java index 7e57e0dd..f78b333c 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -104,7 +105,7 @@ public class PtzController { cmder.presetQuery(device, channelId, event -> { Response response = event.getResponse(); RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); resultHolder.invokeResult(msg); }); @@ -113,11 +114,11 @@ public class PtzController { logger.warn(String.format("获取设备预置位超时")); // 释放rtpserver RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); + msg.setId(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (StringUtils.isEmpty(channelId) ? deviceId : channelId)); msg.setData("获取设备预置位超时"); resultHolder.invokeResult(msg); }); - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (StringUtils.isEmpty(channelId) ? deviceId : channelId), result); return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecoderProxyController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecoderProxyController.java index 734f62f2..f00e2abc 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecoderProxyController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecoderProxyController.java @@ -2,6 +2,9 @@ package com.genersoft.iot.vmp.vmanager.record; import com.genersoft.iot.vmp.conf.MediaConfig; 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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -27,6 +30,8 @@ public class RecoderProxyController { @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired + private IMediaServerService mediaServerService; @Autowired private MediaConfig mediaConfig; @@ -48,7 +53,11 @@ public class RecoderProxyController { return null; } // 后续改为根据Id获取对应的ZLM - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); + IMediaServerItem mediaInfo = mediaServerService.getOne(mediaId); + if (mediaInfo == null) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return null; + } String requestURI = String.format("http://%s:%s%s?%s", mediaInfo.getSdpIp(), mediaConfig.getRecordAssistPort(), 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 bbd4bd59..45eeac84 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 @@ -2,8 +2,11 @@ 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.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; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -16,6 +19,7 @@ import javax.sip.ObjectInUseException; import javax.sip.SipProvider; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; @SuppressWarnings("rawtypes") @Api(tags = "服务控制") @@ -28,17 +32,28 @@ public class ServerController { private ConfigurableApplicationContext context; @Autowired - private IRedisCatchStorage redisCatchStorage; + private IMediaServerService mediaServerService; @ApiOperation("流媒体服务列表") @GetMapping(value = "/media_server/list") @ResponseBody - public Object getMediaServerList(){ - // TODO 为后续多个zlm支持准备 - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - ArrayList result = new ArrayList<>(); - result.add(mediaInfo); + public WVPResult> getMediaServerList(){ + WVPResult> result = new WVPResult<>(); + result.setCode(0); + result.setMsg("success"); + result.setData(mediaServerService.getAll()); + return result; + } + + @ApiOperation("获取流媒体服务") + @GetMapping(value = "/media_server/one/{id}") + @ResponseBody + public WVPResult getMediaServer(@PathVariable String id){ + WVPResult result = new WVPResult<>(); + result.setCode(0); + result.setMsg("success"); + result.setData(mediaServerService.getOne(id)); return result; } 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 51c8c8ed..f0996311 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,11 +1,15 @@ 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; 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; @@ -14,6 +18,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; @SuppressWarnings("rawtypes") @@ -31,6 +36,10 @@ public class StreamProxyController { @Autowired private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaServerService mediaServerService; + @Autowired private IStreamProxyService streamProxyService; @@ -60,6 +69,7 @@ public class StreamProxyController { @ResponseBody public WVPResult save(@RequestBody StreamProxyItem param){ logger.info("添加代理: " + JSONObject.toJSONString(param)); + if (StringUtils.isEmpty(param.getMediaServerId())) param.setMediaServerId("auto"); String msg = streamProxyService.save(param); WVPResult result = new WVPResult<>(); result.setCode(0); @@ -69,10 +79,15 @@ public class StreamProxyController { @ApiOperation("获取ffmpeg.cmd模板") @GetMapping(value = "/ffmpeg_cmd/list") + @ApiImplicitParams({ + @ApiImplicitParam(name = "mediaServerId", value = "流媒体ID", dataTypeClass = String.class), + }) @ResponseBody - public WVPResult getFFmpegCMDs(){ - logger.debug("获取ffmpeg.cmd模板:" ); - JSONObject data = streamProxyService.getFFmpegCMDs(); + public WVPResult getFFmpegCMDs(@RequestParam String mediaServerId){ + logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId ); + + IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + JSONObject data = streamProxyService.getFFmpegCMDs(mediaServerItem); WVPResult result = new WVPResult<>(); result.setCode(0); result.setMsg("success"); @@ -82,12 +97,12 @@ public class StreamProxyController { @ApiOperation("移除代理") @ApiImplicitParams({ - @ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class), - @ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class), + @ApiImplicitParam(name = "app", value = "应用名", required = true, dataTypeClass = String.class), + @ApiImplicitParam(name = "stream", value = "流ID", required = true, dataTypeClass = String.class), }) @DeleteMapping(value = "/del") @ResponseBody - public WVPResult del(String app, String stream){ + public WVPResult del(@RequestParam String app, @RequestParam String stream){ logger.info("移除代理: " + app + "/" + stream); WVPResult result = new WVPResult<>(); if (app == null || stream == null) { diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java index c55824a1..a8e0de4f 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/ApiControlController.java @@ -10,7 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** - * 兼容LiveGBS的API:设备控制 + * API兼容:设备控制 */ @CrossOrigin @RestController diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiController.java index 43ae7d97..8035810a 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/ApiController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** - * 兼容LiveGBS的API:系统接口 + * API兼容:系统接口 */ @Controller @CrossOrigin diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java index 09c94bd9..264f2b2f 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java @@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; /** - * 兼容LiveGBS的API:设备信息 + * API兼容:设备信息 */ @SuppressWarnings("unchecked") @CrossOrigin diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java index 932684a2..ae7e9212 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java @@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; /** - * 兼容LiveGBS的API:实时直播 + * API兼容:实时直播 */ @SuppressWarnings(value = {"rawtypes", "unchecked"}) @CrossOrigin diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml index 127cbcf0..2552e01e 100644 --- a/src/main/resources/all-application.yml +++ b/src/main/resources/all-application.yml @@ -70,9 +70,13 @@ sip: keepalive-timeout: 180 # [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒 register-time-interval: 60 + # TODO [可选] 收到心跳后自动上线, 重启服务后会将所有设备置为离线,默认false,等待注册后上线。设置为true则收到心跳设置为上线。 + # keepalliveToOnline: false #zlm 默认服务器配置 media: + # [可选] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + id: # [必须修改] zlm服务器的内网IP ip: 192.168.0.100 # [可选] 返回流地址时的ip,置空使用 media.ip diff --git a/src/main/resources/wvp.sqlite b/src/main/resources/wvp.sqlite index 7c38c432..f399e6e8 100644 Binary files a/src/main/resources/wvp.sqlite and b/src/main/resources/wvp.sqlite differ diff --git a/web_src/src/components/CloudRecord.vue b/web_src/src/components/CloudRecord.vue index c2692700..284578b5 100644 --- a/web_src/src/components/CloudRecord.vue +++ b/web_src/src/components/CloudRecord.vue @@ -7,22 +7,23 @@
云端录像 +
+ 节点选择: + + + +
-
- 节点选择: - - - -
+ @@ -60,6 +61,7 @@