添加zlm集群支持

This commit is contained in:
64850858 2021-07-16 16:34:51 +08:00
parent 06d78575cc
commit 89a9ab4534
75 changed files with 2431 additions and 914 deletions

10
pom.xml
View File

@ -196,6 +196,16 @@
<version>1.12</version> <version>1.12</version>
</dependency> </dependency>
<!-- &lt;!&ndash; 检测文件编码 &ndash;&gt;-->
<!-- &lt;!&ndash; https://mvnrepository.com/artifact/cpdetector/cpdetector &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>cpdetector</groupId>-->
<!-- <artifactId>cpdetector</artifactId>-->
<!-- <version>1.0.8</version>-->
<!-- </dependency>-->
<!-- onvif协议栈 --> <!-- onvif协议栈 -->
<dependency> <dependency>
<groupId>be.teletask</groupId> <groupId>be.teletask</groupId>

View File

@ -138,6 +138,7 @@ create table stream_proxy
timeout_ms int null, timeout_ms int null,
ffmpeg_cmd_key varchar(255) null, ffmpeg_cmd_key varchar(255) null,
rtp_type varchar(50) null, rtp_type varchar(50) null,
mediaServerId varchar(50) null,
enable_hls bit(1) null, enable_hls bit(1) null,
enable_mp4 bit(1) null, enable_mp4 bit(1) null,
enable bit(1) not null, enable bit(1) not null,
@ -166,4 +167,28 @@ create table user
create_time varchar(50) not null create_time varchar(50) not null
); );
insert into user (username, password, roleId, create_time) values ('admin', '21232f297a57a5a743894a0e4a801fc3', '0', '2021-04-13 14:14:57'); 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
);

View File

@ -19,6 +19,7 @@ public class StreamInfo {
private String rtmp; private String rtmp;
private String rtsp; private String rtsp;
private String rtc; private String rtc;
private String mediaServerId;
private JSONArray tracks; private JSONArray tracks;
public static class TransactionInfo{ public static class TransactionInfo{
@ -165,4 +166,12 @@ public class StreamInfo {
public void setTransactionInfo(TransactionInfo transactionInfo) { public void setTransactionInfo(TransactionInfo transactionInfo) {
this.transactionInfo = transactionInfo; this.transactionInfo = transactionInfo;
} }
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
} }

View File

@ -10,33 +10,31 @@ public class VideoManagerConstants {
public static final String WVP_SERVER_PREFIX = "VMP_wvp_server"; 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 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"; public static final String EVENT_ONLINE_REGISTER = "1";

View File

@ -1,11 +1,16 @@
package com.genersoft.iot.vmp.conf; 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.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@Configuration("mediaConfig") @Configuration("mediaConfig")
public class MediaConfig { public class MediaConfig implements IMediaServerItem {
@Value("${media.id:}")
private String id;
@Value("${media.ip}") @Value("${media.ip}")
private String ip; private String ip;
@ -25,22 +30,22 @@ public class MediaConfig {
@Value("${media.http-port}") @Value("${media.http-port}")
private Integer httpPort; private Integer httpPort;
@Value("${media.http-ssl-port:}") @Value("${media.http-ssl-port:0}")
private Integer httpSSlPort; private Integer httpSSlPort;
@Value("${media.rtmp-port:}") @Value("${media.rtmp-port:0}")
private Integer rtmpPort; private Integer rtmpPort;
@Value("${media.rtmp-ssl-port:}") @Value("${media.rtmp-ssl-port:0}")
private Integer rtmpSSlPort; private Integer rtmpSSlPort;
@Value("${media.rtp-proxy-port:}") @Value("${media.rtp-proxy-port:0}")
private Integer rtpProxyPort; private Integer rtpProxyPort;
@Value("${media.rtsp-port:}") @Value("${media.rtsp-port:0}")
private Integer rtspPort; private Integer rtspPort;
@Value("${media.rtsp-ssl-port:}") @Value("${media.rtsp-ssl-port:0}")
private Integer rtspSSLPort; private Integer rtspSSLPort;
@Value("${media.auto-config:true}") @Value("${media.auto-config:true}")
@ -61,6 +66,23 @@ public class MediaConfig {
@Value("${media.record-assist-port:0}") @Value("${media.record-assist-port:0}")
private Integer recordAssistPort; 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() { public String getIp() {
return ip; return ip;
} }
@ -82,82 +104,114 @@ public class MediaConfig {
this.hookIp = hookIp; this.hookIp = hookIp;
} }
public String getSdpIp() { public String getSipIp() {
if (StringUtils.isEmpty(sdpIp)){ return sipIp;
return ip; }
}else {
return sdpIp; public void setSipIp(String sipIp) {
} this.sipIp = sipIp;
} }
public void setSdpIp(String sdpIp) { public void setSdpIp(String sdpIp) {
this.sdpIp = sdpIp; this.sdpIp = sdpIp;
} }
public String getStreamIp() {
if (StringUtils.isEmpty(streamIp)){
return ip;
}else {
return streamIp;
}
}
public void setStreamIp(String streamIp) { public void setStreamIp(String streamIp) {
this.streamIp = streamIp; this.streamIp = streamIp;
} }
public Integer getHttpPort() { public int getHttpPort() {
return httpPort; return httpPort;
} }
@Override
public void setHttpPort(int httpPort) {
}
public void setHttpPort(Integer httpPort) { public void setHttpPort(Integer httpPort) {
this.httpPort = httpPort; this.httpPort = httpPort;
} }
public Integer getHttpSSlPort() { public int getHttpSSlPort() {
return httpSSlPort; return httpSSlPort;
} }
@Override
public void setHttpSSlPort(int httpSSlPort) {
}
public void setHttpSSlPort(Integer httpSSlPort) { public void setHttpSSlPort(Integer httpSSlPort) {
this.httpSSlPort = httpSSlPort; this.httpSSlPort = httpSSlPort;
} }
public Integer getRtmpPort() { public int getRtmpPort() {
return rtmpPort; return rtmpPort;
} }
@Override
public void setRtmpPort(int rtmpPort) {
}
public void setRtmpPort(Integer rtmpPort) { public void setRtmpPort(Integer rtmpPort) {
this.rtmpPort = rtmpPort; this.rtmpPort = rtmpPort;
} }
public Integer getRtmpSSlPort() { public int getRtmpSSlPort() {
return rtmpSSlPort; return rtmpSSlPort;
} }
@Override
public void setRtmpSSlPort(int rtmpSSlPort) {
}
public void setRtmpSSlPort(Integer rtmpSSlPort) { public void setRtmpSSlPort(Integer rtmpSSlPort) {
this.rtmpSSlPort = rtmpSSlPort; this.rtmpSSlPort = rtmpSSlPort;
} }
public Integer getRtpProxyPort() { public int getRtpProxyPort() {
return rtpProxyPort; if (rtpProxyPort == null) {
return 0;
}else {
return rtpProxyPort;
}
}
@Override
public void setRtpProxyPort(int rtpProxyPort) {
} }
public void setRtpProxyPort(Integer rtpProxyPort) { public void setRtpProxyPort(Integer rtpProxyPort) {
this.rtpProxyPort = rtpProxyPort; this.rtpProxyPort = rtpProxyPort;
} }
public Integer getRtspPort() { public int getRtspPort() {
return rtspPort; return rtspPort;
} }
@Override
public void setRtspPort(int rtspPort) {
}
public void setRtspPort(Integer rtspPort) { public void setRtspPort(Integer rtspPort) {
this.rtspPort = rtspPort; this.rtspPort = rtspPort;
} }
public Integer getRtspSSLPort() { public int getRtspSSLPort() {
return rtspSSLPort; return rtspSSLPort;
} }
@Override
public void setRtspSSLPort(int rtspSSLPort) {
}
public void setRtspSSLPort(Integer rtspSSLPort) { public void setRtspSSLPort(Integer rtspSSLPort) {
this.rtspSSLPort = rtspSSLPort; this.rtspSSLPort = rtspSSLPort;
} }
@ -202,11 +256,101 @@ public class MediaConfig {
this.rtpPortRange = rtpPortRange; this.rtpPortRange = rtpPortRange;
} }
public Integer getRecordAssistPort() { public int getRecordAssistPort() {
return recordAssistPort; return recordAssistPort;
} }
@Override
public void setRecordAssistPort(int recordAssistPort) {
}
public void setRecordAssistPort(Integer recordAssistPort) { public void setRecordAssistPort(Integer recordAssistPort) {
this.recordAssistPort = 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;
}
} }

View File

@ -1,12 +1,16 @@
package com.genersoft.iot.vmp.conf; 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.HttpRequest;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.catalina.connector.ClientAbortException;
import org.mitre.dsmiley.httpproxy.ProxyServlet; import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -24,13 +28,16 @@ public class ProxyServletConfig {
private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class); private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);
@Autowired @Autowired
private MediaConfig mediaConfig; private IMediaServerService mediaServerService;
@Value("${server.port}")
private int serverPort;
@Bean @Bean
public ServletRegistrationBean zlmServletRegistrationBean(){ public ServletRegistrationBean zlmServletRegistrationBean(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZLMProxySerlet(),"/zlm/*"); ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZLMProxySerlet(),"/zlm/*");
servletRegistrationBean.setName("zlm_Proxy"); 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(); servletRegistrationBean.addUrlMappings();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
servletRegistrationBean.addInitParameter("log", "true"); servletRegistrationBean.addInitParameter("log", "true");
@ -38,24 +45,26 @@ public class ProxyServletConfig {
return servletRegistrationBean; return servletRegistrationBean;
} }
class ZLMProxySerlet extends ProxyServlet{ class ZLMProxySerlet extends ProxyServlet{
@Override @Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) { protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString); String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
if (!StringUtils.isEmpty(queryStr)) { IMediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
queryStr += "&secret=" + mediaConfig.getSecret(); if (mediaInfo != null) {
}else { if (!StringUtils.isEmpty(queryStr)) {
queryStr = "secret=" + mediaConfig.getSecret(); queryStr += "&secret=" + mediaInfo.getSecret();
}else {
queryStr = "secret=" + mediaInfo.getSecret();
}
} }
return queryStr; return queryStr;
} }
/**
* 异常处理
*/
@Override @Override
protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){ protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
//System.out.println(e.getMessage());
try { try {
super.handleRequestException(proxyRequest, proxyResonse, e); super.handleRequestException(proxyRequest, proxyResonse, e);
} catch (ServletException servletException) { } catch (ServletException servletException) {
@ -72,6 +81,64 @@ public class ProxyServletConfig {
logger.error("zlm 代理失败: ", e); 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() + "/", "");
}
} }
} }

View File

@ -95,20 +95,43 @@ public class SipLayer implements SipListener {
tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "TCP"); tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "TCP");
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider.addSipListener(this); tcpSipProvider.addSipListener(this);
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getSipPort() + "}");
} catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) { // } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
logger.error(String.format("创建SIP服务失败: %s", e.getMessage())); // 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; return tcpSipProvider;
} }
@Bean("udpSipProvider") @Bean("udpSipProvider")
@DependsOn("sipStack") @DependsOn("sipStack")
private SipProvider startUdpListener() throws Exception { private SipProvider startUdpListener() {
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP"); ListeningPoint udpListeningPoint = null;
SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); SipProvider udpSipProvider = null;
udpSipProvider.addSipListener(this); try {
logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); 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; return udpSipProvider;
} }

View File

@ -94,6 +94,11 @@ public class Device {
*/ */
private String updateTime; private String updateTime;
/**
* 设备使用的媒体id, 默认为null
*/
private String mediaServerId;
public String getDeviceId() { public String getDeviceId() {
return deviceId; return deviceId;
} }
@ -229,4 +234,12 @@ public class Device {
public void setUpdateTime(String updateTime) { public void setUpdateTime(String updateTime) {
this.updateTime = updateTime; this.updateTime = updateTime;
} }
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
} }

View File

@ -9,6 +9,7 @@ public class GbStream extends PlatformGbStream{
private String stream; private String stream;
private String gbId; private String gbId;
private String name; private String name;
private String mediaServerId;
private double longitude; private double longitude;
private double latitude; private double latitude;
private String streamType; private String streamType;
@ -77,4 +78,12 @@ public class GbStream extends PlatformGbStream{
public void setStatus(boolean status) { public void setStatus(boolean status) {
this.status = status; this.status = status;
} }
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
} }

View File

@ -66,6 +66,11 @@ public class SendRtpItem {
*/ */
private int localPort; private int localPort;
/**
* 使用的流媒体
*/
private String mediaServerId;
public String getIp() { public String getIp() {
return ip; return ip;
} }
@ -161,4 +166,12 @@ public class SendRtpItem {
public void setTcpActive(boolean tcpActive) { public void setTcpActive(boolean tcpActive) {
this.tcpActive = tcpActive; this.tcpActive = tcpActive;
} }
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
} }

View File

@ -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.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; 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.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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -32,6 +35,8 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
private IVideoManagerStorager storager; private IVideoManagerStorager storager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired
private IMediaServerService mediaServerService;
@Autowired @Autowired
private SIPCommanderFroPlatform sipCommanderFroPlatform; private SIPCommanderFroPlatform sipCommanderFroPlatform;
@ -62,22 +67,24 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
logger.info("停止[ {} ]的所有推流", event.getPlatformGbID()); logger.info("停止[ {} ]的所有推流", event.getPlatformGbID());
StringBuilder app = new StringBuilder(); StringBuilder app = new StringBuilder();
StringBuilder stream = new StringBuilder(); StringBuilder stream = new StringBuilder();
for (int i = 0; i < sendRtpItems.size(); i++) { for (SendRtpItem sendRtpItem : sendRtpItems) {
if (app.length() != 0) { if (app.length() != 0) {
app.append(","); app.append(",");
} }
app.append(sendRtpItems.get(i).getApp()); app.append(sendRtpItem.getApp());
if (stream.length() != 0) { if (stream.length() != 0) {
stream.append(","); stream.append(",");
} }
stream.append(sendRtpItems.get(i).getStreamId()); stream.append(sendRtpItem.getStreamId());
redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItems.get(i).getChannelId()); redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId());
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app.toString());
param.put("stream", stream.toString());
zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
} }
Map<String, Object> param = new HashMap<>();
param.put("vhost","__defaultVhost__");
param.put("app", app.toString());
param.put("stream", stream.toString());
zlmrtpServerFactory.stopSendRtpStream(param);
} }

View File

@ -9,6 +9,7 @@ import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; 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.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*; import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*;
import com.genersoft.iot.vmp.service.IPlayService; import com.genersoft.iot.vmp.service.IPlayService;
@ -104,6 +105,9 @@ public class SIPProcessorFactory {
@Autowired @Autowired
private ZLMRTPServerFactory zlmrtpServerFactory; private ZLMRTPServerFactory zlmrtpServerFactory;
@Autowired
private IMediaServerService mediaServerService;
// 这里使用注解会导致循环依赖注入暂用springBean // 这里使用注解会导致循环依赖注入暂用springBean
private SipProvider tcpSipProvider; private SipProvider tcpSipProvider;
@ -128,6 +132,7 @@ public class SIPProcessorFactory {
processor.setStorager(storager); processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory); processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setMediaServerService(mediaServerService);
return processor; return processor;
} else if (Request.REGISTER.equals(method)) { } else if (Request.REGISTER.equals(method)) {
RegisterRequestProcessor processor = new RegisterRequestProcessor(); RegisterRequestProcessor processor = new RegisterRequestProcessor();
@ -148,6 +153,7 @@ public class SIPProcessorFactory {
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory); processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setMediaServerService(mediaServerService);
return processor; return processor;
} else if (Request.BYE.equals(method)) { } else if (Request.BYE.equals(method)) {
ByeRequestProcessor processor = new ByeRequestProcessor(); ByeRequestProcessor processor = new ByeRequestProcessor();
@ -155,6 +161,7 @@ public class SIPProcessorFactory {
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory); processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setSIPCommander(cmder); processor.setSIPCommander(cmder);
processor.setMediaServerService(mediaServerService);
return processor; return processor;
} else if (Request.CANCEL.equals(method)) { } else if (Request.CANCEL.equals(method)) {
CancelRequestProcessor processor = new CancelRequestProcessor(); CancelRequestProcessor processor = new CancelRequestProcessor();

View File

@ -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.bean.Device;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; 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:设备能力接口用于定义设备的控制查询能力 * @Description:设备能力接口用于定义设备的控制查询能力
@ -90,7 +92,7 @@ public interface ISIPCommander {
* @param device 视频设备 * @param device 视频设备
* @param channelId 预览通道 * @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 startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求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 = 命令发送成功 * @return true = 命令发送成功
*/ */
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); 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);
} }

View File

