添加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

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() {
return rtpProxyPort;
public int getRtpProxyPort() {
if (rtpProxyPort == null) {
return 0;
}else {
return rtpProxyPort;
}
}
@Override
public void setRtpProxyPort(int rtpProxyPort) {
}
public void setRtpProxyPort(Integer rtpProxyPort) {
this.rtpProxyPort = rtpProxyPort;
}
public Integer getRtspPort() {
public int getRtspPort() {
return rtspPort;
}
@Override
public void setRtspPort(int rtspPort) {
}
public void setRtspPort(Integer rtspPort) {
this.rtspPort = rtspPort;
}
public Integer getRtspSSLPort() {
public int getRtspSSLPort() {
return rtspSSLPort;
}
@Override
public void setRtspSSLPort(int rtspSSLPort) {
}
public void setRtspSSLPort(Integer rtspSSLPort) {
this.rtspSSLPort = rtspSSLPort;
}
@@ -202,11 +256,101 @@ public class MediaConfig {
this.rtpPortRange = rtpPortRange;
}
public Integer getRecordAssistPort() {
public int getRecordAssistPort() {
return recordAssistPort;
}
@Override
public void setRecordAssistPort(int recordAssistPort) {
}
public void setRecordAssistPort(Integer recordAssistPort) {
this.recordAssistPort = recordAssistPort;
}
@Override
public boolean isDocker() {
return docker;
}
@Override
public void setDocker(boolean docker) {
this.docker = docker;
}
public String getSdpIp() {
if (StringUtils.isEmpty(sdpIp)){
return ip;
}else {
return sdpIp;
}
}
public String getStreamIp() {
if (StringUtils.isEmpty(streamIp)){
return ip;
}else {
return streamIp;
}
}
public MediaServerItem getMediaSerItem(){
MediaServerItem mediaServerItem = new MediaServerItem();
mediaServerItem.setId(id);
mediaServerItem.setIp(ip);
mediaServerItem.setDocker(true);
mediaServerItem.setHookIp(hookIp);
mediaServerItem.setSdpIp(sdpIp);
mediaServerItem.setStreamIp(streamIp);
mediaServerItem.setHttpPort(httpPort);
mediaServerItem.setHttpSSlPort(httpSSlPort);
mediaServerItem.setRtmpPort(rtmpPort);
mediaServerItem.setRtmpSSlPort(rtmpSSlPort);
mediaServerItem.setRtpProxyPort(rtpProxyPort);
mediaServerItem.setRtspPort(rtspPort);
mediaServerItem.setRtspSSLPort(rtspSSLPort);
mediaServerItem.setAutoConfig(autoConfig);
mediaServerItem.setSecret(secret);
mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS);
mediaServerItem.setRtpEnable(rtpEnable);
mediaServerItem.setRtpPortRange(rtpPortRange);
mediaServerItem.setRecordAssistPort(recordAssistPort);
mediaServerItem.setCreateTime(createTime);
mediaServerItem.setUpdateTime(updateTime);
mediaServerItem.setCount(count);
return mediaServerItem;
}
@Override
public String getUpdateTime() {
return updateTime;
}
@Override
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
@Override
public String getCreateTime() {
return createTime;
}
@Override
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
@Override
public int getCount() {
return count;
}
@Override
public void setCount(int count) {
this.count = count;
}
}

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");
@@ -38,24 +45,26 @@ public class ProxyServletConfig {
return servletRegistrationBean;
}
class ZLMProxySerlet extends ProxyServlet{
class ZLMProxySerlet extends ProxyServlet{
@Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
if (!StringUtils.isEmpty(queryStr)) {
queryStr += "&secret=" + mediaConfig.getSecret();
}else {
queryStr = "secret=" + mediaConfig.getSecret();
IMediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
if (mediaInfo != null) {
if (!StringUtils.isEmpty(queryStr)) {
queryStr += "&secret=" + mediaInfo.getSecret();
}else {
queryStr = "secret=" + mediaInfo.getSecret();
}
}
return queryStr;
}
/**
* 异常处理
*/
@Override
protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
//System.out.println(e.getMessage());
try {
super.handleRequestException(proxyRequest, proxyResonse, e);
} catch (ServletException servletException) {
@@ -72,6 +81,64 @@ public class ProxyServletConfig {
logger.error("zlm 代理失败: ", e);
}
}
/**
* 对于为按照格式请求的可以直接返回404
*/
@Override
protected String getTargetUri(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null) {
// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
}else {
uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已 其他的也可以
}
return uri;
}
/**
* 动态替换请求目标
*/
@Override
protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
HttpHost host;
if (mediaInfo != null) {
host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
}else {
host = new HttpHost("127.0.0.1", serverPort);
}
return host;
}
/**
* 根据uri获取流媒体信息
*/
IMediaServerItem getMediaInfoByUri(String uri){
String[] split = uri.split("/");
String mediaServerId = split[2];
return mediaServerService.getOne(mediaServerId);
}
/**
* 去掉url中的标志信息
*/
@Override
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
return url;
}
return url.replace(mediaInfo.getId() + "/", "");
}
}
}

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);
udpSipProvider.addSipListener(this);
logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}");
private SipProvider startUdpListener() {
ListeningPoint udpListeningPoint = null;
SipProvider udpSipProvider = null;
try {
udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP");
udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(this);
} catch (TransportNotSupportedException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
logger.error("无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
, sipConfig.getMonitorIp(), sipConfig.getSipPort());
} catch (TooManyListenersException e) {
e.printStackTrace();
} catch (ObjectInUseException e) {
e.printStackTrace();
}
logger.info("Sip Server UDP 启动成功 port [" + sipConfig.getMonitorIp() + ":" + sipConfig.getSipPort() + "]");
return udpSipProvider;
}

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("app", app.toString());
param.put("stream", stream.toString());
zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
}
Map<String, Object> param = new HashMap<>();
param.put("vhost","__defaultVhost__");
param.put("app", app.toString());
param.put("stream", stream.toString());
zlmrtpServerFactory.stopSendRtpStream(param);
}

