添加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>
</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协议栈 -->
<dependency>
<groupId>be.teletask</groupId>

View File

@ -138,6 +138,7 @@ create table stream_proxy
timeout_ms int null,
ffmpeg_cmd_key varchar(255) null,
rtp_type varchar(50) null,
mediaServerId varchar(50) null,
enable_hls bit(1) null,
enable_mp4 bit(1) null,
enable bit(1) not null,
@ -167,3 +168,27 @@ create table user
);
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 rtsp;
private String rtc;
private String mediaServerId;
private JSONArray tracks;
public static class TransactionInfo{
@ -165,4 +166,12 @@ public class StreamInfo {
public void setTransactionInfo(TransactionInfo transactionInfo) {
this.transactionInfo = transactionInfo;
}
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
}

View File

@ -10,33 +10,31 @@ public class VideoManagerConstants {
public static final String WVP_SERVER_PREFIX = "VMP_wvp_server";
public static final String MEDIA_SERVER_PREFIX = "VMP_media_server";
public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream";
public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM";
public static final String DEVICE_PREFIX = "VMP_device_";
public static final String DEVICE_PREFIX = "VMP_DEVICE_";
public static final String CACHEKEY_PREFIX = "VMP_channel_";
public static final String CACHEKEY_PREFIX = "VMP_CHANNEL_";
public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_";
public static final String PLAYER_PREFIX = "VMP_player_";
public static final String PLAYER_PREFIX = "VMP_PLAYER_";
public static final String PLAY_BLACK_PREFIX = "VMP_playback_";
public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
public static final String PLATFORM_PREFIX = "VMP_platform";
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_";
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_platform_keeplive_";
public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_";
public static final String PLATFORM_CATCH_PREFIX = "VMP_platform_catch_";
public static final String PLATFORM_REGISTER_PREFIX = "VMP_PLATFORM_REGISTER_";
public static final String PLATFORM_REGISTER_PREFIX = "VMP_platform_register_";
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_";
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_platform_register_info_";
public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_";
public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_platform_send_rtp_info_";
public static final String Pattern_Topic = "VMP_keeplive_platform_";
public static final String Pattern_Topic = "VMP_KEEPLIVE_PLATFORM_";
public static final String EVENT_ONLINE_REGISTER = "1";

View File

@ -1,11 +1,16 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
@Configuration("mediaConfig")
public class MediaConfig {
public class MediaConfig implements IMediaServerItem {
@Value("${media.id:}")
private String id;
@Value("${media.ip}")
private String ip;
@ -25,22 +30,22 @@ public class MediaConfig {
@Value("${media.http-port}")
private Integer httpPort;
@Value("${media.http-ssl-port:}")
@Value("${media.http-ssl-port:0}")
private Integer httpSSlPort;
@Value("${media.rtmp-port:}")
@Value("${media.rtmp-port:0}")
private Integer rtmpPort;
@Value("${media.rtmp-ssl-port:}")
@Value("${media.rtmp-ssl-port:0}")
private Integer rtmpSSlPort;
@Value("${media.rtp-proxy-port:}")
@Value("${media.rtp-proxy-port:0}")
private Integer rtpProxyPort;
@Value("${media.rtsp-port:}")
@Value("${media.rtsp-port:0}")
private Integer rtspPort;
@Value("${media.rtsp-ssl-port:}")
@Value("${media.rtsp-ssl-port:0}")
private Integer rtspSSLPort;
@Value("${media.auto-config:true}")
@ -61,6 +66,23 @@ public class MediaConfig {
@Value("${media.record-assist-port:0}")
private Integer recordAssistPort;
private String updateTime;
private String createTime;
private boolean docker = false;
private int count;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getIp() {
return ip;
}
@ -82,82 +104,114 @@ public class MediaConfig {
this.hookIp = hookIp;
}
public String getSdpIp() {
if (StringUtils.isEmpty(sdpIp)){
return ip;
}else {
return sdpIp;
public String getSipIp() {
return sipIp;
}
public void setSipIp(String sipIp) {
this.sipIp = sipIp;
}
public void setSdpIp(String sdpIp) {
this.sdpIp = sdpIp;
}
public String getStreamIp() {
if (StringUtils.isEmpty(streamIp)){
return ip;
}else {
return streamIp;
}
}
public void setStreamIp(String streamIp) {
this.streamIp = streamIp;
}
public Integer getHttpPort() {
public int getHttpPort() {
return httpPort;
}
@Override
public void setHttpPort(int httpPort) {
}
public void setHttpPort(Integer httpPort) {
this.httpPort = httpPort;
}
public Integer getHttpSSlPort() {
public int getHttpSSlPort() {
return httpSSlPort;
}
@Override
public void setHttpSSlPort(int httpSSlPort) {
}
public void setHttpSSlPort(Integer httpSSlPort) {
this.httpSSlPort = httpSSlPort;
}
public Integer getRtmpPort() {
public int getRtmpPort() {
return rtmpPort;
}
@Override
public void setRtmpPort(int rtmpPort) {
}
public void setRtmpPort(Integer rtmpPort) {
this.rtmpPort = rtmpPort;
}
public Integer getRtmpSSlPort() {
public int getRtmpSSlPort() {
return rtmpSSlPort;
}
@Override
public void setRtmpSSlPort(int rtmpSSlPort) {
}
public void setRtmpSSlPort(Integer rtmpSSlPort) {
this.rtmpSSlPort = rtmpSSlPort;
}
public Integer getRtpProxyPort() {
public int getRtpProxyPort() {
if (rtpProxyPort == null) {
return 0;
}else {
return rtpProxyPort;
}
}
@Override
public void setRtpProxyPort(int rtpProxyPort) {
}
public void setRtpProxyPort(Integer rtpProxyPort) {
this.rtpProxyPort = rtpProxyPort;
}
public Integer getRtspPort() {
public int getRtspPort() {
return rtspPort;
}
@Override
public void setRtspPort(int rtspPort) {
}
public void setRtspPort(Integer rtspPort) {
this.rtspPort = rtspPort;
}
public Integer getRtspSSLPort() {
public int getRtspSSLPort() {
return rtspSSLPort;
}
@Override
public void setRtspSSLPort(int rtspSSLPort) {
}
public void setRtspSSLPort(Integer rtspSSLPort) {
this.rtspSSLPort = rtspSSLPort;
}
@ -202,11 +256,101 @@ public class MediaConfig {
this.rtpPortRange = rtpPortRange;
}
public Integer getRecordAssistPort() {
public int getRecordAssistPort() {
return recordAssistPort;
}
@Override
public void setRecordAssistPort(int recordAssistPort) {
}
public void setRecordAssistPort(Integer recordAssistPort) {
this.recordAssistPort = recordAssistPort;
}
@Override
public boolean isDocker() {
return docker;
}
@Override
public void setDocker(boolean docker) {
this.docker = docker;
}
public String getSdpIp() {
if (StringUtils.isEmpty(sdpIp)){
return ip;
}else {
return sdpIp;
}
}
public String getStreamIp() {
if (StringUtils.isEmpty(streamIp)){
return ip;
}else {
return streamIp;
}
}
public MediaServerItem getMediaSerItem(){
MediaServerItem mediaServerItem = new MediaServerItem();
mediaServerItem.setId(id);
mediaServerItem.setIp(ip);
mediaServerItem.setDocker(true);
mediaServerItem.setHookIp(hookIp);
mediaServerItem.setSdpIp(sdpIp);
mediaServerItem.setStreamIp(streamIp);
mediaServerItem.setHttpPort(httpPort);
mediaServerItem.setHttpSSlPort(httpSSlPort);
mediaServerItem.setRtmpPort(rtmpPort);
mediaServerItem.setRtmpSSlPort(rtmpSSlPort);
mediaServerItem.setRtpProxyPort(rtpProxyPort);
mediaServerItem.setRtspPort(rtspPort);
mediaServerItem.setRtspSSLPort(rtspSSLPort);
mediaServerItem.setAutoConfig(autoConfig);
mediaServerItem.setSecret(secret);
mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS);
mediaServerItem.setRtpEnable(rtpEnable);
mediaServerItem.setRtpPortRange(rtpPortRange);
mediaServerItem.setRecordAssistPort(recordAssistPort);
mediaServerItem.setCreateTime(createTime);
mediaServerItem.setUpdateTime(updateTime);
mediaServerItem.setCount(count);
return mediaServerItem;
}
@Override
public String getUpdateTime() {
return updateTime;
}
@Override
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
@Override
public String getCreateTime() {
return createTime;
}
@Override
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
@Override
public int getCount() {
return count;
}
@Override
public void setCount(int count) {
this.count = count;
}
}

View File

@ -1,12 +1,16 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.catalina.connector.ClientAbortException;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -24,13 +28,16 @@ public class ProxyServletConfig {
private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);
@Autowired
private MediaConfig mediaConfig;
private IMediaServerService mediaServerService;
@Value("${server.port}")
private int serverPort;
@Bean
public ServletRegistrationBean zlmServletRegistrationBean(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZLMProxySerlet(),"/zlm/*");
servletRegistrationBean.setName("zlm_Proxy");
servletRegistrationBean.addInitParameter("targetUri", String.format("http://%s:%s", mediaConfig.getIp(), mediaConfig.getHttpPort()));
servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080");
servletRegistrationBean.addUrlMappings();
if (logger.isDebugEnabled()) {
servletRegistrationBean.addInitParameter("log", "true");
@ -39,23 +46,25 @@ public class ProxyServletConfig {
}
class ZLMProxySerlet extends ProxyServlet{
@Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
IMediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
if (mediaInfo != null) {
if (!StringUtils.isEmpty(queryStr)) {
queryStr += "&secret=" + mediaConfig.getSecret();
queryStr += "&secret=" + mediaInfo.getSecret();
}else {
queryStr = "secret=" + mediaConfig.getSecret();
queryStr = "secret=" + mediaInfo.getSecret();
}
}
return queryStr;
}
/**
* 异常处理
*/
@Override
protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
//System.out.println(e.getMessage());
try {
super.handleRequestException(proxyRequest, proxyResonse, e);
} catch (ServletException servletException) {
@ -72,6 +81,64 @@ public class ProxyServletConfig {
logger.error("zlm 代理失败: ", e);
}
}
/**
* 对于为按照格式请求的可以直接返回404
*/
@Override
protected String getTargetUri(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null) {
// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
}else {
uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已 其他的也可以
}
return uri;
}
/**
* 动态替换请求目标
*/
@Override
protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
HttpHost host;
if (mediaInfo != null) {
host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
}else {
host = new HttpHost("127.0.0.1", serverPort);
}
return host;
}
/**
* 根据uri获取流媒体信息
*/
IMediaServerItem getMediaInfoByUri(String uri){
String[] split = uri.split("/");
String mediaServerId = split[2];
return mediaServerService.getOne(mediaServerId);
}
/**
* 去掉url中的标志信息
*/
@Override
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
return url;
}
return url.replace(mediaInfo.getId() + "/", "");
}
}
}

View File

@ -95,20 +95,43 @@ public class SipLayer implements SipListener {
tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "TCP");
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider.addSipListener(this);
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}");
} catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
logger.error(String.format("创建SIP服务失败: %s", e.getMessage()));
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getSipPort() + "}");
// } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
// logger.error(String.format("创建SIP服务失败: %s", e.getMessage()));
// }
} catch (TransportNotSupportedException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
logger.error("无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
, sipConfig.getMonitorIp(), sipConfig.getSipPort());
} catch (TooManyListenersException e) {
e.printStackTrace();
} catch (ObjectInUseException e) {
e.printStackTrace();
}
return tcpSipProvider;
}
@Bean("udpSipProvider")
@DependsOn("sipStack")
private SipProvider startUdpListener() throws Exception {
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP");
SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
private SipProvider startUdpListener() {
ListeningPoint udpListeningPoint = null;
SipProvider udpSipProvider = null;
try {
udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP");
udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(this);
logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}");
} 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;
}

View File

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

View File

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

View File

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

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.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.slf4j.Logger;
@ -32,6 +35,8 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
private IVideoManagerStorager storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private SIPCommanderFroPlatform sipCommanderFroPlatform;
@ -62,22 +67,24 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
logger.info("停止[ {} ]的所有推流", event.getPlatformGbID());
StringBuilder app = new StringBuilder();
StringBuilder stream = new StringBuilder();
for (int i = 0; i < sendRtpItems.size(); i++) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
if (app.length() != 0) {
app.append(",");
}
app.append(sendRtpItems.get(i).getApp());
app.append(sendRtpItem.getApp());
if (stream.length() != 0) {
stream.append(",");
}
stream.append(sendRtpItems.get(i).getStreamId());
redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItems.get(i).getChannelId());
}
stream.append(sendRtpItem.getStreamId());
redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId());
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
Map<String, Object> param = new HashMap<>();
param.put("vhost","__defaultVhost__");
param.put("vhost", "__defaultVhost__");
param.put("app", app.toString());
param.put("stream", stream.toString());
zlmrtpServerFactory.stopSendRtpStream(param);
zlmrtpServerFactory.stopSendRtpStream(mediaInfo, 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.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*;
import com.genersoft.iot.vmp.service.IPlayService;
@ -104,6 +105,9 @@ public class SIPProcessorFactory {
@Autowired
private ZLMRTPServerFactory zlmrtpServerFactory;
@Autowired
private IMediaServerService mediaServerService;
// 这里使用注解会导致循环依赖注入暂用springBean
private SipProvider tcpSipProvider;
@ -128,6 +132,7 @@ public class SIPProcessorFactory {
processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setMediaServerService(mediaServerService);
return processor;
} else if (Request.REGISTER.equals(method)) {
RegisterRequestProcessor processor = new RegisterRequestProcessor();
@ -148,6 +153,7 @@ public class SIPProcessorFactory {
processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setMediaServerService(mediaServerService);
return processor;
} else if (Request.BYE.equals(method)) {
ByeRequestProcessor processor = new ByeRequestProcessor();
@ -155,6 +161,7 @@ public class SIPProcessorFactory {
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setSIPCommander(cmder);
processor.setMediaServerService(mediaServerService);
return processor;
} else if (Request.CANCEL.equals(method)) {
CancelRequestProcessor processor = new CancelRequestProcessor();

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.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
@ -90,7 +92,7 @@ public interface ISIPCommander {
* @param device 视频设备
* @param channelId 预览通道
*/
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 请求回放视频流
@ -100,7 +102,7 @@ public interface ISIPCommander {
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 视频流停止
@ -288,12 +290,4 @@ public interface ISIPCommander {
* @return true = 命令发送成功
*/
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
/**
* 释放rtpserver
* @param device
* @param channelId
*/
void closeRTPServer(Device device, String channelId);
}

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

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

View File

@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import org.springframework.stereotype.Component;
import java.util.*;
@ -30,7 +32,7 @@ public class ZLMHttpHookSubscribe {
}
public interface Event{
void response(JSONObject response);
void response(IMediaServerItem mediaServerItem, JSONObject response);
}
private Map<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.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
@ -45,11 +47,11 @@ public class ZLMMediaListManager {
private ZLMHttpHookSubscribe subscribe;
public void updateMediaList() {
public void updateMediaList(MediaServerItem mediaServerItem) {
storager.clearMediaList();
// 使用异步的当时更新媒体流列表
zlmresTfulUtils.getMediaList((mediaList ->{
zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{
if (mediaList == null) return;
String dataStr = mediaList.getString("data");
@ -57,10 +59,10 @@ public class ZLMMediaListManager {
Map<String, StreamPushItem> result = new HashMap<>();
List<StreamPushItem> streamPushItems = null;
// 获取所有的国标关联
List<GbStream> gbStreams = gbStreamMapper.selectAll();
// List<GbStream> gbStreams = gbStreamMapper.selectAllByMediaServerId(mediaServerItem.getId());
if (code == 0 ) {
if (dataStr != null) {
streamPushItems = streamPushService.handleJSON(dataStr);
streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
}
}else {
logger.warn("更新视频流失败错误code " + code);
@ -72,24 +74,27 @@ public class ZLMMediaListManager {
JSONObject jsonObject = new JSONObject();
jsonObject.put("app", streamPushItem.getApp());
jsonObject.put("stream", streamPushItem.getStream());
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,(response)->{
updateMedia(response.getString("app"), response.getString("stream"));
});
jsonObject.put("mediaServerId", mediaServerItem.getId());
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,
(IMediaServerItem mediaServerItemInuse, JSONObject response)->{
updateMedia(mediaServerItem, response.getString("app"), response.getString("stream"));
}
);
}
}
}));
}
public void addMedia(String app, String streamId) {
public void addMedia(IMediaServerItem mediaServerItem, String app, String streamId) {
//使用异步更新推流
updateMedia(app, streamId);
updateMedia(mediaServerItem, app, streamId);
}
public void updateMedia(String app, String streamId) {
public void updateMedia(IMediaServerItem mediaServerItem, String app, String streamId) {
//使用异步更新推流
zlmresTfulUtils.getMediaList(app, streamId, "rtmp", json->{
zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{
if (json == null) return;
String dataStr = json.getString("data");
@ -99,7 +104,7 @@ public class ZLMMediaListManager {
List<StreamPushItem> streamPushItems = null;
if (code == 0 ) {
if (dataStr != null) {
streamPushItems = streamPushService.handleJSON(dataStr);
streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
}
}else {
logger.warn("更新视频流失败错误code " + code);
@ -122,32 +127,32 @@ public class ZLMMediaListManager {
}
}
public void clearAllSessions() {
logger.info("清空所有国标相关的session");
JSONObject allSessionJSON = zlmresTfulUtils.getAllSession();
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
HashSet<String> allLocalPorts = new HashSet();
if (allSessionJSON.getInteger("code") == 0) {
JSONArray data = allSessionJSON.getJSONArray("data");
if (data.size() > 0) {
for (int i = 0; i < data.size(); i++) {
JSONObject sessionJOSN = data.getJSONObject(i);
Integer local_port = sessionJOSN.getInteger("local_port");
if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) &&
!local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){
allLocalPorts.add(sessionJOSN.getInteger("local_port") + "");
}
}
}
}
if (allLocalPorts.size() > 0) {
List<String> result = new ArrayList<>(allLocalPorts);
String localPortSStr = String.join(",", result);
zlmresTfulUtils.kickSessions(localPortSStr);
}
}
// public void clearAllSessions() {
// logger.info("清空所有国标相关的session");
// JSONObject allSessionJSON = zlmresTfulUtils.getAllSession();
// ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
// HashSet<String> allLocalPorts = new HashSet();
// if (allSessionJSON.getInteger("code") == 0) {
// JSONArray data = allSessionJSON.getJSONArray("data");
// if (data.size() > 0) {
// for (int i = 0; i < data.size(); i++) {
// JSONObject sessionJOSN = data.getJSONObject(i);
// Integer local_port = sessionJOSN.getInteger("local_port");
// if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) &&
// !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) &&
// !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) &&
// !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) &&
// !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) &&
// !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){
// allLocalPorts.add(sessionJOSN.getInteger("local_port") + "");
// }
// }
// }
// }
// if (allLocalPorts.size() > 0) {
// List<String> result = new ArrayList<>(allLocalPorts);
// String localPortSStr = String.join(",", result);
// 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.JSONObject;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@ -21,23 +23,18 @@ public class ZLMRESTfulUtils {
private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class);
@Autowired
private MediaConfig mediaConfig;
public interface RequestCallback{
void run(JSONObject response);
}
public JSONObject sendPost(String api, Map<String, Object> param, RequestCallback callback) {
public JSONObject sendPost(IMediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
OkHttpClient client = new OkHttpClient();
String url = String.format("http://%s:%s/index/api/%s", mediaConfig.getIp(), mediaConfig.getHttpPort(), api);
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
JSONObject responseJSON = null;
logger.debug(url);
FormBody.Builder builder = new FormBody.Builder();
builder.add("secret",mediaConfig.getSecret());
builder.add("secret",mediaServerItem.getSecret());
if (param != null && param.keySet().size() > 0) {
for (String key : param.keySet()){
if (param.get(key) != null) {
@ -96,14 +93,14 @@ public class ZLMRESTfulUtils {
}
public void sendPostForImg(String api, Map<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();
String url = String.format("http://%s:%s/index/api/%s", mediaConfig.getIp(), mediaConfig.getHttpPort(), api);
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
JSONObject responseJSON = null;
logger.debug(url);
FormBody.Builder builder = new FormBody.Builder();
builder.add("secret",mediaConfig.getSecret());
builder.add("secret",mediaServerItem.getSecret());
if (param != null && param.keySet().size() > 0) {
for (String key : param.keySet()){
if (param.get(key) != null) {
@ -142,39 +139,39 @@ public class ZLMRESTfulUtils {
}
public JSONObject getMediaList(String app, String stream, String schema, RequestCallback callback){
public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream, String schema, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
if (app != null) param.put("app",app);
if (stream != null) param.put("stream",stream);
if (schema != null) param.put("schema",schema);
param.put("vhost","__defaultVhost__");
return sendPost("getMediaList",param, callback);
return sendPost(mediaServerItem, "getMediaList",param, callback);
}
public JSONObject getMediaList(String app, String stream){
return getMediaList(app, stream,null, null);
public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream){
return getMediaList(mediaServerItem, app, stream,null, null);
}
public JSONObject getMediaList(RequestCallback callback){
return sendPost("getMediaList",null, callback);
public JSONObject getMediaList(IMediaServerItem mediaServerItem,RequestCallback callback){
return sendPost(mediaServerItem, "getMediaList",null, callback);
}
public JSONObject getMediaInfo(String app, String schema, String stream){
public JSONObject getMediaInfo(IMediaServerItem mediaServerItem,String app, String schema, String stream){
Map<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("schema",schema);
param.put("stream",stream);
param.put("vhost","__defaultVhost__");
return sendPost("getMediaInfo",param, null);
return sendPost(mediaServerItem, "getMediaInfo",param, null);
}
public JSONObject getRtpInfo(String stream_id){
public JSONObject getRtpInfo(IMediaServerItem mediaServerItem,String stream_id){
Map<String, Object> param = new HashMap<>();
param.put("stream_id",stream_id);
return sendPost("getRtpInfo",param, null);
return sendPost(mediaServerItem, "getRtpInfo",param, null);
}
public JSONObject addFFmpegSource(String src_url, String dst_url, String timeout_ms,
public JSONObject addFFmpegSource(IMediaServerItem mediaServerItem,String src_url, String dst_url, String timeout_ms,
boolean enable_hls, boolean enable_mp4, String ffmpeg_cmd_key){
logger.info(src_url);
logger.info(dst_url);
@ -185,44 +182,44 @@ public class ZLMRESTfulUtils {
param.put("enable_hls", enable_hls);
param.put("enable_mp4", enable_mp4);
param.put("ffmpeg_cmd_key", ffmpeg_cmd_key);
return sendPost("addFFmpegSource",param, null);
return sendPost(mediaServerItem, "addFFmpegSource",param, null);
}
public JSONObject delFFmpegSource(String key){
public JSONObject delFFmpegSource(IMediaServerItem mediaServerItem,String key){
Map<String, Object> param = new HashMap<>();
param.put("key", key);
return sendPost("delFFmpegSource",param, null);
return sendPost(mediaServerItem, "delFFmpegSource",param, null);
}
public JSONObject getMediaServerConfig(){
return sendPost("getServerConfig",null, null);
public JSONObject getMediaServerConfig(IMediaServerItem mediaServerItem){
return sendPost(mediaServerItem, "getServerConfig",null, null);
}
public JSONObject setServerConfig(Map<String, Object> param){
return sendPost("setServerConfig",param, null);
public JSONObject setServerConfig(IMediaServerItem mediaServerItem, Map<String, Object> param){
return sendPost(mediaServerItem,"setServerConfig",param, null);
}
public JSONObject openRtpServer(Map<String, Object> param){
return sendPost("openRtpServer",param, null);
public JSONObject openRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param){
return sendPost(mediaServerItem, "openRtpServer",param, null);
}
public JSONObject closeRtpServer(Map<String, Object> param) {
return sendPost("closeRtpServer",param, null);
public JSONObject closeRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param) {
return sendPost(mediaServerItem, "closeRtpServer",param, null);
}
public JSONObject listRtpServer() {
return sendPost("listRtpServer",null, null);
public JSONObject listRtpServer(IMediaServerItem mediaServerItem) {
return sendPost(mediaServerItem, "listRtpServer",null, null);
}
public JSONObject startSendRtp(Map<String, Object> param) {
return sendPost("startSendRtp",param, null);
public JSONObject startSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
return sendPost(mediaServerItem, "startSendRtp",param, null);
}
public JSONObject stopSendRtp(Map<String, Object> param) {
return sendPost("stopSendRtp",param, null);
public JSONObject stopSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
return sendPost(mediaServerItem, "stopSendRtp",param, null);
}
public JSONObject addStreamProxy(String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
public JSONObject addStreamProxy(IMediaServerItem mediaServerItem,String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app);
@ -231,33 +228,33 @@ public class ZLMRESTfulUtils {
param.put("enable_hls", enable_hls?1:0);
param.put("enable_mp4", enable_mp4?1:0);
param.put("rtp_type", rtp_type);
return sendPost("addStreamProxy",param, null);
return sendPost(mediaServerItem, "addStreamProxy",param, null);
}
public JSONObject closeStreams(String app, String stream) {
public JSONObject closeStreams(IMediaServerItem mediaServerItem,String app, String stream) {
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app);
param.put("stream", stream);
param.put("force", 1);
return sendPost("close_streams",param, null);
return sendPost(mediaServerItem, "close_streams",param, null);
}
public JSONObject getAllSession() {
return sendPost("getAllSession",null, null);
public JSONObject getAllSession(IMediaServerItem mediaServerItem) {
return sendPost(mediaServerItem, "getAllSession",null, null);
}
public void kickSessions(String localPortSStr) {
public void kickSessions(IMediaServerItem mediaServerItem, String localPortSStr) {
Map<String, Object> param = new HashMap<>();
param.put("local_port", localPortSStr);
sendPost("kick_sessions",param, null);
sendPost(mediaServerItem, "kick_sessions",param, null);
}
public void getSnap(String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
public void getSnap(IMediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
Map<String, Object> param = new HashMap<>();
param.put("url", flvUrl);
param.put("timeout_sec", timeout_sec);
param.put("expire_sec", expire_sec);
sendPostForImg("getSnap",param, targetPath, fileName);
sendPostForImg(mediaServerItem, "getSnap",param, targetPath, fileName);
}
}

View File

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

View File

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

View File

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

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

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

View File

@ -76,6 +76,11 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
*/
private String vhost;
/**
* 使用的流媒体ID
*/
private String mediaServerId;
public String getVhost() {
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.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
/**
* 媒体信息业务
@ -14,23 +16,8 @@ public interface IMediaService {
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream);
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks, String addr);
/**
* 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
@ -38,5 +25,21 @@ public interface IMediaService {
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String addr);
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaServerItem, String app, String stream, JSONArray tracks);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr);
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.github.pagehelper.PageInfo;
@ -8,7 +9,7 @@ import java.util.List;
public interface IStreamPushService {
List<StreamPushItem> handleJSON(String json);
List<StreamPushItem> handleJSON(String json, IMediaServerItem mediaServerItem);
/**
* 将应用名和流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.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.service.IMediaService;
@ -21,30 +24,53 @@ public class MediaServiceImpl implements IMediaService {
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Override
public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks) {
return getStreamInfoByAppAndStream(app, stream, tracks, null);
public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks) {
return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null);
}
@Override
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream) {
return getStreamInfoByAppAndStreamWithCheck(app, stream, null);
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) {
StreamInfo streamInfo = null;
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo == null) {
return streamInfo;
}
JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, app, stream);
if (mediaList != null) {
if (mediaList.getInteger("code") == 0) {
JSONArray data = mediaList.getJSONArray("data");
if (data == null) return null;
JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class);
JSONArray tracks = mediaJSON.getJSONArray("tracks");
streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks);
}
}
return streamInfo;
}
@Override
public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks, String addr) {
ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId) {
return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null);
}
@Override
public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) {
StreamInfo streamInfoResult = new StreamInfo();
streamInfoResult.setStreamId(stream);
streamInfoResult.setApp(app);
if (addr == null) {
addr = mediaInfo.getStreamIp();
}
streamInfoResult.setMediaServerId(mediaInfo.getId());
streamInfoResult.setRtmp(String.format("rtmp://%s:%s/%s/%s", addr, mediaInfo.getRtmpPort(), app, stream));
streamInfoResult.setRtsp(String.format("rtsp://%s:%s/%s/%s", addr, mediaInfo.getRtspPort(), app, stream));
streamInfoResult.setFlv(String.format("http://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpPort(), app, stream));
@ -60,19 +86,4 @@ public class MediaServiceImpl implements IMediaService {
return streamInfoResult;
}
@Override
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String addr) {
StreamInfo streamInfo = null;
JSONObject mediaList = zlmresTfulUtils.getMediaList(app, stream);
if (mediaList != null) {
if (mediaList.getInteger("code") == 0) {
JSONArray data = mediaList.getJSONArray("data");
if (data == null) return null;
JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class);
JSONArray tracks = mediaJSON.getJSONArray("tracks");
streamInfo = getStreamInfoByAppAndStream(app, stream, tracks, addr);
}
}
return streamInfo;
}
}

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,9 +12,10 @@ import java.util.List;
public interface GbStreamMapper {
@Insert("INSERT INTO gb_stream (app, stream, gbId, name, " +
"longitude, latitude, streamType, status) VALUES" +
"longitude, latitude, streamType, mediaServerId, status) VALUES" +
"('${app}', '${stream}', '${gbId}', '${name}', " +
"'${longitude}', '${latitude}', '${streamType}', ${status})")
"'${longitude}', '${latitude}', '${streamType}', " +
"'${mediaServerId}', ${status})")
int add(GbStream gbStream);
@Update("UPDATE gb_stream " +
@ -25,6 +26,7 @@ public interface GbStreamMapper {
"streamType=#{streamType}," +
"longitude=#{longitude}, " +
"latitude=#{latitude}," +
"mediaServerId=#{mediaServerId}," +
"status=${status} " +
"WHERE app=#{app} AND stream=#{stream} AND gbId=#{gbId}")
int update(GbStream gbStream);
@ -52,4 +54,7 @@ public interface GbStreamMapper {
"SET status=${status} " +
"WHERE app=#{app} AND stream=#{stream}")
void setStatus(String app, String stream, boolean status);
@Select("SELECT gs.*, pgs.platformId FROM gb_stream gs LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream WHERE mediaServerId=#{mediaServerId} ")
List<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
public interface StreamProxyMapper {
@Insert("INSERT INTO stream_proxy (type, app, stream, url, src_url, dst_url, " +
"timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable) VALUES" +
"('${type}','${app}', '${stream}', '${url}', '${src_url}', '${dst_url}', " +
"'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable} )")
@Insert("INSERT INTO stream_proxy (type, app, stream,mediaServerId, url, src_url, dst_url, " +
"timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, createTime) VALUES" +
"('${type}','${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " +
"'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, '${createTime}' )")
int add(StreamProxyItem streamProxyDto);
@Update("UPDATE stream_proxy " +
@ -21,6 +21,7 @@ public interface StreamProxyMapper {
"app=#{app}," +
"stream=#{stream}," +
"url=#{url}, " +
"mediaServerId=#{mediaServerId}, " +
"src_url=#{src_url}," +
"dst_url=#{dst_url}, " +
"timeout_ms=#{timeout_ms}, " +
@ -35,12 +36,17 @@ public interface StreamProxyMapper {
@Delete("DELETE FROM stream_proxy WHERE app=#{app} AND stream=#{stream}")
int del(String app, String stream);
@Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream")
@Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.createTime desc")
List<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);
@Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream}")
@Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.createTime desc")
StreamProxyItem selectOne(String app, String stream);
@Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st " +
"LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " +
"WHERE st.enable=${enable} and st.mediaServerId = '${id}' order by st.createTime desc")
List<StreamProxyItem> selectForEnableInMediaServer(String id, boolean enable);
}

View File

@ -11,14 +11,15 @@ import java.util.List;
public interface StreamPushMapper {
@Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
"createStamp, aliveSecond) VALUES" +
"createStamp, aliveSecond, mediaServerId) VALUES" +
"('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " +
"'${createStamp}', '${aliveSecond}' )")
"'${createStamp}', '${aliveSecond}', '${mediaServerId}' )")
int add(StreamPushItem streamPushItem);
@Update("UPDATE stream_push " +
"SET app=#{app}," +
"stream=#{stream}," +
"mediaServerId=#{mediaServerId}," +
"totalReaderCount=#{totalReaderCount}, " +
"originType=#{originType}," +
"originTypeStr=#{originTypeStr}, " +
@ -41,10 +42,10 @@ public interface StreamPushMapper {
@Insert("<script>" +
"INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
"createStamp, aliveSecond) " +
"createStamp, aliveSecond, mediaServerId) " +
"VALUES <foreach collection='streamPushItems' item='item' index='index' >" +
"( '${item.app}', '${item.stream}', '${item.totalReaderCount}', '${item.originType}', " +
"'${item.originTypeStr}','${item.createStamp}', '${item.aliveSecond}' )" +
"'${item.originTypeStr}','${item.createStamp}', '${item.aliveSecond}', '${item.mediaServerId}' )" +
" </foreach>" +
"</script>")
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.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
@ -87,26 +89,6 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
return (StreamInfo)redis.get(playLeys.get(0).toString());
}
/**
* 更新流媒体信息
* @param ZLMServerConfig
* @return
*/
@Override
public boolean updateMediaInfo(ZLMServerConfig ZLMServerConfig) {
ZLMServerConfig.setUpdateTime(format.format(new Date(System.currentTimeMillis())));
return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX, ZLMServerConfig);
}
/**
* 获取流媒体信息
* @return
*/
@Override
public ZLMServerConfig getMediaInfo() {
return (ZLMServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX);
}
@Override
public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
Map<String, StreamInfo> streamInfos = new HashMap<>();
@ -297,7 +279,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
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++) {
String key = (String) onlineDevices.get(i);
redis.del(key);
@ -308,4 +290,5 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
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.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -70,6 +71,9 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
@Autowired
private VideoStreamSessionManager streamSession;
@Autowired
private MediaServerMapper mediaServerMapper;
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@ -459,6 +463,8 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
boolean result = false;
streamProxyItem.setStreamType("proxy");
streamProxyItem.setStatus(true);
String now = this.format.format(new Date(System.currentTimeMillis()));
streamProxyItem.setCreateTime(now);
try {
if (gbStreamMapper.add(streamProxyItem)<0 || streamProxyMapper.add(streamProxyItem) < 0) {
//事务回滚
@ -467,6 +473,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
result = true;
dataSourceTransactionManager.commit(transactionStatus); //手动提交
}catch (Exception e) {
logger.error("向数据库添加流代理失败:", e);
dataSourceTransactionManager.rollback(transactionStatus);
}
return result;
@ -599,4 +606,21 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
public void updateParentPlatformStatus(String platformGbID, boolean online) {
platformMapper.updateParentPlatformStatus(platformGbID, online);
}
@Override
public void updateMediaServer(MediaServerItem mediaServerItem) {
String now = this.format.format(new Date(System.currentTimeMillis()));
mediaServerItem.setUpdateTime(now);
if (mediaServerMapper.queryOne(mediaServerItem.getId()) != null) {
mediaServerMapper.update(mediaServerItem);
}else {
mediaServerItem.setCreateTime(now);
mediaServerMapper.add(mediaServerItem);
}
}
@Override
public List<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.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
@ -78,7 +79,7 @@ public class DeviceConfig {
cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (StringUtils.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
@ -87,7 +88,7 @@ public class DeviceConfig {
logger.warn(String.format("设备配置操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (StringUtils.isEmpty(channelId) ? deviceId : channelId));
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("Status", "Timeout");
@ -95,7 +96,7 @@ public class DeviceConfig {
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (StringUtils.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
@ -123,7 +124,7 @@ public class DeviceConfig {
cmder.deviceConfigQuery(device, channelId, configType, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (StringUtils.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
@ -132,11 +133,11 @@ public class DeviceConfig {
logger.warn(String.format("获取设备配置超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (StringUtils.isEmpty(channelId) ? deviceId : channelId));
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (StringUtils.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}

View File

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

View File

@ -43,11 +43,13 @@ public class MediaController {
@ApiImplicitParams({
@ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class),
@ApiImplicitParam(name = "stream", value = "流id", dataTypeClass = String.class),
@ApiImplicitParam(name = "mediaServerId", value = "媒体服务器id", dataTypeClass = String.class),
})
@GetMapping(value = "/stream_info_by_app_and_stream")
@ResponseBody
public StreamInfo getStreamInfoByAppAndStream(String app, String stream){
return mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream);
public StreamInfo getStreamInfoByAppAndStream(@RequestParam String app, @RequestParam String stream, @RequestParam String mediaServerId){
return mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream,mediaServerId);
}

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

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.RequestMessage;
//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.service.IPlayService;
import io.swagger.annotations.Api;
@ -87,9 +89,18 @@ public class PlaybackController {
cmder.streamByeCmd(deviceId, channelId);
}
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> {
IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
if (newMediaServerItem == null) {
logger.warn(String.format("设备回放超时deviceId%s channelId%s", deviceId, channelId));
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
msg.setData("Timeout");
resultHolder.invokeResult(msg);
return result;
}
cmder.playbackStreamCmd(newMediaServerItem, device, channelId, startTime, endTime, (IMediaServerItem mediaServerItem, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString());
playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
}, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

@ -7,22 +7,23 @@
<el-main>
<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>
<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;">
<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>
</div>
</div>
<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-column prop="app" label="应用名" align="center">
@ -60,6 +61,7 @@
<script>
import uiHeader from './UiHeader.vue'
import cloudRecordDetail from './CloudRecordDetail.vue'
import MediaServer from './service/MediaServer'
export default {
name: 'app',
components: {
@ -78,6 +80,7 @@
count:15,
total:0,
loading: false,
mediaServerObj : new MediaServer(),
recordDetail: false
};
@ -107,20 +110,13 @@
},
getMediaServerList: function (){
let that = this;
this.$axios({
method: 'get',
url:`/api/server/media_server/list`,
}).then(function (res) {
console.log(res)
that.mediaServerList = res.data;
that.mediaServerObj.getMediaServerList((data)=>{
that.mediaServerList = data;
if (that.mediaServerList.length > 0) {
that.mediaServer = that.mediaServerList[0]
that.getRecordList();
}
}).catch(function (error) {
console.log(error);
});
})
},
getRecordList: function (){
let that = this;

View File

@ -17,6 +17,8 @@
</el-table-column>
<el-table-column prop="gbId" label="国标编码" width="150" align="center">
</el-table-column>
<el-table-column prop="mediaServerId" label="流媒体" width="150" align="center">
</el-table-column>
<el-table-column label="开始时间" align="center" >
<template slot-scope="scope">
<el-button-group>
@ -134,7 +136,8 @@
url:`/api/media/stream_info_by_app_and_stream`,
params: {
app: row.app,
stream: row.stream
stream: row.stream,
mediaServerId: row.mediaServerId
}
}).then(function (res) {
that.getListLoading = false;

View File

@ -32,6 +32,15 @@
</div>
</template>
</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 label="启用" width="120" align="center">
<template slot-scope="scope">
@ -147,8 +156,6 @@
count: that.count
}
}).then(function (res) {
console.log(res);
console.log(res.data.list);
that.total = res.data.total;
that.streamProxyList = res.data.list;
that.getListLoading = false;
@ -170,7 +177,6 @@
this.getListLoading = false;
if (res.data.code == 0 ){
if (res.data.data.length > 0) {
console.log(res.data.data)
this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{
if (url != null) {
this.$refs.onvifEdit.close();
@ -200,7 +206,8 @@
url:`/api/media/stream_info_by_app_and_stream`,
params: {
app: row.app,
stream: row.stream
stream: row.stream,
mediaServerId: row.mediaServerId
}
}).then(function (res) {
that.getListLoading = false;

View File

@ -7,6 +7,17 @@
<el-main>
<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>
<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;">
<el-popover placement="bottom" width="750" height="300" trigger="click">
<div style="height: 600px;overflow:auto;">
@ -53,6 +64,7 @@
<script>
import uiHeader from './UiHeader.vue'
import MediaServer from './service/MediaServer'
import echarts from 'echarts';
export default {
@ -87,30 +99,51 @@ export default {
chartInterval: 0, //
allSessionData: [],
visible: false,
serverConfig: {}
serverConfig: {},
mediaServer : new MediaServer(),
mediaServerChoose : null,
loadCount : 0,
mediaServerList : []
};
},
mounted() {
this.getAllSession();
this.initTable();
this.updateData();
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() {
clearInterval(this.chartInterval); //
},
methods: {
chooseMediaChange: function (val) {
this.loadCount = 0
this.initTable()
this.updateData();
},
updateData: function () {
this.getThreadsLoad();
this.getLoadCount();
this.getAllSession();
},
/**
* 获取线程状态
*/
getThreadsLoad: function () {
let that = this;
if (that.mediaServerChoose != null) {
this.$axios({
method: 'get',
url: '/zlm/index/api/getThreadsLoad'
url: '/zlm/' + that.mediaServerChoose +'/index/api/getThreadsLoad'
}).then(function (res) {
if (res.data.code == 0) {
that.tableOption.xAxis.data.push(new Date().toLocaleTimeString('chinese', {
@ -149,6 +182,18 @@ export default {
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 () {
let that = this;
@ -242,10 +287,9 @@ export default {
getAllSession: function () {
let that = this;
that.allSessionData = [];
console.log("地址:" + '/zlm/index/api/getAllSession');
this.$axios({
method: 'get',
url: '/zlm/index/api/getAllSession'
url: '/zlm/' + that.mediaServerChoose +'/index/api/getAllSession'
}).then(function (res) {
res.data.data.forEach(item => {
let data = {

View File

@ -39,6 +39,22 @@
<el-form-item label="超时时间:毫秒" prop="timeout_ms" v-if="proxyParam.type=='ffmpeg'">
<el-input v-model="proxyParam.timeout_ms" clearable></el-input>
</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-input v-model="proxyParam.ffmpeg_cmd_key" clearable></el-input>-->
<el-select
@ -68,6 +84,7 @@
<el-option label="组播" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="国标平台">
<el-select
v-model="proxyParam.platformGbId"
@ -106,6 +123,8 @@
</template>
<script>
import MediaServer from './../service/MediaServer'
export default {
name: "streamProxyEdit",
props: {},
@ -134,27 +153,8 @@ export default {
isLoging: false,
dialogLoading: false,
onSubmit_text: "立即创建",
platformList: [{
id: 1,
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,
}],
platformList: [],
mediaServer: new MediaServer(),
proxyParam: {
name: null,
type: "default",
@ -170,7 +170,9 @@ export default {
enable_hls: true,
enable_mp4: false,
platformGbId: null,
mediaServerId: "auto",
},
mediaServerList:{},
ffmpegCmdList:{},
rules: {
@ -193,7 +195,6 @@ export default {
}
let that = this;
this.$axios({
method: 'get',
url:`/api/platform/query/10000/0`
@ -202,17 +203,28 @@ export default {
}).catch(function (error) {
console.log(error);
});
this.$axios({
this.mediaServer.getMediaServerList((data)=>{
this.mediaServerList = data;
})
},
mediaServerIdChange:function (){
let that = this;
if (that.proxyParam.mediaServerId !== "auto"){
that.$axios({
method: 'get',
url:`/api/proxy/ffmpeg_cmd/list`
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 () {
console.log("onSubmit");
this.dialogLoading = true;
var that = this;
that.$axios({
@ -239,7 +251,6 @@ export default {
});
},
close: function () {
console.log("关闭添加视频平台");
this.showDialog = false;
this.dialogLoading = false;
this.$refs.streamProxy.resetFields();

View File

@ -181,6 +181,7 @@ export default {
showVideoDialog: false,
streamId: '',
app : '',
mediaServerId : '',
convertKey: '',
deviceId: '',
channelId: '',
@ -218,7 +219,7 @@ export default {
if (tab.name == "codec") {
this.$axios({
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) {
that.tracksLoading = false;
if (res.data.code == 0 && res.data.online) {
@ -235,12 +236,11 @@ export default {
}
},
openDialog: function (tab, deviceId, channelId, param) {
console.log("openDialog")
console.log(param)
this.tabActiveName = tab;
this.channelId = channelId;
this.deviceId = deviceId;
this.streamId = "";
this.mediaServerId = "";
this.app = "";
this.videoUrl = ""
if (!!this.$refs.videoPlayer) {
@ -257,8 +257,8 @@ export default {
break;
case "streamPlay":
this.tabActiveName = "media";
this.showRrecord = false,
this.showPtz = false,
this.showRrecord = false;
this.showPtz = false;
this.play(param.streamInfo, param.hasAudio)
break;
case "control":
@ -269,19 +269,17 @@ export default {
console.log(val)
},
play: function (streamInfo, hasAudio) {
this.hasAudio = hasAudio;
this.isLoging = false;
// this.videoUrl = streamInfo.rtc;
this.videoUrl = this.getUrlByStreamInfo(streamInfo);
this.streamId = streamInfo.streamId;
this.app = streamInfo.app;
this.mediaServerId = streamInfo.mediaServerId;
this.playFromStreamInfo(false, streamInfo)
},
getUrlByStreamInfo(streamInfo){
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 `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;
return streamInfo.ws_flv;
@ -430,6 +428,7 @@ export default {
var streamInfo = res.data;
that.app = streamInfo.app;
that.streamId = streamInfo.streamId;
that.mediaServerId = streamInfo.mediaServerId;
that.videoUrl = that.getUrlByStreamInfo(streamInfo);
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;