@ -13,11 +13,10 @@ import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.UserSetup; 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.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.ZLMRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import gov.nist.javax.sip.message.SIPRequest; 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.DateUtil;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import org.springframework.util.StringUtils;
/** /**
* @Description:设备能力接口用于定义设备的控制查询能力 * @Description:设备能力接口用于定义设备的控制查询能力
@ -77,12 +77,6 @@ public class SIPCommander implements ISIPCommander {
@Autowired @Autowired
private ZLMRTPServerFactory zlmrtpServerFactory; private ZLMRTPServerFactory zlmrtpServerFactory;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private MediaConfig mediaConfig;
@Autowired @Autowired
private UserSetup userSetup; private UserSetup userSetup;
@ -340,48 +334,45 @@ public class SIPCommander implements ISIPCommander {
* @param errorEvent sip错误订阅 * @param errorEvent sip错误订阅
*/ */
@Override @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; String streamId = null;
try { try {
if (device == null) return; if (device == null) return;
String streamMode = device.getStreamMode().toUpperCase();
String ssrc = streamSession.createPlaySsrc(); String ssrc = streamSession.createPlaySsrc();
if (mediaConfig.isRtpEnable()) { if (mediaServerItem.isRtpEnable()) {
streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
}else { }else {
streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); 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; Integer mediaPort = null;
// 使用动态udp端口 // 使用动态udp端口
if (mediaConfig.isRtpEnable()) { if (mediaServerItem.isRtpEnable()) {
mediaPort = zlmrtpServerFactory.createRTPServer(streamId); mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
}else { }else {
mediaPort = mediaInfo.getRtpProxyPort(); mediaPort = mediaServerItem.getRtpProxyPort();
} }
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
// 添加订阅 // 添加订阅
JSONObject subscribeKey = new JSONObject(); JSONObject subscribeKey = new JSONObject();
subscribeKey.put("app", "rtp"); subscribeKey.put("app", "rtp");
subscribeKey.put("stream", streamId); subscribeKey.put("stream", streamId);
subscribeKey.put("regist", true); subscribeKey.put("regist", true);
subscribeKey.put("mediaServerId", mediaServerItem.getId());
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; if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
event.response(json); event.response(mediaServerItemInUse, json);
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
}); });
// //
StringBuffer content = new StringBuffer(200); StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n"); content.append("v=0\r\n");
// content.append("o=" + sipConfig.getSipId() + " 0 0 IN IP4 "+mediaInfo.getWanIp()+"\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("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("t=0 0\r\n");
if (userSetup.isSeniorSdp()) { if (userSetup.isSeniorSdp()) {
@ -459,21 +450,32 @@ public class SIPCommander implements ISIPCommander {
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/ */
@Override @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) { , SipSubscribe.Event errorEvent) {
try { try {
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
String ssrc = streamSession.createPlayBackSsrc(); String ssrc = streamSession.createPlayBackSsrc();
String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); 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(); JSONObject subscribeKey = new JSONObject();
subscribeKey.put("app", "rtp"); subscribeKey.put("app", "rtp");
subscribeKey.put("stream", streamId); subscribeKey.put("stream", streamId);
subscribeKey.put("regist", true); subscribeKey.put("regist", true);
subscribeKey.put("mediaServerId", mediaServerItem.getId());
logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString()); 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; if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
event.response(json); event.response(mediaServerItemInUse, json);
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); 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("o="+sipConfig.getSipId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n");
content.append("s=Playback\r\n"); content.append("s=Playback\r\n");
content.append("u="+channelId+":0\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)+" " content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "
+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); +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(); String streamMode = device.getStreamMode().toUpperCase();
if (userSetup.isSeniorSdp()) { if (userSetup.isSeniorSdp()) {
@ -560,56 +558,63 @@ public class SIPCommander implements ISIPCommander {
/** /**
* 视频流停止 * 视频流停止, 不使用回调
*
*/ */
@Override @Override
public void streamByeCmd(String deviceId, String channelId) { public void streamByeCmd(String deviceId, String channelId) {
streamByeCmd(deviceId, channelId, null); streamByeCmd(deviceId, channelId, null);
} }
/**
* 视频流停止
*/
@Override @Override
public void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent) { public void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent) {
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
try { try {
ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId); ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
// 服务重启后, 无法直接发送bye 通过手动构建发送 // 服务重启后, 无法直接发送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) { if (transaction == null) {
logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
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);
}
return; return;
} }
Dialog dialog = transaction.getDialog(); Dialog dialog = transaction.getDialog();
if (dialog == null) { if (dialog == null) {
logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId);
return; return;
} }
Request byeRequest = dialog.createRequest(Request.BYE); Request byeRequest = dialog.createRequest(Request.BYE);
@ -632,7 +637,7 @@ public class SIPCommander implements ISIPCommander {
} }
dialog.sendRequest(clientTransaction); dialog.sendRequest(clientTransaction);
zlmrtpServerFactory.closeRTPServer(streamSession.getStreamId(deviceId, channelId));
streamSession.remove(deviceId, channelId); streamSession.remove(deviceId, channelId);
} catch (SipException | ParseException e) { } catch (SipException | ParseException e) {
e.printStackTrace(); e.printStackTrace();
@ -721,7 +726,7 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<Control>\r\n"); cmdXml.append("<Control>\r\n");
cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n"); cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
if (XmlUtil.isEmpty(channelId)) { if (StringUtils.isEmpty(channelId)) {
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
} else { } else {
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
@ -821,16 +826,16 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n"); cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) {
cmdXml.append("<Info>\r\n"); cmdXml.append("<Info>\r\n");
} }
if (!XmlUtil.isEmpty(alarmMethod)) { if (!StringUtils.isEmpty(alarmMethod)) {
cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n"); cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
} }
if (!XmlUtil.isEmpty(alarmType)) { if (!StringUtils.isEmpty(alarmType)) {
cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n"); cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
} }
if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) {
cmdXml.append("</Info>\r\n"); cmdXml.append("</Info>\r\n");
} }
cmdXml.append("</Control>\r\n"); cmdXml.append("</Control>\r\n");
@ -863,7 +868,7 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<Control>\r\n"); cmdXml.append("<Control>\r\n");
cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n"); cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
if (XmlUtil.isEmpty(channelId)) { if (StringUtils.isEmpty(channelId)) {
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
} else { } else {
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
@ -901,7 +906,7 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<Control>\r\n"); cmdXml.append("<Control>\r\n");
cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n"); cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
if (XmlUtil.isEmpty(channelId)) { if (StringUtils.isEmpty(channelId)) {
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
} else { } else {
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
@ -969,13 +974,13 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<Control>\r\n"); cmdXml.append("<Control>\r\n");
cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n"); cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
if (XmlUtil.isEmpty(channelId)) { if (StringUtils.isEmpty(channelId)) {
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
} else { } else {
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
} }
cmdXml.append("<BasicParam>\r\n"); cmdXml.append("<BasicParam>\r\n");
if (!XmlUtil.isEmpty(name)) { if (!StringUtils.isEmpty(name)) {
cmdXml.append("<Name>" + name + "</Name>\r\n"); cmdXml.append("<Name>" + name + "</Name>\r\n");
} }
if (NumericUtil.isInteger(expiration)) { if (NumericUtil.isInteger(expiration)) {
@ -1169,22 +1174,22 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<CmdType>Alarm</CmdType>\r\n"); cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
if (!XmlUtil.isEmpty(startPriority)) { if (!StringUtils.isEmpty(startPriority)) {
cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n"); cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
} }
if (!XmlUtil.isEmpty(endPriority)) { if (!StringUtils.isEmpty(endPriority)) {
cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n"); cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
} }
if (!XmlUtil.isEmpty(alarmMethod)) { if (!StringUtils.isEmpty(alarmMethod)) {
cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n"); cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
} }
if (!XmlUtil.isEmpty(alarmType)) { if (!StringUtils.isEmpty(alarmType)) {
cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n"); cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
} }
if (!XmlUtil.isEmpty(startTime)) { if (!StringUtils.isEmpty(startTime)) {
cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n"); cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
} }
if (!XmlUtil.isEmpty(endTime)) { if (!StringUtils.isEmpty(endTime)) {
cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n"); cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
} }
cmdXml.append("</Query>\r\n"); cmdXml.append("</Query>\r\n");
@ -1218,7 +1223,7 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<Query>\r\n"); cmdXml.append("<Query>\r\n");
cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n"); cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
if (XmlUtil.isEmpty(channelId)) { if (StringUtils.isEmpty(channelId)) {
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
} else { } else {
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
@ -1253,7 +1258,7 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<Query>\r\n"); cmdXml.append("<Query>\r\n");
cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n"); cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
if (XmlUtil.isEmpty(channelId)) { if (StringUtils.isEmpty(channelId)) {
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
} else { } else {
cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
@ -1365,22 +1370,22 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("<CmdType>Alarm</CmdType>\r\n"); cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
if (!XmlUtil.isEmpty(startPriority)) { if (!StringUtils.isEmpty(startPriority)) {
cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n"); cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
} }
if (!XmlUtil.isEmpty(endPriority)) { if (!StringUtils.isEmpty(endPriority)) {
cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n"); cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
} }
if (!XmlUtil.isEmpty(alarmMethod)) { if (!StringUtils.isEmpty(alarmMethod)) {
cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n"); cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
} }
if (!XmlUtil.isEmpty(alarmType)) { if (!StringUtils.isEmpty(alarmType)) {
cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n"); cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
} }
if (!XmlUtil.isEmpty(startTime)) { if (!StringUtils.isEmpty(startTime)) {
cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n"); cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
} }
if (!XmlUtil.isEmpty(endTime)) { if (!StringUtils.isEmpty(endTime)) {
cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n"); cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
} }
cmdXml.append("</Query>\r\n"); cmdXml.append("</Query>\r\n");
@ -1431,16 +1436,4 @@ public class SIPCommander implements ISIPCommander {
clientTransaction.sendRequest(); clientTransaction.sendRequest();
return clientTransaction; 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);
}
} }

View File

@ -16,6 +16,8 @@ import javax.sip.message.Request;
import gov.nist.javax.sip.SipStackImpl; import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.SIPRequest; import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.stack.SIPServerTransaction; import gov.nist.javax.sip.stack.SIPServerTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* @Description:处理接收IPCamera发来的SIP协议请求消息 * @Description:处理接收IPCamera发来的SIP协议请求消息
@ -24,6 +26,8 @@ import gov.nist.javax.sip.stack.SIPServerTransaction;
*/ */
public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcessor { public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcessor {
private final static Logger logger = LoggerFactory.getLogger(SIPRequestAbstractProcessor.class);
protected RequestEvent evt; protected RequestEvent evt;
private SipProvider tcpSipProvider; private SipProvider tcpSipProvider;
@ -64,9 +68,9 @@ public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcesso
} }
} }
} catch (TransactionAlreadyExistsException e) { } catch (TransactionAlreadyExistsException e) {
e.printStackTrace(); logger.error(e.getMessage());
} catch (TransactionUnavailableException e) { } catch (TransactionUnavailableException e) {
e.printStackTrace(); logger.error(e.getMessage());
} }
} }
return serverTransaction; return serverTransaction;

View File

@ -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.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; 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.IRedisCatchStorage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -30,6 +33,8 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor {
private ZLMRTPServerFactory zlmrtpServerFactory; private ZLMRTPServerFactory zlmrtpServerFactory;
private IMediaServerService mediaServerService;
/** /**
* 处理 ACK请求 * 处理 ACK请求
* *
@ -76,18 +81,22 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor {
while (!rtpPushed) { while (!rtpPushed) {
try { try {
if (System.currentTimeMillis() - startTime < 30 * 1000) { 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; rtpPushed = true;
logger.info("已获取设备推流,开始向上级推流"); logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",
zlmrtpServerFactory.startSendRtpStream(param); streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());
zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
} else { } else {
logger.info("等待设备推流......."); logger.info("等待设备推流[{}/{}].......",
streamInfo.getApp() ,streamInfo.getStreamId());
Thread.sleep(1000); Thread.sleep(1000);
continue; continue;
} }
} else { } else {
rtpPushed = true; rtpPushed = true;
logger.info("设备推流超时,终止向上级推流"); logger.info("设备推流[{}/{}]超时,终止向上级推流",
streamInfo.getApp() ,streamInfo.getStreamId());
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
@ -123,4 +132,12 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor {
public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) { public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {
this.zlmrtpServerFactory = zlmrtpServerFactory; this.zlmrtpServerFactory = zlmrtpServerFactory;
} }
public IMediaServerService getMediaServerService() {
return mediaServerService;
}
public void setMediaServerService(IMediaServerService mediaServerService) {
this.mediaServerService = mediaServerService;
}
} }

View File

@ -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.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; 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.IRedisCatchStorage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -38,6 +41,8 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
private ZLMRTPServerFactory zlmrtpServerFactory; private ZLMRTPServerFactory zlmrtpServerFactory;
private IMediaServerService mediaServerService;
/** /**
* 处理BYE请求 * 处理BYE请求
* @param evt * @param evt
@ -60,9 +65,10 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
param.put("stream",streamId); param.put("stream",streamId);
param.put("ssrc",sendRtpItem.getSsrc()); param.put("ssrc",sendRtpItem.getSsrc());
logger.info("停止向上级推流:" + streamId); logger.info("停止向上级推流:" + streamId);
zlmrtpServerFactory.stopSendRtpStream(param); IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
redisCatchStorage.deleteSendRTPServer(platformGbId, channelId); redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
if (zlmrtpServerFactory.totalReaderCount(sendRtpItem.getApp(), streamId) == 0) { if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
logger.info(streamId + "无其它观看者,通知设备停止推流"); logger.info(streamId + "无其它观看者,通知设备停止推流");
cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId); cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);
} }
@ -112,4 +118,11 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
this.cmder = cmder; this.cmder = cmder;
} }
public IMediaServerService getMediaServerService() {
return mediaServerService;
}
public void setMediaServerService(IMediaServerService mediaServerService) {
this.mediaServerService = mediaServerService;
}
} }

View File

@ -11,12 +11,16 @@ import javax.sip.header.*;
import javax.sip.message.Request; import javax.sip.message.Request;
import javax.sip.message.Response; 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.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.*; 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.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
@ -51,6 +55,8 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
private ZLMRTPServerFactory zlmrtpServerFactory; private ZLMRTPServerFactory zlmrtpServerFactory;
private IMediaServerService mediaServerService;
public ZLMRTPServerFactory getZlmrtpServerFactory() { public ZLMRTPServerFactory getZlmrtpServerFactory() {
return zlmrtpServerFactory; return zlmrtpServerFactory;
} }
@ -91,6 +97,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
// 查询平台下是否有该通道 // 查询平台下是否有该通道
DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId); GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
IMediaServerItem mediaServerItem = null;
// 不是通道可能是直播流 // 不是通道可能是直播流
if (channel != null && gbStream == null ) { if (channel != null && gbStream == null ) {
if (channel.getStatus() == 0) { if (channel.getStatus() == 0) {
@ -100,8 +107,15 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
} }
responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在发181呼叫转接中 responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在发181呼叫转接中
}else if(channel == null && gbStream != null){ }else if(channel == null && gbStream != null){
Boolean streamReady = zlmrtpServerFactory.isStreamReady(gbStream.getApp(), gbStream.getStream()); String mediaServerId = gbStream.getMediaServerId();
if (!streamReady) { 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()); logger.info("[ app={}, stream={} ]通道离线返回400",gbStream.getApp(), gbStream.getStream());
responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline"); responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
return; return;
@ -130,8 +144,8 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
//boolean recvonly = false; //boolean recvonly = false;
boolean mediaTransmissionTCP = false; boolean mediaTransmissionTCP = false;
Boolean tcpActive = null; Boolean tcpActive = null;
for (int i = 0; i < mediaDescriptions.size(); i++) { for (Object description : mediaDescriptions) {
MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); MediaDescription mediaDescription = (MediaDescription) description;
Media media = mediaDescription.getMedia(); Media media = mediaDescription.getMedia();
Vector mediaFormats = media.getMediaFormats(false); Vector mediaFormats = media.getMediaFormats(false);
@ -147,7 +161,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
mediaTransmissionTCP = true; mediaTransmissionTCP = true;
if ("active".equals(setup)) { if ("active".equals(setup)) {
tcpActive = true; tcpActive = true;
}else if ("passive".equals(setup)) { } else if ("passive".equals(setup)) {
tcpActive = false; tcpActive = false;
} }
} }
@ -174,7 +188,13 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
responseAck(evt, Response.SERVER_INTERNAL_ERROR); responseAck(evt, Response.SERVER_INTERNAL_ERROR);
return; 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, device.getDeviceId(), channelId,
mediaTransmissionTCP); mediaTransmissionTCP);
if (tcpActive != null) { if (tcpActive != null) {
@ -189,18 +209,18 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
// 写入redis 超时时回复 // 写入redis 超时时回复
redisCatchStorage.updateSendRTPSever(sendRtpItem); redisCatchStorage.updateSendRTPSever(sendRtpItem);
// 通知下级推流 // 通知下级推流
PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{ PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{
// 收到推流 回复200OK, 等待ack // 收到推流 回复200OK, 等待ack
// if (sendRtpItem == null) return; // if (sendRtpItem == null) return;
sendRtpItem.setStatus(1); sendRtpItem.setStatus(1);
redisCatchStorage.updateSendRTPSever(sendRtpItem); redisCatchStorage.updateSendRTPSever(sendRtpItem);
// TODO 添加对tcp的支持 // TODO 添加对tcp的支持
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
StringBuffer content = new StringBuffer(200); StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n"); 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("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("t=0 0\r\n");
content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
content.append("a=sendonly\r\n"); content.append("a=sendonly\r\n");
@ -217,7 +237,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); e.printStackTrace();
} }
} ,(event -> { } ,((event) -> {
// 未知错误直接转发设备点播的错误 // 未知错误直接转发设备点播的错误
Response response = null; Response response = null;
try { try {
@ -232,7 +252,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
} }
}else if (gbStream != null) { }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, gbStream.getApp(), gbStream.getStream(), channelId,
mediaTransmissionTCP); mediaTransmissionTCP);
@ -251,12 +271,11 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
sendRtpItem.setStatus(1); sendRtpItem.setStatus(1);
redisCatchStorage.updateSendRTPSever(sendRtpItem); redisCatchStorage.updateSendRTPSever(sendRtpItem);
// TODO 添加对tcp的支持 // TODO 添加对tcp的支持
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
StringBuffer content = new StringBuffer(200); StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n"); 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("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("t=0 0\r\n");
content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n"); content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
content.append("a=sendonly\r\n"); content.append("a=sendonly\r\n");
@ -444,4 +463,12 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
this.redisCatchStorage = redisCatchStorage; this.redisCatchStorage = redisCatchStorage;
} }
public IMediaServerService getMediaServerService() {
return mediaServerService;
}
public void setMediaServerService(IMediaServerService mediaServerService) {
this.mediaServerService = mediaServerService;
}
} }

View File

@ -282,7 +282,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
//String result = XmlUtil.getText(rootElement, "Result"); //String result = XmlUtil.getText(rootElement, "Result");
// 回复200 OK // 回复200 OK
responseAck(evt); responseAck(evt);
if (rootElement.getName().equals("Response")) {//} !XmlUtil.isEmpty(result)) { if (rootElement.getName().equals("Response")) {//} !StringUtils.isEmpty(result)) {
// 此处是对本平台发出DeviceControl指令的应答 // 此处是对本平台发出DeviceControl指令的应答
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
XmlUtil.node2Json(rootElement, json); 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 platformId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
String targetGBId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.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)) { if (deviceId.equals(targetGBId)) {
// 远程启动本平台需要在重新启动程序后先对SipStack解绑 // 远程启动本平台需要在重新启动程序后先对SipStack解绑
logger.info("执行远程启动本平台命令"); 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"); String cmdString = XmlUtil.getText(rootElement,"PTZCmd");
Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId); Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId);
cmder.fronEndCmd(device, deviceId, cmdString); cmder.fronEndCmd(device, deviceId, cmdString);
@ -421,7 +421,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
String deviceId = XmlUtil.getText(rootElement, "DeviceID"); String deviceId = XmlUtil.getText(rootElement, "DeviceID");
// 回复200 OK // 回复200 OK
responseAck(evt); responseAck(evt);
if (rootElement.getName().equals("Response")) {// !XmlUtil.isEmpty(result)) { if (rootElement.getName().equals("Response")) {// !StringUtils.isEmpty(result)) {
// 此处是对本平台发出DeviceControl指令的应答 // 此处是对本平台发出DeviceControl指令的应答
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
XmlUtil.node2Json(rootElement, json); XmlUtil.node2Json(rootElement, json);
@ -718,7 +718,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
deviceAlarm.setLatitude(0.00); deviceAlarm.setLatitude(0.00);
} }
if (!XmlUtil.isEmpty(deviceAlarm.getAlarmMethod())) { if (!StringUtils.isEmpty(deviceAlarm.getAlarmMethod())) {
if ( deviceAlarm.getAlarmMethod().equals("4")) { if ( deviceAlarm.getAlarmMethod().equals("4")) {
MobilePosition mobilePosition = new MobilePosition(); MobilePosition mobilePosition = new MobilePosition();
mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); mobilePosition.setDeviceId(deviceAlarm.getDeviceId());

View File

@ -17,6 +17,7 @@ import org.dom4j.Element;
import org.dom4j.io.SAXReader; import org.dom4j.io.SAXReader;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
/** /**
* 基于dom4j的工具包 * 基于dom4j的工具包
@ -114,12 +115,12 @@ public class XmlUtil {
// 如果是属性 // 如果是属性
for (Object o : element.attributes()) { for (Object o : element.attributes()) {
Attribute attr = (Attribute) o; Attribute attr = (Attribute) o;
if (!isEmpty(attr.getValue())) { if (!StringUtils.isEmpty(attr.getValue())) {
json.put("@" + attr.getName(), attr.getValue()); json.put("@" + attr.getName(), attr.getValue());
} }
} }
List<Element> chdEl = element.elements(); List<Element> chdEl = element.elements();
if (chdEl.isEmpty() && !isEmpty(element.getText())) {// 如果没有子元素,只有一个值 if (chdEl.isEmpty() && !StringUtils.isEmpty(element.getText())) {// 如果没有子元素,只有一个值
json.put(element.getName(), element.getText()); json.put(element.getName(), element.getText());
} }
@ -150,7 +151,7 @@ public class XmlUtil {
} else { // 子元素没有子元素 } else { // 子元素没有子元素
for (Object o : element.attributes()) { for (Object o : element.attributes()) {
Attribute attr = (Attribute) o; Attribute attr = (Attribute) o;
if (!isEmpty(attr.getValue())) { if (!StringUtils.isEmpty(attr.getValue())) {
json.put("@" + attr.getName(), 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;
}
} }

View File

@ -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;
// }
//}

View File

@ -9,6 +9,9 @@ import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.UserSetup; import com.genersoft.iot.vmp.conf.UserSetup;
import com.genersoft.iot.vmp.gb28181.bean.Device; 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.service.IPlayService; import com.genersoft.iot.vmp.service.IPlayService;
@ -53,10 +56,10 @@ public class ZLMHttpHookListener {
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private IMediaServerService mediaServerService;
@Autowired @Autowired
private ZLMServerManger zlmServerManger; private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired @Autowired
private ZLMMediaListManager zlmMediaListManager; private ZLMMediaListManager zlmMediaListManager;
@ -81,6 +84,7 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_flow_report API调用参数" + json.toString()); logger.debug("ZLM HOOK on_flow_report API调用参数" + json.toString());
} }
String mediaServerId = json.getString("mediaServerId");
JSONObject ret = new JSONObject(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
ret.put("msg", "success"); ret.put("msg", "success");
@ -98,6 +102,7 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString());
} }
String mediaServerId = json.getString("mediaServerId");
JSONObject ret = new JSONObject(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
ret.put("err", ""); ret.put("err", "");
@ -117,9 +122,14 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_play API调用参数" + json.toString()); 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); ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json);
if (subscribe != null ) { if (subscribe != null ) {
subscribe.response(json); IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo != null) {
subscribe.response(mediaInfo, json);
}
} }
JSONObject ret = new JSONObject(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
@ -133,20 +143,25 @@ public class ZLMHttpHookListener {
*/ */
@ResponseBody @ResponseBody
@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onPublish(@RequestBody JSONObject json){ public ResponseEntity<String> onPublish(@RequestBody JSONObject json) {
logger.debug("ZLM HOOK on_publish API调用参数" + json.toString()); 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); 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(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
ret.put("msg", "success"); ret.put("msg", "success");
ret.put("enableHls", true); ret.put("enableHls", true);
ret.put("enableMP4", userSetup.isRecordPushLive()); ret.put("enableMP4", userSetup.isRecordPushLive());
ret.put("enableRtxp", true); ret.put("enableRtxp", true);
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
} }
/** /**
@ -160,6 +175,7 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_record_mp4 API调用参数" + json.toString()); logger.debug("ZLM HOOK on_record_mp4 API调用参数" + json.toString());
} }
String mediaServerId = json.getString("mediaServerId");
JSONObject ret = new JSONObject(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
ret.put("msg", "success"); ret.put("msg", "success");
@ -177,6 +193,7 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_rtsp_realm API调用参数" + json.toString()); logger.debug("ZLM HOOK on_rtsp_realm API调用参数" + json.toString());
} }
String mediaServerId = json.getString("mediaServerId");
JSONObject ret = new JSONObject(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
ret.put("realm", ""); ret.put("realm", "");
@ -195,6 +212,7 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_rtsp_auth API调用参数" + json.toString()); logger.debug("ZLM HOOK on_rtsp_auth API调用参数" + json.toString());
} }
String mediaServerId = json.getString("mediaServerId");
JSONObject ret = new JSONObject(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
ret.put("encrypted", false); ret.put("encrypted", false);
@ -216,9 +234,15 @@ public class ZLMHttpHookListener {
// TODO 如果是带有rtpstream则开启按需拉流 // TODO 如果是带有rtpstream则开启按需拉流
// String app = json.getString("app"); // String app = json.getString("app");
// String stream = json.getString("stream"); // String stream = json.getString("stream");
String mediaServerId = json.getString("mediaServerId");
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json); 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(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
@ -237,9 +261,15 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_changed API调用参数" + json.toString()); 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); 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 // 流消失移除redis play
String app = json.getString("app"); String app = json.getString("app");
@ -251,6 +281,11 @@ public class ZLMHttpHookListener {
logger.info("[stream: " + streamId + "] on_stream_changed->>" + schema); logger.info("[stream: " + streamId + "] on_stream_changed->>" + schema);
} }
if ("rtmp".equals(schema)){ if ("rtmp".equals(schema)){
if (regist) {
mediaServerService.addCount(mediaServerId);
}else {
mediaServerService.removeCount(mediaServerId);
}
if ("rtp".equals(app) && !regist ) { if ("rtp".equals(app) && !regist ) {
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
if (streamInfo!=null){ if (streamInfo!=null){
@ -262,10 +297,11 @@ public class ZLMHttpHookListener {
} }
}else { }else {
if (!"rtp".equals(app) ){ if (!"rtp".equals(app) ){
IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
if (regist) { if (regist) {
zlmMediaListManager.addMedia(app, streamId); zlmMediaListManager.addMedia(mediaServerItem, app, streamId);
}else { }else {
zlmMediaListManager.removeMedia(app, streamId); zlmMediaListManager.removeMedia( app, streamId);
} }
} }
} }
@ -288,7 +324,7 @@ public class ZLMHttpHookListener {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_none_reader API调用参数" + json.toString()); logger.debug("ZLM HOOK on_stream_none_reader API调用参数" + json.toString());
} }
String mediaServerId = json.getString("mediaServerId");
String streamId = json.getString("stream"); String streamId = json.getString("stream");
String app = json.getString("app"); String app = json.getString("app");
@ -329,11 +365,12 @@ public class ZLMHttpHookListener {
@ResponseBody @ResponseBody
@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json){ public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_not_found API调用参数" + json.toString()); 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 app = json.getString("app");
String streamId = json.getString("stream"); String streamId = json.getString("stream");
if ("rtp".equals(app) && streamId.contains("gb_play") ) { if ("rtp".equals(app) && streamId.contains("gb_play") ) {
@ -344,9 +381,9 @@ public class ZLMHttpHookListener {
Device device = storager.queryVideoDevice(deviceId); Device device = storager.queryVideoDevice(deviceId);
if (device != null) { if (device != null) {
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
cmder.playStreamCmd(device, channelId, (JSONObject response) -> { cmder.playStreamCmd(mediaInfo, device, channelId, (IMediaServerItem mediaServerItemInuse, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString()); logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); playService.onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
}, null); }, null);
} }
@ -367,26 +404,19 @@ public class ZLMHttpHookListener {
*/ */
@ResponseBody @ResponseBody
@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){ public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_server_started API调用参数" + json.toString()); logger.debug("ZLM HOOK on_server_started API调用参数" + jsonObject.toString());
} }
String remoteAddr = request.getRemoteAddr();
// String data = json.getString("data"); jsonObject.put("ip", remoteAddr);
// List<MediaServerConfig> mediaServerConfigs = JSON.parseArray(JSON.toJSONString(json), MediaServerConfig.class);
// MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0);
List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_started); List<ZLMHttpHookSubscribe.Event> 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) { 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(); JSONObject ret = new JSONObject();
ret.put("code", 0); ret.put("code", 0);
ret.put("msg", "success"); ret.put("msg", "success");

View File

@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.media.zlm; package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONObject; 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 org.springframework.stereotype.Component;
import java.util.*; import java.util.*;
@ -30,7 +32,7 @@ public class ZLMHttpHookSubscribe {
} }
public interface Event{ public interface Event{
void response(JSONObject response); void response(IMediaServerItem mediaServerItem, JSONObject response);
} }
private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>(); private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();