View File

@@ -9,6 +9,7 @@ import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.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) {
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,20 +87,22 @@ public class ZLMRTPServerFactory {
return result;
}
public boolean closeRTPServer(String streamId) {
public boolean closeRTPServer(IMediaServerItem serverItem, String streamId) {
boolean result = false;
Map<String, Object> param = new HashMap<>();
param.put("stream_id", streamId);
JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(param);
if (jsonObject != null ) {
if (jsonObject.getInteger("code") == 0) {
result = jsonObject.getInteger("hit") == 1;
if (serverItem !=null){
Map<String, Object> param = new HashMap<>();
param.put("stream_id", streamId);
JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param);
if (jsonObject != null ) {
if (jsonObject.getInteger("code") == 0) {
result = jsonObject.getInteger("hit") == 1;
}else {
logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
}
}else {
logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
// 检查ZLM状态
logger.error("关闭RTP Server 失败: 请检查ZLM服务");
}
}else {
// 检查ZLM状态
logger.error("关闭RTP Server 失败: 请检查ZLM服务");
}
return result;
}
@@ -131,11 +135,11 @@ public class ZLMRTPServerFactory {
* @param tcp 是否为tcp
* @return SendRtpItem
*/
public SendRtpItem createSendRtpItem(String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
public SendRtpItem createSendRtpItem(IMediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
String playSsrc = SsrcUtil.getPlaySsrc();
int localPort = createRTPServer(SsrcUtil.getPlaySsrc());
int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
if (localPort != -1) {
closeRTPServer(playSsrc);
closeRTPServer(serverItem, playSsrc);
}else {
logger.error("没有可用的端口");
return null;
@@ -150,6 +154,7 @@ public class ZLMRTPServerFactory {
sendRtpItem.setTcp(tcp);
sendRtpItem.setApp("rtp");
sendRtpItem.setLocalPort(localPort);
sendRtpItem.setMediaServerId(serverItem.getId());
return sendRtpItem;
}
@@ -163,11 +168,11 @@ public class ZLMRTPServerFactory {
* @param tcp 是否为tcp
* @return SendRtpItem
*/
public SendRtpItem createSendRtpItem(String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
public SendRtpItem createSendRtpItem(IMediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
String playSsrc = SsrcUtil.getPlaySsrc();
int localPort = createRTPServer(SsrcUtil.getPlaySsrc());
int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
if (localPort != -1) {
closeRTPServer(playSsrc);
closeRTPServer(serverItem, playSsrc);
}else {
logger.error("没有可用的端口");
return null;
@@ -182,21 +187,21 @@ public class ZLMRTPServerFactory {
sendRtpItem.setChannelId(channelId);
sendRtpItem.setTcp(tcp);
sendRtpItem.setLocalPort(localPort);
sendRtpItem.setMediaServerId(serverItem.getId());
return sendRtpItem;
}
/**
* 调用zlm RESTful API —— startSendRtp
*/
public Boolean startSendRtpStream(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,17 +153,18 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Override
public void del(String app, String stream) {
StreamProxyItem streamProxyItem = new StreamProxyItem();
streamProxyItem.setApp(app);
streamProxyItem.setStream(stream);
JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem);
if (jsonObject.getInteger("code") == 0) {
StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream);
if (streamProxyItem != null) {
videoManagerStorager.deleteStreamProxy(app, stream);
// 如果关联了国标那么移除关联
gbStreamMapper.del(app, stream);
platformGbStreamMapper.delByAppAndStream(app, stream);
// TODO 如果关联的推流, 那么状态设置为离线
JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem);
if (jsonObject != null && jsonObject.getInteger("code") == 0) {
// 如果关联了国标那么移除关联
gbStreamMapper.del(app, stream);
platformGbStreamMapper.delByAppAndStream(app, stream);
// TODO 如果关联的推流, 那么状态设置为离线
}
}
}
@Override
@@ -168,9 +198,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
}
@Override
public JSONObject getFFmpegCMDs() {
public JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem) {
JSONObject result = new JSONObject();
JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig();
JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0
&& mediaServerConfigResuly.getJSONArray("data").size() > 0){
JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0);

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,25 +212,38 @@ 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 (jsonObject != null && jsonObject.getInteger("code") == 0) {
result.put("code", 0);
JSONObject data = jsonObject.getJSONObject("data");
if (data != null && data.getBoolean("flag")) {
result.put("code", "0");
result.put("msg", "success");
}else {
}
}else {
result.put("code", 1);
result.put("msg", "delFFmpegSource fail");
if (mediaServerId == null) {
result.put("code", 400);
result.put("msg", "mediaServerId is null");
return new ResponseEntity<String>( result.toJSONString(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK);
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo == null) {
result.put("code", 0);
result.put("msg", "使用的流媒体已经停止运行");
return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK);
}else {
JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaInfo, key);
logger.info(jsonObject.toJSONString());
if (jsonObject != null && jsonObject.getInteger("code") == 0) {
result.put("code", 0);
JSONObject data = jsonObject.getJSONObject("data");
if (data != null && data.getBoolean("flag")) {
result.put("code", "0");
result.put("msg", "success");
}else {
}
}else {
result.put("code", 1);
result.put("msg", "delFFmpegSource fail");
}
return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK);
}
}
@ApiOperation("语音广播命令")
@@ -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