View File

@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; 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.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.gb28181.bean.GbStream; import com.genersoft.iot.vmp.gb28181.bean.GbStream;
@ -45,11 +47,11 @@ public class ZLMMediaListManager {
private ZLMHttpHookSubscribe subscribe; private ZLMHttpHookSubscribe subscribe;
public void updateMediaList() { public void updateMediaList(MediaServerItem mediaServerItem) {
storager.clearMediaList(); storager.clearMediaList();
// 使用异步的当时更新媒体流列表 // 使用异步的当时更新媒体流列表
zlmresTfulUtils.getMediaList((mediaList ->{ zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{
if (mediaList == null) return; if (mediaList == null) return;
String dataStr = mediaList.getString("data"); String dataStr = mediaList.getString("data");
@ -57,10 +59,10 @@ public class ZLMMediaListManager {
Map<String, StreamPushItem> result = new HashMap<>(); Map<String, StreamPushItem> result = new HashMap<>();
List<StreamPushItem> streamPushItems = null; List<StreamPushItem> streamPushItems = null;
// 获取所有的国标关联 // 获取所有的国标关联
List<GbStream> gbStreams = gbStreamMapper.selectAll(); // List<GbStream> gbStreams = gbStreamMapper.selectAllByMediaServerId(mediaServerItem.getId());
if (code == 0 ) { if (code == 0 ) {
if (dataStr != null) { if (dataStr != null) {
streamPushItems = streamPushService.handleJSON(dataStr); streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
} }
}else { }else {
logger.warn("更新视频流失败错误code " + code); logger.warn("更新视频流失败错误code " + code);
@ -72,24 +74,27 @@ public class ZLMMediaListManager {
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put("app", streamPushItem.getApp()); jsonObject.put("app", streamPushItem.getApp());
jsonObject.put("stream", streamPushItem.getStream()); jsonObject.put("stream", streamPushItem.getStream());
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,(response)->{ jsonObject.put("mediaServerId", mediaServerItem.getId());
updateMedia(response.getString("app"), response.getString("stream")); 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; if (json == null) return;
String dataStr = json.getString("data"); String dataStr = json.getString("data");
@ -99,7 +104,7 @@ public class ZLMMediaListManager {
List<StreamPushItem> streamPushItems = null; List<StreamPushItem> streamPushItems = null;
if (code == 0 ) { if (code == 0 ) {
if (dataStr != null) { if (dataStr != null) {
streamPushItems = streamPushService.handleJSON(dataStr); streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
} }
}else { }else {
logger.warn("更新视频流失败错误code " + code); logger.warn("更新视频流失败错误code " + code);
@ -122,32 +127,32 @@ public class ZLMMediaListManager {
} }
} }
public void clearAllSessions() { // public void clearAllSessions() {
logger.info("清空所有国标相关的session"); // logger.info("清空所有国标相关的session");
JSONObject allSessionJSON = zlmresTfulUtils.getAllSession(); // JSONObject allSessionJSON = zlmresTfulUtils.getAllSession();
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); // ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
HashSet<String> allLocalPorts = new HashSet(); // HashSet<String> allLocalPorts = new HashSet();
if (allSessionJSON.getInteger("code") == 0) { // if (allSessionJSON.getInteger("code") == 0) {
JSONArray data = allSessionJSON.getJSONArray("data"); // JSONArray data = allSessionJSON.getJSONArray("data");
if (data.size() > 0) { // if (data.size() > 0) {
for (int i = 0; i < data.size(); i++) { // for (int i = 0; i < data.size(); i++) {
JSONObject sessionJOSN = data.getJSONObject(i); // JSONObject sessionJOSN = data.getJSONObject(i);
Integer local_port = sessionJOSN.getInteger("local_port"); // Integer local_port = sessionJOSN.getInteger("local_port");
if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) && // if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) && // !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) && // !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) && // !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) && // !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){ // !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){
allLocalPorts.add(sessionJOSN.getInteger("local_port") + ""); // allLocalPorts.add(sessionJOSN.getInteger("local_port") + "");
} // }
} // }
} // }
} // }
if (allLocalPorts.size() > 0) { // if (allLocalPorts.size() > 0) {
List<String> result = new ArrayList<>(allLocalPorts); // List<String> result = new ArrayList<>(allLocalPorts);
String localPortSStr = String.join(",", result); // String localPortSStr = String.join(",", result);
zlmresTfulUtils.kickSessions(localPortSStr); // zlmresTfulUtils.kickSessions(localPortSStr);
} // }
} // }
} }

View File

@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaConfig; 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 okhttp3.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -21,23 +23,18 @@ public class ZLMRESTfulUtils {
private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class);
@Autowired
private MediaConfig mediaConfig;
public interface RequestCallback{ public interface RequestCallback{
void run(JSONObject response); void run(JSONObject response);
} }
public JSONObject sendPost(String api, Map<String, Object> param, RequestCallback callback) { public JSONObject sendPost(IMediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
OkHttpClient client = new OkHttpClient(); 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; JSONObject responseJSON = null;
logger.debug(url); logger.debug(url);
FormBody.Builder builder = new FormBody.Builder(); FormBody.Builder builder = new FormBody.Builder();
builder.add("secret",mediaConfig.getSecret()); builder.add("secret",mediaServerItem.getSecret());
if (param != null && param.keySet().size() > 0) { if (param != null && param.keySet().size() > 0) {
for (String key : param.keySet()){ for (String key : param.keySet()){
if (param.get(key) != null) { if (param.get(key) != null) {
@ -96,14 +93,14 @@ public class ZLMRESTfulUtils {
} }
public void sendPostForImg(String api, Map<String, Object> param, String targetPath, String fileName) { public void sendPostForImg(IMediaServerItem mediaServerItem, String api, Map<String, Object> param, String targetPath, String fileName) {
OkHttpClient client = new OkHttpClient(); 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; JSONObject responseJSON = null;
logger.debug(url); logger.debug(url);
FormBody.Builder builder = new FormBody.Builder(); FormBody.Builder builder = new FormBody.Builder();
builder.add("secret",mediaConfig.getSecret()); builder.add("secret",mediaServerItem.getSecret());
if (param != null && param.keySet().size() > 0) { if (param != null && param.keySet().size() > 0) {
for (String key : param.keySet()){ for (String key : param.keySet()){
if (param.get(key) != null) { 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<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
if (app != null) param.put("app",app); if (app != null) param.put("app",app);
if (stream != null) param.put("stream",stream); if (stream != null) param.put("stream",stream);
if (schema != null) param.put("schema",schema); if (schema != null) param.put("schema",schema);
param.put("vhost","__defaultVhost__"); param.put("vhost","__defaultVhost__");
return sendPost("getMediaList",param, callback); return sendPost(mediaServerItem, "getMediaList",param, callback);
} }
public JSONObject getMediaList(String app, String stream){ public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream){
return getMediaList(app, stream,null, null); return getMediaList(mediaServerItem, app, stream,null, null);
} }
public JSONObject getMediaList(RequestCallback callback){ public JSONObject getMediaList(IMediaServerItem mediaServerItem,RequestCallback callback){
return sendPost("getMediaList",null, 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<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("app",app); param.put("app",app);
param.put("schema",schema); param.put("schema",schema);
param.put("stream",stream); param.put("stream",stream);
param.put("vhost","__defaultVhost__"); 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<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("stream_id",stream_id); 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){ boolean enable_hls, boolean enable_mp4, String ffmpeg_cmd_key){
logger.info(src_url); logger.info(src_url);
logger.info(dst_url); logger.info(dst_url);
@ -185,44 +182,44 @@ public class ZLMRESTfulUtils {
param.put("enable_hls", enable_hls); param.put("enable_hls", enable_hls);
param.put("enable_mp4", enable_mp4); param.put("enable_mp4", enable_mp4);
param.put("ffmpeg_cmd_key", ffmpeg_cmd_key); 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<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("key", key); param.put("key", key);
return sendPost("delFFmpegSource",param, null); return sendPost(mediaServerItem, "delFFmpegSource",param, null);
} }
public JSONObject getMediaServerConfig(){ public JSONObject getMediaServerConfig(IMediaServerItem mediaServerItem){
return sendPost("getServerConfig",null, null); return sendPost(mediaServerItem, "getServerConfig",null, null);
} }
public JSONObject setServerConfig(Map<String, Object> param){ public JSONObject setServerConfig(IMediaServerItem mediaServerItem, Map<String, Object> param){
return sendPost("setServerConfig",param, null); return sendPost(mediaServerItem,"setServerConfig",param, null);
} }
public JSONObject openRtpServer(Map<String, Object> param){ public JSONObject openRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param){
return sendPost("openRtpServer",param, null); return sendPost(mediaServerItem, "openRtpServer",param, null);
} }
public JSONObject closeRtpServer(Map<String, Object> param) { public JSONObject closeRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param) {
return sendPost("closeRtpServer",param, null); return sendPost(mediaServerItem, "closeRtpServer",param, null);
} }
public JSONObject listRtpServer() { public JSONObject listRtpServer(IMediaServerItem mediaServerItem) {
return sendPost("listRtpServer",null, null); return sendPost(mediaServerItem, "listRtpServer",null, null);
} }
public JSONObject startSendRtp(Map<String, Object> param) { public JSONObject startSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
return sendPost("startSendRtp",param, null); return sendPost(mediaServerItem, "startSendRtp",param, null);
} }
public JSONObject stopSendRtp(Map<String, Object> param) { public JSONObject stopSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
return sendPost("stopSendRtp",param, null); 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<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__"); param.put("vhost", "__defaultVhost__");
param.put("app", app); param.put("app", app);
@ -231,33 +228,33 @@ public class ZLMRESTfulUtils {
param.put("enable_hls", enable_hls?1:0); param.put("enable_hls", enable_hls?1:0);
param.put("enable_mp4", enable_mp4?1:0); param.put("enable_mp4", enable_mp4?1:0);
param.put("rtp_type", rtp_type); 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<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__"); param.put("vhost", "__defaultVhost__");
param.put("app", app); param.put("app", app);
param.put("stream", stream); param.put("stream", stream);
param.put("force", 1); param.put("force", 1);
return sendPost("close_streams",param, null); return sendPost(mediaServerItem, "close_streams",param, null);
} }
public JSONObject getAllSession() { public JSONObject getAllSession(IMediaServerItem mediaServerItem) {
return sendPost("getAllSession",null, null); return sendPost(mediaServerItem, "getAllSession",null, null);
} }
public void kickSessions(String localPortSStr) { public void kickSessions(IMediaServerItem mediaServerItem, String localPortSStr) {
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("local_port", localPortSStr); 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<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("url", flvUrl); param.put("url", flvUrl);
param.put("timeout_sec", timeout_sec); param.put("timeout_sec", timeout_sec);
param.put("expire_sec", expire_sec); param.put("expire_sec", expire_sec);
sendPostForImg("getSnap",param, targetPath, fileName); sendPostForImg(mediaServerItem, "getSnap",param, targetPath, fileName);
} }
} }

View File

@ -5,6 +5,8 @@ import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SsrcUtil; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -30,10 +32,10 @@ public class ZLMRTPServerFactory {
private Map<String, Integer> currentStreams = null; private Map<String, Integer> currentStreams = null;
public int createRTPServer(String streamId) { public int createRTPServer(IMediaServerItem mediaServerItem, String streamId) {
if (currentStreams == null) { if (currentStreams == null) {
currentStreams = new HashMap<>(); currentStreams = new HashMap<>();
JSONObject jsonObject = zlmresTfulUtils.listRtpServer(); JSONObject jsonObject = zlmresTfulUtils.listRtpServer(mediaServerItem);
if (jsonObject != null) { if (jsonObject != null) {
JSONArray data = jsonObject.getJSONArray("data"); JSONArray data = jsonObject.getJSONArray("data");
if (data != null) { if (data != null) {
@ -48,7 +50,7 @@ public class ZLMRTPServerFactory {
if (currentStreams.get(streamId) != null) { if (currentStreams.get(streamId) != null) {
Map<String, Object> closeRtpServerParam = new HashMap<>(); Map<String, Object> closeRtpServerParam = new HashMap<>();
closeRtpServerParam.put("stream_id", streamId); closeRtpServerParam.put("stream_id", streamId);
zlmresTfulUtils.closeRtpServer(closeRtpServerParam); zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);
currentStreams.remove(streamId); currentStreams.remove(streamId);
} }
@ -58,7 +60,7 @@ public class ZLMRTPServerFactory {
param.put("port", newPort); param.put("port", newPort);
param.put("enable_tcp", 1); param.put("enable_tcp", 1);
param.put("stream_id", streamId); param.put("stream_id", streamId);
JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); JSONObject jsonObject = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
if (jsonObject != null) { if (jsonObject != null) {
switch (jsonObject.getInteger("code")){ switch (jsonObject.getInteger("code")){
@ -68,11 +70,11 @@ public class ZLMRTPServerFactory {
case -300: // id已经存在, 可能已经在其他端口推流 case -300: // id已经存在, 可能已经在其他端口推流
Map<String, Object> closeRtpServerParam = new HashMap<>(); Map<String, Object> closeRtpServerParam = new HashMap<>();
closeRtpServerParam.put("stream_id", streamId); closeRtpServerParam.put("stream_id", streamId);
zlmresTfulUtils.closeRtpServer(closeRtpServerParam); zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);
result = newPort; result = newPort;
break; break;
case -400: // 端口占用 case -400: // 端口占用
result= createRTPServer(streamId); result= createRTPServer(mediaServerItem, streamId);
break; break;
default: default:
logger.error("创建RTP Server 失败 {}: " + jsonObject.getString("msg"), newPort); logger.error("创建RTP Server 失败 {}: " + jsonObject.getString("msg"), newPort);
@ -85,20 +87,22 @@ public class ZLMRTPServerFactory {
return result; return result;
} }
public boolean closeRTPServer(String streamId) { public boolean closeRTPServer(IMediaServerItem serverItem, String streamId) {
boolean result = false; boolean result = false;
Map<String, Object> param = new HashMap<>(); if (serverItem !=null){
param.put("stream_id", streamId); Map<String, Object> param = new HashMap<>();
JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(param); param.put("stream_id", streamId);
if (jsonObject != null ) { JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param);
if (jsonObject.getInteger("code") == 0) { if (jsonObject != null ) {
result = jsonObject.getInteger("hit") == 1; if (jsonObject.getInteger("code") == 0) {
result = jsonObject.getInteger("hit") == 1;
}else {
logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
}
}else { }else {
logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); // 检查ZLM状态
logger.error("关闭RTP Server 失败: 请检查ZLM服务");
} }
}else {
// 检查ZLM状态
logger.error("关闭RTP Server 失败: 请检查ZLM服务");
} }
return result; return result;
} }
@ -131,11 +135,11 @@ public class ZLMRTPServerFactory {
* @param tcp 是否为tcp * @param tcp 是否为tcp
* @return SendRtpItem * @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(); String playSsrc = SsrcUtil.getPlaySsrc();
int localPort = createRTPServer(SsrcUtil.getPlaySsrc()); int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
if (localPort != -1) { if (localPort != -1) {
closeRTPServer(playSsrc); closeRTPServer(serverItem, playSsrc);
}else { }else {
logger.error("没有可用的端口"); logger.error("没有可用的端口");
return null; return null;
@ -150,6 +154,7 @@ public class ZLMRTPServerFactory {
sendRtpItem.setTcp(tcp); sendRtpItem.setTcp(tcp);
sendRtpItem.setApp("rtp"); sendRtpItem.setApp("rtp");
sendRtpItem.setLocalPort(localPort); sendRtpItem.setLocalPort(localPort);
sendRtpItem.setMediaServerId(serverItem.getId());
return sendRtpItem; return sendRtpItem;
} }
@ -163,11 +168,11 @@ public class ZLMRTPServerFactory {
* @param tcp 是否为tcp * @param tcp 是否为tcp
* @return SendRtpItem * @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(); String playSsrc = SsrcUtil.getPlaySsrc();
int localPort = createRTPServer(SsrcUtil.getPlaySsrc()); int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
if (localPort != -1) { if (localPort != -1) {
closeRTPServer(playSsrc); closeRTPServer(serverItem, playSsrc);
}else { }else {
logger.error("没有可用的端口"); logger.error("没有可用的端口");
return null; return null;
@ -182,21 +187,21 @@ public class ZLMRTPServerFactory {
sendRtpItem.setChannelId(channelId); sendRtpItem.setChannelId(channelId);
sendRtpItem.setTcp(tcp); sendRtpItem.setTcp(tcp);
sendRtpItem.setLocalPort(localPort); sendRtpItem.setLocalPort(localPort);
sendRtpItem.setMediaServerId(serverItem.getId());
return sendRtpItem; return sendRtpItem;
} }
/** /**
* 调用zlm RESTful API startSendRtp * 调用zlm RESTful API startSendRtp
*/ */
public Boolean startSendRtpStream(Map<String, Object>param) { public Boolean startSendRtpStream(IMediaServerItem mediaServerItem, Map<String, Object>param) {
Boolean result = false; Boolean result = false;
JSONObject jsonObject = zlmresTfulUtils.startSendRtp(param); JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServerItem, param);
logger.info(jsonObject.toJSONString());
if (jsonObject == null) { if (jsonObject == null) {
logger.error("RTP推流失败: 请检查ZLM服务"); logger.error("RTP推流失败: 请检查ZLM服务");
} else if (jsonObject.getInteger("code") == 0) { } else if (jsonObject.getInteger("code") == 0) {
result= true; result= true;
logger.info("RTP推流请求成功,本地推流端口:" + jsonObject.getString("local_port")); logger.info("RTP推流[ {}/{} ]请求成功,本地推流端口:{}" ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"));
} else { } else {
logger.error("RTP推流失败: " + jsonObject.getString("msg")); logger.error("RTP推流失败: " + jsonObject.getString("msg"));
} }
@ -206,16 +211,16 @@ public class ZLMRTPServerFactory {
/** /**
* 查询待转推的流是否就绪 * 查询待转推的流是否就绪
*/ */
public Boolean isRtpReady(String streamId) { public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId); JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtmp", streamId);
return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
} }
/** /**
* 查询待转推的流是否就绪 * 查询待转推的流是否就绪
*/ */
public Boolean isStreamReady(String app, String streamId) { public Boolean isStreamReady(IMediaServerItem mediaServerItem, String app, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(app, "rtmp", streamId); JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
} }
@ -224,18 +229,17 @@ public class ZLMRTPServerFactory {
* @param streamId * @param streamId
* @return * @return
*/ */
public int totalReaderCount(String app, String streamId) { public int totalReaderCount(IMediaServerItem mediaServerItem, String app, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(app, "rtmp", streamId); JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
return mediaInfo.getInteger("totalReaderCount"); return mediaInfo.getInteger("totalReaderCount");
} }
/** /**
* 调用zlm RESTful API stopSendRtp * 调用zlm RESTful API stopSendRtp
*/ */
public Boolean stopSendRtpStream(Map<String, Object>param) { public Boolean stopSendRtpStream(IMediaServerItem mediaServerItem,Map<String, Object>param) {
Boolean result = false; Boolean result = false;
JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(param); JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
logger.info(jsonObject.toJSONString());
if (jsonObject == null) { if (jsonObject == null) {
logger.error("停止RTP推流失败: 请检查ZLM服务"); logger.error("停止RTP推流失败: 请检查ZLM服务");
} else if (jsonObject.getInteger("code") == 0) { } else if (jsonObject.getInteger("code") == 0) {

View File

@ -4,7 +4,10 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaConfig; 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.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.IStreamProxyService;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -14,10 +17,10 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.HashMap; import javax.print.attribute.standard.Media;
import java.util.List; import java.util.*;
import java.util.Map;
@Component @Component
@Order(value=1) @Order(value=1)
@ -25,140 +28,131 @@ public class ZLMRunner implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
@Autowired private Map<String, Boolean> startGetMedia;
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;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private ZLMMediaListManager zlmMediaListManager;
@Autowired @Autowired
private ZLMHttpHookSubscribe hookSubscribe; private ZLMHttpHookSubscribe hookSubscribe;
@Autowired
private ZLMServerManger zlmServerManger;
@Autowired @Autowired
private IStreamProxyService streamProxyService; private IStreamProxyService streamProxyService;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private MediaConfig mediaConfig;
@Override @Override
public void run(String... strings) throws Exception { public void run(String... strings) throws Exception {
// 订阅 zlm启动事件 IMediaServerItem presetMediaServer = mediaServerService.getOneByHostAndPort(
hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null,(response)->{ mediaConfig.getIp(), mediaConfig.getHttpPort());
ZLMServerConfig ZLMServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class); if (presetMediaServer != null) {
zLmRunning(ZLMServerConfig); 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信息 // 获取zlm信息
logger.info("等待zlm接入..."); logger.info("等待默认zlm接入...");
startGetMedia = true;
ZLMServerConfig ZLMServerConfig = getMediaServerConfig();
if (ZLMServerConfig != null) { // 获取所有的zlm 并开启主动连接
zLmRunning(ZLMServerConfig); List<IMediaServerItem> 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<String> allZlmId = startGetMedia.keySet();
for (String id : allZlmId) {
logger.error("[ {} ]]主动连接失败,不再主动连接", id);
startGetMedia.put(id, false);
}
}
}
}, 60 * 1000 * 2);
} }
public ZLMServerConfig getMediaServerConfig() { public ZLMServerConfig getMediaServerConfig(IMediaServerItem mediaServerItem) {
if (!startGetMedia) return null; if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) return null;
JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(); JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
ZLMServerConfig ZLMServerConfig = null; ZLMServerConfig ZLMServerConfig = null;
if (responseJSON != null) { if (responseJSON != null) {
JSONArray data = responseJSON.getJSONArray("data"); JSONArray data = responseJSON.getJSONArray("data");
if (data != null && data.size() > 0) { if (data != null && data.size() > 0) {
ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
ZLMServerConfig.setIp(mediaServerItem.getIp());
} }
} else { } else {
logger.error("getMediaServerConfig失败, 1s后重试"); logger.error("[ {} ]-[ {}:{} ]主动连接失败失败, 2s后重试",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
try { try {
Thread.sleep(1000); Thread.sleep(2000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
ZLMServerConfig = getMediaServerConfig(); ZLMServerConfig = getMediaServerConfig(mediaServerItem);
} }
return ZLMServerConfig; 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<String, Object> 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重启后 * zlm 连接成功或者zlm重启后
*/ */
private void zLmRunning(ZLMServerConfig zlmServerConfig){ // private void zLmRunning(ZLMServerConfig zlmServerConfig){
logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功..."); // logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功...");
// 关闭循环获取zlm配置 // // 关闭循环获取zlm配置
startGetMedia = false; // startGetMedia = false;
if (mediaConfig.isAutoConfig()) saveZLMConfig(); // MediaServerItem mediaServerItem = new MediaServerItem(zlmServerConfig, sipIp);
zlmServerManger.updateServerCatch(zlmServerConfig); // storager.updateMediaServer(mediaServerItem);
//
// 清空所有session // if (mediaServerItem.isAutoConfig()) setZLMConfig(mediaServerItem);
// zlmMediaListManager.clearAllSessions(); // zlmServerManger.updateServerCatchFromHook(zlmServerConfig);
//
// 更新流列表 // // 清空所有session
zlmMediaListManager.updateMediaList(); //// zlmMediaListManager.clearAllSessions();
// 恢复流代理 //
List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnable(true); // // 更新流列表
for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { // zlmMediaListManager.updateMediaList(mediaServerItem);
logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); // // 恢复流代理, 只查找这个这个流媒体
JSONObject jsonObject = streamProxyService.addStreamProxyToZlm(streamProxyDto); // List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer(
if (jsonObject == null) { // mediaServerItem.getId(), true);
// 设置为未启用 // for (StreamProxyItem streamProxyDto : streamProxyListForEnable) {
logger.info("恢复流代理失败,请检查流地址后重新启用" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); // logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream());
streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream()); // JSONObject jsonObject = streamProxyService.addStreamProxyToZlm(streamProxyDto);
}else if (jsonObject.getInteger("code") != 0){ // TODO 将错误信息存入数据库 前端展示 // if (jsonObject == null) {
logger.info("恢复流代理失败:" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream() + "[ " + JSONObject.toJSONString(jsonObject) + " ]"); // // 设置为未启用
streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream()); // logger.info("恢复流代理失败,请检查流地址后重新启用" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream());
} // streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream());
} // }
} // }
// }
} }

View File

@ -34,12 +34,15 @@ public class ZLMServerConfig {
@JSONField(name = "general.streamNoneReaderDelayMS") @JSONField(name = "general.streamNoneReaderDelayMS")
private String generalStreamNoneReaderDelayMS; private String generalStreamNoneReaderDelayMS;
@JSONField(name = "ip")
private String ip; private String ip;
private String sdpIp; private String sdpIp;
private String streamIp; private String streamIp;
private String hookIp;
private String updateTime; private String updateTime;
private String createTime; private String createTime;
@ -66,7 +69,7 @@ public class ZLMServerConfig {
private String hookEnable; private String hookEnable;
@JSONField(name = "hook.on_flow_report") @JSONField(name = "hook.on_flow_report")
private Integer hookOnFlowReport; private String hookOnFlowReport;
@JSONField(name = "hook.on_http_access") @JSONField(name = "hook.on_http_access")
private String hookOnHttpAccess; private String hookOnHttpAccess;
@ -117,7 +120,7 @@ public class ZLMServerConfig {
private String httpNotFound; private String httpNotFound;
@JSONField(name = "http.port") @JSONField(name = "http.port")
private Integer httpPort; private int httpPort;
@JSONField(name = "http.rootPath") @JSONField(name = "http.rootPath")
private String httpRootPath; private String httpRootPath;
@ -126,7 +129,7 @@ public class ZLMServerConfig {
private String httpSendBufSize; private String httpSendBufSize;
@JSONField(name = "http.sslport") @JSONField(name = "http.sslport")
private Integer httpSSLport; private int httpSSLport;
@JSONField(name = "multicast.addrMax") @JSONField(name = "multicast.addrMax")
private String multicastAddrMax; private String multicastAddrMax;
@ -159,10 +162,10 @@ public class ZLMServerConfig {
private String rtmpModifyStamp; private String rtmpModifyStamp;
@JSONField(name = "rtmp.port") @JSONField(name = "rtmp.port")
private Integer rtmpPort; private int rtmpPort;
@JSONField(name = "rtmp.sslport") @JSONField(name = "rtmp.sslport")
private Integer rtmpSslPort; private int rtmpSslPort;
@JSONField(name = "rtp.audioMtuSize") @JSONField(name = "rtp.audioMtuSize")
private String rtpAudioMtuSize; private String rtpAudioMtuSize;
@ -186,7 +189,7 @@ public class ZLMServerConfig {
private String rtpProxyDumpDir; private String rtpProxyDumpDir;
@JSONField(name = "rtp_proxy.port") @JSONField(name = "rtp_proxy.port")
private Integer rtpProxyPort; private int rtpProxyPort;
@JSONField(name = "rtp_proxy.timeoutSec") @JSONField(name = "rtp_proxy.timeoutSec")
private String rtpProxyTimeoutSec; private String rtpProxyTimeoutSec;
@ -201,10 +204,10 @@ public class ZLMServerConfig {
private String rtspKeepAliveSecond; private String rtspKeepAliveSecond;
@JSONField(name = "rtsp.port") @JSONField(name = "rtsp.port")
private Integer rtspPort; private int rtspPort;
@JSONField(name = "rtsp.sslport") @JSONField(name = "rtsp.sslport")
private Integer rtspSSlport; private int rtspSSlport;
@JSONField(name = "shell.maxReqSize") @JSONField(name = "shell.maxReqSize")
private String shellMaxReqSize; private String shellMaxReqSize;
@ -212,6 +215,15 @@ public class ZLMServerConfig {
@JSONField(name = "shell.shell") @JSONField(name = "shell.shell")
private String shellPhell; private String shellPhell;
public String getHookIp() {
return hookIp;
}
public void setHookIp(String hookIp) {
this.hookIp = hookIp;
}
public String getApiDebug() { public String getApiDebug() {
return apiDebug; return apiDebug;
} }
@ -388,11 +400,11 @@ public class ZLMServerConfig {
this.hookEnable = hookEnable; this.hookEnable = hookEnable;
} }
public Integer getHookOnFlowReport() { public String getHookOnFlowReport() {
return hookOnFlowReport; return hookOnFlowReport;
} }
public void setHookOnFlowReport(Integer hookOnFlowReport) { public void setHookOnFlowReport(String hookOnFlowReport) {
this.hookOnFlowReport = hookOnFlowReport; this.hookOnFlowReport = hookOnFlowReport;
} }
@ -524,11 +536,11 @@ public class ZLMServerConfig {
this.httpNotFound = httpNotFound; this.httpNotFound = httpNotFound;
} }
public Integer getHttpPort() { public int getHttpPort() {
return httpPort; return httpPort;
} }
public void setHttpPort(Integer httpPort) { public void setHttpPort(int httpPort) {
this.httpPort = httpPort; this.httpPort = httpPort;
} }
@ -548,11 +560,11 @@ public class ZLMServerConfig {
this.httpSendBufSize = httpSendBufSize; this.httpSendBufSize = httpSendBufSize;
} }
public Integer getHttpSSLport() { public int getHttpSSLport() {
return httpSSLport; return httpSSLport;
} }
public void setHttpSSLport(Integer httpSSLport) { public void setHttpSSLport(int httpSSLport) {
this.httpSSLport = httpSSLport; this.httpSSLport = httpSSLport;
} }
@ -636,19 +648,19 @@ public class ZLMServerConfig {
this.rtmpModifyStamp = rtmpModifyStamp; this.rtmpModifyStamp = rtmpModifyStamp;
} }
public Integer getRtmpPort() { public int getRtmpPort() {
return rtmpPort; return rtmpPort;
} }
public void setRtmpPort(Integer rtmpPort) { public void setRtmpPort(int rtmpPort) {
this.rtmpPort = rtmpPort; this.rtmpPort = rtmpPort;
} }
public Integer getRtmpSslPort() { public int getRtmpSslPort() {
return rtmpSslPort; return rtmpSslPort;
} }
public void setRtmpSslPort(Integer rtmpSslPort) { public void setRtmpSslPort(int rtmpSslPort) {
this.rtmpSslPort = rtmpSslPort; this.rtmpSslPort = rtmpSslPort;
} }
@ -708,11 +720,11 @@ public class ZLMServerConfig {
this.rtpProxyDumpDir = rtpProxyDumpDir; this.rtpProxyDumpDir = rtpProxyDumpDir;
} }
public Integer getRtpProxyPort() { public int getRtpProxyPort() {
return rtpProxyPort; return rtpProxyPort;
} }
public void setRtpProxyPort(Integer rtpProxyPort) { public void setRtpProxyPort(int rtpProxyPort) {
this.rtpProxyPort = rtpProxyPort; this.rtpProxyPort = rtpProxyPort;
} }
@ -748,19 +760,19 @@ public class ZLMServerConfig {
this.rtspKeepAliveSecond = rtspKeepAliveSecond; this.rtspKeepAliveSecond = rtspKeepAliveSecond;
} }
public Integer getRtspPort() { public int getRtspPort() {
return rtspPort; return rtspPort;
} }
public void setRtspPort(Integer rtspPort) { public void setRtspPort(int rtspPort) {
this.rtspPort = rtspPort; this.rtspPort = rtspPort;
} }
public Integer getRtspSSlport() { public int getRtspSSlport() {
return rtspSSlport; return rtspSSlport;
} }
public void setRtspSSlport(Integer rtspSSlport) { public void setRtspSSlport(int rtspSSlport) {
this.rtspSSlport = rtspSSlport; this.rtspSSlport = rtspSSlport;
} }

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -78,6 +78,10 @@ public class MediaItem {
*/ */
private String vhost; private String vhost;
/**
* 是否是docker部署 docker部署不会自动更新zlm使用的端口需要自己手动修改
*/
private boolean docker;
public static class MediaTrack { public static class MediaTrack {
/** /**
@ -364,4 +368,12 @@ public class MediaItem {
public OriginSock getOriginSock() { public OriginSock getOriginSock() {
return originSock; return originSock;
} }
public boolean isDocker() {
return docker;
}
public void setDocker(boolean docker) {
this.docker = docker;
}
} }

View File

@ -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;
}
}

View File

@ -7,6 +7,7 @@ public class StreamProxyItem extends GbStream {
private String type; private String type;
private String app; private String app;
private String stream; private String stream;
private String mediaServerId;
private String url; private String url;
private String src_url; private String src_url;
private String dst_url; private String dst_url;
@ -17,6 +18,7 @@ public class StreamProxyItem extends GbStream {
private boolean enable_hls; private boolean enable_hls;
private boolean enable_mp4; private boolean enable_mp4;
private String platformGbId; private String platformGbId;
private String createTime;
public String getType() { public String getType() {
return type; return type;
@ -42,6 +44,16 @@ public class StreamProxyItem extends GbStream {
this.stream = stream; this.stream = stream;
} }
@Override
public String getMediaServerId() {
return mediaServerId;
}
@Override
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
public String getUrl() { public String getUrl() {
return url; return url;
} }
@ -122,4 +134,12 @@ public class StreamProxyItem extends GbStream {
public void setPlatformGbId(String platformGbId) { public void setPlatformGbId(String platformGbId) {
this.platformGbId = platformGbId; this.platformGbId = platformGbId;
} }
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
} }

View File

@ -76,6 +76,11 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
*/ */
private String vhost; private String vhost;
/**
* 使用的流媒体ID
*/
private String mediaServerId;
public String getVhost() { public String getVhost() {
return vhost; return vhost;
} }
@ -202,5 +207,14 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
} }
@Override
public String getMediaServerId() {
return mediaServerId;
}
@Override
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
} }

View File

@ -0,0 +1,33 @@
package com.genersoft.iot.vmp.media.zlm.dto;
/**
* 记录zlm运行中一些参数
*/
public class ZLMRunInfo {
/**
* zlm当前流数量
*/
private int mediaCount;
/**
* 在线状态
*/
private boolean online;
public int getMediaCount() {
return mediaCount;
}
public void setMediaCount(int mediaCount) {
this.mediaCount = mediaCount;
}
public boolean isOnline() {
return online;
}
public void setOnline(boolean online) {
this.online = online;
}
}

View File

@ -0,0 +1,44 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import java.util.List;
/**
* 媒体服务节点
*/
public interface IMediaServerService {
List<IMediaServerItem> 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);
}

View File

@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.genersoft.iot.vmp.common.StreamInfo; 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 * @param stream
* @return * @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在一台主机的情况 * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
@ -38,5 +25,21 @@ public interface IMediaService {
* @param stream * @param stream
* @return * @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);
} }

View File

@ -1,8 +1,11 @@
package com.genersoft.iot.vmp.service; package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson.JSONObject; 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.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; 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; 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 { public interface IPlayService {
void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid); void onPublishHandlerForPlayBack(IMediaServerItem mediaServerItem,JSONObject resonse, String deviceId, String channelId, String uuid);
void onPublishHandlerForPlay(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);
} }

View File

@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.service; package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson.JSONObject; 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.StreamProxyItem;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
@ -61,5 +63,5 @@ public interface IStreamProxyService {
* 获取ffmpeg.cmd模板 * 获取ffmpeg.cmd模板
* @return * @return
*/ */
JSONObject getFFmpegCMDs(); JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem);
} }

View File

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

View File

@ -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<String, IMediaServerItem> zlmServers = new HashMap<>(); // 所有数据库的zlm的缓存
private Map<String, Integer> 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<MediaServerItem> 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<IMediaServerItem> getAll() {
if (zlmServers.size() == 0) {
init();
}
List<IMediaServerItem> result = new ArrayList<>();
for (String id : zlmServers.keySet()) {
IMediaServerItem mediaServerItem = zlmServers.get(id);
mediaServerItem.setCount(zlmServerStatus.get(id) == null ? 0 : zlmServerStatus.get(id));
result.add(mediaServerItem);
}
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<Integer> 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<String, Object> 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());
}
}
}

View File

@ -6,6 +6,9 @@ import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.service.IMediaService; import com.genersoft.iot.vmp.service.IMediaService;
@ -21,30 +24,53 @@ public class MediaServiceImpl implements IMediaService {
@Autowired @Autowired
private IVideoManagerStorager storager; private IVideoManagerStorager storager;
@Autowired
private IMediaServerService mediaServerService;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;
@Override @Override
public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks) { public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks) {
return getStreamInfoByAppAndStream(app, stream, tracks, null); return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null);
} }
@Override @Override
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream) { public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) {
return getStreamInfoByAppAndStreamWithCheck(app, stream, null); 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 @Override
public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks, String addr) { public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId) {
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null);
}
@Override
public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) {
StreamInfo streamInfoResult = new StreamInfo(); StreamInfo streamInfoResult = new StreamInfo();
streamInfoResult.setStreamId(stream); streamInfoResult.setStreamId(stream);
streamInfoResult.setApp(app); streamInfoResult.setApp(app);
if (addr == null) { if (addr == null) {
addr = mediaInfo.getStreamIp(); addr = mediaInfo.getStreamIp();
} }
streamInfoResult.setMediaServerId(mediaInfo.getId());
streamInfoResult.setRtmp(String.format("rtmp://%s:%s/%s/%s", addr, mediaInfo.getRtmpPort(), app, stream)); 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.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)); 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; 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;
}
} }

View File

@ -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.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
@ -59,6 +62,9 @@ public class PlayServiceImpl implements IPlayService {
@Autowired @Autowired
private IMediaService mediaService; private IMediaService mediaService;
@Autowired
private IMediaServerService mediaServerService;
@Autowired @Autowired
private VideoStreamSessionManager streamSession; private VideoStreamSessionManager streamSession;
@ -67,8 +73,18 @@ public class PlayServiceImpl implements IPlayService {
@Override @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(); 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); Device device = storager.queryVideoDevice(deviceId);
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
playResult.setDevice(device); playResult.setDevice(device);
@ -82,7 +98,7 @@ public class PlayServiceImpl implements IPlayService {
result.onTimeout(()->{ result.onTimeout(()->{
logger.warn(String.format("设备点播超时deviceId%s channelId%s", deviceId, channelId)); logger.warn(String.format("设备点播超时deviceId%s channelId%s", deviceId, channelId));
// 释放rtpserver // 释放rtpserver
cmder.closeRTPServer(playResult.getDevice(), channelId); mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid()); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid());
WVPResult wvpResult = new WVPResult(); WVPResult wvpResult = new WVPResult();
@ -115,9 +131,10 @@ public class PlayServiceImpl implements IPlayService {
WVPResult wvpResult = (WVPResult)responseEntity.getBody(); WVPResult wvpResult = (WVPResult)responseEntity.getBody();
if (wvpResult.getCode() == 0) { if (wvpResult.getCode() == 0) {
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData(); StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
IMediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
String streamUrl = streamInfoForSuccess.getFmp4(); String streamUrl = streamInfoForSuccess.getFmp4();
// 请求截图 // 请求截图
zlmresTfulUtils.getSnap(streamUrl, 15, 1, path, fileName); zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
} }
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
@ -126,17 +143,17 @@ public class PlayServiceImpl implements IPlayService {
}); });
if (streamInfo == null) { if (streamInfo == null) {
// 发送点播消息 // 发送点播消息
cmder.playStreamCmd(device, channelId, (JSONObject response) -> { cmder.playStreamCmd(mediaServerItem, device, channelId, (IMediaServerItem mediaServerItemInUse, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString()); logger.info("收到订阅消息: " + response.toJSONString());
onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid.toString());
if (hookEvent != null) { if (hookEvent != null) {
hookEvent.response(response); hookEvent.response(mediaServerItem, response);
} }
}, event -> { }, (event) -> {
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
Response response = event.getResponse(); Response response = event.getResponse();
cmder.closeRTPServer(playResult.getDevice(), channelId); mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
WVPResult wvpResult = new WVPResult(); WVPResult wvpResult = new WVPResult();
wvpResult.setCode(-1); wvpResult.setCode(-1);
wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
@ -158,7 +175,10 @@ public class PlayServiceImpl implements IPlayService {
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
return playResult; 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")) { if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
@ -171,16 +191,16 @@ public class PlayServiceImpl implements IPlayService {
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
if (hookEvent != null) { if (hookEvent != null) {
hookEvent.response(JSONObject.parseObject(JSON.toJSONString(streamInfo))); hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
} }
} else { } else {
redisCatchStorage.stopPlay(streamInfo); redisCatchStorage.stopPlay(streamInfo);
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); 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()); logger.info("收到订阅消息: " + response.toJSONString());
onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
}, event -> { }, (event) -> {
cmder.closeRTPServer(playResult.getDevice(), channelId); mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
Response response = event.getResponse(); Response response = event.getResponse();
@ -198,10 +218,10 @@ public class PlayServiceImpl implements IPlayService {
} }
@Override @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(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); 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) { if (streamInfo != null) {
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
if (deviceChannel != null) { if (deviceChannel != null) {
@ -234,10 +254,26 @@ public class PlayServiceImpl implements IPlayService {
} }
@Override @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(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); 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) { if (streamInfo != null) {
redisCatchStorage.startPlayback(streamInfo); redisCatchStorage.startPlayback(streamInfo);
msg.setData(JSON.toJSONString(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"); String streamId = resonse.getString("stream");
JSONArray tracks = resonse.getJSONArray("tracks"); JSONArray tracks = resonse.getJSONArray("tracks");
StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream("rtp", streamId, tracks); StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks);
streamInfo.setDeviceID(deviceId); streamInfo.setDeviceID(deviceId);
streamInfo.setChannelId(channelId); streamInfo.setChannelId(channelId);
return streamInfo; return streamInfo;

View File

@ -2,10 +2,12 @@ package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.GbStream; 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.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.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.service.IGbStreamService; 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; 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.storager.dao.StreamProxyMapper;
import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -25,6 +29,8 @@ import java.util.List;
@Service @Service
public class StreamProxyServiceImpl implements IStreamProxyService { public class StreamProxyServiceImpl implements IStreamProxyService {
private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class);
@Autowired @Autowired
private IVideoManagerStorager videoManagerStorager; private IVideoManagerStorager videoManagerStorager;
@ -32,7 +38,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;;
@Autowired @Autowired
private StreamProxyMapper streamProxyMapper; private StreamProxyMapper streamProxyMapper;
@ -46,15 +52,28 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Autowired @Autowired
private IGbStreamService gbStreamService; private IGbStreamService gbStreamService;
@Autowired
private IMediaServerService mediaServerService;
@Override @Override
public String save(StreamProxyItem param) { 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(), String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(),
param.getStream() ); param.getStream() );
param.setDst_url(dstUrl); param.setDst_url(dstUrl);
StringBuffer result = new StringBuffer(); StringBuffer result = new StringBuffer();
boolean streamLive = false; boolean streamLive = false;
param.setMediaServerId(mediaInfo.getId());
// 更新 // 更新
if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) {
if (videoManagerStorager.updateStreamProxy(param)) { if (videoManagerStorager.updateStreamProxy(param)) {
@ -81,6 +100,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
videoManagerStorager.updateStreamProxy(param); videoManagerStorager.updateStreamProxy(param);
} }
} }
}else {
result.append("保存失败");
} }
} }
@ -99,11 +120,18 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Override @Override
public JSONObject addStreamProxyToZlm(StreamProxyItem param) { public JSONObject addStreamProxyToZlm(StreamProxyItem param) {
JSONObject result = null; 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())){ 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()); param.isEnable_hls(), param.isEnable_mp4(), param.getRtp_type());
}else if ("ffmpeg".equals(param.getType())) { }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.getTimeout_ms() + "", param.isEnable_hls(), param.isEnable_mp4(),
param.getFfmpeg_cmd_key()); param.getFfmpeg_cmd_key());
} }
@ -112,8 +140,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Override @Override
public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) { 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; return result;
} }
@ -124,17 +153,18 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Override @Override
public void del(String app, String stream) { public void del(String app, String stream) {
StreamProxyItem streamProxyItem = new StreamProxyItem(); StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream);
streamProxyItem.setApp(app); if (streamProxyItem != null) {
streamProxyItem.setStream(stream);
JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem);
if (jsonObject.getInteger("code") == 0) {
videoManagerStorager.deleteStreamProxy(app, stream); videoManagerStorager.deleteStreamProxy(app, stream);
// 如果关联了国标那么移除关联 JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem);
gbStreamMapper.del(app, stream); if (jsonObject != null && jsonObject.getInteger("code") == 0) {
platformGbStreamMapper.delByAppAndStream(app, stream); // 如果关联了国标那么移除关联
// TODO 如果关联的推流 那么状态设置为离线 gbStreamMapper.del(app, stream);
platformGbStreamMapper.delByAppAndStream(app, stream);
// TODO 如果关联的推流 那么状态设置为离线
}
} }
} }
@Override @Override
@ -168,9 +198,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
} }
@Override @Override
public JSONObject getFFmpegCMDs() { public JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(); JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0 if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0
&& mediaServerConfigResuly.getJSONArray("data").size() > 0){ && mediaServerConfigResuly.getJSONArray("data").size() > 0){
JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0); JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0);

View File

@ -5,9 +5,13 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.TypeReference;
import com.genersoft.iot.vmp.gb28181.bean.GbStream; import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 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.MediaItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IStreamPushService; 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.GbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.StreamPushMapper; import com.genersoft.iot.vmp.storager.dao.StreamPushMapper;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
@ -32,8 +36,14 @@ public class StreamPushServiceImpl implements IStreamPushService {
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IMediaServerService mediaServerService;
@Override @Override
public List<StreamPushItem> handleJSON(String jsonData) { public List<StreamPushItem> handleJSON(String jsonData, IMediaServerItem mediaServerItem) {
if (jsonData == null) return null; if (jsonData == null) return null;
Map<String, StreamPushItem> result = new HashMap<>(); Map<String, StreamPushItem> result = new HashMap<>();
@ -50,6 +60,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
if (streamPushItem == null) { if (streamPushItem == null) {
streamPushItem = new StreamPushItem(); streamPushItem = new StreamPushItem();
streamPushItem.setApp(item.getApp()); streamPushItem.setApp(item.getApp());
streamPushItem.setMediaServerId(mediaServerItem.getId());
streamPushItem.setStream(item.getStream()); streamPushItem.setStream(item.getStream());
streamPushItem.setAliveSecond(item.getAliveSecond()); streamPushItem.setAliveSecond(item.getAliveSecond());
streamPushItem.setCreateStamp(item.getCreateStamp()); streamPushItem.setCreateStamp(item.getCreateStamp());
@ -87,7 +98,8 @@ public class StreamPushServiceImpl implements IStreamPushService {
@Override @Override
public boolean removeFromGB(GbStream stream) { public boolean removeFromGB(GbStream stream) {
int del = gbStreamMapper.del(stream.getApp(), stream.getStream()); 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) { if (mediaList == null) {
streamPushMapper.del(stream.getApp(), stream.getStream()); streamPushMapper.del(stream.getApp(), stream.getStream());
} }

View File

@ -2,10 +2,11 @@ package com.genersoft.iot.vmp.storager;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo; 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.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; 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.List;
import java.util.Map; import java.util.Map;
@ -40,19 +41,6 @@ public interface IRedisCatchStorage {
StreamInfo queryPlayByDevice(String deviceId, String channelId); StreamInfo queryPlayByDevice(String deviceId, String channelId);
/**
* 更新流媒体信息
* @param ZLMServerConfig
* @return
*/
boolean updateMediaInfo(ZLMServerConfig ZLMServerConfig);
/**
* 获取流媒体信息
* @return
*/
ZLMServerConfig getMediaInfo();
Map<String, StreamInfo> queryPlayByDeviceId(String deviceId); Map<String, StreamInfo> queryPlayByDeviceId(String deviceId);
boolean startPlayback(StreamInfo stream); boolean startPlayback(StreamInfo stream);
@ -114,6 +102,13 @@ public interface IRedisCatchStorage {
*/ */
void clearCatchByDeviceId(String deviceId); void clearCatchByDeviceId(String deviceId);
/**
* 获取mediaServer节点
* @param mediaServerId
* @return
*/
// MediaServerItem getMediaInfo(String mediaServerId);
/** /**
* 设置所有设备离线 * 设置所有设备离线
*/ */

View File

@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.storager;
import java.util.List; import java.util.List;
import com.genersoft.iot.vmp.gb28181.bean.*; 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.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
@ -365,4 +367,12 @@ public interface IVideoManagerStorager {
* @param online * @param online
*/ */
void updateParentPlatformStatus(String platformGbID, boolean online); void updateParentPlatformStatus(String platformGbID, boolean online);
/**
* 更新媒体节点
* @param mediaServerItem
*/
void updateMediaServer(MediaServerItem mediaServerItem);
List<StreamProxyItem> getStreamProxyListForEnableInMediaServer(String id, boolean b);
} }

View File

@ -12,9 +12,10 @@ import java.util.List;
public interface GbStreamMapper { public interface GbStreamMapper {
@Insert("INSERT INTO gb_stream (app, stream, gbId, name, " + @Insert("INSERT INTO gb_stream (app, stream, gbId, name, " +
"longitude, latitude, streamType, status) VALUES" + "longitude, latitude, streamType, mediaServerId, status) VALUES" +
"('${app}', '${stream}', '${gbId}', '${name}', " + "('${app}', '${stream}', '${gbId}', '${name}', " +
"'${longitude}', '${latitude}', '${streamType}', ${status})") "'${longitude}', '${latitude}', '${streamType}', " +
"'${mediaServerId}', ${status})")
int add(GbStream gbStream); int add(GbStream gbStream);
@Update("UPDATE gb_stream " + @Update("UPDATE gb_stream " +
@ -25,6 +26,7 @@ public interface GbStreamMapper {
"streamType=#{streamType}," + "streamType=#{streamType}," +
"longitude=#{longitude}, " + "longitude=#{longitude}, " +
"latitude=#{latitude}," + "latitude=#{latitude}," +
"mediaServerId=#{mediaServerId}," +
"status=${status} " + "status=${status} " +
"WHERE app=#{app} AND stream=#{stream} AND gbId=#{gbId}") "WHERE app=#{app} AND stream=#{stream} AND gbId=#{gbId}")
int update(GbStream gbStream); int update(GbStream gbStream);
@ -52,4 +54,7 @@ public interface GbStreamMapper {
"SET status=${status} " + "SET status=${status} " +
"WHERE app=#{app} AND stream=#{stream}") "WHERE app=#{app} AND stream=#{stream}")
void setStatus(String app, String stream, boolean status); 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<GbStream> selectAllByMediaServerId(String mediaServerId);
} }

View File

@ -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 = {" <script>" +
"UPDATE media_server " +
"SET updateTime='${updateTime}'" +
"<if test=\"ip != null\">, ip='${ip}'</if>" +
"<if test=\"hookIp != null\">, hookIp='${hookIp}'</if>" +
"<if test=\"sdpIp != null\">, sdpIp='${sdpIp}'</if>" +
"<if test=\"streamIp != null\">, streamIp='${streamIp}'</if>" +
"<if test=\"httpPort != null\">, httpPort=${httpPort}</if>" +
"<if test=\"httpSSlPort != null\">, httpSSlPort=${httpSSlPort}</if>" +
"<if test=\"rtmpPort != null\">, rtmpPort=${rtmpPort}</if>" +
"<if test=\"rtmpSSlPort != null\">, rtmpSSlPort=${rtmpSSlPort}</if>" +
"<if test=\"rtpProxyPort != null\">, rtpProxyPort=${rtpProxyPort}</if>" +
"<if test=\"rtspPort != null\">, rtspPort=${rtspPort}</if>" +
"<if test=\"rtspSSLPort != null\">, rtspSSLPort=${rtspSSLPort}</if>" +
"<if test=\"autoConfig != null\">, autoConfig=${autoConfig}</if>" +
"<if test=\"streamNoneReaderDelayMS != null\">, streamNoneReaderDelayMS=${streamNoneReaderDelayMS}</if>" +
"<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
"<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
"<if test=\"secret != null\">, secret='${secret}'</if>" +
"<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
"WHERE id='${id}'"+
" </script>"})
int update(IMediaServerItem mediaServerItem);
@Select("SELECT * FROM media_server WHERE id='${id}'")
MediaServerItem queryOne(String id);
@Select("SELECT * FROM media_server")
List<MediaServerItem> 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);
}

View File

@ -10,10 +10,10 @@ import java.util.List;
@Repository @Repository
public interface StreamProxyMapper { public interface StreamProxyMapper {
@Insert("INSERT INTO stream_proxy (type, app, stream, url, src_url, dst_url, " + @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) VALUES" + "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, createTime) VALUES" +
"('${type}','${app}', '${stream}', '${url}', '${src_url}', '${dst_url}', " + "('${type}','${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " +
"'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable} )") "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, '${createTime}' )")
int add(StreamProxyItem streamProxyDto); int add(StreamProxyItem streamProxyDto);
@Update("UPDATE stream_proxy " + @Update("UPDATE stream_proxy " +
@ -21,6 +21,7 @@ public interface StreamProxyMapper {
"app=#{app}," + "app=#{app}," +
"stream=#{stream}," + "stream=#{stream}," +
"url=#{url}, " + "url=#{url}, " +
"mediaServerId=#{mediaServerId}, " +
"src_url=#{src_url}," + "src_url=#{src_url}," +
"dst_url=#{dst_url}, " + "dst_url=#{dst_url}, " +
"timeout_ms=#{timeout_ms}, " + "timeout_ms=#{timeout_ms}, " +
@ -35,12 +36,17 @@ public interface StreamProxyMapper {
@Delete("DELETE FROM stream_proxy WHERE app=#{app} AND stream=#{stream}") @Delete("DELETE FROM stream_proxy WHERE app=#{app} AND stream=#{stream}")
int del(String app, String 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<StreamProxyItem> selectAll(); List<StreamProxyItem> 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<StreamProxyItem> selectForEnable(boolean enable); List<StreamProxyItem> 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); 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<StreamProxyItem> selectForEnableInMediaServer(String id, boolean enable);
} }

View File

@ -11,14 +11,15 @@ import java.util.List;
public interface StreamPushMapper { public interface StreamPushMapper {
@Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
"createStamp, aliveSecond) VALUES" + "createStamp, aliveSecond, mediaServerId) VALUES" +
"('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " + "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " +
"'${createStamp}', '${aliveSecond}' )") "'${createStamp}', '${aliveSecond}', '${mediaServerId}' )")
int add(StreamPushItem streamPushItem); int add(StreamPushItem streamPushItem);
@Update("UPDATE stream_push " + @Update("UPDATE stream_push " +
"SET app=#{app}," + "SET app=#{app}," +
"stream=#{stream}," + "stream=#{stream}," +
"mediaServerId=#{mediaServerId}," +
"totalReaderCount=#{totalReaderCount}, " + "totalReaderCount=#{totalReaderCount}, " +
"originType=#{originType}," + "originType=#{originType}," +
"originTypeStr=#{originTypeStr}, " + "originTypeStr=#{originTypeStr}, " +
@ -41,10 +42,10 @@ public interface StreamPushMapper {
@Insert("<script>" + @Insert("<script>" +
"INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + "INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
"createStamp, aliveSecond) " + "createStamp, aliveSecond, mediaServerId) " +
"VALUES <foreach collection='streamPushItems' item='item' index='index' >" + "VALUES <foreach collection='streamPushItems' item='item' index='index' >" +
"( '${item.app}', '${item.stream}', '${item.totalReaderCount}', '${item.originType}', " + "( '${item.app}', '${item.stream}', '${item.totalReaderCount}', '${item.originType}', " +
"'${item.originTypeStr}','${item.createStamp}', '${item.aliveSecond}' )" + "'${item.originTypeStr}','${item.createStamp}', '${item.aliveSecond}', '${item.mediaServerId}' )" +
" </foreach>" + " </foreach>" +
"</script>") "</script>")
void addAll(List<StreamPushItem> streamPushItems); void addAll(List<StreamPushItem> streamPushItems);

View File

@ -5,6 +5,8 @@ import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.*; 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.utils.redis.RedisUtil; 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()); 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 @Override
public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) { public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
Map<String, StreamInfo> streamInfos = new HashMap<>(); Map<String, StreamInfo> streamInfos = new HashMap<>();
@ -297,7 +279,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override @Override
public void outlineForAll() { public void outlineForAll() {
List<Object> onlineDevices = redis.scan(String.format("%S*", VideoManagerConstants.KEEPLIVEKEY_PREFIX)); List<Object> onlineDevices = redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX + "*" );
for (int i = 0; i < onlineDevices.size(); i++) { for (int i = 0; i < onlineDevices.size(); i++) {
String key = (String) onlineDevices.get(i); String key = (String) onlineDevices.get(i);
redis.del(key); redis.del(key);
@ -308,4 +290,5 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
public void updateWVPInfo(JSONObject jsonObject) { public void updateWVPInfo(JSONObject jsonObject) {
} }
} }

View File

@ -5,6 +5,7 @@ import java.util.*;
import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 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.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -70,6 +71,9 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
@Autowired @Autowired
private VideoStreamSessionManager streamSession; private VideoStreamSessionManager streamSession;
@Autowired
private MediaServerMapper mediaServerMapper;
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@ -459,6 +463,8 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
boolean result = false; boolean result = false;
streamProxyItem.setStreamType("proxy"); streamProxyItem.setStreamType("proxy");
streamProxyItem.setStatus(true); streamProxyItem.setStatus(true);
String now = this.format.format(new Date(System.currentTimeMillis()));
streamProxyItem.setCreateTime(now);
try { try {
if (gbStreamMapper.add(streamProxyItem)<0 || streamProxyMapper.add(streamProxyItem) < 0) { if (gbStreamMapper.add(streamProxyItem)<0 || streamProxyMapper.add(streamProxyItem) < 0) {
//事务回滚 //事务回滚
@ -467,6 +473,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
result = true; result = true;
dataSourceTransactionManager.commit(transactionStatus); //手动提交 dataSourceTransactionManager.commit(transactionStatus); //手动提交
}catch (Exception e) { }catch (Exception e) {
logger.error("向数据库添加流代理失败:", e);
dataSourceTransactionManager.rollback(transactionStatus); dataSourceTransactionManager.rollback(transactionStatus);
} }
return result; return result;
@ -599,4 +606,21 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
public void updateParentPlatformStatus(String platformGbID, boolean online) { public void updateParentPlatformStatus(String platformGbID, boolean online) {
platformMapper.updateParentPlatformStatus(platformGbID, 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<StreamProxyItem> getStreamProxyListForEnableInMediaServer(String id, boolean enable) {
return streamProxyMapper.selectForEnableInMediaServer(id, enable);
}
} }

View File

@ -25,6 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
@ -78,7 +79,7 @@ public class DeviceConfig {
cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> {
Response response = event.getResponse(); Response response = event.getResponse();
RequestMessage msg = new RequestMessage(); 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())); msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
}); });
@ -87,7 +88,7 @@ public class DeviceConfig {
logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); logger.warn(String.format("设备配置操作超时, 设备未返回应答指令"));
// 释放rtpserver // 释放rtpserver
RequestMessage msg = new RequestMessage(); 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(); JSONObject json = new JSONObject();
json.put("DeviceID", deviceId); json.put("DeviceID", deviceId);
json.put("Status", "Timeout"); json.put("Status", "Timeout");
@ -95,7 +96,7 @@ public class DeviceConfig {
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
resultHolder.invokeResult(msg); 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; return result;
} }
@ -123,7 +124,7 @@ public class DeviceConfig {
cmder.deviceConfigQuery(device, channelId, configType, event -> { cmder.deviceConfigQuery(device, channelId, configType, event -> {
Response response = event.getResponse(); Response response = event.getResponse();
RequestMessage msg = new RequestMessage(); 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())); msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
}); });
@ -132,11 +133,11 @@ public class DeviceConfig {
logger.warn(String.format("获取设备配置超时")); logger.warn(String.format("获取设备配置超时"));
// 释放rtpserver // 释放rtpserver
RequestMessage msg = new RequestMessage(); 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."); msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg); 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; return result;
} }

View File

@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
@ -97,7 +98,7 @@ public class DeviceControl {
cmder.recordCmd(device, channelId, recordCmdStr, event -> { cmder.recordCmd(device, channelId, recordCmdStr, event -> {
Response response = event.getResponse(); Response response = event.getResponse();
RequestMessage msg = new RequestMessage(); 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())); msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
}); });
@ -106,11 +107,11 @@ public class DeviceControl {
logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令"));
// 释放rtpserver // 释放rtpserver
RequestMessage msg = new RequestMessage(); 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."); msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg); 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; return result;
} }
@ -254,7 +255,7 @@ public class DeviceControl {
cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> {
Response response = event.getResponse(); Response response = event.getResponse();
RequestMessage msg = new RequestMessage(); 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())); msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
}); });
@ -263,7 +264,7 @@ public class DeviceControl {
logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令"));
// 释放rtpserver // 释放rtpserver
RequestMessage msg = new RequestMessage(); 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(); JSONObject json = new JSONObject();
json.put("DeviceID", deviceId); json.put("DeviceID", deviceId);
json.put("Status", "Timeout"); json.put("Status", "Timeout");
@ -271,7 +272,7 @@ public class DeviceControl {
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
resultHolder.invokeResult(msg); 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; return result;
} }
} }

View File

@ -43,11 +43,13 @@ public class MediaController {
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class), @ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class),
@ApiImplicitParam(name = "stream", value = "流id", 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") @GetMapping(value = "/stream_info_by_app_and_stream")
@ResponseBody @ResponseBody
public StreamInfo getStreamInfoByAppAndStream(String app, String stream){ public StreamInfo getStreamInfoByAppAndStream(@RequestParam String app, @RequestParam String stream, @RequestParam String mediaServerId){
return mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream);
return mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream,mediaServerId);
} }

View File

@ -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.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
@ -72,6 +75,9 @@ public class PlayController {
@Autowired @Autowired
private IMediaService mediaService; private IMediaService mediaService;
@Autowired
private IMediaServerService mediaServerService;
@ApiOperation("开始点播") @ApiOperation("开始点播")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
@ -81,8 +87,10 @@ public class PlayController {
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
@PathVariable String channelId) { @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(); return playResult.getResult();
} }
@ -102,8 +110,8 @@ public class PlayController {
// 录像查询以channelId作为deviceId查询 // 录像查询以channelId作为deviceId查询
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result); resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result);
Device device = storager.queryVideoDevice(deviceId);
cmder.streamByeCmd(deviceId, channelId, event -> { cmder.streamByeCmd(deviceId, channelId, (event) -> {
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
if (streamInfo == null) { if (streamInfo == null) {
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();
@ -120,6 +128,7 @@ public class PlayController {
msg.setData(String.format("success")); msg.setData(String.format("success"));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
} }
mediaServerService.closeRTPServer(device, channelId);
}); });
if (deviceId != null || channelId != null) { if (deviceId != null || channelId != null) {
@ -165,16 +174,16 @@ public class PlayController {
logger.warn("视频转码API调用失败, 视频流已经停止!"); logger.warn("视频转码API调用失败, 视频流已经停止!");
return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK);
} }
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); IMediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
if (!rtpInfo.getBoolean("exist")) { if (!rtpInfo.getBoolean("exist")) {
logger.warn("视频转码API调用失败, 视频流已停止推流!"); logger.warn("视频转码API调用失败, 视频流已停止推流!");
return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK);
} else { } else {
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(),
streamId ); streamId );
String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), 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()); logger.info(jsonObject.toJSONString());
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
if (jsonObject != null && jsonObject.getInteger("code") == 0) { if (jsonObject != null && jsonObject.getInteger("code") == 0) {
@ -182,7 +191,7 @@ public class PlayController {
JSONObject data = jsonObject.getJSONObject("data"); JSONObject data = jsonObject.getJSONObject("data");
if (data != null) { if (data != null) {
result.put("key", data.getString("key")); result.put("key", data.getString("key"));
StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId); StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId, mediaInfo.getId());
result.put("data", streamInfoResult); result.put("data", streamInfoResult);
} }
}else { }else {
@ -203,25 +212,38 @@ public class PlayController {
@ApiImplicitParam(name = "key", value = "视频流key", dataTypeClass = String.class), @ApiImplicitParam(name = "key", value = "视频流key", dataTypeClass = String.class),
}) })
@PostMapping("/convertStop/{key}") @PostMapping("/convertStop/{key}")
public ResponseEntity<String> playConvertStop(@PathVariable String key) { public ResponseEntity<String> playConvertStop(@PathVariable String key, String mediaServerId) {
JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(key);
logger.info(jsonObject.toJSONString());
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
if (jsonObject != null && jsonObject.getInteger("code") == 0) { if (mediaServerId == null) {
result.put("code", 0); result.put("code", 400);
JSONObject data = jsonObject.getJSONObject("data"); result.put("msg", "mediaServerId is null");
if (data != null && data.getBoolean("flag")) { return new ResponseEntity<String>( result.toJSONString(), HttpStatus.BAD_REQUEST);
result.put("code", "0");
result.put("msg", "success");
}else {
}
}else {
result.put("code", 1);
result.put("msg", "delFFmpegSource fail");
} }
return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK); IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo == null) {
result.put("code", 0);
result.put("msg", "使用的流媒体已经停止运行");
return new ResponseEntity<String>( 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<String>( result.toJSONString(), HttpStatus.OK);
}
} }
@ApiOperation("语音广播命令") @ApiOperation("语音广播命令")
@ -249,7 +271,7 @@ public class PlayController {
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
return result; return result;
} }
cmder.audioBroadcastCmd(device, event -> { cmder.audioBroadcastCmd(device, (event) -> {
Response response = event.getResponse(); Response response = event.getResponse();
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId); msg.setId(DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId);

View File

@ -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.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; //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.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.service.IPlayService; import com.genersoft.iot.vmp.service.IPlayService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@ -87,9 +89,18 @@ public class PlaybackController {
cmder.streamByeCmd(deviceId, channelId); cmder.streamByeCmd(deviceId, channelId);
} }
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); 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()); logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
}, event -> { }, event -> {
Response response = event.getResponse(); Response response = event.getResponse();
RequestMessage msg = new RequestMessage(); RequestMessage msg = new RequestMessage();

View File

@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
@ -104,7 +105,7 @@ public class PtzController {
cmder.presetQuery(device, channelId, event -> { cmder.presetQuery(device, channelId, event -> {
Response response = event.getResponse(); Response response = event.getResponse();
RequestMessage msg = new RequestMessage(); 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())); msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
}); });
@ -113,11 +114,11 @@ public class PtzController {
logger.warn(String.format("获取设备预置位超时")); logger.warn(String.format("获取设备预置位超时"));
// 释放rtpserver // 释放rtpserver
RequestMessage msg = new RequestMessage(); 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("获取设备预置位超时"); msg.setData("获取设备预置位超时");
resultHolder.invokeResult(msg); 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; return result;
} }
} }

View File

@ -2,6 +2,9 @@ package com.genersoft.iot.vmp.vmanager.record;
import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -27,6 +30,8 @@ public class RecoderProxyController {
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired
private IMediaServerService mediaServerService;
@Autowired @Autowired
private MediaConfig mediaConfig; private MediaConfig mediaConfig;
@ -48,7 +53,11 @@ public class RecoderProxyController {
return null; return null;
} }
// 后续改为根据Id获取对应的ZLM // 后续改为根据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", String requestURI = String.format("http://%s:%s%s?%s",
mediaInfo.getSdpIp(), mediaInfo.getSdpIp(),
mediaConfig.getRecordAssistPort(), mediaConfig.getRecordAssistPort(),

View File

@ -2,8 +2,11 @@ package com.genersoft.iot.vmp.vmanager.server;
import com.genersoft.iot.vmp.VManageBootstrap; import com.genersoft.iot.vmp.VManageBootstrap;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; 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.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.SpringBeanFactory; import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import gov.nist.javax.sip.SipStackImpl; import gov.nist.javax.sip.SipStackImpl;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -16,6 +19,7 @@ import javax.sip.ObjectInUseException;
import javax.sip.SipProvider; import javax.sip.SipProvider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Api(tags = "服务控制") @Api(tags = "服务控制")
@ -28,17 +32,28 @@ public class ServerController {
private ConfigurableApplicationContext context; private ConfigurableApplicationContext context;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IMediaServerService mediaServerService;
@ApiOperation("流媒体服务列表") @ApiOperation("流媒体服务列表")
@GetMapping(value = "/media_server/list") @GetMapping(value = "/media_server/list")
@ResponseBody @ResponseBody
public Object getMediaServerList(){ public WVPResult<List<IMediaServerItem>> getMediaServerList(){
// TODO 为后续多个zlm支持准备 WVPResult<List<IMediaServerItem>> result = new WVPResult<>();
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); result.setCode(0);
ArrayList<ZLMServerConfig> result = new ArrayList<>(); result.setMsg("success");
result.add(mediaInfo); result.setData(mediaServerService.getAll());
return result;
}
@ApiOperation("获取流媒体服务")
@GetMapping(value = "/media_server/one/{id}")
@ResponseBody
public WVPResult<IMediaServerItem> getMediaServer(@PathVariable String id){
WVPResult<IMediaServerItem> result = new WVPResult<>();
result.setCode(0);
result.setMsg("success");
result.setData(mediaServerService.getOne(id));
return result; return result;
} }

View File

@ -1,11 +1,15 @@
package com.genersoft.iot.vmp.vmanager.streamProxy; package com.genersoft.iot.vmp.vmanager.streamProxy;
import com.alibaba.fastjson.JSONObject; 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.StreamProxyItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import io.netty.util.internal.StringUtil;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiImplicitParams;
@ -14,6 +18,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -31,6 +36,10 @@ public class StreamProxyController {
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired
private IMediaServerService mediaServerService;
@Autowired @Autowired
private IStreamProxyService streamProxyService; private IStreamProxyService streamProxyService;
@ -60,6 +69,7 @@ public class StreamProxyController {
@ResponseBody @ResponseBody
public WVPResult save(@RequestBody StreamProxyItem param){ public WVPResult save(@RequestBody StreamProxyItem param){
logger.info("添加代理: " + JSONObject.toJSONString(param)); logger.info("添加代理: " + JSONObject.toJSONString(param));
if (StringUtils.isEmpty(param.getMediaServerId())) param.setMediaServerId("auto");
String msg = streamProxyService.save(param); String msg = streamProxyService.save(param);
WVPResult<Object> result = new WVPResult<>(); WVPResult<Object> result = new WVPResult<>();
result.setCode(0); result.setCode(0);
@ -69,10 +79,15 @@ public class StreamProxyController {
@ApiOperation("获取ffmpeg.cmd模板") @ApiOperation("获取ffmpeg.cmd模板")
@GetMapping(value = "/ffmpeg_cmd/list") @GetMapping(value = "/ffmpeg_cmd/list")
@ApiImplicitParams({
@ApiImplicitParam(name = "mediaServerId", value = "流媒体ID", dataTypeClass = String.class),
})
@ResponseBody @ResponseBody
public WVPResult getFFmpegCMDs(){ public WVPResult getFFmpegCMDs(@RequestParam String mediaServerId){
logger.debug("获取ffmpeg.cmd模板" ); logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId );
JSONObject data = streamProxyService.getFFmpegCMDs();
IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
JSONObject data = streamProxyService.getFFmpegCMDs(mediaServerItem);
WVPResult<JSONObject> result = new WVPResult<>(); WVPResult<JSONObject> result = new WVPResult<>();
result.setCode(0); result.setCode(0);
result.setMsg("success"); result.setMsg("success");
@ -82,12 +97,12 @@ public class StreamProxyController {
@ApiOperation("移除代理") @ApiOperation("移除代理")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class), @ApiImplicitParam(name = "app", value = "应用名", required = true, dataTypeClass = String.class),
@ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class), @ApiImplicitParam(name = "stream", value = "流ID", required = true, dataTypeClass = String.class),
}) })
@DeleteMapping(value = "/del") @DeleteMapping(value = "/del")
@ResponseBody @ResponseBody
public WVPResult del(String app, String stream){ public WVPResult del(@RequestParam String app, @RequestParam String stream){
logger.info("移除代理: " + app + "/" + stream); logger.info("移除代理: " + app + "/" + stream);
WVPResult<Object> result = new WVPResult<>(); WVPResult<Object> result = new WVPResult<>();
if (app == null || stream == null) { if (app == null || stream == null) {

View File

@ -10,7 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
* 兼容LiveGBS的API设备控制 * API兼容设备控制
*/ */
@CrossOrigin @CrossOrigin
@RestController @RestController

View File

@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
/** /**
* 兼容LiveGBS的API系统接口 * API兼容系统接口
*/ */
@Controller @Controller
@CrossOrigin @CrossOrigin

View File

@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
/** /**
* 兼容LiveGBS的API设备信息 * API兼容设备信息
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@CrossOrigin @CrossOrigin

View File

@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
/** /**
* 兼容LiveGBS的API实时直播 * API兼容实时直播
*/ */
@SuppressWarnings(value = {"rawtypes", "unchecked"}) @SuppressWarnings(value = {"rawtypes", "unchecked"})
@CrossOrigin @CrossOrigin

View File

@ -70,9 +70,13 @@ sip:
keepalive-timeout: 180 keepalive-timeout: 180
# [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒 # [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒
register-time-interval: 60 register-time-interval: 60
# TODO [可选] 收到心跳后自动上线, 重启服务后会将所有设备置为离线默认false等待注册后上线。设置为true则收到心跳设置为上线。
# keepalliveToOnline: false
#zlm 默认服务器配置 #zlm 默认服务器配置
media: media:
# [可选] zlm服务器唯一id用于触发hook时区别是哪台服务器,general.mediaServerId
id:
# [必须修改] zlm服务器的内网IP # [必须修改] zlm服务器的内网IP
ip: 192.168.0.100 ip: 192.168.0.100
# [可选] 返回流地址时的ip置空使用 media.ip # [可选] 返回流地址时的ip置空使用 media.ip

Binary file not shown.

View File

@ -7,22 +7,23 @@
<el-main> <el-main>
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;"> <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;">
<span style="font-size: 1rem; font-weight: bold;">云端录像</span> <span style="font-size: 1rem; font-weight: bold;">云端录像</span>
<div style="position: absolute; right: 5rem; top: 0.3rem;">
节点选择: <el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServer" placeholder="请选择" default-first-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id + '( ' + item.streamIp + ' )'"
:value="item">
</el-option>
</el-select>
</div>
<div style="position: absolute; right: 1rem; top: 0.3rem;"> <div style="position: absolute; right: 1rem; top: 0.3rem;">
<el-button v-if="!recordDetail" icon="el-icon-refresh-right" circle size="mini" :loading="loading" @click="getRecordList()"></el-button> <el-button v-if="!recordDetail" icon="el-icon-refresh-right" circle size="mini" :loading="loading" @click="getRecordList()"></el-button>
<el-button v-if="recordDetail" icon="el-icon-arrow-left" circle size="mini" @click="backToList()"></el-button> <el-button v-if="recordDetail" icon="el-icon-arrow-left" circle size="mini" @click="backToList()"></el-button>
</div> </div>
</div> </div>
<div v-if="!recordDetail"> <div v-if="!recordDetail">
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;">
节点选择: <el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServer" placeholder="请选择" default-first-option>
<el-option
v-for="item in mediaServerList"
:key="item.generalMediaServerId"
:label="item.generalMediaServerId + '( ' + item.wanIp + ' )'"
:value="item">
</el-option>
</el-select>
</div>
<!--设备列表--> <!--设备列表-->
<el-table :data="recordList" border style="width: 100%" :height="winHeight"> <el-table :data="recordList" border style="width: 100%" :height="winHeight">
<el-table-column prop="app" label="应用名" align="center"> <el-table-column prop="app" label="应用名" align="center">
@ -60,6 +61,7 @@
<script> <script>
import uiHeader from './UiHeader.vue' import uiHeader from './UiHeader.vue'
import cloudRecordDetail from './CloudRecordDetail.vue' import cloudRecordDetail from './CloudRecordDetail.vue'
import MediaServer from './service/MediaServer'
export default { export default {
name: 'app', name: 'app',
components: { components: {
@ -78,6 +80,7 @@
count:15, count:15,
total:0, total:0,
loading: false, loading: false,
mediaServerObj : new MediaServer(),
recordDetail: false recordDetail: false
}; };
@ -107,20 +110,13 @@
}, },
getMediaServerList: function (){ getMediaServerList: function (){
let that = this; let that = this;
this.$axios({ that.mediaServerObj.getMediaServerList((data)=>{
method: 'get', that.mediaServerList = data;
url:`/api/server/media_server/list`,
}).then(function (res) {
console.log(res)
that.mediaServerList = res.data;
if (that.mediaServerList.length > 0) { if (that.mediaServerList.length > 0) {
that.mediaServer = that.mediaServerList[0] that.mediaServer = that.mediaServerList[0]
that.getRecordList(); that.getRecordList();
} }
})
}).catch(function (error) {
console.log(error);
});
}, },
getRecordList: function (){ getRecordList: function (){
let that = this; let that = this;

View File

@ -17,6 +17,8 @@
</el-table-column> </el-table-column>
<el-table-column prop="gbId" label="国标编码" width="150" align="center"> <el-table-column prop="gbId" label="国标编码" width="150" align="center">
</el-table-column> </el-table-column>
<el-table-column prop="mediaServerId" label="流媒体" width="150" align="center">
</el-table-column>
<el-table-column label="开始时间" align="center" > <el-table-column label="开始时间" align="center" >
<template slot-scope="scope"> <template slot-scope="scope">
<el-button-group> <el-button-group>
@ -29,7 +31,7 @@
{{(scope.row.status == false && scope.row.gbId == null) || scope.row.status ?'是':'否'}} {{(scope.row.status == false && scope.row.gbId == null) || scope.row.status ?'是':'否'}}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="360" align="center" fixed="right"> <el-table-column label="操作" width="360" align="center" fixed="right">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button-group> <el-button-group>
@ -125,7 +127,7 @@
that.getDeviceListLoading = false; that.getDeviceListLoading = false;
}); });
}, },
playPuhsh: function(row){ playPuhsh: function(row){
let that = this; let that = this;
this.getListLoading = true; this.getListLoading = true;
@ -134,7 +136,8 @@
url:`/api/media/stream_info_by_app_and_stream`, url:`/api/media/stream_info_by_app_and_stream`,
params: { params: {
app: row.app, app: row.app,
stream: row.stream stream: row.stream,
mediaServerId: row.mediaServerId
} }
}).then(function (res) { }).then(function (res) {
that.getListLoading = false; that.getListLoading = false;

View File

@ -32,6 +32,15 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="mediaServerId" label="流媒体" width="150" align="center"></el-table-column>
<el-table-column label="类型" width="100" align="center">
<template slot-scope="scope">
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{scope.row.type}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="gbId" label="国标编码" width="180" align="center" show-overflow-tooltip/> <el-table-column prop="gbId" label="国标编码" width="180" align="center" show-overflow-tooltip/>
<el-table-column label="启用" width="120" align="center"> <el-table-column label="启用" width="120" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
@ -147,8 +156,6 @@
count: that.count count: that.count
} }
}).then(function (res) { }).then(function (res) {
console.log(res);
console.log(res.data.list);
that.total = res.data.total; that.total = res.data.total;
that.streamProxyList = res.data.list; that.streamProxyList = res.data.list;
that.getListLoading = false; that.getListLoading = false;
@ -170,7 +177,6 @@
this.getListLoading = false; this.getListLoading = false;
if (res.data.code == 0 ){ if (res.data.code == 0 ){
if (res.data.data.length > 0) { if (res.data.data.length > 0) {
console.log(res.data.data)
this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{ this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{
if (url != null) { if (url != null) {
this.$refs.onvifEdit.close(); this.$refs.onvifEdit.close();
@ -200,7 +206,8 @@
url:`/api/media/stream_info_by_app_and_stream`, url:`/api/media/stream_info_by_app_and_stream`,
params: { params: {
app: row.app, app: row.app,
stream: row.stream stream: row.stream,
mediaServerId: row.mediaServerId
} }
}).then(function (res) { }).then(function (res) {
that.getListLoading = false; that.getListLoading = false;

View File

@ -7,6 +7,17 @@
<el-main> <el-main>
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;"> <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;">
<span style="font-size: 1rem; font-weight: bold;">控制台</span> <span style="font-size: 1rem; font-weight: bold;">控制台</span>
<div style="position: absolute; right: 17rem; top: 0.3rem;">
节点选择: <el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServerChoose" placeholder="请选择" default-first-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id + '( ' + item.streamIp + ' )'"
:value="item.id">
</el-option>
</el-select>
<span >{{loadCount}}</span>
</div>
<div style="position: absolute; right: 1rem; top: 0.3rem;"> <div style="position: absolute; right: 1rem; top: 0.3rem;">
<el-popover placement="bottom" width="750" height="300" trigger="click"> <el-popover placement="bottom" width="750" height="300" trigger="click">
<div style="height: 600px;overflow:auto;"> <div style="height: 600px;overflow:auto;">
@ -53,6 +64,7 @@
<script> <script>
import uiHeader from './UiHeader.vue' import uiHeader from './UiHeader.vue'
import MediaServer from './service/MediaServer'
import echarts from 'echarts'; import echarts from 'echarts';
export default { export default {
@ -87,68 +99,101 @@ export default {
chartInterval: 0, // chartInterval: 0, //
allSessionData: [], allSessionData: [],
visible: false, visible: false,
serverConfig: {} serverConfig: {},
mediaServer : new MediaServer(),
mediaServerChoose : null,
loadCount : 0,
mediaServerList : []
}; };
}, },
mounted() { mounted() {
this.getAllSession();
this.initTable(); this.initTable();
this.updateData(); this.updateData();
this.chartInterval = setInterval(this.updateData, 3000); this.chartInterval = setInterval(this.updateData, 3000);
this.mediaServer.getMediaServerList((data)=>{
this.mediaServerList = data.data;
if (this.mediaServerList && this.mediaServerList.length > 0) {
this.mediaServerChoose = this.mediaServerList[0].id
this.loadCount = this.mediaServerList[0].count;
this.getThreadsLoad();
this.getAllSession();
}
})
}, },
destroyed() { destroyed() {
clearInterval(this.chartInterval); // clearInterval(this.chartInterval); //
}, },
methods: { methods: {
chooseMediaChange: function (val) {
this.loadCount = 0
this.initTable()
this.updateData();
},
updateData: function () { updateData: function () {
this.getThreadsLoad(); this.getThreadsLoad();
this.getLoadCount();
this.getAllSession();
}, },
/** /**
* 获取线程状态 * 获取线程状态
*/ */
getThreadsLoad: function () { getThreadsLoad: function () {
let that = this; let that = this;
this.$axios({ if (that.mediaServerChoose != null) {
this.$axios({
method: 'get', method: 'get',
url: '/zlm/index/api/getThreadsLoad' url: '/zlm/' + that.mediaServerChoose +'/index/api/getThreadsLoad'
}).then(function (res) { }).then(function (res) {
if (res.data.code == 0) { if (res.data.code == 0) {
that.tableOption.xAxis.data.push(new Date().toLocaleTimeString('chinese', { that.tableOption.xAxis.data.push(new Date().toLocaleTimeString('chinese', {
hour12: false hour12: false
})); }));
that.table1Option.xAxis.data.push(new Date().toLocaleTimeString('chinese', { that.table1Option.xAxis.data.push(new Date().toLocaleTimeString('chinese', {
hour12: false hour12: false
})); }));
for (var i = 0; i < res.data.data.length; i++) { for (var i = 0; i < res.data.data.length; i++) {
if (that.tableOption.series[i] === undefined) { if (that.tableOption.series[i] === undefined) {
let data = { let data = {
data: [], data: [],
type: 'line' type: 'line'
}; };
let data1 = { let data1 = {
data: [], data: [],
type: 'line' type: 'line'
}; };
data.data.push(res.data.data[i].delay); data.data.push(res.data.data[i].delay);
data1.data.push(res.data.data[i].load); data1.data.push(res.data.data[i].load);
that.tableOption.series.push(data); that.tableOption.series.push(data);
that.table1Option.series.push(data1); that.table1Option.series.push(data1);
} else { } else {
that.tableOption.series[i].data.push(res.data.data[i].delay); that.tableOption.series[i].data.push(res.data.data[i].delay);
that.table1Option.series[i].data.push(res.data.data[i].load); that.table1Option.series[i].data.push(res.data.data[i].load);
}
} }
that.tableOption.dataZoom[0].start = that.charZoomStart; }
that.tableOption.dataZoom[0].end = that.charZoomEnd; that.tableOption.dataZoom[0].start = that.charZoomStart;
that.table1Option.dataZoom[0].start = that.charZoomStart; that.tableOption.dataZoom[0].end = that.charZoomEnd;
that.table1Option.dataZoom[0].end = that.charZoomEnd; that.table1Option.dataZoom[0].start = that.charZoomStart;
//that.myChart = echarts.init(document.getElementById('ThreadsLoad')); that.table1Option.dataZoom[0].end = that.charZoomEnd;
that.myChart.setOption(that.tableOption, true); //that.myChart = echarts.init(document.getElementById('ThreadsLoad'));
// that.myChart1 = echarts.init(document.getElementById('WorkThreadsLoad')); that.myChart.setOption(that.tableOption, true);
that.myChart1.setOption(that.table1Option, true); // that.myChart1 = echarts.init(document.getElementById('WorkThreadsLoad'));
that.myChart1.setOption(that.table1Option, true);
} }
}); });
}
},
getLoadCount: function (){
let that = this;
if (that.mediaServerChoose != null) {
that.mediaServer.getMediaServer(that.mediaServerChoose, (data)=>{
if (data.code == 0) {
that.loadCount = data.data.count
}
})
}
}, },
initTable: function () { initTable: function () {
let that = this; let that = this;
@ -242,10 +287,9 @@ export default {
getAllSession: function () { getAllSession: function () {
let that = this; let that = this;
that.allSessionData = []; that.allSessionData = [];
console.log("地址:" + '/zlm/index/api/getAllSession');
this.$axios({ this.$axios({
method: 'get', method: 'get',
url: '/zlm/index/api/getAllSession' url: '/zlm/' + that.mediaServerChoose +'/index/api/getAllSession'
}).then(function (res) { }).then(function (res) {
res.data.data.forEach(item => { res.data.data.forEach(item => {
let data = { let data = {

View File

@ -39,6 +39,22 @@
<el-form-item label="超时时间:毫秒" prop="timeout_ms" v-if="proxyParam.type=='ffmpeg'"> <el-form-item label="超时时间:毫秒" prop="timeout_ms" v-if="proxyParam.type=='ffmpeg'">
<el-input v-model="proxyParam.timeout_ms" clearable></el-input> <el-input v-model="proxyParam.timeout_ms" clearable></el-input>
</el-form-item> </el-form-item>
<el-form-item label="节点选择" prop="rtp_type">
<el-select
v-model="proxyParam.mediaServerId"
@change="mediaServerIdChange"
style="width: 100%"
placeholder="请选择拉流节点"
>
<el-option label="自动选择" value="auto"></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="FFmpeg命令模板" prop="ffmpeg_cmd_key" v-if="proxyParam.type=='ffmpeg'"> <el-form-item label="FFmpeg命令模板" prop="ffmpeg_cmd_key" v-if="proxyParam.type=='ffmpeg'">
<!-- <el-input v-model="proxyParam.ffmpeg_cmd_key" clearable></el-input>--> <!-- <el-input v-model="proxyParam.ffmpeg_cmd_key" clearable></el-input>-->
<el-select <el-select
@ -68,6 +84,7 @@
<el-option label="组播" value="2"></el-option> <el-option label="组播" value="2"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="国标平台"> <el-form-item label="国标平台">
<el-select <el-select
v-model="proxyParam.platformGbId" v-model="proxyParam.platformGbId"
@ -106,6 +123,8 @@
</template> </template>
<script> <script>
import MediaServer from './../service/MediaServer'
export default { export default {
name: "streamProxyEdit", name: "streamProxyEdit",
props: {}, props: {},
@ -134,27 +153,8 @@ export default {
isLoging: false, isLoging: false,
dialogLoading: false, dialogLoading: false,
onSubmit_text: "立即创建", onSubmit_text: "立即创建",
platformList: [{ platformList: [],
id: 1, mediaServer: new MediaServer(),
enable: true,
name: "141",
serverGBId: "34020000002000000001",
serverGBDomain: "3402000000",
serverIP: "192.168.1.141",
serverPort: 15060,
deviceGBId: "34020000002000000001",
deviceIp: "192.168.1.20",
devicePort: "5060",
username: "34020000002000000001",
password: "12345678",
expires: "300",
keepTimeout: "60",
transport: "UDP",
characterSet: "GB2312",
ptz: false,
rtcp: false,
status: true,
}],
proxyParam: { proxyParam: {
name: null, name: null,
type: "default", type: "default",
@ -170,7 +170,9 @@ export default {
enable_hls: true, enable_hls: true,
enable_mp4: false, enable_mp4: false,
platformGbId: null, platformGbId: null,
mediaServerId: "auto",
}, },
mediaServerList:{},
ffmpegCmdList:{}, ffmpegCmdList:{},
rules: { rules: {
@ -193,7 +195,6 @@ export default {
} }
let that = this; let that = this;
this.$axios({ this.$axios({
method: 'get', method: 'get',
url:`/api/platform/query/10000/0` url:`/api/platform/query/10000/0`
@ -202,17 +203,28 @@ export default {
}).catch(function (error) { }).catch(function (error) {
console.log(error); console.log(error);
}); });
this.$axios({ this.mediaServer.getMediaServerList((data)=>{
method: 'get', this.mediaServerList = data;
url:`/api/proxy/ffmpeg_cmd/list` })
}).then(function (res) { },
that.ffmpegCmdList = res.data.data; mediaServerIdChange:function (){
}).catch(function (error) { let that = this;
console.log(error); if (that.proxyParam.mediaServerId !== "auto"){
}); that.$axios({
method: 'get',
url:`/api/proxy/ffmpeg_cmd/list`,
params: {
mediaServerId: that.proxyParam.mediaServerId
}
}).then(function (res) {
that.ffmpegCmdList = res.data.data;
}).catch(function (error) {
console.log(error);
});
}
}, },
onSubmit: function () { onSubmit: function () {
console.log("onSubmit");
this.dialogLoading = true; this.dialogLoading = true;
var that = this; var that = this;
that.$axios({ that.$axios({
@ -239,7 +251,6 @@ export default {
}); });
}, },
close: function () { close: function () {
console.log("关闭添加视频平台");
this.showDialog = false; this.showDialog = false;
this.dialogLoading = false; this.dialogLoading = false;
this.$refs.streamProxy.resetFields(); this.$refs.streamProxy.resetFields();

View File

@ -181,6 +181,7 @@ export default {
showVideoDialog: false, showVideoDialog: false,
streamId: '', streamId: '',
app : '', app : '',
mediaServerId : '',
convertKey: '', convertKey: '',
deviceId: '', deviceId: '',
channelId: '', channelId: '',
@ -218,7 +219,7 @@ export default {
if (tab.name == "codec") { if (tab.name == "codec") {
this.$axios({ this.$axios({
method: 'get', method: 'get',
url: '/zlm/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId
}).then(function (res) { }).then(function (res) {
that.tracksLoading = false; that.tracksLoading = false;
if (res.data.code == 0 && res.data.online) { if (res.data.code == 0 && res.data.online) {
@ -235,12 +236,11 @@ export default {
} }
}, },
openDialog: function (tab, deviceId, channelId, param) { openDialog: function (tab, deviceId, channelId, param) {
console.log("openDialog")
console.log(param)
this.tabActiveName = tab; this.tabActiveName = tab;
this.channelId = channelId; this.channelId = channelId;
this.deviceId = deviceId; this.deviceId = deviceId;
this.streamId = ""; this.streamId = "";
this.mediaServerId = "";
this.app = ""; this.app = "";
this.videoUrl = "" this.videoUrl = ""
if (!!this.$refs.videoPlayer) { if (!!this.$refs.videoPlayer) {
@ -257,8 +257,8 @@ export default {
break; break;
case "streamPlay": case "streamPlay":
this.tabActiveName = "media"; this.tabActiveName = "media";
this.showRrecord = false, this.showRrecord = false;
this.showPtz = false, this.showPtz = false;
this.play(param.streamInfo, param.hasAudio) this.play(param.streamInfo, param.hasAudio)
break; break;
case "control": case "control":
@ -269,19 +269,17 @@ export default {
console.log(val) console.log(val)
}, },
play: function (streamInfo, hasAudio) { play: function (streamInfo, hasAudio) {
this.hasAudio = hasAudio; this.hasAudio = hasAudio;
this.isLoging = false; this.isLoging = false;
// this.videoUrl = streamInfo.rtc; // this.videoUrl = streamInfo.rtc;
this.videoUrl = this.getUrlByStreamInfo(streamInfo); this.videoUrl = this.getUrlByStreamInfo(streamInfo);
this.streamId = streamInfo.streamId; this.streamId = streamInfo.streamId;
this.app = streamInfo.app; this.app = streamInfo.app;
this.mediaServerId = streamInfo.mediaServerId;
this.playFromStreamInfo(false, streamInfo) this.playFromStreamInfo(false, streamInfo)
}, },
getUrlByStreamInfo(streamInfo){ getUrlByStreamInfo(streamInfo){
let baseZlmApi = process.env.NODE_ENV === 'development'?`${location.host}/debug/zlm`:`${location.host}/zlm` let baseZlmApi = process.env.NODE_ENV === 'development'?`${location.host}/debug/zlm`:`${location.host}/zlm`
console.log(12121212)
console.log(baseZlmApi)
// return `${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`; // return `${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;
// return `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`; // return `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;
return streamInfo.ws_flv; return streamInfo.ws_flv;
@ -430,6 +428,7 @@ export default {
var streamInfo = res.data; var streamInfo = res.data;
that.app = streamInfo.app; that.app = streamInfo.app;
that.streamId = streamInfo.streamId; that.streamId = streamInfo.streamId;
that.mediaServerId = streamInfo.mediaServerId;
that.videoUrl = that.getUrlByStreamInfo(streamInfo); that.videoUrl = that.getUrlByStreamInfo(streamInfo);
that.recordPlay = true; that.recordPlay = true;
}); });

View File

@ -0,0 +1,32 @@
import axios from 'axios';
class MediaServer{
constructor() {
this.$axios = axios;
}
getMediaServerList(callback){
this.$axios({
method: 'get',
url:`/api/server/media_server/list`,
}).then(function (res) {
if (typeof (callback) == "function") callback(res.data)
}).catch(function (error) {
console.log(error);
});
}
getMediaServer(id, callback){
this.$axios({
method: 'get',
url:`/api/server/media_server/one/` + id,
}).then(function (res) {
if (typeof (callback) == "function") callback(res.data)
}).catch(function (error) {
console.log(error);
});
}
}
export default MediaServer;