Merge remote-tracking branch 'origin/1078' into 1078

This commit is contained in:
648540858 2024-04-11 22:46:03 +08:00
commit 4005a12403
153 changed files with 6522 additions and 4244 deletions

View File

@ -1,6 +1,6 @@
<!-- 云端录像 -->
# 云端录像
云端录像是对录制在zlm服务下的录像文件的管理录像的文件路径默认在ZLM/www/record下使用云端录像功能必须部署wvp-pro-assist主要通过调用wvp-pro-assist的接口完成各种功能
云端录像是对录制在zlm服务下的录像文件的管理录像的文件路径默认在ZLM/www/record下。
如果你需要24小时的录像目前有一个这种方案可以参考[7*24不间断录像](./_content/ability/continuous_recording.md)。
1. 云段录像支持录像文件的查看,播放(可能因为编码的原因导致无法播放);
2. 支持录像的下载;

View File

@ -16,7 +16,6 @@ WVP-PRO使用Spring boot开发maven管理依赖。对于熟悉spring开发的
|----------------|------------------------------------------|-------------------------|
| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 |
| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现以及各种视频流格式的分发支持 | 是 |
| wvp-pro-assist | wvp的辅助录像程序也可单独跟zlm一起使用提供录像控制,录像合并下载接口 | 否(不安装只是影响云端录像功能和国标录像下载) |
## 2 安装依赖
| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 |

View File

@ -2,7 +2,7 @@
# 部署
**请仔细阅读以下内容**
1. WVP-PRO与ZLM支持分开部署但是wvp-pro-assist必须与zlm部署在同一台主机;
1. WVP-PRO与ZLM支持分开部署;
2. 需要开放的端口
| 服务 | 端口 | 类型 | 必选 |
|-----|:-------------------------|-------------|-------|
@ -18,11 +18,10 @@
| zlm | rtp.port-range(在wvp中配置) | udp and tcp | 多端口开放 |
3. 测试环境部署建议所有服务部署在一台主机,关闭防火墙,减少因网络出现问题的可能;
4. WVP-PRO与ZLM支持分开部署但是wvp-pro-assist必须与zlm部署在同一台主机;
5. 生产环境按需开放端口但是建议修改默认端口尤其是5060端口易受到攻击;
6. zlm使用docker部署的情况要求端口映射一致比如映射5060,应将外部端口也映射为5060端口;
7. zlm与wvp会保持高频率的通信所以不要去将wvp与zlm分属在两个网络比如wvp在内网zlm却在公网的情况。
8. 启动服务以linux为例
4. 生产环境按需开放端口但是建议修改默认端口尤其是5060端口易受到攻击;
5. zlm使用docker部署的情况要求端口映射一致比如映射5060,应将外部端口也映射为5060端口;
6. zlm与wvp会保持高频率的通信所以不要去将wvp与zlm分属在两个网络比如wvp在内网zlm却在公网的情况。
7. 启动服务以linux为例
**启动WVP-PRO**
```shell
nohup java -jar wvp-pro-*.jar &

View File

@ -11,7 +11,7 @@
<groupId>com.genersoft</groupId>
<artifactId>wvp-pro</artifactId>
<version>2.7.0</version>
<version>2.7.1</version>
<name>web video platform</name>
<description>国标28181视频平台</description>
<packaging>${project.packaging}</packaging>
@ -99,6 +99,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>

View File

@ -9,6 +9,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
@ -24,6 +25,7 @@ import java.util.Collections;
@ServletComponentScan("com.genersoft.iot.vmp.conf")
@SpringBootApplication
@EnableScheduling
@EnableCaching
public class VManageBootstrap extends SpringBootServletInitializer {
private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.common;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import io.swagger.v3.oas.annotations.media.Schema;
@ -70,7 +71,7 @@ public class StreamInfo implements Serializable, Cloneable{
@Schema(description = "流媒体ID")
private String mediaServerId;
@Schema(description = "流编码信息")
private Object tracks;
private MediaInfo mediaInfo;
@Schema(description = "开始时间")
private String startTime;
@Schema(description = "结束时间")
@ -83,6 +84,9 @@ public class StreamInfo implements Serializable, Cloneable{
@Schema(description = "是否暂停(录像回放使用)")
private boolean pause;
@Schema(description = "产生源类型,包括 unknown = 0,rtmp_push=1,rtsp_push=2,rtp_push=3,pull=4,ffmpeg_pull=5,mp4_vod=6,device_chn=7")
private int originType;
public void setFlv(StreamURL flv) {
this.flv = flv;
}
@ -191,8 +195,7 @@ public class StreamInfo implements Serializable, Cloneable{
}
}
public void setFlv(String host, int port, int sslPort, String app, String stream, String callIdParam) {
String file = String.format("%s/%s.live.flv%s", app, stream, callIdParam);
public void setFlv(String host, int port, int sslPort, String file) {
if (port > 0) {
this.flv = new StreamURL("http", host, port, file);
}
@ -203,6 +206,15 @@ public class StreamInfo implements Serializable, Cloneable{
}
}
public void setWsFlv(String host, int port, int sslPort, String file) {
if (port > 0) {
this.ws_flv = new StreamURL("ws", host, port, file);
}
if (sslPort > 0) {
this.wss_flv = new StreamURL("wss", host, sslPort, file);
}
}
public void setFmp4(String host, int port, int sslPort, String app, String stream, String callIdParam) {
String file = String.format("%s/%s.live.mp4%s", app, stream, callIdParam);
if (port > 0) {
@ -473,12 +485,12 @@ public class StreamInfo implements Serializable, Cloneable{
this.mediaServerId = mediaServerId;
}
public Object getTracks() {
return tracks;
public MediaInfo getMediaInfo() {
return mediaInfo;
}
public void setTracks(Object tracks) {
this.tracks = tracks;
public void setMediaInfo(MediaInfo mediaInfo) {
this.mediaInfo = mediaInfo;
}
public String getStartTime() {
@ -615,4 +627,12 @@ public class StreamInfo implements Serializable, Cloneable{
public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
this.downLoadFilePath = downLoadFilePath;
}
public int getOriginType() {
return originType;
}
public void setOriginType(int originType) {
this.originType = originType;
}
}

View File

@ -12,9 +12,9 @@ public class VideoManagerConstants {
public static final String WVP_SERVER_STREAM_PREFIX = "VMP_SIGNALLING_STREAM_";
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_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS_";
public static final String ONLINE_MEDIA_SERVERS_PREFIX = "VMP_ONLINE_MEDIA_SERVERS:";
public static final String DEVICE_PREFIX = "VMP_DEVICE_";

View File

@ -1,14 +1,10 @@
package com.genersoft.iot.vmp.conf;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
import com.genersoft.iot.vmp.vmanager.cloudRecord.CloudRecordController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -16,7 +12,6 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@ -35,9 +30,6 @@ public class CloudRecordTimer {
@Autowired
private CloudRecordServiceMapper cloudRecordServiceMapper;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
/**
* 定时查询待删除的录像文件
*/
@ -46,12 +38,12 @@ public class CloudRecordTimer {
public void execute(){
logger.info("[录像文件定时清理] 开始清理过期录像文件");
// 获取配置了assist的流媒体节点
List<MediaServerItem> mediaServerItemList = mediaServerService.getAllOnline();
List<MediaServer> mediaServerItemList = mediaServerService.getAllOnline();
if (mediaServerItemList.isEmpty()) {
return;
}
long result = 0;
for (MediaServerItem mediaServerItem : mediaServerItemList) {
for (MediaServer mediaServerItem : mediaServerItemList) {
Calendar lastCalendar = Calendar.getInstance();
if (mediaServerItem.getRecordDay() > 0) {
@ -69,10 +61,10 @@ public class CloudRecordTimer {
// TODO 后续可以删除空了的过期日期文件夹
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
if (jsonObject.getInteger("code") != 0) {
logger.warn("[录像文件定时清理] 删除磁盘文件错误: {}:{}", cloudRecordItem.getFilePath(), jsonObject);
if (deleteResult) {
logger.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
}
}
result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);

View File

@ -1,6 +1,6 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -42,12 +42,24 @@ public class MediaConfig{
@Value("${media.stream-ip:${media.ip}}")
private String streamIp;
@Value("${media.http-port}")
@Value("${media.http-port:0}")
private Integer httpPort;
@Value("${media.flv-port:0}")
private Integer flvPort = 0;
@Value("${media.ws-flv-port:0}")
private Integer wsFlvPort = 0;
@Value("${media.http-ssl-port:0}")
private Integer httpSSlPort = 0;
@Value("${media.flv-ssl-port:0}")
private Integer flvSSlPort = 0;
@Value("${media.ws-flv-ssl-port:0}")
private Integer wsFlvSSlPort = 0;
@Value("${media.rtmp-port:0}")
private Integer rtmpPort = 0;
@ -87,6 +99,9 @@ public class MediaConfig{
@Value("${media.record-path:}")
private String recordPath;
@Value("${media.type:zlm}")
private String type;
public String getId() {
return id;
}
@ -196,36 +211,59 @@ public class MediaConfig{
return sipDomain;
}
public MediaServerItem getMediaSerItem(){
MediaServerItem mediaServerItem = new MediaServerItem();
mediaServerItem.setId(id);
mediaServerItem.setIp(ip);
mediaServerItem.setDefaultServer(true);
mediaServerItem.setHookIp(getHookIp());
mediaServerItem.setSdpIp(getSdpIp());
mediaServerItem.setStreamIp(getStreamIp());
mediaServerItem.setHttpPort(httpPort);
mediaServerItem.setHttpSSlPort(httpSSlPort);
mediaServerItem.setRtmpPort(rtmpPort);
mediaServerItem.setRtmpSSlPort(rtmpSSlPort);
mediaServerItem.setRtpProxyPort(getRtpProxyPort());
mediaServerItem.setRtspPort(rtspPort);
mediaServerItem.setRtspSSLPort(rtspSSLPort);
mediaServerItem.setAutoConfig(autoConfig);
mediaServerItem.setSecret(secret);
mediaServerItem.setRtpEnable(rtpEnable);
mediaServerItem.setRtpPortRange(rtpPortRange);
mediaServerItem.setSendRtpPortRange(rtpSendPortRange);
mediaServerItem.setRecordAssistPort(recordAssistPort);
mediaServerItem.setHookAliveInterval(30.00f);
mediaServerItem.setRecordDay(recordDay);
if (recordPath != null) {
mediaServerItem.setRecordPath(recordPath);
public MediaServer getMediaSerItem(){
MediaServer mediaServer = new MediaServer();
mediaServer.setId(id);
mediaServer.setIp(ip);
mediaServer.setDefaultServer(true);
mediaServer.setHookIp(getHookIp());
mediaServer.setSdpIp(getSdpIp());
mediaServer.setStreamIp(getStreamIp());
mediaServer.setHttpPort(httpPort);
if (flvPort == 0) {
mediaServer.setFlvPort(httpPort);
}else {
mediaServer.setFlvPort(flvPort);
}
if (wsFlvPort == 0) {
mediaServer.setWsFlvPort(httpPort);
}else {
mediaServer.setWsFlvPort(wsFlvPort);
}
if (flvSSlPort == 0) {
mediaServer.setFlvSSLPort(httpSSlPort);
}else {
mediaServer.setFlvSSLPort(flvSSlPort);
}
if (wsFlvSSlPort == 0) {
mediaServer.setWsFlvSSLPort(httpSSlPort);
}else {
mediaServer.setWsFlvSSLPort(wsFlvSSlPort);
}
mediaServerItem.setCreateTime(DateUtil.getNow());
mediaServerItem.setUpdateTime(DateUtil.getNow());
return mediaServerItem;
mediaServer.setHttpSSlPort(httpSSlPort);
mediaServer.setRtmpPort(rtmpPort);
mediaServer.setRtmpSSlPort(rtmpSSlPort);
mediaServer.setRtpProxyPort(getRtpProxyPort());
mediaServer.setRtspPort(rtspPort);
mediaServer.setRtspSSLPort(rtspSSLPort);
mediaServer.setAutoConfig(autoConfig);
mediaServer.setSecret(secret);
mediaServer.setRtpEnable(rtpEnable);
mediaServer.setRtpPortRange(rtpPortRange);
mediaServer.setSendRtpPortRange(rtpSendPortRange);
mediaServer.setRecordAssistPort(recordAssistPort);
mediaServer.setHookAliveInterval(10f);
mediaServer.setRecordDay(recordDay);
mediaServer.setStatus(false);
mediaServer.setType(type);
if (recordPath != null) {
mediaServer.setRecordPath(recordPath);
}
mediaServer.setCreateTime(DateUtil.getNow());
mediaServer.setUpdateTime(DateUtil.getNow());
return mediaServer;
}
public Integer getRecordDay() {

View File

@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.conf;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
@ -9,6 +11,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 配置mybatis
@ -21,7 +24,29 @@ public class MybatisConfig {
private UserSetting userSetting;
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
public DatabaseIdProvider databaseIdProvider() {
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
properties.setProperty("Oracle", "oracle");
properties.setProperty("MySQL", "mysql");
properties.setProperty("DB2", "db2");
properties.setProperty("Derby", "derby");
properties.setProperty("H2", "h2");
properties.setProperty("HSQL", "hsql");
properties.setProperty("Informix", "informix");
properties.setProperty("MS-SQL", "ms-sql");
properties.setProperty("PostgreSQL", "postgresql");
properties.setProperty("Sybase", "sybase");
properties.setProperty("Hana", "hana");
properties.setProperty("DM", "dm");
properties.setProperty("KingbaseES", "kingbase");
properties.setProperty("KingBase8", "kingbase");
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource, DatabaseIdProvider databaseIdProvider) throws Exception {
final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
@ -30,6 +55,7 @@ public class MybatisConfig {
}
config.setMapUnderscoreToCamelCase(true);
sqlSessionFactory.setConfiguration(config);
sqlSessionFactory.setDatabaseIdProvider(databaseIdProvider);
return sqlSessionFactory.getObject();
}

View File

@ -1,7 +1,7 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
@ -54,7 +54,7 @@ public class ProxyServletConfig {
@Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
MediaServer mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
if (mediaInfo != null) {
if (!ObjectUtils.isEmpty(queryStr)) {
queryStr += "&secret=" + mediaInfo.getSecret();
@ -103,7 +103,7 @@ public class ProxyServletConfig {
@Override
protected String getTargetUri(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null) {
@ -121,7 +121,7 @@ public class ProxyServletConfig {
@Override
protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
HttpHost host;
if (mediaInfo != null) {
host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
@ -135,7 +135,7 @@ public class ProxyServletConfig {
/**
* 根据uri获取流媒体信息
*/
MediaServerItem getMediaInfoByUri(String uri){
MediaServer getMediaInfoByUri(String uri){
String[] split = uri.split("/");
String mediaServerId = split[2];
if ("default".equalsIgnoreCase(mediaServerId)) {
@ -151,7 +151,7 @@ public class ProxyServletConfig {
@Override
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
logger.error("[ZLM服务访问代理]错误处理url信息时未找到流媒体信息=>{}", requestURI);
@ -181,7 +181,7 @@ public class ProxyServletConfig {
@Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
MediaServer mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
if (mediaInfo == null) {
return null;
}
@ -238,7 +238,7 @@ public class ProxyServletConfig {
@Override
protected String getTargetUri(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null) {
@ -256,7 +256,7 @@ public class ProxyServletConfig {
@Override
protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
HttpHost host;
if (mediaInfo != null) {
host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
@ -270,7 +270,7 @@ public class ProxyServletConfig {
/**
* 根据uri获取流媒体信息
*/
MediaServerItem getMediaInfoByUri(String uri){
MediaServer getMediaInfoByUri(String uri){
String[] split = uri.split("/");
String mediaServerId = split[2];
if ("default".equalsIgnoreCase(mediaServerId)) {
@ -287,7 +287,7 @@ public class ProxyServletConfig {
@Override
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServer mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
logger.error("[录像服务访问代理]错误处理url信息时未找到流媒体信息=>{}", requestURI);

View File

@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.conf;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -14,9 +13,6 @@ public class WVPTimerTask {
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IMediaServerService mediaServerService;
@Value("${server.port}")
private int serverPort;

View File

@ -51,8 +51,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
if (StringUtils.isBlank(jwt)) {
jwt = request.getParameter(JwtUtils.getHeader());
if (StringUtils.isBlank(jwt)) {
chain.doFilter(request, response);
return;
jwt = request.getHeader(JwtUtils.getApiKeyHeader());
if (StringUtils.isBlank(jwt)) {
chain.doFilter(request, response);
return;
}
}
}

View File

@ -1,8 +1,12 @@
package com.genersoft.iot.vmp.conf.security;
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
import com.genersoft.iot.vmp.service.IUserApiKeyService;
import com.genersoft.iot.vmp.service.IUserService;
import com.genersoft.iot.vmp.storager.dao.dto.User;
import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
@ -20,8 +24,13 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
@Component
public class JwtUtils implements InitializingBean {
@ -30,6 +39,8 @@ public class JwtUtils implements InitializingBean {
public static final String HEADER = "access-token";
public static final String API_KEY_HEADER = "api-key";
private static final String AUDIENCE = "Audience";
private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae";
@ -37,17 +48,28 @@ public class JwtUtils implements InitializingBean {
/**
* token过期时间(分钟)
*/
public static final long expirationTime = 30 * 24 * 60;
public static final long EXPIRATION_TIME = 30 * 24 * 60;
private static RsaJsonWebKey rsaJsonWebKey;
private static IUserService userService;
private static IUserApiKeyService userApiKeyService;
public static String getApiKeyHeader() {
return API_KEY_HEADER;
}
@Resource
public void setUserService(IUserService userService) {
JwtUtils.userService = userService;
}
@Resource
public void setUserApiKeyService(IUserApiKeyService userApiKeyService) {
JwtUtils.userApiKeyService = userApiKeyService;
}
@Override
public void afterPropertiesSet() {
try {
@ -59,17 +81,34 @@ public class JwtUtils implements InitializingBean {
/**
* 创建密钥对
*
* @throws JoseException JoseException
*/
private RsaJsonWebKey generateRsaJsonWebKey() throws JoseException {
// 生成一个RSA密钥对该密钥对将用于JWT的签名和验证包装在JWK中
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// 给JWK一个密钥ID
rsaJsonWebKey.setKeyId(keyId);
RsaJsonWebKey rsaJsonWebKey = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("/jwk.json"), StandardCharsets.UTF_8))) {
String jwkJson = reader.readLine();
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwkJson);
List<JsonWebKey> jsonWebKeys = jsonWebKeySet.getJsonWebKeys();
if (!jsonWebKeys.isEmpty()) {
JsonWebKey jsonWebKey = jsonWebKeys.get(0);
if (jsonWebKey instanceof RsaJsonWebKey) {
rsaJsonWebKey = (RsaJsonWebKey) jsonWebKey;
}
}
} catch (Exception e) {
// ignored
}
if (rsaJsonWebKey == null) {
// 生成一个RSA密钥对该密钥对将用于JWT的签名和验证包装在JWK中
rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// 给JWK一个密钥ID
rsaJsonWebKey.setKeyId(keyId);
}
return rsaJsonWebKey;
}
public static String createToken(String username) {
public static String createToken(String username, Long expirationTime, Map<String, Object> extra) {
try {
/*
* iss (issuer) 发行人
@ -83,13 +122,17 @@ public class JwtUtils implements InitializingBean {
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
// 令牌将过期的时间 分钟
claims.setExpirationTimeMinutesInTheFuture(expirationTime);
if (expirationTime != null) {
claims.setExpirationTimeMinutesInTheFuture(expirationTime);
}
claims.setNotBeforeMinutesInThePast(0);
claims.setSubject("login");
claims.setAudience(AUDIENCE);
//添加自定义参数,必须是字符串类型
claims.setClaim("userName", username);
if (extra != null) {
extra.forEach(claims::setClaim);
}
//jws
JsonWebSignature jws = new JsonWebSignature();
//签名算法RS256
@ -104,10 +147,17 @@ public class JwtUtils implements InitializingBean {
} catch (JoseException e) {
logger.error("[Token生成失败] {}", e.getMessage());
}
return null;
}
public static String createToken(String username, Long expirationTime) {
return createToken(username, expirationTime, null);
}
public static String createToken(String username) {
return createToken(username, EXPIRATION_TIME);
}
public static String getHeader() {
return HEADER;
}
@ -118,8 +168,8 @@ public class JwtUtils implements InitializingBean {
try {
JwtConsumer consumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setMaxFutureValidityInMinutes(5256000)
//.setRequireExpirationTime()
//.setMaxFutureValidityInMinutes(5256000)
.setAllowedClockSkewInSeconds(30)
.setRequireSubject()
//.setExpectedIssuer("")
@ -129,15 +179,27 @@ public class JwtUtils implements InitializingBean {
JwtClaims claims = consumer.processToClaims(token);
NumericDate expirationTime = claims.getExpirationTime();
// 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
// 剩余时间
long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
if (timeRemaining < 5 * 60) {
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
if (expirationTime != null) {
// 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
// 剩余时间
long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
if (timeRemaining < 5 * 60) {
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
} else {
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
}
} else {
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
}
Long apiKeyId = claims.getClaimValue("apiKeyId", Long.class);
if (apiKeyId != null) {
UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(apiKeyId.intValue());
if (userApiKey == null || !userApiKey.isEnable()) {
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
}
}
String username = (String) claims.getClaimValue("userName");
User user = userService.getUserByUsername(username);

View File

@ -117,7 +117,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.authorizeRequests()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
.antMatchers("/api/user/login", "/index/hook/**", "/swagger-ui/**", "/doc.html").permitAll()
.antMatchers("/api/user/login", "/index/hook/**","/index/hook/abl/**", "/swagger-ui/**", "/doc.html").permitAll()
.anyRequest().authenticated()
// 异常处理器
.and()

View File

@ -1,7 +1,7 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
import gov.nist.javax.sip.message.SIPResponse;
@ -15,7 +15,7 @@ public class AudioBroadcastCatch {
public AudioBroadcastCatch(
String deviceId,
String channelId,
MediaServerItem mediaServerItem,
MediaServer mediaServerItem,
String app,
String stream,
AudioBroadcastEvent event,
@ -48,7 +48,7 @@ public class AudioBroadcastCatch {
/**
* 流媒体信息
*/
private MediaServerItem mediaServerItem;
private MediaServer mediaServerItem;
/**
* 关联的流APP
@ -109,11 +109,11 @@ public class AudioBroadcastCatch {
return sipTransactionInfo;
}
public MediaServerItem getMediaServerItem() {
public MediaServer getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServerItem mediaServerItem) {
public void setMediaServerItem(MediaServer mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}

View File

@ -1,11 +1,11 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
public class InviteStreamInfo {
public InviteStreamInfo(MediaServerItem mediaServerItem, JSONObject response, String callId, String app, String stream) {
public InviteStreamInfo(MediaServer mediaServerItem, JSONObject response, String callId, String app, String stream) {
this.mediaServerItem = mediaServerItem;
this.response = response;
this.callId = callId;
@ -13,17 +13,17 @@ public class InviteStreamInfo {
this.stream = stream;
}
private MediaServerItem mediaServerItem;
private MediaServer mediaServerItem;
private JSONObject response;
private String callId;
private String app;
private String stream;
public MediaServerItem getMediaServerItem() {
public MediaServer getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServerItem mediaServerItem) {
public void setMediaServerItem(MediaServer mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}

View File

@ -1,5 +1,7 @@
package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
public class SendRtpItem {
/**
@ -122,6 +124,39 @@ public class SendRtpItem {
*/
private String receiveStream;
public static SendRtpItem getInstance(RequestPushStreamMsg requestPushStreamMsg) {
SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setMediaServerId(requestPushStreamMsg.getMediaServerId());
sendRtpItem.setApp(requestPushStreamMsg.getApp());
sendRtpItem.setStream(requestPushStreamMsg.getStream());
sendRtpItem.setIp(requestPushStreamMsg.getIp());
sendRtpItem.setPort(requestPushStreamMsg.getPort());
sendRtpItem.setSsrc(requestPushStreamMsg.getSsrc());
sendRtpItem.setTcp(requestPushStreamMsg.isTcp());
sendRtpItem.setLocalPort(requestPushStreamMsg.getSrcPort());
sendRtpItem.setPt(requestPushStreamMsg.getPt());
sendRtpItem.setUsePs(requestPushStreamMsg.isPs());
sendRtpItem.setOnlyAudio(requestPushStreamMsg.isOnlyAudio());
return sendRtpItem;
}
public static SendRtpItem getInstance(String app, String stream, String ssrc, String dstIp, Integer dstPort, boolean tcp, int sendLocalPort, Integer pt) {
SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setApp(app);
sendRtpItem.setStream(stream);
sendRtpItem.setSsrc(ssrc);
sendRtpItem.setTcp(tcp);
sendRtpItem.setLocalPort(sendLocalPort);
sendRtpItem.setIp(dstIp);
sendRtpItem.setPort(dstPort);
if (pt != null) {
sendRtpItem.setPt(pt);
}
return sendRtpItem;
}
public String getIp() {
return ip;
}

View File

@ -1,18 +1,17 @@
package com.genersoft.iot.vmp.gb28181.event;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent;
import com.genersoft.iot.vmp.gb28181.event.device.RequestTimeoutEvent;
import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEvent;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition.MobilePositionEvent;
import com.genersoft.iot.vmp.media.zlm.event.ZLMOfflineEvent;
import com.genersoft.iot.vmp.media.zlm.event.ZLMOnlineEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent;
import javax.sip.TimeoutEvent;
import java.util.ArrayList;
import java.util.HashSet;
@ -40,14 +39,14 @@ public class EventPublisher {
applicationEventPublisher.publishEvent(alarmEvent);
}
public void zlmOfflineEventPublish(String mediaServerId){
ZLMOfflineEvent outEvent = new ZLMOfflineEvent(this);
public void mediaServerOfflineEventPublish(String mediaServerId){
MediaServerOfflineEvent outEvent = new MediaServerOfflineEvent(this);
outEvent.setMediaServerId(mediaServerId);
applicationEventPublisher.publishEvent(outEvent);
}
public void zlmOnlineEventPublish(String mediaServerId) {
ZLMOnlineEvent outEvent = new ZLMOnlineEvent(this);
public void mediaServerOnlineEventPublish(String mediaServerId) {
MediaServerOnlineEvent outEvent = new MediaServerOnlineEvent(this);
outEvent.setMediaServerId(mediaServerId);
applicationEventPublisher.publishEvent(outEvent);
}

View File

@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.gb28181.session;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -18,11 +20,14 @@ import java.util.stream.Stream;
@Component
public class AudioBroadcastManager {
private final static Logger logger = LoggerFactory.getLogger(AudioBroadcastManager.class);
@Autowired
private SipConfig config;
public static Map<String, AudioBroadcastCatch> data = new ConcurrentHashMap<>();
public void update(AudioBroadcastCatch audioBroadcastCatch) {
if (SipUtils.isFrontEnd(audioBroadcastCatch.getDeviceId())) {
audioBroadcastCatch.setChannelId(audioBroadcastCatch.getDeviceId());

View File

@ -1,16 +1,14 @@
package com.genersoft.iot.vmp.gb28181.task;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IPlatformService;
import com.genersoft.iot.vmp.service.impl.PlatformServiceImpl;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -53,9 +51,6 @@ public class SipRunner implements CommandLineRunner {
@Autowired
private IDeviceService deviceService;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private IMediaServerService mediaServerService;
@ -105,17 +100,12 @@ public class SipRunner implements CommandLineRunner {
List<SendRtpItem> sendRtpItems = redisCatchStorage.queryAllSendRTPServer();
if (sendRtpItems.size() > 0) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServer mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStream());
if (mediaServerItem != null) {
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
Map<String, Object> param = new HashMap<>();
param.put("vhost","__defaultVhost__");
param.put("app",sendRtpItem.getApp());
param.put("stream",sendRtpItem.getStream());
param.put("ssrc",sendRtpItem.getSsrc());
JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
if (jsonObject != null && jsonObject.getInteger("code") == 0) {
boolean stopResult = mediaServerService.stopSendRtp(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
if (stopResult) {
ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
if (platform != null) {
try {

View File

@ -7,8 +7,8 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import gov.nist.javax.sip.message.SIPRequest;
@ -100,7 +100,7 @@ public interface ISIPCommander {
* @param device 视频设备
* @param channel 预览通道
*/
void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void playStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
/**
* 请求回放视频流
@ -110,7 +110,7 @@ public interface ISIPCommander {
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
/**
* 请求历史媒体下载
@ -121,9 +121,9 @@ public interface ISIPCommander {
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
* @param downloadSpeed 下载倍速参数
*/
void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
String startTime, String endTime, int downloadSpeed, ZlmHttpHookSubscribe.Event hookEvent,
SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
String startTime, String endTime, int downloadSpeed, HookSubscribe.Event hookEvent,
SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
/**
@ -131,7 +131,7 @@ public interface ISIPCommander {
*/
void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void talkStreamCmd(MediaServer mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException;

View File

@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
@ -154,8 +154,8 @@ public interface ISIPCommanderForPlatform {
void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServer mediaServerItem,
SSRCInfo ssrcInfo, HookSubscribe.Event event, SipSubscribe.Event okEvent,
SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException;
void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;

View File

@ -7,10 +7,6 @@ import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@ -18,14 +14,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamPush;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.utils.DateUtil;
import gov.nist.javax.sip.message.SIPRequest;
@ -44,7 +37,6 @@ import javax.sip.SipFactory;
import javax.sip.header.CallIdHeader;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
/**
@ -77,13 +69,11 @@ public class SIPCommander implements ISIPCommander {
private UserSetting userSetting;
@Autowired
private ZlmHttpHookSubscribe subscribe;
private HookSubscribe subscribe;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private ZLMServerFactory zlmServerFactory;
/**
@ -275,8 +265,8 @@ public class SIPCommander implements ISIPCommander {
* @param errorEvent sip错误订阅
*/
@Override
public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel,
ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
public void playStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel,
HookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
String stream = ssrcInfo.getStream();
if (device == null) {
@ -284,11 +274,11 @@ public class SIPCommander implements ISIPCommander {
}
logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId());
subscribe.addSubscribe(rtpHook, (hookData) -> {
if (event != null) {
event.response(mediaServerItemInUse, hookParam);
subscribe.removeSubscribe(hookSubscribe);
event.response(hookData);
subscribe.removeSubscribe(rtpHook);
}
});
String sdpIp;
@ -384,8 +374,8 @@ public class SIPCommander implements ISIPCommander {
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
@Override
public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
String startTime, String endTime, ZlmHttpHookSubscribe.Event hookEvent,
public void playbackStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
String startTime, String endTime, HookSubscribe.Event hookEvent,
SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
@ -458,13 +448,13 @@ public class SIPCommander implements ISIPCommander {
//ssrc
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcInfo.getStream(), mediaServerItem.getId());
// 添加订阅
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
subscribe.addSubscribe(rtpHook, (hookData) -> {
if (hookEvent != null) {
hookEvent.response(mediaServerItemInUse, hookParam);
hookEvent.response(hookData);
}
subscribe.removeSubscribe(hookSubscribe);
subscribe.removeSubscribe(rtpHook);
});
Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
@ -486,10 +476,10 @@ public class SIPCommander implements ISIPCommander {
* @param downloadSpeed 下载倍速参数
*/
@Override
public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
public void downloadStreamCmd(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
String startTime, String endTime, int downloadSpeed,
ZlmHttpHookSubscribe.Event hookEvent,
SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
HookSubscribe.Event hookEvent,
SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
String sdpIp;
@ -559,19 +549,18 @@ public class SIPCommander implements ISIPCommander {
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcInfo.getStream(), mediaServerItem.getId());
// 添加订阅
CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
String callId= newCallIdHeader.getCallId();
subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {
subscribe.addSubscribe(rtpHook, (hookData) -> {
logger.debug("sipc 添加订阅===callId {}",callId);
hookEvent.response(mediaServerItemInUse, hookParam);
subscribe.removeSubscribe(hookSubscribe);
hookSubscribe.getContent().put("regist", false);
hookSubscribe.getContent().put("schema", "rtsp");
hookEvent.response(hookData);
subscribe.removeSubscribe(rtpHook);
// 添加流注销的订阅注销了后向设备发送bye
subscribe.addSubscribe(hookSubscribe,
(mediaServerItemForEnd, hookParam1) -> {
Hook departureHook = Hook.getInstance(HookType.on_media_departure, "rtp", ssrcInfo.getStream(), mediaServerItem.getId());
subscribe.addSubscribe(departureHook,
(departureHookData) -> {
logger.info("[录像]下载结束, 发送BYE");
try {
streamByeCmd(device, channelId, ssrcInfo.getStream(), callId);
@ -595,7 +584,7 @@ public class SIPCommander implements ISIPCommander {
}
@Override
public void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
public void talkStreamCmd(MediaServer mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
String stream = sendRtpItem.getStream();
@ -609,20 +598,20 @@ public class SIPCommander implements ISIPCommander {
}
logger.info("[语音喊话] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), sendRtpItem.getPort());
HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
subscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItemInUse, hookParam) -> {
Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId());
subscribe.addSubscribe(hook, (hookData) -> {
if (event != null) {
event.response(mediaServerItemInUse, hookParam);
subscribe.removeSubscribe(hookSubscribeForStreamChange);
event.response(hookData);
subscribe.removeSubscribe(hook);
}
});
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
callIdHeader.setCallId(callId);
HookSubscribeForStreamPush hookSubscribeForStreamPush = HookSubscribeFactory.on_publish("rtp", stream, null, mediaServerItem.getId());
subscribe.addSubscribe(hookSubscribeForStreamPush, (mediaServerItemInUse, hookParam) -> {
Hook publishHook = Hook.getInstance(HookType.on_publish, "rtp", stream, mediaServerItem.getId());
subscribe.addSubscribe(publishHook, (hookData) -> {
if (eventForPush != null) {
eventForPush.response(mediaServerItemInUse, hookParam);
eventForPush.response(hookData);
}
});
//
@ -1265,7 +1254,6 @@ public class SIPCommander implements ISIPCommander {
* @param startPriority 报警起始级别可选
* @param endPriority 报警终止级别可选
* @param alarmMethod 报警方式条件可选
* @param alarmType 报警类型
* @param startTime 报警发生起始时间可选
* @param endTime 报警发生终止时间可选
* @return true = 命令发送成功

View File

@ -13,13 +13,11 @@ import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -66,9 +64,6 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
@Autowired
private SipSubscribe sipSubscribe;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private SipLayer sipLayer;
@ -76,7 +71,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
private SIPSender sipSender;
@Autowired
private ZlmHttpHookSubscribe subscribe;
private HookSubscribe subscribe;
@Autowired
private UserSetting userSetting;
@ -844,10 +839,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}
logger.info("[向上级发送BYE] {}/{}", platform.getServerGBId(), sendRtpItem.getChannelId());
String mediaServerId = sendRtpItem.getMediaServerId();
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
if (mediaServerItem != null) {
mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
zlmServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStream());
mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStream());
}
SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
if (byeRequest == null) {
@ -895,8 +890,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}
@Override
public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServer mediaServerItem,
SSRCInfo ssrcInfo, HookSubscribe.Event event, SipSubscribe.Event okEvent,
SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException {
String stream = ssrcInfo.getStream();
@ -905,11 +900,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
}
logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId());
subscribe.addSubscribe(hook, (hookData) -> {
if (event != null) {
event.response(mediaServerItemInUse, hookParam);
subscribe.removeSubscribe(hookSubscribe);
event.response(hookData);
subscribe.removeSubscribe(hook);
}
});
String sdpIp = mediaServerItem.getSdpIp();
@ -949,7 +944,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), request, (e -> {
streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream());
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
subscribe.removeSubscribe(hookSubscribe);
subscribe.removeSubscribe(hook);
errorEvent.response(e);
}), e -> {
ResponseEvent responseEvent = (ResponseEvent) e.event;

View File

@ -1,19 +1,17 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
@ -31,8 +29,6 @@ import javax.sip.header.CallIdHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderAddress;
import javax.sip.header.ToHeader;
import java.util.HashMap;
import java.util.Map;
/**
* SIP命令类型 ACK请求
@ -65,12 +61,6 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
@Autowired
private IDeviceService deviceService;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private ZlmHttpHookSubscribe hookSubscribe;
@Autowired
private IMediaServerService mediaServerService;
@ -104,7 +94,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
logger.info("收到ACKrtp/{} TCP主动方式后续处理", sendRtpItem.getStream());
return;
}
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
logger.info("收到ACKrtp/{}开始向上级推流, 目标={}:{}SSRC={}, 协议:{}",
sendRtpItem.getStream(),
sendRtpItem.getIp(),
@ -115,19 +105,21 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(fromUserId);
if (parentPlatform != null) {
Map<String, Object> param = getSendRtpParam(sendRtpItem);
if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) {
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(
sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStream(),
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(),
sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio());
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, json -> {
playService.startSendRtpStreamHand(sendRtpItem, parentPlatform, json, param, callIdHeader);
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(sendRtpItem);
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, () -> {
playService.startSendRtpStreamFailHand(sendRtpItem, parentPlatform, callIdHeader);
});
} else {
JSONObject startSendRtpStreamResult = sendRtp(sendRtpItem, mediaInfo, param);
if (startSendRtpStreamResult != null) {
playService.startSendRtpStreamHand(sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
try {
if (sendRtpItem.isTcpActive()) {
mediaServerService.startSendRtpPassive(mediaInfo, parentPlatform, sendRtpItem, null);
} else {
mediaServerService.startSendRtp(mediaInfo, parentPlatform, sendRtpItem);
}
}catch (ControllerException e) {
logger.error("RTP推流失败: {}", e.getMessage());
playService.startSendRtpStreamFailHand(sendRtpItem, parentPlatform, callIdHeader);
}
}
}else {
@ -144,56 +136,17 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
logger.warn("[收到ACK]:来自{},目标为({})的推流信息为找到流体服务[{}]信息",fromUserId, toUserId, sendRtpItem.getMediaServerId());
return;
}
Map<String, Object> param = getSendRtpParam(sendRtpItem);
JSONObject startSendRtpStreamResult = sendRtp(sendRtpItem, mediaInfo, param);
if (startSendRtpStreamResult != null) {
playService.startSendRtpStreamHand(sendRtpItem, device, startSendRtpStreamResult, param, callIdHeader);
try {
if (sendRtpItem.isTcpActive()) {
mediaServerService.startSendRtpPassive(mediaInfo, null, sendRtpItem, null);
} else {
mediaServerService.startSendRtp(mediaInfo, null, sendRtpItem);
}
}catch (ControllerException e) {
logger.error("RTP推流失败: {}", e.getMessage());
playService.startSendRtpStreamFailHand(sendRtpItem, null, callIdHeader);
}
}
}
private Map<String, Object> getSendRtpParam(SendRtpItem sendRtpItem) {
String isUdp = sendRtpItem.isTcp() ? "0" : "1";
Map<String, Object> param = new HashMap<>(12);
param.put("vhost","__defaultVhost__");
param.put("app",sendRtpItem.getApp());
param.put("stream",sendRtpItem.getStream());
param.put("ssrc", sendRtpItem.getSsrc());
param.put("dst_url",sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
param.put("src_port", sendRtpItem.getLocalPort());
param.put("pt", sendRtpItem.getPt());
param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
param.put("is_udp", isUdp);
if (!sendRtpItem.isTcp()) {
// udp模式下开启rtcp保活
param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
}
return param;
}
private JSONObject sendRtp(SendRtpItem sendRtpItem, MediaServerItem mediaInfo, Map<String, Object> param){
JSONObject startSendRtpStreamResult = null;
if (sendRtpItem.getLocalPort() != 0) {
if (sendRtpItem.isTcpActive()) {
startSendRtpStreamResult = zlmServerFactory.startSendRtpPassive(mediaInfo, param);
}else {
param.put("dst_url", sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
startSendRtpStreamResult = zlmServerFactory.startSendRtpStream(mediaInfo, param);
}
}else {
if (sendRtpItem.isTcpActive()) {
startSendRtpStreamResult = zlmServerFactory.startSendRtpPassive(mediaInfo, param);
}else {
param.put("dst_url", sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
startSendRtpStreamResult = zlmServerFactory.startSendRtpStream(mediaInfo, param);
}
}
return startSendRtpStreamResult;
}
}

View File

@ -6,15 +6,15 @@ import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
@ -35,8 +35,6 @@ import javax.sip.SipException;
import javax.sip.header.CallIdHeader;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
/**
* SIP命令类型 BYE请求
@ -74,12 +72,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
@Autowired
private IVideoManagerStorage storager;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private SSRCFactory ssrcFactory;
@Autowired
private IMediaServerService mediaServerService;
@ -109,7 +101,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
/**
* 处理BYE请求
* @param evt
*/
@Override
public void process(RequestEvent evt) {
@ -127,11 +118,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
logger.info("[收到bye] 来自{},停止通道:{}, 类型: {}, callId: {}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getPlayType(), callIdHeader.getCallId());
String streamId = sendRtpItem.getStream();
Map<String, Object> param = new HashMap<>();
param.put("vhost","__defaultVhost__");
param.put("app",sendRtpItem.getApp());
param.put("stream",streamId);
param.put("ssrc",sendRtpItem.getSsrc());
logger.info("[收到bye] 停止推流:{}, 媒体节点: {}", streamId, sendRtpItem.getMediaServerId());
if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
@ -145,10 +131,10 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
redisGbPlayMsgListener.sendMsgForStopSendRtpStream(sendRtpItem.getServerId(), streamMsg);
}
}else {
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
callIdHeader.getCallId(), null);
zlmServerFactory.stopSendRtpStream(mediaInfo, param);
mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
if (userSetting.getUseCustomSsrcForParentInvite()) {
mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
}
@ -165,16 +151,16 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
}
}
}else {
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
callIdHeader.getCallId(), null);
zlmServerFactory.stopSendRtpStream(mediaInfo, param);
mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
if (userSetting.getUseCustomSsrcForParentInvite()) {
mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
}
}
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
if (mediaInfo != null) {
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
if (mediaServer != null) {
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
if (audioBroadcastCatch != null && audioBroadcastCatch.getSipTransactionInfo().getCallId().equals(callIdHeader.getCallId())) {
// 来自上级平台的停止对讲
@ -182,8 +168,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
}
int totalReaderCount = zlmServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
if (totalReaderCount <= 0) {
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), streamId);
if (mediaInfo.getReaderCount() <= 0) {
logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
@ -245,7 +232,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
}
}
// 释放ssrc
MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
MediaServer mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
if (mediaServerItem != null) {
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
}

View File

@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
@ -18,12 +19,18 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.*;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.IInviteStreamService;
import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
@ -54,7 +61,6 @@ import javax.sip.header.CallIdHeader;
import javax.sip.message.Response;
import java.text.ParseException;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
@ -106,14 +112,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
@Autowired
private AudioBroadcastManager audioBroadcastManager;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private ZlmHttpHookSubscribe zlmHttpHookSubscribe;
private HookSubscribe hookSubscribe;
@Autowired
private SIPProcessorObserver sipProcessorObserver;
@ -192,7 +195,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
PlatformCatalog catalog = storager.getCatalog(requesterId, channelId);
MediaServerItem mediaServerItem = null;
MediaServer mediaServerItem = null;
StreamPushItem streamPushItem = null;
StreamProxyItem proxyByAppAndStream = null;
// 不是通道可能是直播流
@ -375,8 +378,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} else {
streamTypeStr = "UDP";
}
logger.info("[上级Invite] {}, 平台:{} 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc);
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
logger.info("[上级Invite] {}, 平台:{} 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc{}",
sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc);
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp());
if (tcpActive != null) {
@ -398,7 +402,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
Long finalStopTime = stopTime;
ErrorCallback<Object> hookEvent = (code, msg, data) -> {
StreamInfo streamInfo = (StreamInfo)data;
MediaServerItem mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId());
MediaServer mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId());
logger.info("[上级Invite]下级已经开始推流。 回复200OK(SDP) {}/{}", streamInfo.getApp(), streamInfo.getStream());
// * 0 等待设备推流上来
// * 1 下级已经推流等待上级平台回复ack
@ -455,42 +459,21 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
responseSdpAck(request, content.toString(), platform);
// tcp主动模式回复sdp后开启监听
if (sendRtpItem.isTcpActive()) {
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
Map<String, Object> param = new HashMap<>(12);
param.put("vhost","__defaultVhost__");
param.put("app",sendRtpItem.getApp());
param.put("stream",sendRtpItem.getStream());
param.put("ssrc", sendRtpItem.getSsrc());
if (!sendRtpItem.isTcpActive()) {
param.put("dst_url",sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
}
String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
param.put("is_udp", is_Udp);
param.put("src_port", localPort);
param.put("pt", sendRtpItem.getPt());
param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
if (!sendRtpItem.isTcp()) {
// 开启rtcp保活
param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
}
JSONObject startSendRtpStreamResult = zlmServerFactory.startSendRtpPassive(mediaInfo, param);
if (startSendRtpStreamResult != null) {
startSendRtpStreamHand(evt, sendRtpItem, null, startSendRtpStreamResult, param, callIdHeader);
}
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
try {
mediaServerService.startSendRtpPassive(mediaServer, platform, sendRtpItem, 5);
}catch (ControllerException e) {}
}
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 回复SdpAck", e);
}
};
ErrorCallback<Object> errorEvent = ((statusCode, msg, data) -> {
logger.info("[上级Invite] {}, 失败, 平台:{} 通道:{}, code {} msg{}", sessionName, username, channelId, statusCode, msg);
// 未知错误直接转发设备点播的错误
try {
if (statusCode > 0) {
Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
}
Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
} catch (ParseException | SipException e) {
logger.error("未处理的异常 ", e);
}
@ -501,7 +484,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
String startTimeStr = DateUtil.urlFormatter.format(start);
String endTimeStr = DateUtil.urlFormatter.format(end);
String stream = device.getDeviceId() + "_" + channelId + "_" + startTimeStr + "_" + endTimeStr;
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0,false, false, device.getStreamModeForParam());
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0,false,!channel.isHasAudio(), false, device.getStreamModeForParam());
sendRtpItem.setStream(stream);
// 写入redis 超时时回复
redisCatchStorage.updateSendRTPSever(sendRtpItem);
@ -531,7 +514,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD);
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.isHasAudio(), false, device.getStreamModeForParam());
sendRtpItem.setStream(ssrcInfo.getStream());
// 写入redis 超时时回复
redisCatchStorage.updateSendRTPSever(sendRtpItem);
@ -581,12 +564,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
if ("push".equals(gbStream.getStreamType())) {
if (streamPushItem != null) {
// 从redis查询是否正在接收这个推流
OnStreamChangedHookParam pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream());
StreamPushItem pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream());
if (pushListItem != null) {
StreamPushItem transform = streamPushService.transform(pushListItem);
transform.setSelf(userSetting.getServerId().equals(pushListItem.getSeverId()));
pushListItem.setSelf(userSetting.getServerId().equals(pushListItem.getServerId()));
// 推流状态
pushStream(evt, request, gbStream, transform, platform, callIdHeader, mediaServerItem, port, tcpActive,
pushStream(evt, request, gbStream, pushListItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
}else {
// 未推流 拉起
@ -633,13 +615,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
* 安排推流
*/
private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform,
CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
CallIdHeader callIdHeader, MediaServer mediaServer,
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
String channelId, String addressStr, String ssrc, String requesterId) {
Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
Boolean streamReady = mediaServerService.isStreamReady(mediaServer, gbStream.getApp(), gbStream.getStream());
if (streamReady != null && streamReady) {
// 自平台内容
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServer, addressStr, port, ssrc, requesterId,
gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
if (sendRtpItem == null) {
@ -660,7 +643,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
sendRtpItem.setCallId(callIdHeader.getCallId());
sendRtpItem.setFromTag(request.getFromTag());
SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
SIPResponse response = sendStreamAck(mediaServer, request, sendRtpItem, platform, evt);
if (response != null) {
sendRtpItem.setToTag(response.getToTag());
}
@ -671,15 +654,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
CallIdHeader callIdHeader, MediaServer mediaServerItem,
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
String channelId, String addressStr, String ssrc, String requesterId) {
// 推流
if (streamPushItem.isSelf()) {
Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
if (streamReady != null && streamReady) {
// 自平台内容
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
if (sendRtpItem == null) {
@ -723,24 +706,23 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
* 通知流上线
*/
private void notifyStreamOnline(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
CallIdHeader callIdHeader, MediaServer mediaServerItem,
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
String channelId, String addressStr, String ssrc, String requesterId) {
if ("proxy".equals(gbStream.getStreamType())) {
// TODO 控制启用以使设备上线
logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
// 监听流上线
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(gbStream.getApp(), gbStream.getStream(), true, "rtsp", mediaServerItem.getId());
zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, hookParam) -> {
OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam;
logger.info("[上级点播]拉流代理已经就绪, {}/{}", streamChangedHookParam.getApp(), streamChangedHookParam.getStream());
Hook hook = Hook.getInstance(HookType.on_media_arrival, gbStream.getApp(), gbStream.getStream(), mediaServerItem.getId());
this.hookSubscribe.addSubscribe(hook, (hookData) -> {
logger.info("[上级点播]拉流代理已经就绪, {}/{}", hookData.getApp(), hookData.getStream());
dynamicTask.stop(callIdHeader.getCallId());
pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
});
dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream());
zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
this.hookSubscribe.removeSubscribe(hook);
}, userSetting.getPlatformPlayTimeout());
boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream());
if (!start) {
@ -749,7 +731,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
}
zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
this.hookSubscribe.removeSubscribe(hook);
dynamicTask.stop(callIdHeader.getCallId());
}
} else if ("push".equals(gbStream.getStreamType())) {
@ -790,7 +772,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
dynamicTask.stop(callIdHeader.getCallId());
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
if (serverId.equals(userSetting.getServerId())) {
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
app, stream, channelId, mediaTransmissionTCP, platform.isRtcp());
if (sendRtpItem == null) {
@ -846,7 +828,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
* 来自其他wvp的推流
*/
private void otherWvpPushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
CallIdHeader callIdHeader, MediaServer mediaServerItem,
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
String channelId, String addressStr, String ssrc, String requesterId) {
logger.info("[级联点播]直播流来自其他平台发送redis消息");
@ -909,7 +891,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
});
}
public SIPResponse sendStreamAck(MediaServerItem mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
public SIPResponse sendStreamAck(MediaServer mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
String sdpIp = mediaServerItem.getSdpIp();
if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) {
@ -1055,7 +1037,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
logger.info("设备{}请求语音流,地址:{}:{}ssrc{}, {}", requesterId, addressStr, port, gb28181Sdp.getSsrc(),
mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP");
MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem();
MediaServer mediaServerItem = broadcastCatch.getMediaServerItem();
if (mediaServerItem == null) {
logger.warn("未找到语音喊话使用的zlm");
try {
@ -1070,7 +1052,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP", sdp.getSessionName().getValue());
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, gb28181Sdp.getSsrc(), requesterId,
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, gb28181Sdp.getSsrc(), requesterId,
device.getDeviceId(), broadcastCatch.getChannelId(),
mediaTransmissionTCP, false);
@ -1104,7 +1086,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
redisCatchStorage.updateSendRTPSever(sendRtpItem);
Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream());
Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream());
if (streamReady) {
sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, gb28181Sdp.getSsrc());
} else {
@ -1132,7 +1114,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
}
SIPResponse sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServerItem mediaServerItem, boolean mediaTransmissionTCP, String ssrc) {
SIPResponse sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServer mediaServerItem, boolean mediaTransmissionTCP, String ssrc) {
SIPResponse sipResponse = null;
try {
sendRtpItem.setStatus(2);

View File

@ -37,7 +37,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
private final String method = "MESSAGE";
private static Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
private static final Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
@Autowired
private SIPProcessorObserver sipProcessorObserver;
@ -100,9 +100,9 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
deviceNotFoundEvent.setCallId(callIdHeader.getCallId());
SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent);
sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
};
}
}else {
Element rootElement = null;
Element rootElement;
try {
rootElement = getRootElement(evt);
if (rootElement == null) {
@ -110,24 +110,24 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
responseAck(request, Response.BAD_REQUEST, "content is null");
return;
}
String name = rootElement.getName();
IMessageHandler messageHandler = messageHandlerMap.get(name);
if (messageHandler != null) {
if (device != null) {
messageHandler.handForDevice(evt, device, rootElement);
}else { // 由于上面已经判断都为null则直接返回所以这里device和parentPlatform必有一个不为null
messageHandler.handForPlatform(evt, parentPlatform, rootElement);
}
}else {
// 不支持的message
// 不存在则回复415
responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
}
} catch (DocumentException e) {
logger.warn("解析XML消息内容异常", e);
// 不存在则回复404
responseAck(request, Response.BAD_REQUEST, e.getMessage());
}
String name = rootElement.getName();
IMessageHandler messageHandler = messageHandlerMap.get(name);
if (messageHandler != null) {
if (device != null) {
messageHandler.handForDevice(evt, device, rootElement);
}else { // 由于上面已经判断都为null则直接返回所以这里device和parentPlatform必有一个不为null
messageHandler.handForPlatform(evt, parentPlatform, rootElement);
}
}else {
// 不支持的message
// 不存在则回复415
responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
}
}
} catch (SipException e) {
logger.warn("SIP 回复错误", e);

View File

@ -1,18 +1,15 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IPlatformService;
import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -64,9 +61,6 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
@Autowired
private AudioBroadcastManager audioBroadcastManager;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@ -121,15 +115,14 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
return;
}
MediaServerItem mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad(null);
MediaServer mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad(null);
commanderForPlatform.broadcastResultCmd(platform, deviceChannel, sn, true, eventResult->{
logger.info("[国标级联] 语音喊话 回复失败 platform {} 错误:{}/{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg);
}, eventResult->{
// 消息发送成功 向上级发送invite获取推流
try {
platformService.broadcastInvite(platform, deviceChannel.getChannelId(), mediaServerForMinimumLoad, (mediaServerItem, hookParam)->{
OnStreamChangedHookParam streamChangedHookParam = (OnStreamChangedHookParam)hookParam;
platformService.broadcastInvite(platform, deviceChannel.getChannelId(), mediaServerForMinimumLoad, (hookData)->{
// 上级平台推流成功
AudioBroadcastCatch broadcastCatch = audioBroadcastManager.get(device.getDeviceId(), targetId);
if (broadcastCatch != null ) {
@ -137,20 +130,20 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
logger.info("[国标级联] 语音喊话 设备正在使用中 platform {} channel: {}",
platform.getServerGBId(), deviceChannel.getChannelId());
// 查看语音通道已经建立且已经占用 回复BYE
platformService.stopBroadcast(platform, deviceChannel, streamChangedHookParam.getStream(), true, mediaServerItem);
platformService.stopBroadcast(platform, deviceChannel, hookData.getStream(), true, hookData.getMediaServer());
}else {
// 查看语音通道已经建立但是未占用
broadcastCatch.setApp(streamChangedHookParam.getApp());
broadcastCatch.setStream(streamChangedHookParam.getStream());
broadcastCatch.setMediaServerItem(mediaServerItem);
broadcastCatch.setApp(hookData.getApp());
broadcastCatch.setStream(hookData.getStream());
broadcastCatch.setMediaServerItem(hookData.getMediaServer());
audioBroadcastManager.update(broadcastCatch);
// 推流到设备
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, targetId, streamChangedHookParam.getStream(), null);
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, targetId, hookData.getStream(), null);
if (sendRtpItem == null) {
logger.warn("[国标级联] 语音喊话 异常,未找到发流信息, channelId: {}, stream: {}", targetId, streamChangedHookParam.getStream());
logger.info("[国标级联] 语音喊话 重新开始channelId: {}, stream: {}", targetId, streamChangedHookParam.getStream());
logger.warn("[国标级联] 语音喊话 异常,未找到发流信息, channelId: {}, stream: {}", targetId, hookData.getStream());
logger.info("[国标级联] 语音喊话 重新开始channelId: {}, stream: {}", targetId, hookData.getStream());
try {
playService.audioBroadcastCmd(device, targetId, mediaServerItem, streamChangedHookParam.getApp(), streamChangedHookParam.getStream(), 60, true, msg -> {
playService.audioBroadcastCmd(device, targetId, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> {
logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId);
});
} catch (SipException | InvalidArgumentException | ParseException e) {
@ -158,17 +151,18 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp
}
}else {
// 发流
JSONObject jsonObject = zlmServerFactory.startSendRtp(mediaServerItem, sendRtpItem);
if (jsonObject != null && jsonObject.getInteger("code") == 0 ) {
logger.info("[语音喊话] 自动推流成功, device: {}, channel: {}", device.getDeviceId(), targetId);
}else {
logger.info("[语音喊话] 推流失败, 结果: {}", jsonObject);
try {
mediaServerService.startSendRtp(hookData.getMediaServer(),null, sendRtpItem);
}catch (ControllerException e) {
logger.info("[语音喊话] 推流失败, 结果: {}", e.getMessage());
return;
}
logger.info("[语音喊话] 自动推流成功, device: {}, channel: {}", device.getDeviceId(), targetId);
}
}
}else {
try {
playService.audioBroadcastCmd(device, targetId, mediaServerItem, streamChangedHookParam.getApp(), streamChangedHookParam.getStream(), 60, true, msg -> {
playService.audioBroadcastCmd(device, targetId, hookData.getMediaServer(), hookData.getApp(), hookData.getStream(), 60, true, msg -> {
logger.info("[语音喊话] 通道建立成功, device: {}, channel: {}", device.getDeviceId(), targetId);
});
} catch (SipException | InvalidArgumentException | ParseException e) {

View File

@ -13,9 +13,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.service.IInviteStreamService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@ -64,7 +64,7 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
private VideoStreamSessionManager sessionManager;
@Autowired
private ZlmHttpHookSubscribe subscribe;
private HookSubscribe subscribe;
@Autowired
private IInviteStreamService inviteStreamService;
@ -106,9 +106,8 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage());
}
// 去除监听流注销自动停止下载的监听
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcTransaction.getStream(), false, "rtsp", ssrcTransaction.getMediaServerId());
subscribe.removeSubscribe(hookSubscribe);
Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", ssrcTransaction.getStream(), ssrcTransaction.getMediaServerId());
subscribe.removeSubscribe(hook);
// 如果级联播放需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题需要将点播CallId进行上下级绑定
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
if (sendRtpItem != null) {

View File

@ -0,0 +1,64 @@
package com.genersoft.iot.vmp.media;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 启动是从配置文件加载节点信息以及发送个节点状态管理去控制节点状态
*/
@Component
@Order(value=12)
public class MediaServerConfig implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(MediaServerConfig.class);
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private MediaConfig mediaConfig;
@Override
public void run(String... strings) throws Exception {
// 清理所有在线节点的缓存信息
mediaServerService.clearMediaServerForOnline();
MediaServer defaultMediaServer = mediaServerService.getDefaultMediaServer();
MediaServer mediaSerItemInConfig = mediaConfig.getMediaSerItem();
if (defaultMediaServer != null && mediaSerItemInConfig.getId().equals(defaultMediaServer.getId())) {
mediaServerService.update(mediaSerItemInConfig);
}else {
if (defaultMediaServer != null) {
mediaServerService.delete(defaultMediaServer.getId());
}
MediaServer mediaServerItem = mediaServerService.getOneFromDatabase(mediaSerItemInConfig.getId());
if (mediaServerItem == null) {
mediaServerService.add(mediaSerItemInConfig);
}else {
mediaServerService.update(mediaSerItemInConfig);
}
}
// 发送媒体节点变化事件
mediaServerService.syncCatchFromDatabase();
// 获取所有的zlm 并开启主动连接
List<MediaServer> all = mediaServerService.getAllFromDatabase();
logger.info("[媒体节点] 加载节点列表, 共{}个节点", all.size());
MediaServerChangeEvent event = new MediaServerChangeEvent(this);
event.setMediaServerItemList(all);
applicationEventPublisher.publishEvent(event);
}
}

View File

@ -0,0 +1,306 @@
package com.genersoft.iot.vmp.media.bean;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
/**
* 视频信息
*/
@Schema(description = "视频信息")
public class MediaInfo {
@Schema(description = "应用名")
private String app;
@Schema(description = "流ID")
private String stream;
@Schema(description = "流媒体节点")
private MediaServer mediaServer;
@Schema(description = "协议")
private String schema;
@Schema(description = "观看人数")
private Integer readerCount;
@Schema(description = "视频编码类型")
private String videoCodec;
@Schema(description = "视频宽度")
private Integer width;
@Schema(description = "视频高度")
private Integer height;
@Schema(description = "音频编码类型")
private String audioCodec;
@Schema(description = "音频通道数")
private Integer audioChannels;
@Schema(description = "音频采样率")
private Integer audioSampleRate;
@Schema(description = "音频采样率")
private Long duration;
@Schema(description = "在线")
private Boolean online;
@Schema(description = "unknown = 0,rtmp_push=1,rtsp_push=2,rtp_push=3,pull=4,ffmpeg_pull=5,mp4_vod=6,device_chn=7")
private Integer originType;
@Schema(description = "存活时间,单位秒")
private Long aliveSecond;
@Schema(description = "数据产生速度单位byte/s")
private Long bytesSpeed;
public static MediaInfo getInstance(JSONObject jsonObject, MediaServer mediaServer) {
MediaInfo mediaInfo = new MediaInfo();
mediaInfo.setMediaServer(mediaServer);
String app = jsonObject.getString("app");
mediaInfo.setApp(app);
String stream = jsonObject.getString("stream");
mediaInfo.setStream(stream);
String schema = jsonObject.getString("schema");
mediaInfo.setSchema(schema);
Integer totalReaderCount = jsonObject.getInteger("totalReaderCount");
Boolean online = jsonObject.getBoolean("online");
Integer originType = jsonObject.getInteger("originType");
Long aliveSecond = jsonObject.getLong("aliveSecond");
Long bytesSpeed = jsonObject.getLong("bytesSpeed");
if (totalReaderCount != null) {
mediaInfo.setReaderCount(totalReaderCount);
}
if (online != null) {
mediaInfo.setOnline(online);
}
if (originType != null) {
mediaInfo.setOriginType(originType);
}
if (aliveSecond != null) {
mediaInfo.setAliveSecond(aliveSecond);
}
if (bytesSpeed != null) {
mediaInfo.setBytesSpeed(bytesSpeed);
}
JSONArray jsonArray = jsonObject.getJSONArray("tracks");
if (jsonArray.isEmpty()) {
return null;
}
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject trackJson = jsonArray.getJSONObject(i);
Integer channels = trackJson.getInteger("channels");
Integer codecId = trackJson.getInteger("codec_id");
Integer codecType = trackJson.getInteger("codec_type");
Integer sampleRate = trackJson.getInteger("sample_rate");
Integer height = trackJson.getInteger("height");
Integer width = trackJson.getInteger("height");
Long duration = trackJson.getLongValue("duration");
if (channels != null) {
mediaInfo.setAudioChannels(channels);
}
if (sampleRate != null) {
mediaInfo.setAudioSampleRate(sampleRate);
}
if (height != null) {
mediaInfo.setHeight(height);
}
if (width != null) {
mediaInfo.setWidth(width);
}
if (duration > 0L) {
mediaInfo.setDuration(duration);
}
if (codecId != null) {
switch (codecId) {
case 0:
mediaInfo.setVideoCodec("H264");
break;
case 1:
mediaInfo.setVideoCodec("H265");
break;
case 2:
mediaInfo.setAudioCodec("AAC");
break;
case 3:
mediaInfo.setAudioCodec("G711A");
break;
case 4:
mediaInfo.setAudioCodec("G711U");
break;
}
}
}
return mediaInfo;
}
public static MediaInfo getInstance(OnStreamChangedHookParam param, MediaServer mediaServer) {
MediaInfo mediaInfo = new MediaInfo();
mediaInfo.setApp(param.getApp());
mediaInfo.setStream(param.getStream());
mediaInfo.setSchema(param.getSchema());
mediaInfo.setMediaServer(mediaServer);
mediaInfo.setReaderCount(param.getTotalReaderCount());
mediaInfo.setOnline(param.isRegist());
mediaInfo.setOriginType(param.getOriginType());
mediaInfo.setAliveSecond(param.getAliveSecond());
mediaInfo.setBytesSpeed(param.getBytesSpeed());
List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
if (tracks == null || tracks.isEmpty()) {
return mediaInfo;
}
for (OnStreamChangedHookParam.MediaTrack mediaTrack : tracks) {
switch (mediaTrack.getCodec_id()) {
case 0:
mediaInfo.setVideoCodec("H264");
break;
case 1:
mediaInfo.setVideoCodec("H265");
break;
case 2:
mediaInfo.setAudioCodec("AAC");
break;
case 3:
mediaInfo.setAudioCodec("G711A");
break;
case 4:
mediaInfo.setAudioCodec("G711U");
break;
}
if (mediaTrack.getSample_rate() > 0) {
mediaInfo.setAudioSampleRate(mediaTrack.getSample_rate());
}
if (mediaTrack.getChannels() > 0) {
mediaInfo.setAudioChannels(mediaTrack.getChannels());
}
if (mediaTrack.getHeight() > 0) {
mediaInfo.setHeight(mediaTrack.getHeight());
}
if (mediaTrack.getWidth() > 0) {
mediaInfo.setWidth(mediaTrack.getWidth());
}
}
return mediaInfo;
}
public Integer getReaderCount() {
return readerCount;
}
public void setReaderCount(Integer readerCount) {
this.readerCount = readerCount;
}
public String getVideoCodec() {
return videoCodec;
}
public void setVideoCodec(String videoCodec) {
this.videoCodec = videoCodec;
}
public Integer getWidth() {
return width;
}
public void setWidth(Integer width) {
this.width = width;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
public String getAudioCodec() {
return audioCodec;
}
public void setAudioCodec(String audioCodec) {
this.audioCodec = audioCodec;
}
public Integer getAudioChannels() {
return audioChannels;
}
public void setAudioChannels(Integer audioChannels) {
this.audioChannels = audioChannels;
}
public Integer getAudioSampleRate() {
return audioSampleRate;
}
public void setAudioSampleRate(Integer audioSampleRate) {
this.audioSampleRate = audioSampleRate;
}
public Long getDuration() {
return duration;
}
public void setDuration(Long duration) {
this.duration = duration;
}
public Boolean getOnline() {
return online;
}
public void setOnline(Boolean online) {
this.online = online;
}
public Integer getOriginType() {
return originType;
}
public void setOriginType(Integer originType) {
this.originType = originType;
}
public Long getAliveSecond() {
return aliveSecond;
}
public void setAliveSecond(Long aliveSecond) {
this.aliveSecond = aliveSecond;
}
public Long getBytesSpeed() {
return bytesSpeed;
}
public void setBytesSpeed(Long bytesSpeed) {
this.bytesSpeed = bytesSpeed;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public MediaServer getMediaServer() {
return mediaServer;
}
public void setMediaServer(MediaServer mediaServer) {
this.mediaServer = mediaServer;
}
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
}

View File

@ -1,12 +1,12 @@
package com.genersoft.iot.vmp.media.zlm.dto;
package com.genersoft.iot.vmp.media.bean;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.util.ObjectUtils;
@Schema(description = "流媒体服务信息")
public class MediaServerItem{
public class MediaServer {
@Schema(description = "ID")
private String id;
@ -32,6 +32,18 @@ public class MediaServerItem{
@Schema(description = "RTMP端口")
private int rtmpPort;
@Schema(description = "flv端口")
private int flvPort;
@Schema(description = "https-flv端口")
private int flvSSLPort;
@Schema(description = "ws-flv端口")
private int wsFlvPort;
@Schema(description = "wss-flv端口")
private int wsFlvSSLPort;
@Schema(description = "RTMPS端口")
private int rtmpSSlPort;
@ -85,18 +97,24 @@ public class MediaServerItem{
@Schema(description = "录像存储路径")
private String recordPath;
@Schema(description = "类型: zlm/abl")
private String type;
public MediaServerItem() {
public MediaServer() {
}
public MediaServerItem(ZLMServerConfig zlmServerConfig, String sipIp) {
public MediaServer(ZLMServerConfig zlmServerConfig, String sipIp) {
id = zlmServerConfig.getGeneralMediaServerId();
ip = zlmServerConfig.getIp();
hookIp = ObjectUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp();
sdpIp = ObjectUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp();
streamIp = ObjectUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp();
httpPort = zlmServerConfig.getHttpPort();
flvPort = zlmServerConfig.getHttpPort();
wsFlvPort = zlmServerConfig.getHttpPort();
httpSSlPort = zlmServerConfig.getHttpSSLport();
flvSSLPort = zlmServerConfig.getHttpSSLport();
wsFlvSSLPort = zlmServerConfig.getHttpSSLport();
rtmpPort = zlmServerConfig.getRtmpPort();
rtmpSSlPort = zlmServerConfig.getRtmpSslPort();
rtpProxyPort = zlmServerConfig.getRtpProxyPort();
@ -318,4 +336,44 @@ public class MediaServerItem{
public void setRecordPath(String recordPath) {
this.recordPath = recordPath;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getFlvPort() {
return flvPort;
}
public void setFlvPort(int flvPort) {
this.flvPort = flvPort;
}
public int getFlvSSLPort() {
return flvSSLPort;
}
public void setFlvSSLPort(int flvSSLPort) {
this.flvSSLPort = flvSSLPort;
}
public int getWsFlvPort() {
return wsFlvPort;
}
public void setWsFlvPort(int wsFlvPort) {
this.wsFlvPort = wsFlvPort;
}
public int getWsFlvSSLPort() {
return wsFlvSSLPort;
}
public void setWsFlvSSLPort(int wsFlvSSLPort) {
this.wsFlvSSLPort = wsFlvSSLPort;
}
}

View File

@ -0,0 +1,92 @@
package com.genersoft.iot.vmp.media.bean;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
public class RecordInfo {
private String fileName;
private String filePath;
private long fileSize;
private String folder;
private String url;
private long startTime;
private double timeLen;
public static RecordInfo getInstance(OnRecordMp4HookParam hookParam) {
RecordInfo recordInfo = new RecordInfo();
recordInfo.setFileName(hookParam.getFile_name());
recordInfo.setUrl(hookParam.getUrl());
recordInfo.setFolder(hookParam.getFolder());
recordInfo.setFilePath(hookParam.getFile_path());
recordInfo.setFileSize(hookParam.getFile_size());
recordInfo.setStartTime(hookParam.getStart_time());
recordInfo.setTimeLen(hookParam.getTime_len());
return recordInfo;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public double getTimeLen() {
return timeLen;
}
public void setTimeLen(double timeLen) {
this.timeLen = timeLen;
}
@Override
public String toString() {
return "RecordInfo{" +
"文件名称='" + fileName + '\'' +
", 文件路径='" + filePath + '\'' +
", 文件大小=" + fileSize +
", 开始时间=" + startTime +
", 时长=" + timeLen +
'}';
}
}

View File

@ -0,0 +1,59 @@
package com.genersoft.iot.vmp.media.bean;
public class ResultForOnPublish {
private boolean enable_audio;
private boolean enable_mp4;
private int mp4_max_second;
private String mp4_save_path;
private String stream_replace;
private Integer modify_stamp;
public boolean isEnable_audio() {
return enable_audio;
}
public void setEnable_audio(boolean enable_audio) {
this.enable_audio = enable_audio;
}
public boolean isEnable_mp4() {
return enable_mp4;
}
public void setEnable_mp4(boolean enable_mp4) {
this.enable_mp4 = enable_mp4;
}
public int getMp4_max_second() {
return mp4_max_second;
}
public void setMp4_max_second(int mp4_max_second) {
this.mp4_max_second = mp4_max_second;
}
public String getMp4_save_path() {
return mp4_save_path;
}
public void setMp4_save_path(String mp4_save_path) {
this.mp4_save_path = mp4_save_path;
}
public String getStream_replace() {
return stream_replace;
}
public void setStream_replace(String stream_replace) {
this.stream_replace = stream_replace;
}
public Integer getModify_stamp() {
return modify_stamp;
}
public void setModify_stamp(Integer modify_stamp) {
this.modify_stamp = modify_stamp;
}
}

View File

@ -0,0 +1,86 @@
package com.genersoft.iot.vmp.media.event.hook;
/**
* zlm hook事件的参数
* @author lin
*/
public class Hook {
private HookType hookType;
private String app;
private String stream;
private String mediaServerId;
private Long createTime;
public static Hook getInstance(HookType hookType, String app, String stream, String mediaServerId) {
Hook hookSubscribe = new Hook();
hookSubscribe.setApp(app);
hookSubscribe.setStream(stream);
hookSubscribe.setHookType(hookType);
hookSubscribe.setMediaServerId(mediaServerId);
hookSubscribe.setCreateTime(System.currentTimeMillis());
return hookSubscribe;
}
public HookType getHookType() {
return hookType;
}
public void setHookType(HookType hookType) {
this.hookType = hookType;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Hook) {
Hook param = (Hook) obj;
return param.getHookType().equals(this.hookType)
&& param.getApp().equals(this.app)
&& param.getStream().equals(this.stream)
&& param.getMediaServerId().equals(this.mediaServerId);
}else {
return false;
}
}
@Override
public String toString() {
return this.getHookType() + this.getApp() + this.getStream() + this.getMediaServerId();
}
}

View File

@ -0,0 +1,132 @@
package com.genersoft.iot.vmp.media.event.hook;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.RecordInfo;
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
import com.genersoft.iot.vmp.media.event.media.MediaEvent;
import com.genersoft.iot.vmp.media.event.media.MediaPublishEvent;
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* Hook返回的内容
*/
public class HookData {
/**
* 应用名
*/
private String app;
/**
* 流ID
*/
private String stream;
/**
* 流媒体节点
*/
private MediaServer mediaServer;
/**
* 协议
*/
private String schema;
/**
* 流信息
*/
private MediaInfo mediaInfo;
/**
* 录像信息
*/
private RecordInfo recordInfo;
@Schema(description = "推流的额外参数")
private String params;
public static HookData getInstance(MediaEvent mediaEvent) {
HookData hookData = new HookData();
if (mediaEvent instanceof MediaPublishEvent) {
MediaPublishEvent event = (MediaPublishEvent) mediaEvent;
hookData.setApp(event.getApp());
hookData.setStream(event.getStream());
hookData.setSchema(event.getSchema());
hookData.setMediaServer(event.getMediaServer());
hookData.setParams(event.getParams());
}else if (mediaEvent instanceof MediaArrivalEvent) {
MediaArrivalEvent event = (MediaArrivalEvent) mediaEvent;
hookData.setApp(event.getApp());
hookData.setStream(event.getStream());
hookData.setSchema(event.getSchema());
hookData.setMediaServer(event.getMediaServer());
hookData.setMediaInfo(event.getMediaInfo());
}else if (mediaEvent instanceof MediaRecordMp4Event) {
MediaRecordMp4Event event = (MediaRecordMp4Event) mediaEvent;
hookData.setApp(event.getApp());
hookData.setStream(event.getStream());
hookData.setSchema(event.getSchema());
hookData.setMediaServer(event.getMediaServer());
hookData.setRecordInfo(event.getRecordInfo());
}else {
hookData.setApp(mediaEvent.getApp());
hookData.setStream(mediaEvent.getStream());
hookData.setSchema(mediaEvent.getSchema());
hookData.setMediaServer(mediaEvent.getMediaServer());
}
return hookData;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public MediaServer getMediaServer() {
return mediaServer;
}
public void setMediaServer(MediaServer mediaServer) {
this.mediaServer = mediaServer;
}
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
public MediaInfo getMediaInfo() {
return mediaInfo;
}
public void setMediaInfo(MediaInfo mediaInfo) {
this.mediaInfo = mediaInfo;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public RecordInfo getRecordInfo() {
return recordInfo;
}
public void setRecordInfo(RecordInfo recordInfo) {
this.recordInfo = recordInfo;
}
}

View File

@ -0,0 +1,107 @@
package com.genersoft.iot.vmp.media.event.hook;
import com.genersoft.iot.vmp.media.event.media.*;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* zlm hook事件的参数
* @author lin
*/
@Component
public class HookSubscribe {
/**
* 订阅数据过期时间
*/
private final long subscribeExpire = 5 * 60 * 1000;
@FunctionalInterface
public interface Event{
void response(HookData data);
}
/**
* 流到来的处理
*/
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(MediaArrivalEvent event) {
if (event.getSchema() == null || "rtsp".equals(event.getSchema())) {
sendNotify(HookType.on_media_arrival, event);
}
}
/**
* 流结束事件
*/
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(MediaDepartureEvent event) {
if (event.getSchema() == null || "rtsp".equals(event.getSchema())) {
sendNotify(HookType.on_media_departure, event);
}
}
/**
* 推流鉴权事件
*/
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(MediaPublishEvent event) {
sendNotify(HookType.on_publish, event);
}
/**
* 推流鉴权事件
*/
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(MediaRecordMp4Event event) {
sendNotify(HookType.on_record_mp4, event);
}
private final Map<String, Event> allSubscribes = new ConcurrentHashMap<>();
private final Map<String, Hook> allHook = new ConcurrentHashMap<>();
private void sendNotify(HookType hookType, MediaEvent event) {
Hook paramHook = Hook.getInstance(hookType, event.getApp(), event.getStream(), event.getMediaServer().getId());
Event hookSubscribeEvent = allSubscribes.get(paramHook.toString());
if (hookSubscribeEvent != null) {
HookData data = HookData.getInstance(event);
hookSubscribeEvent.response(data);
}
}
public void addSubscribe(Hook hook, HookSubscribe.Event event) {
if (hook.getCreateTime() == null) {
hook.setCreateTime(System.currentTimeMillis());
}
allSubscribes.put(hook.toString(), event);
allHook.put(hook.toString(), hook);
}
public void removeSubscribe(Hook hook) {
allSubscribes.remove(hook.toString());
allHook.remove(hook.toString());
}
/**
* 对订阅数据进行过期清理
*/
@Scheduled(fixedRate=subscribeExpire) //每5分钟执行一次
public void execute(){
long expireTime = System.currentTimeMillis() - subscribeExpire;
for (Hook hook : allHook.values()) {
if (hook.getCreateTime() < expireTime) {
allSubscribes.remove(hook.toString());
allHook.remove(hook.toString());
}
}
}
}

View File

@ -0,0 +1,15 @@
package com.genersoft.iot.vmp.media.event.hook;
/**
* hook类型
* @author lin
*/
public enum HookType {
on_publish,
on_record_mp4,
on_media_arrival,
on_media_departure,
on_rtp_server_timeout,
}

View File

@ -0,0 +1,46 @@
package com.genersoft.iot.vmp.media.event.media;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
/**
* 流到来事件
*/
public class MediaArrivalEvent extends MediaEvent {
public MediaArrivalEvent(Object source) {
super(source);
}
public static MediaArrivalEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer){
MediaArrivalEvent mediaArrivalEvent = new MediaArrivalEvent(source);
mediaArrivalEvent.setMediaInfo(MediaInfo.getInstance(hookParam, mediaServer));
mediaArrivalEvent.setApp(hookParam.getApp());
mediaArrivalEvent.setStream(hookParam.getStream());
mediaArrivalEvent.setMediaServer(mediaServer);
mediaArrivalEvent.setSchema(hookParam.getSchema());
mediaArrivalEvent.setCallId(hookParam.getCallId());
return mediaArrivalEvent;
}
private MediaInfo mediaInfo;
private String callId;
public MediaInfo getMediaInfo() {
return mediaInfo;
}
public void setMediaInfo(MediaInfo mediaInfo) {
this.mediaInfo = mediaInfo;
}
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
}

View File

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.media.event.media;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
/**
* 流离开事件
*/
public class MediaDepartureEvent extends MediaEvent {
public MediaDepartureEvent(Object source) {
super(source);
}
public static MediaDepartureEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer){
MediaDepartureEvent mediaDepartureEven = new MediaDepartureEvent(source);
mediaDepartureEven.setApp(hookParam.getApp());
mediaDepartureEven.setStream(hookParam.getStream());
mediaDepartureEven.setSchema(hookParam.getSchema());
mediaDepartureEven.setMediaServer(mediaServer);
return mediaDepartureEven;
}
}

View File

@ -0,0 +1,56 @@
package com.genersoft.iot.vmp.media.event.media;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import org.springframework.context.ApplicationEvent;
/**
* 流到来事件
*/
public class MediaEvent extends ApplicationEvent {
public MediaEvent(Object source) {
super(source);
}
private String app;
private String stream;
private MediaServer mediaServer;
private String schema;
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public MediaServer getMediaServer() {
return mediaServer;
}
public void setMediaServer(MediaServer mediaServer) {
this.mediaServer = mediaServer;
}
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
}

View File

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.media.event.media;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamNotFoundHookParam;
/**
* 流未找到
*/
public class MediaNotFoundEvent extends MediaEvent {
public MediaNotFoundEvent(Object source) {
super(source);
}
public static MediaNotFoundEvent getInstance(Object source, OnStreamNotFoundHookParam hookParam, MediaServer mediaServer){
MediaNotFoundEvent mediaDepartureEven = new MediaNotFoundEvent(source);
mediaDepartureEven.setApp(hookParam.getApp());
mediaDepartureEven.setStream(hookParam.getStream());
mediaDepartureEven.setSchema(hookParam.getSchema());
mediaDepartureEven.setMediaServer(mediaServer);
return mediaDepartureEven;
}
}

View File

@ -0,0 +1,33 @@
package com.genersoft.iot.vmp.media.event.media;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnPublishHookParam;
/**
* 推流鉴权事件
*/
public class MediaPublishEvent extends MediaEvent {
public MediaPublishEvent(Object source) {
super(source);
}
public static MediaPublishEvent getInstance(Object source, OnPublishHookParam hookParam, MediaServer mediaServer){
MediaPublishEvent mediaPublishEvent = new MediaPublishEvent(source);
mediaPublishEvent.setApp(hookParam.getApp());
mediaPublishEvent.setStream(hookParam.getStream());
mediaPublishEvent.setMediaServer(mediaServer);
mediaPublishEvent.setSchema(hookParam.getSchema());
mediaPublishEvent.setParams(hookParam.getParams());
return mediaPublishEvent;
}
private String params;
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
}

View File

@ -0,0 +1,35 @@
package com.genersoft.iot.vmp.media.event.media;
import com.genersoft.iot.vmp.media.bean.RecordInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
/**
* 录像文件生成事件
*/
public class MediaRecordMp4Event extends MediaEvent {
public MediaRecordMp4Event(Object source) {
super(source);
}
private RecordInfo recordInfo;
public static MediaRecordMp4Event getInstance(Object source, OnRecordMp4HookParam hookParam, MediaServer mediaServer){
MediaRecordMp4Event mediaRecordMp4Event = new MediaRecordMp4Event(source);
mediaRecordMp4Event.setApp(hookParam.getApp());
mediaRecordMp4Event.setStream(hookParam.getStream());
RecordInfo recordInfo = RecordInfo.getInstance(hookParam);
mediaRecordMp4Event.setRecordInfo(recordInfo);
mediaRecordMp4Event.setMediaServer(mediaServer);
return mediaRecordMp4Event;
}
public RecordInfo getRecordInfo() {
return recordInfo;
}
public void setRecordInfo(RecordInfo recordInfo) {
this.recordInfo = recordInfo;
}
}

View File

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.media.event.media;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
/**
* RtpServer收流超时事件
*/
public class MediaRtpServerTimeoutEvent extends MediaEvent {
public MediaRtpServerTimeoutEvent(Object source) {
super(source);
}
public static MediaRtpServerTimeoutEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServer mediaServer){
MediaRtpServerTimeoutEvent mediaDepartureEven = new MediaRtpServerTimeoutEvent(source);
mediaDepartureEven.setApp(hookParam.getApp());
mediaDepartureEven.setStream(hookParam.getStream());
mediaDepartureEven.setSchema(hookParam.getSchema());
mediaDepartureEven.setMediaServer(mediaServer);
return mediaDepartureEven;
}
}

View File

@ -0,0 +1,52 @@
package com.genersoft.iot.vmp.media.event.mediaServer;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamNotFoundHookParam;
import org.springframework.context.ApplicationEvent;
/**
* 发送流停止事件
*/
public class MediaSendRtpStoppedEvent extends ApplicationEvent {
public MediaSendRtpStoppedEvent(Object source) {
super(source);
}
private String app;
private String stream;
private MediaServer mediaServer;
public static MediaSendRtpStoppedEvent getInstance(Object source, OnStreamNotFoundHookParam hookParam, MediaServer mediaServer){
MediaSendRtpStoppedEvent mediaDepartureEven = new MediaSendRtpStoppedEvent(source);
mediaDepartureEven.setApp(hookParam.getApp());
mediaDepartureEven.setStream(hookParam.getStream());
mediaDepartureEven.setMediaServer(mediaServer);
return mediaDepartureEven;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getStream() {
return stream;
}
public void setStream(String stream) {
this.stream = stream;
}
public MediaServer getMediaServer() {
return mediaServer;
}
public void setMediaServer(MediaServer mediaServer) {
this.mediaServer = mediaServer;
}
}

View File

@ -0,0 +1,34 @@
package com.genersoft.iot.vmp.media.event.mediaServer;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import org.springframework.context.ApplicationEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MediaServerChangeEvent extends ApplicationEvent {
public MediaServerChangeEvent(Object source) {
super(source);
}
private List<MediaServer> mediaServerItemList;
public List<MediaServer> getMediaServerItemList() {
return mediaServerItemList;
}
public void setMediaServerItemList(List<MediaServer> mediaServerItemList) {
this.mediaServerItemList = mediaServerItemList;
}
public void setMediaServerItemList(MediaServer... mediaServerItemArray) {
this.mediaServerItemList = new ArrayList<>();
this.mediaServerItemList.addAll(Arrays.asList(mediaServerItemArray));
}
public void setMediaServerItem(List<MediaServer> mediaServerItemList) {
this.mediaServerItemList = mediaServerItemList;
}
}

View File

@ -0,0 +1,11 @@
package com.genersoft.iot.vmp.media.event.mediaServer;
/**
* zlm在线事件
*/
public class MediaServerDeleteEvent extends MediaServerEventAbstract {
public MediaServerDeleteEvent(Object source) {
super(source);
}
}

View File

@ -1,9 +1,8 @@
package com.genersoft.iot.vmp.media.zlm.event;
package com.genersoft.iot.vmp.media.event.mediaServer;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import org.springframework.context.ApplicationEvent;
public abstract class ZLMEventAbstract extends ApplicationEvent {
public abstract class MediaServerEventAbstract extends ApplicationEvent {
private static final long serialVersionUID = 1L;
@ -11,7 +10,7 @@ public abstract class ZLMEventAbstract extends ApplicationEvent {
private String mediaServerId;
public ZLMEventAbstract(Object source) {
public MediaServerEventAbstract(Object source) {
super(source);
}

View File

@ -0,0 +1,11 @@
package com.genersoft.iot.vmp.media.event.mediaServer;
/**
* zlm离线事件类
*/
public class MediaServerOfflineEvent extends MediaServerEventAbstract {
public MediaServerOfflineEvent(Object source) {
super(source);
}
}

View File

@ -0,0 +1,11 @@
package com.genersoft.iot.vmp.media.event.mediaServer;
/**
* zlm在线事件
*/
public class MediaServerOnlineEvent extends MediaServerEventAbstract {
public MediaServerOnlineEvent(Object source) {
super(source);
}
}

View File

@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.media.zlm.event;
package com.genersoft.iot.vmp.media.event.mediaServer;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.service.IStreamPushService;
@ -20,9 +19,9 @@ import org.springframework.stereotype.Component;
* @date: 2020年5月6日 下午1:51:23
*/
@Component
public class ZLMStatusEventListener {
public class MediaServerStatusEventListener {
private final static Logger logger = LoggerFactory.getLogger(ZLMStatusEventListener.class);
private final static Logger logger = LoggerFactory.getLogger(MediaServerStatusEventListener.class);
@Autowired
private IStreamPushService streamPushService;
@ -30,16 +29,13 @@ public class ZLMStatusEventListener {
@Autowired
private IStreamProxyService streamProxyService;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private IPlayService playService;
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(ZLMOnlineEvent event) {
logger.info("[ZLM] 上线 ID" + event.getMediaServerId());
public void onApplicationEvent(MediaServerOnlineEvent event) {
logger.info("[媒体节点] 上线 ID" + event.getMediaServerId());
streamPushService.zlmServerOnline(event.getMediaServerId());
streamProxyService.zlmServerOnline(event.getMediaServerId());
playService.zlmServerOnline(event.getMediaServerId());
@ -47,11 +43,10 @@ public class ZLMStatusEventListener {
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(ZLMOfflineEvent event) {
public void onApplicationEvent(MediaServerOfflineEvent event) {
logger.info("[ZLM] 离线ID" + event.getMediaServerId());
logger.info("[媒体节点] 离线ID" + event.getMediaServerId());
// 处理ZLM离线
mediaServerService.zlmServerOffline(event.getMediaServerId());
streamProxyService.zlmServerOffline(event.getMediaServerId());
streamPushService.zlmServerOffline(event.getMediaServerId());
playService.zlmServerOffline(event.getMediaServerId());

View File

@ -0,0 +1,61 @@
package com.genersoft.iot.vmp.media.service;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import java.util.List;
import java.util.Map;
public interface IMediaNodeServerService {
int createRTPServer(MediaServer mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode);
void closeRtpServer(MediaServer mediaServer, String streamId);
void closeRtpServer(MediaServer mediaServer, String streamId, CommonCallback<Boolean> callback);
void closeStreams(MediaServer mediaServer, String app, String stream);
Boolean updateRtpServerSSRC(MediaServer mediaServer, String stream, String ssrc);
boolean checkNodeId(MediaServer mediaServer);
void online(MediaServer mediaServer);
MediaServer checkMediaServer(String ip, int port, String secret);
boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName);
List<StreamInfo> getMediaList(MediaServer mediaServer, String app, String stream, String callId);
Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream);
void getSnap(MediaServer mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName);
MediaInfo getMediaInfo(MediaServer mediaServer, String app, String stream);
Boolean pauseRtpCheck(MediaServer mediaServer, String streamKey);
Boolean resumeRtpCheck(MediaServer mediaServer, String streamKey);
String getFfmpegCmd(MediaServer mediaServer, String cmdKey);
WVPResult<String> addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey);
WVPResult<String> addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType);
Boolean delFFmpegSource(MediaServer mediaServer, String streamKey);
Boolean delStreamProxy(MediaServer mediaServer, String streamKey);
Map<String, String> getFFmpegCMDs(MediaServer mediaServer);
void startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout);
void startSendRtpStream(MediaServer mediaServer, SendRtpItem sendRtpItem);
}

View File

@ -0,0 +1,150 @@
package com.genersoft.iot.vmp.media.service;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import java.util.List;
import java.util.Map;
/**
* 媒体服务节点
*/
public interface IMediaServerService {
List<MediaServer> getAllOnlineList();
List<MediaServer> getAll();
List<MediaServer> getAllFromDatabase();
List<MediaServer> getAllOnline();
MediaServer getOne(String generalMediaServerId);
void syncCatchFromDatabase();
MediaServer getMediaServerForMinimumLoad(Boolean hasAssist);
void updateVmServer(List<MediaServer> mediaServerItemList);
SSRCInfo openRTPServer(MediaServer mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
boolean isPlayback, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode);
void closeRTPServer(MediaServer mediaServerItem, String streamId);
void closeRTPServer(MediaServer mediaServerItem, String streamId, CommonCallback<Boolean> callback);
Boolean updateRtpServerSSRC(MediaServer mediaServerItem, String streamId, String ssrc);
void closeRTPServer(String mediaServerId, String streamId);
void clearRTPServer(MediaServer mediaServerItem);
void update(MediaServer mediaSerItem);
void addCount(String mediaServerId);
void removeCount(String mediaServerId);
void releaseSsrc(String mediaServerItemId, String ssrc);
void clearMediaServerForOnline();
void add(MediaServer mediaSerItem);
void resetOnlineServerItem(MediaServer serverItem);
MediaServer checkMediaServer(String ip, int port, String secret, String type);
boolean checkMediaRecordServer(String ip, int port);
void delete(String id);
MediaServer getDefaultMediaServer();
MediaServerLoad getLoad(MediaServer mediaServerItem);
List<MediaServer> getAllWithAssistPort();
MediaServer getOneFromDatabase(String id);
boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
boolean deleteRecordDirectory(MediaServer mediaServerItem, String app, String stream, String date, String fileName);
List<StreamInfo> getMediaList(MediaServer mediaInfo, String app, String stream, String callId);
Boolean connectRtpServer(MediaServer mediaServerItem, String address, int port, String stream);
void getSnap(MediaServer mediaServerItemInuse, String streamUrl, int timeoutSec, int expireSec, String path, String fileName);
MediaInfo getMediaInfo(MediaServer mediaServerItem, String app, String stream);
Boolean pauseRtpCheck(MediaServer mediaServerItem, String streamKey);
boolean resumeRtpCheck(MediaServer mediaServerItem, String streamKey);
String getFfmpegCmd(MediaServer mediaServer, String cmdKey);
void closeStreams(MediaServer mediaServerItem, String app, String stream);
WVPResult<String> addFFmpegSource(MediaServer mediaServerItem, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey);
WVPResult<String> addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType);
Boolean delFFmpegSource(MediaServer mediaServerItem, String streamKey);
Boolean delStreamProxy(MediaServer mediaServerItem, String streamKey);
Map<String, String> getFFmpegCMDs(MediaServer mediaServer);
/**
* 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr, boolean authority);
/**
* 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay);
Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId);
void startSendRtpPassive(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout);
void startSendRtp(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem);
SendRtpItem createSendRtpItem(MediaServer mediaServerItem, String addressStr, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean mediaTransmissionTCP, boolean rtcp);
SendRtpItem createSendRtpItem(MediaServer serverItem, String ip, int port, String ssrc, String platformId,
String app, String stream, String channelId, boolean tcp, boolean rtcp);
}

View File

@ -0,0 +1,890 @@
package com.genersoft.iot.vmp.media.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent;
import com.genersoft.iot.vmp.media.service.IMediaNodeServerService;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
import com.genersoft.iot.vmp.service.IInviteStreamService;
import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.utils.JsonUtil;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.time.LocalDateTime;
import java.util.*;
/**
* 媒体服务器节点管理
*/
@Service
@DS("master")
public class MediaServerServiceImpl implements IMediaServerService {
private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class);
@Autowired
private SSRCFactory ssrcFactory;
@Autowired
private UserSetting userSetting;
@Autowired
private MediaServerMapper mediaServerMapper;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IInviteStreamService inviteStreamService;
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private Map<String, IMediaNodeServerService> nodeServerServiceMap;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private MediaConfig mediaConfig;
@Autowired
private SendRtpPortManager sendRtpPortManager;
/**
* 流到来的处理
*/
@Async("taskExecutor")
@org.springframework.context.event.EventListener
public void onApplicationEvent(MediaArrivalEvent event) {
if ("rtsp".equals(event.getSchema())) {
logger.info("流变化:注册 app->{}, stream->{}", event.getApp(), event.getStream());
addCount(event.getMediaServer().getId());
}
}
/**
* 流离开的处理
*/
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(MediaDepartureEvent event) {
if ("rtsp".equals(event.getSchema())) {
logger.info("流变化:注销, app->{}, stream->{}", event.getApp(), event.getStream());
removeCount(event.getMediaServer().getId());
}
}
/**
* 初始化
*/
@Override
public void updateVmServer(List<MediaServer> mediaServerList) {
logger.info("[媒体服务节点] 缓存初始化 ");
for (MediaServer mediaServer : mediaServerList) {
if (ObjectUtils.isEmpty(mediaServer.getId())) {
continue;
}
// 更新
if (!ssrcFactory.hasMediaServerSSRC(mediaServer.getId())) {
ssrcFactory.initMediaServerSSRC(mediaServer.getId(), null);
}
// 查询redis是否存在此mediaServer
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServer.getId();
Boolean hasKey = redisTemplate.hasKey(key);
if (hasKey != null && ! hasKey) {
redisTemplate.opsForValue().set(key, mediaServer);
}
}
}
@Override
public SSRCInfo openRTPServer(MediaServer mediaServer, String streamId, String presetSsrc, boolean ssrcCheck,
boolean isPlayback, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) {
if (mediaServer == null || mediaServer.getId() == null) {
logger.info("[openRTPServer] 失败, mediaServer == null || mediaServer.getId() == null");
return null;
}
// 获取mediaServer可用的ssrc
String ssrc;
if (presetSsrc != null) {
ssrc = presetSsrc;
}else {
if (isPlayback) {
ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId());
}else {
ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId());
}
}
if (streamId == null) {
streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
}
if (ssrcCheck && tcpMode > 0) {
// 目前zlm不支持 tcp模式更新ssrc暂时关闭ssrc校验
logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc但是tcp模式zlm收流目前无法更新ssrc可能收流超时此时请使用udp收流或者关闭ssrc校验");
}
int rtpServerPort;
if (mediaServer.isRtpEnable()) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[openRTPServer] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return null;
}
rtpServerPort = mediaNodeServerService.createRTPServer(mediaServer, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, onlyAuto, disableAudio, reUsePort, tcpMode);
} else {
rtpServerPort = mediaServer.getRtpProxyPort();
}
return new SSRCInfo(rtpServerPort, ssrc, streamId);
}
@Override
public void closeRTPServer(MediaServer mediaServer, String streamId) {
if (mediaServer == null) {
return;
}
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[closeRTPServer] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return;
}
mediaNodeServerService.closeRtpServer(mediaServer, streamId);
}
@Override
public void closeRTPServer(MediaServer mediaServer, String streamId, CommonCallback<Boolean> callback) {
if (mediaServer == null) {
callback.run(false);
return;
}
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[closeRTPServer] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return;
}
mediaNodeServerService.closeRtpServer(mediaServer, streamId, callback);
}
@Override
public void closeRTPServer(String mediaServerId, String streamId) {
MediaServer mediaServer = this.getOne(mediaServerId);
if (mediaServer == null) {
return;
}
if (mediaServer.isRtpEnable()) {
closeRTPServer(mediaServer, streamId);
}
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[closeRTPServer] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return;
}
mediaNodeServerService.closeStreams(mediaServer, "rtp", streamId);
}
@Override
public Boolean updateRtpServerSSRC(MediaServer mediaServer, String streamId, String ssrc) {
if (mediaServer == null) {
return false;
}
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[updateRtpServerSSRC] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return false;
}
return mediaNodeServerService.updateRtpServerSSRC(mediaServer, streamId, ssrc);
}
@Override
public void releaseSsrc(String mediaServerId, String ssrc) {
MediaServer mediaServer = getOne(mediaServerId);
if (mediaServer == null || ssrc == null) {
return;
}
ssrcFactory.releaseSsrc(mediaServerId, ssrc);
}
/**
* 媒体服务节点 重启后重置他的推流信息 TODO 给正在使用的设备发送停止命令
*/
@Override
public void clearRTPServer(MediaServer mediaServer) {
ssrcFactory.reset(mediaServer.getId());
}
@Override
public void update(MediaServer mediaSerItem) {
mediaServerMapper.update(mediaSerItem);
MediaServer mediaServerInRedis = getOne(mediaSerItem.getId());
MediaServer mediaServerInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId());
if (mediaServerInDataBase == null) {
return;
}
mediaServerInDataBase.setStatus(mediaSerItem.isStatus());
if (mediaServerInRedis == null || !ssrcFactory.hasMediaServerSSRC(mediaServerInDataBase.getId())) {
ssrcFactory.initMediaServerSSRC(mediaServerInDataBase.getId(),null);
}
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerInDataBase.getId();
redisTemplate.opsForValue().set(key, mediaServerInDataBase);
if (mediaServerInDataBase.isStatus()) {
resetOnlineServerItem(mediaServerInDataBase);
}else {
// 发送事件
MediaServerChangeEvent event = new MediaServerChangeEvent(this);
event.setMediaServerItemList(mediaServerInDataBase);
applicationEventPublisher.publishEvent(event);
}
}
@Override
public List<MediaServer> getAllOnlineList() {
List<MediaServer> result = new ArrayList<>();
List<Object> mediaServerKeys = RedisUtil.scan(redisTemplate, String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + ":" ));
String onlineKey = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
for (Object mediaServerKey : mediaServerKeys) {
String key = (String) mediaServerKey;
MediaServer mediaServer = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServer.class);
if (Objects.isNull(mediaServer)) {
continue;
}
// 检查状态
Double aDouble = redisTemplate.opsForZSet().score(onlineKey, mediaServer.getId());
if (aDouble != null) {
mediaServer.setStatus(true);
}
result.add(mediaServer);
}
result.sort((serverItem1, serverItem2)->{
int sortResult = 0;
LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter);
LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter);
sortResult = localDateTime1.compareTo(localDateTime2);
return sortResult;
});
return result;
}
@Override
public List<MediaServer> getAll() {
List<MediaServer> mediaServerList = mediaServerMapper.queryAll();
if (mediaServerList.isEmpty()) {
return new ArrayList<>();
}
for (MediaServer mediaServer : mediaServerList) {
MediaServer mediaServerInRedis = getOne(mediaServer.getId());
if (mediaServerInRedis != null) {
mediaServer.setStatus(mediaServerInRedis.isStatus());
}
}
return mediaServerList;
}
@Override
public List<MediaServer> getAllFromDatabase() {
return mediaServerMapper.queryAll();
}
@Override
public List<MediaServer> getAllOnline() {
String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
Set<Object> mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
List<MediaServer> result = new ArrayList<>();
if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) {
for (Object mediaServerId : mediaServerIdSet) {
String mediaServerIdStr = (String) mediaServerId;
String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerIdStr;
result.add((MediaServer) redisTemplate.opsForValue().get(serverKey));
}
}
Collections.reverse(result);
return result;
}
/**
* 获取单个媒体服务节点服务器
* @param mediaServerId 服务id
* @return mediaServer
*/
@Override
public MediaServer getOne(String mediaServerId) {
if (mediaServerId == null) {
return null;
}
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerId;
return JsonUtil.redisJsonToObject(redisTemplate, key, MediaServer.class);
}
@Override
public MediaServer getDefaultMediaServer() {
return mediaServerMapper.queryDefault();
}
@Override
public void clearMediaServerForOnline() {
String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
redisTemplate.delete(key);
}
@Override
public void add(MediaServer mediaServer) {
mediaServer.setCreateTime(DateUtil.getNow());
mediaServer.setUpdateTime(DateUtil.getNow());
if (mediaServer.getHookAliveInterval() == null || mediaServer.getHookAliveInterval() == 0F) {
mediaServer.setHookAliveInterval(10F);
}
if (mediaServer.getType() == null) {
logger.info("[添加媒体节点] 失败, mediaServer的类型为空");
return;
}
if (mediaServerMapper.queryOne(mediaServer.getId()) != null) {
logger.info("[添加媒体节点] 失败, 媒体服务ID已存在请修改媒体服务器配置, {}", mediaServer.getId());
throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败媒体服务ID [ " + mediaServer.getId() + " ] 已存在,请修改媒体服务器配置");
}
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[添加媒体节点] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return;
}
mediaServerMapper.add(mediaServer);
if (mediaServer.isStatus()) {
mediaNodeServerService.online(mediaServer);
}else {
// 发送事件
MediaServerChangeEvent event = new MediaServerChangeEvent(this);
event.setMediaServerItemList(mediaServer);
applicationEventPublisher.publishEvent(event);
}
}
@Override
public void resetOnlineServerItem(MediaServer serverItem) {
// 更新缓存
String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
// 使用zset的分数作为当前并发量 默认值设置为0
if (redisTemplate.opsForZSet().score(key, serverItem.getId()) == null) { // 不存在则设置默认值 已存在则重置
redisTemplate.opsForZSet().add(key, serverItem.getId(), 0L);
// 查询服务流数量
int count = getMediaList(serverItem);
redisTemplate.opsForZSet().add(key, serverItem.getId(), count);
}else {
clearRTPServer(serverItem);
}
}
private int getMediaList(MediaServer serverItem) {
return 0;
}
@Override
public void addCount(String mediaServerId) {
if (mediaServerId == null) {
return;
}
String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
redisTemplate.opsForZSet().incrementScore(key, mediaServerId, 1);
}
@Override
public void removeCount(String mediaServerId) {
String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
redisTemplate.opsForZSet().incrementScore(key, mediaServerId, - 1);
}
/**
* 获取负载最低的节点
* @return mediaServer
*/
@Override
public MediaServer getMediaServerForMinimumLoad(Boolean hasAssist) {
String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId();
Long size = redisTemplate.opsForZSet().zCard(key);
if (size == null || size == 0) {
logger.info("获取负载最低的节点时无在线节点");
return null;
}
// 获取分数最低的及并发最低的
Set<Object> objects = redisTemplate.opsForZSet().range(key, 0, -1);
ArrayList<Object> mediaServerObjectS = new ArrayList<>(objects);
MediaServer mediaServer = null;
if (hasAssist == null) {
String mediaServerId = (String)mediaServerObjectS.get(0);
mediaServer = getOne(mediaServerId);
}else if (hasAssist) {
for (Object mediaServerObject : mediaServerObjectS) {
String mediaServerId = (String)mediaServerObject;
MediaServer serverItem = getOne(mediaServerId);
if (serverItem.getRecordAssistPort() > 0) {
mediaServer = serverItem;
break;
}
}
}else if (!hasAssist) {
for (Object mediaServerObject : mediaServerObjectS) {
String mediaServerId = (String)mediaServerObject;
MediaServer serverItem = getOne(mediaServerId);
if (serverItem.getRecordAssistPort() == 0) {
mediaServer = serverItem;
break;
}
}
}
return mediaServer;
}
@Override
public MediaServer checkMediaServer(String ip, int port, String secret, String type) {
if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "此连接已存在");
}
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(type);
if (mediaNodeServerService == null) {
logger.info("[closeRTPServer] 失败, mediaServer的类型 {},未找到对应的实现类", type);
return null;
}
MediaServer mediaServer = mediaNodeServerService.checkMediaServer(ip, port, secret);
if (mediaServer != null) {
if (mediaServerMapper.queryOne(mediaServer.getId()) != null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体服务ID [" + mediaServer.getId() + " ] 已存在,请修改媒体服务器配置");
}
}
return mediaServer;
}
@Override
public boolean checkMediaRecordServer(String ip, int port) {
boolean result = false;
OkHttpClient client = new OkHttpClient();
String url = String.format("http://%s:%s/index/api/record", ip, port);
Request request = new Request.Builder()
.get()
.url(url)
.build();
try {
Response response = client.newCall(request).execute();
if (response != null) {
result = true;
}
} catch (Exception e) {}
return result;
}
@Override
public void delete(String id) {
mediaServerMapper.delOne(id);
redisTemplate.opsForZSet().remove(VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(), id);
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + id;
redisTemplate.delete(key);
// 发送节点移除通知
MediaServerDeleteEvent event = new MediaServerDeleteEvent(this);
event.setMediaServerId(id);
applicationEventPublisher.publishEvent(event);
}
@Override
public MediaServer getOneFromDatabase(String mediaServerId) {
return mediaServerMapper.queryOne(mediaServerId);
}
@Override
public void syncCatchFromDatabase() {
List<MediaServer> allInCatch = getAllOnlineList();
List<MediaServer> allInDatabase = mediaServerMapper.queryAll();
Map<String, MediaServer> mediaServerMap = new HashMap<>();
for (MediaServer mediaServer : allInDatabase) {
mediaServerMap.put(mediaServer.getId(), mediaServer);
}
for (MediaServer mediaServer : allInCatch) {
// 清除数据中不存在但redis缓存数据
if (!mediaServerMap.containsKey(mediaServer.getId())) {
delete(mediaServer.getId());
}
}
}
@Override
public MediaServerLoad getLoad(MediaServer mediaServer) {
MediaServerLoad result = new MediaServerLoad();
result.setId(mediaServer.getId());
result.setPush(redisCatchStorage.getPushStreamCount(mediaServer.getId()));
result.setProxy(redisCatchStorage.getProxyStreamCount(mediaServer.getId()));
result.setGbReceive(inviteStreamService.getStreamInfoCount(mediaServer.getId()));
result.setGbSend(redisCatchStorage.getGbSendCount(mediaServer.getId()));
return result;
}
@Override
public List<MediaServer> getAllWithAssistPort() {
return mediaServerMapper.queryAllWithAssistPort();
}
@Override
public boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType());
if (mediaNodeServerService == null) {
logger.info("[stopSendRtp] 失败, mediaServer的类型 {},未找到对应的实现类", mediaInfo.getType());
return false;
}
return mediaNodeServerService.stopSendRtp(mediaInfo, app, stream, ssrc);
}
@Override
public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[stopSendRtp] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return false;
}
return mediaNodeServerService.deleteRecordDirectory(mediaServer, app, stream, date, fileName);
}
@Override
public List<StreamInfo> getMediaList(MediaServer mediaServer, String app, String stream, String callId) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[getMediaList] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return new ArrayList<>();
}
return mediaNodeServerService.getMediaList(mediaServer, app, stream, callId);
}
@Override
public Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[connectRtpServer] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return false;
}
return mediaNodeServerService.connectRtpServer(mediaServer, address, port, stream);
}
@Override
public void getSnap(MediaServer mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[getSnap] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return;
}
mediaNodeServerService.getSnap(mediaServer, streamUrl, timeoutSec, expireSec, path, fileName);
}
@Override
public MediaInfo getMediaInfo(MediaServer mediaServer, String app, String stream) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[getMediaInfo] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return null;
}
return mediaNodeServerService.getMediaInfo(mediaServer, app, stream);
}
@Override
public Boolean pauseRtpCheck(MediaServer mediaServer, String streamKey) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[pauseRtpCheck] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return false;
}
return mediaNodeServerService.pauseRtpCheck(mediaServer, streamKey);
}
@Override
public boolean resumeRtpCheck(MediaServer mediaServer, String streamKey) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[pauseRtpCheck] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return false;
}
return mediaNodeServerService.resumeRtpCheck(mediaServer, streamKey);
}
@Override
public String getFfmpegCmd(MediaServer mediaServer, String cmdKey) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[getFfmpegCmd] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return null;
}
return mediaNodeServerService.getFfmpegCmd(mediaServer, cmdKey);
}
@Override
public void closeStreams(MediaServer mediaServer, String app, String stream) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[closeStreams] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return;
}
mediaNodeServerService.closeStreams(mediaServer, app, stream);
}
@Override
public WVPResult<String> addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[addFFmpegSource] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return WVPResult.fail(ErrorCode.ERROR400);
}
return mediaNodeServerService.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs, enableAudio, enableMp4, ffmpegCmdKey);
}
@Override
public WVPResult<String> addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[addStreamProxy] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return WVPResult.fail(ErrorCode.ERROR400);
}
return mediaNodeServerService.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType);
}
@Override
public Boolean delFFmpegSource(MediaServer mediaServer, String streamKey) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[delFFmpegSource] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return false;
}
return mediaNodeServerService.delFFmpegSource(mediaServer, streamKey);
}
@Override
public Boolean delStreamProxy(MediaServer mediaServerItem, String streamKey) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServerItem.getType());
if (mediaNodeServerService == null) {
logger.info("[delStreamProxy] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServerItem.getType());
return false;
}
return mediaNodeServerService.delStreamProxy(mediaServerItem, streamKey);
}
@Override
public Map<String, String> getFFmpegCMDs(MediaServer mediaServer) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[getFFmpegCMDs] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return new HashMap<>();
}
return mediaNodeServerService.getFFmpegCMDs(mediaServer);
}
@Override
public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId) {
return getStreamInfoByAppAndStream(mediaServerItem, app, stream, mediaInfo, null, callId, true);
}
@Override
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) {
StreamInfo streamInfo = null;
if (mediaServerId == null) {
mediaServerId = mediaConfig.getId();
}
MediaServer mediaInfo = getOne(mediaServerId);
if (mediaInfo == null) {
return null;
}
String calld = null;
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
if (streamAuthorityInfo != null) {
calld = streamAuthorityInfo.getCallId();
}
List<StreamInfo> streamInfoList = getMediaList(mediaInfo, app, stream, calld);
if (streamInfoList.isEmpty()) {
return null;
}else {
return streamInfoList.get(0);
}
}
@Override
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority) {
return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null, authority);
}
@Override
public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay) {
StreamInfo streamInfoResult = new StreamInfo();
streamInfoResult.setStream(stream);
streamInfoResult.setApp(app);
if (addr == null) {
addr = mediaServer.getStreamIp();
}
streamInfoResult.setIp(addr);
streamInfoResult.setMediaServerId(mediaServer.getId());
String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId;
streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam);
streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam);
if ("abl".equals(mediaServer.getType())) {
String flvFile = String.format("%s/%s.flv%s", app, stream, callIdParam);
streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile);
streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile);
}else {
String flvFile = String.format("%s/%s.live.flv%s", app, stream, callIdParam);
streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile);
streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile);
}
streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam);
streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam);
streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam);
streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay);
streamInfoResult.setMediaInfo(mediaInfo);
return streamInfoResult;
}
@Override
public Boolean isStreamReady(MediaServer mediaServer, String rtp, String streamId) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[isStreamReady] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
return false;
}
MediaInfo mediaInfo = mediaNodeServerService.getMediaInfo(mediaServer, rtp, streamId);
return mediaInfo != null;
}
@Override
public void startSendRtpPassive(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[startSendRtpPassive] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
}
mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout);
sendPlatformStartPlayMsg(platform, sendRtpItem);
}
@Override
public void startSendRtp(MediaServer mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem) {
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
if (mediaNodeServerService == null) {
logger.info("[startSendRtpStream] 失败, mediaServer的类型 {},未找到对应的实现类", mediaServer.getType());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类");
}
logger.info("[开始推流] rtp/{}, 目标={}:{}SSRC={}, RTCP={}", sendRtpItem.getStream(),
sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp());
mediaNodeServerService.startSendRtpStream(mediaServer, sendRtpItem);
if (platform != null) {
sendPlatformStartPlayMsg(platform, sendRtpItem);
}
}
private void sendPlatformStartPlayMsg(ParentPlatform platform, SendRtpItem sendRtpItem) {
if (sendRtpItem.getPlayType() == InviteStreamType.PUSH && platform != null) {
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(),
sendRtpItem.getChannelId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(),
sendRtpItem.getMediaServerId());
messageForPushChannel.setPlatFormIndex(platform.getId());
redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel);
}
}
@Override
public SendRtpItem createSendRtpItem(MediaServer mediaServer, String ip, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean isTcp, boolean rtcp) {
int localPort = sendRtpPortManager.getNextPort(mediaServer);
if (localPort == 0) {
return null;
}
SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setIp(ip);
sendRtpItem.setPort(port);
sendRtpItem.setSsrc(ssrc);
sendRtpItem.setPlatformId(deviceId);
sendRtpItem.setDeviceId(deviceId);
sendRtpItem.setChannelId(channelId);
sendRtpItem.setTcp(isTcp);
sendRtpItem.setRtcp(rtcp);
sendRtpItem.setApp("rtp");
sendRtpItem.setLocalPort(localPort);
sendRtpItem.setServerId(userSetting.getServerId());
sendRtpItem.setMediaServerId(mediaServer.getId());
return sendRtpItem;
}
@Override
public SendRtpItem createSendRtpItem(MediaServer serverItem, String ip, int port, String ssrc, String platformId,
String app, String stream, String channelId, boolean tcp, boolean rtcp){
int localPort = sendRtpPortManager.getNextPort(serverItem);
if (localPort == 0) {
return null;
}
SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setIp(ip);
sendRtpItem.setPort(port);
sendRtpItem.setSsrc(ssrc);
sendRtpItem.setApp(app);
sendRtpItem.setStream(stream);
sendRtpItem.setPlatformId(platformId);
sendRtpItem.setChannelId(channelId);
sendRtpItem.setTcp(tcp);
sendRtpItem.setLocalPort(localPort);
sendRtpItem.setServerId(userSetting.getServerId());
sendRtpItem.setMediaServerId(serverItem.getId());
sendRtpItem.setRtcp(rtcp);
return sendRtpItem;
}
}

View File

@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.utils.SSLSocketClientUtil;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
@ -70,7 +70,7 @@ public class AssistRESTfulUtils {
}
public JSONObject sendGet(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
public JSONObject sendGet(MediaServer mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
OkHttpClient client = getClient();
if (mediaServerItem == null) {
@ -155,7 +155,7 @@ public class AssistRESTfulUtils {
return responseJSON;
}
public JSONObject sendPost(MediaServerItem mediaServerItem, String url,
public JSONObject sendPost(MediaServer mediaServerItem, String url,
JSONObject param, ZLMRESTfulUtils.RequestCallback callback,
Integer readTimeOut) {
OkHttpClient client = getClient(readTimeOut);
@ -244,12 +244,12 @@ public class AssistRESTfulUtils {
return responseJSON;
}
public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
public JSONObject getInfo(MediaServer mediaServerItem, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
return sendGet(mediaServerItem, "api/record/info",param, callback);
}
public JSONObject addTask(MediaServerItem mediaServerItem, String app, String stream, String startTime,
public JSONObject addTask(MediaServer mediaServerItem, String app, String stream, String startTime,
String endTime, String callId, List<String> filePathList, String remoteHost) {
JSONObject videoTaskInfoJSON = new JSONObject();
@ -266,7 +266,7 @@ public class AssistRESTfulUtils {
return sendPost(mediaServerItem, urlStr, videoTaskInfoJSON, null, 30);
}
public JSONObject queryTaskList(MediaServerItem mediaServerItem, String app, String stream, String callId,
public JSONObject queryTaskList(MediaServer mediaServerItem, String app, String stream, String callId,
String taskId, Boolean isEnd, String scheme) {
Map<String, Object> param = new HashMap<>();
if (!ObjectUtils.isEmpty(app)) {

View File

@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.media.zlm;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
@ -30,7 +30,7 @@ public class SendRtpPortManager {
private final String KEY = "VM_MEDIA_SEND_RTP_PORT_";
public synchronized int getNextPort(MediaServerItem mediaServer) {
public synchronized int getNextPort(MediaServer mediaServer) {
if (mediaServer == null) {
logger.warn("[发送端口管理] 参数错误mediaServer为NULL");
return -1;
@ -83,6 +83,7 @@ public class SendRtpPortManager {
}
private synchronized int getSendPort(int startPort, int endPort, String sendIndexKey, Map<Integer, SendRtpItem> sendRtpItemMap){
// TODO 这里改为只取偶数端口
RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory());
if (redisAtomicInteger.get() < startPort) {
redisAtomicInteger.set(startPort);

View File

@ -2,55 +2,40 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.media.zlm.dto.HookType;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.media.*;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaSendRtpStoppedEvent;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerKeepaliveEvent;
import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerStartEvent;
import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.OtherPsSendInfo;
import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo;
import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import javax.servlet.http.HttpServletRequest;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @description:针对 ZLMediaServer的hook事件监听
@ -106,7 +91,7 @@ public class ZLMHttpHookListener {
private ZLMMediaListManager zlmMediaListManager;
@Autowired
private ZlmHttpHookSubscribe subscribe;
private HookSubscribe subscribe;
@Autowired
private UserSetting userSetting;
@ -130,25 +115,25 @@ public class ZLMHttpHookListener {
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
/**
* 服务器定时上报时间上报间隔可配置默认10s上报一次
*/
@ResponseBody
@PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
taskExecutor.execute(() -> {
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, param);
}
try {
HookZlmServerKeepaliveEvent event = new HookZlmServerKeepaliveEvent(this);
MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
if (mediaServerItem != null) {
event.setMediaServerItem(mediaServerItem);
applicationEventPublisher.publishEvent(event);
}
});
mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
}catch (Exception e) {
logger.info("[ZLM-HOOK-心跳] 发送通知失败 ", e);
}
return HookResult.SUCCESS();
}
@ -156,32 +141,17 @@ public class ZLMHttpHookListener {
* 播放器鉴权事件rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件
*/
@ResponseBody
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
public HookResult onPlay(@RequestBody OnPlayHookParam param) {
if (logger.isDebugEnabled()) {
logger.debug("[ZLM HOOK] 播放鉴权:{}->{}", param.getMediaServerId(), param);
}
String mediaServerId = param.getMediaServerId();
taskExecutor.execute(() -> {
JSONObject json = (JSONObject) JSON.toJSON(param);
ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
if (subscribe != null) {
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo != null) {
subscribe.response(mediaInfo, param);
}
}
});
if (!"rtp".equals(param.getApp())) {
Map<String, String> paramMap = urlParamToMap(param.getParams());
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
return new HookResult(401, "Unauthorized");
}
Map<String, String> paramMap = urlParamToMap(param.getParams());
// 对于播放流进行鉴权
boolean authenticateResult = mediaService.authenticatePlay(param.getApp(), param.getStream(), paramMap.get("callId"));
if (!authenticateResult) {
logger.info("[ZLM HOOK] 播放鉴权 失败:{}->{}", param.getMediaServerId(), param);
return new HookResult(401, "Unauthorized");
}
logger.info("[ZLM HOOK] 播放鉴权成功:{}->{}", param.getMediaServerId(), param);
return HookResult.SUCCESS();
}
@ -195,136 +165,24 @@ public class ZLMHttpHookListener {
JSONObject json = (JSONObject) JSON.toJSON(param);
logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param);
// TODO 加快处理速度
String mediaServerId = json.getString("mediaServerId");
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo == null) {
return new HookResultForOnPublish(200, "success");
}
// 推流鉴权的处理
if (!"rtp".equals(param.getApp())) {
StreamProxyItem stream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
if (stream != null) {
HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
result.setEnable_audio(stream.isEnableAudio());
result.setEnable_mp4(stream.isEnableMp4());
return result;
}
if (userSetting.getPushAuthority()) {
// 推流鉴权
if (param.getParams() == null) {
logger.info("推流鉴权失败: 缺少必要参数sign=md5(user表的pushKey)");
return new HookResultForOnPublish(401, "Unauthorized");
}
Map<String, String> paramMap = urlParamToMap(param.getParams());
String sign = paramMap.get("sign");
if (sign == null) {
logger.info("推流鉴权失败: 缺少必要参数sign=md5(user表的pushKey)");
return new HookResultForOnPublish(401, "Unauthorized");
}
// 推流自定义播放鉴权码
String callId = paramMap.get("callId");
// 鉴权配置
boolean hasAuthority = userService.checkPushAuthority(callId, sign);
if (!hasAuthority) {
logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
return new HookResultForOnPublish(401, "Unauthorized");
}
StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
streamAuthorityInfo.setCallId(callId);
streamAuthorityInfo.setSign(sign);
// 鉴权通过
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
}
} else {
zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
if (mediaServer == null) {
return new HookResultForOnPublish(0, "success");
}
HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
result.setEnable_audio(true);
taskExecutor.execute(() -> {
ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
if (subscribe != null) {
subscribe.response(mediaInfo, param);
}
});
// 是否录像
if ("rtp".equals(param.getApp())) {
result.setEnable_mp4(userSetting.getRecordSip());
} else {
result.setEnable_mp4(userSetting.isRecordPushLive());
ResultForOnPublish resultForOnPublish = mediaService.authenticatePublish(mediaServer, param.getApp(), param.getStream(), param.getParams());
if (resultForOnPublish != null) {
HookResultForOnPublish successResult = HookResultForOnPublish.getInstance(resultForOnPublish);
logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, successResult);
return successResult;
}else {
HookResultForOnPublish fail = HookResultForOnPublish.Fail();
logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, fail);
return fail;
}
// 国标流
if ("rtp".equals(param.getApp())) {
InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
// 单端口模式下修改流 ID
if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
if (inviteInfo != null) {
result.setStream_replace(inviteInfo.getStream());
logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
}
}
// 设置音频信息及录制信息
List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
// 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
streamAuthorityInfo.setApp(param.getApp());
streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
String channelId = ssrcTransactionForAll.get(0).getChannelId();
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
if (deviceChannel != null) {
result.setEnable_audio(deviceChannel.isHasAudio());
}
// 如果是录像下载就设置视频间隔十秒
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
// 获取录像的总时长然后设置为这个视频的时长
InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
long difference = DateUtil.getDifference(startTime, endTime) / 1000;
result.setMp4_max_second((int) difference);
result.setEnable_mp4(true);
// 设置为2保证得到的mp4的时长是正常的
result.setModify_stamp(2);
}
}
// 如果是talk对讲则默认获取声音
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
result.setEnable_audio(true);
}
}
} else if (param.getApp().equals("broadcast")) {
result.setEnable_audio(true);
} else if (param.getApp().equals("talk")) {
result.setEnable_audio(true);
}
if (param.getApp().equalsIgnoreCase("rtp")) {
String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + param.getStream();
OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo) redisTemplate.opsForValue().get(receiveKey);
String receiveKeyForPS = VideoManagerConstants.WVP_OTHER_RECEIVE_PS_INFO + userSetting.getServerId() + "_" + param.getStream();
OtherPsSendInfo otherPsSendInfo = (OtherPsSendInfo) redisTemplate.opsForValue().get(receiveKeyForPS);
if (otherRtpSendInfo != null || otherPsSendInfo != null) {
result.setEnable_mp4(true);
}
}
logger.info("[ZLM HOOK]推流鉴权 响应:{}->{}->>>>{}", param.getMediaServerId(), param, result);
return result;
}
@ -335,226 +193,20 @@ public class ZLMHttpHookListener {
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
if (param.isRegist()) {
logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
} else {
logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
if (mediaServer == null) {
return HookResult.SUCCESS();
}
JSONObject json = (JSONObject) JSON.toJSON(param);
taskExecutor.execute(() -> {
ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
if (mediaInfo == null) {
logger.info("[ZLM HOOK] 流变化未找到ZLM, {}", param.getMediaServerId());
return;
}
if (subscribe != null) {
subscribe.response(mediaInfo, param);
}
List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
// TODO 重构此处逻辑
if (param.isRegist()) {
// 处理流注册的鉴权信息 流注销这里不再删除鉴权信息下次来了新的鉴权信息会对就的进行覆盖
if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
if (streamAuthorityInfo == null) {
streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
} else {
streamAuthorityInfo.setOriginType(param.getOriginType());
streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
}
redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
}
}
if ("rtsp".equals(param.getSchema())) {
logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
if (param.isRegist()) {
mediaServerService.addCount(param.getMediaServerId());
} else {
mediaServerService.removeCount(param.getMediaServerId());
}
int updateStatusResult = streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream());
if (updateStatusResult > 0) {
}
if ("rtp".equals(param.getApp()) && !param.isRegist()) {
InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
inviteStreamService.removeInviteInfo(inviteInfo);
storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
}
} else if ("broadcast".equals(param.getApp())) {
// 语音对讲推流 stream需要满足格式deviceId_channelId
if (param.getStream().indexOf("_") > 0) {
String[] streamArray = param.getStream().split("_");
if (streamArray.length == 2) {
String deviceId = streamArray[0];
String channelId = streamArray[1];
Device device = deviceService.getDevice(deviceId);
if (device != null) {
if (param.isRegist()) {
if (audioBroadcastManager.exit(deviceId, channelId)) {
playService.stopAudioBroadcast(deviceId, channelId);
}
// 开启语音对讲通道
try {
playService.audioBroadcastCmd(device, channelId, mediaInfo, param.getApp(), param.getStream(), 60, false, (msg) -> {
logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
});
} catch (InvalidArgumentException | ParseException | SipException e) {
logger.error("[命令发送失败] 语音对讲: {}", e.getMessage());
}
} else {
// 流注销
playService.stopAudioBroadcast(deviceId, channelId);
}
} else {
logger.info("[语音对讲] 未找到设备:{}", deviceId);
}
}
}
} else if ("talk".equals(param.getApp())) {
// 语音对讲推流 stream需要满足格式deviceId_channelId
if (param.getStream().indexOf("_") > 0) {
String[] streamArray = param.getStream().split("_");
if (streamArray.length == 2) {
String deviceId = streamArray[0];
String channelId = streamArray[1];
Device device = deviceService.getDevice(deviceId);
if (device != null) {
if (param.isRegist()) {
if (audioBroadcastManager.exit(deviceId, channelId)) {
playService.stopAudioBroadcast(deviceId, channelId);
}
// 开启语音对讲通道
playService.talkCmd(device, channelId, mediaInfo, param.getStream(), (msg) -> {
logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
});
} else {
// 流注销
playService.stopTalk(device, channelId, param.isRegist());
}
} else {
logger.info("[语音对讲] 未找到设备:{}", deviceId);
}
}
}
} else {
if (!"rtp".equals(param.getApp())) {
String type = OriginType.values()[param.getOriginType()].getType();
if (param.isRegist()) {
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(
param.getApp(), param.getStream());
String callId = null;
if (streamAuthorityInfo != null) {
callId = streamAuthorityInfo.getCallId();
}
StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo,
param.getApp(), param.getStream(), tracks, callId);
param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param);
if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
param.setSeverId(userSetting.getServerId());
zlmMediaListManager.addPush(param);
// 冗余数据自己系统中自用
redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
}
} else {
// 兼容流注销时类型从redis记录获取
OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
param.getApp(), param.getStream(), param.getMediaServerId());
if (onStreamChangedHookParam != null) {
type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
if ("PUSH".equalsIgnoreCase(type)) {
// 冗余数据自己系统中自用
redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
}
}
GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
if (gbStream != null) {
// eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
}
zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
}
GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
if (gbStream != null) {
if (userSetting.isUsePushingAsStatus()) {
eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist() ? CatalogEvent.ON : CatalogEvent.OFF);
}
}
if (type != null) {
// 发送流变化redis消息
JSONObject jsonObject = new JSONObject();
jsonObject.put("serverId", userSetting.getServerId());
jsonObject.put("app", param.getApp());
jsonObject.put("stream", param.getStream());
jsonObject.put("register", param.isRegist());
jsonObject.put("mediaServerId", param.getMediaServerId());
redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
}
}
}
if (!param.isRegist()) {
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
if (!sendRtpItems.isEmpty()) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
if (sendRtpItem == null) {
continue;
}
if (sendRtpItem.getApp().equals(param.getApp())) {
logger.info(sendRtpItem.toString());
if (userSetting.getServerId().equals(sendRtpItem.getServerId())) {
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
sendRtpItem.getPlatformId(), null, userSetting.getServerId(), param.getMediaServerId());
// 通知其他wvp停止发流
redisCatchStorage.sendPushStreamClose(messageForPushChannel);
}else {
String platformId = sendRtpItem.getPlatformId();
ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
Device device = deviceService.getDevice(platformId);
try {
if (platform != null) {
commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
sendRtpItem.getCallId(), sendRtpItem.getStream());
} else {
cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
if (sendRtpItem.getPlayType().equals(InviteStreamType.BROADCAST)
|| sendRtpItem.getPlayType().equals(InviteStreamType.TALK)) {
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
if (audioBroadcastCatch != null) {
// 来自上级平台的停止对讲
logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
audioBroadcastManager.del(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
}
}
}
} catch (SipException | InvalidArgumentException | ParseException |
SsrcTransactionNotFoundException e) {
logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
}
}
}
}
}
}
}
});
if (param.isRegist()) {
logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
MediaArrivalEvent mediaArrivalEvent = MediaArrivalEvent.getInstance(this, param, mediaServer);
applicationEventPublisher.publishEvent(mediaArrivalEvent);
} else {
logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
MediaDepartureEvent mediaDepartureEvent = MediaDepartureEvent.getInstance(this, param, mediaServer);
applicationEventPublisher.publishEvent(mediaDepartureEvent);
}
return HookResult.SUCCESS();
}
@ -568,104 +220,9 @@ public class ZLMHttpHookListener {
logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(),
param.getApp(), param.getStream());
JSONObject ret = new JSONObject();
ret.put("code", 0);
// 国标类型的流
if ("rtp".equals(param.getApp())) {
ret.put("close", userSetting.getStreamOnDemand());
// 国标流 点播/录像回放/录像下载
InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
// 点播
if (inviteInfo != null) {
// 录像下载
if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) {
ret.put("close", false);
return ret;
}
// 收到无人观看说明流也没有在往上级推送
if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) {
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId(
inviteInfo.getChannelId());
if (!sendRtpItems.isEmpty()) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
try {
commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
}
redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
sendRtpItem.getCallId(), sendRtpItem.getStream());
if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
}
}
}
}
Device device = deviceService.getDevice(inviteInfo.getDeviceId());
if (device != null) {
try {
// 多查询一次防止已经被处理了
InviteInfo info = inviteStreamService.getInviteInfo(inviteInfo.getType(),
inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
if (info != null) {
cmder.streamByeCmd(device, inviteInfo.getChannelId(),
inviteInfo.getStream(), null);
} else {
logger.info("[无人观看] 未找到设备的点播信息: {} 流:{}", inviteInfo.getDeviceId(), param.getStream());
}
} catch (InvalidArgumentException | ParseException | SipException |
SsrcTransactionNotFoundException e) {
logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
}
} else {
logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
}
inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
inviteInfo.getChannelId(), inviteInfo.getStream());
storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
return ret;
}
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, param.getStream(), null);
if (sendRtpItem != null && "talk".equals(sendRtpItem.getApp())) {
ret.put("close", false);
return ret;
}
} else if ("talk".equals(param.getApp()) || "broadcast".equals(param.getApp())) {
ret.put("close", false);
} else {
// 非国标流 推流/拉流代理
// 拉流代理
StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
if (streamProxyItem != null) {
if (streamProxyItem.isEnableRemoveNoneReader()) {
// 无人观看自动移除
ret.put("close", true);
streamProxyService.del(param.getApp(), param.getStream());
String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrcUrl();
logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url);
} else if (streamProxyItem.isEnableDisableNoneReader()) {
// 无人观看停用
ret.put("close", true);
// 修改数据
streamProxyService.stop(param.getApp(), param.getStream());
} else {
// 无人观看不做处理
ret.put("close", false);
}
return ret;
}
// TODO 推流具有主动性暂时不做处理
// StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
// if (streamPushItem != null) {
// // TODO 发送停止
//
// }
}
boolean close = mediaService.closeStreamOnNoneReader(param.getMediaServerId(), param.getApp(), param.getStream(), param.getSchema());
ret.put("code", close);
return ret;
}
@ -674,119 +231,17 @@ public class ZLMHttpHookListener {
*/
@ResponseBody
@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
public HookResult onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
DeferredResult<HookResult> defaultResult = new DeferredResult<>();
MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
return defaultResult;
}
if ("rtp".equals(param.getApp())) {
String[] s = param.getStream().split("_");
if ((s.length != 2 && s.length != 4)) {
defaultResult.setResult(HookResult.SUCCESS());
return defaultResult;
}
String deviceId = s[0];
String channelId = s[1];
Device device = redisCatchStorage.getDevice(deviceId);
if (device == null || !device.isOnLine()) {
defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
return defaultResult;
}
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
if (deviceChannel == null) {
defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
return defaultResult;
}
if (s.length == 2) {
logger.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
RequestMessage msg = new RequestMessage();
String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
boolean exist = resultHolder.exist(key, null);
msg.setKey(key);
String uuid = UUID.randomUUID().toString();
msg.setId(uuid);
DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
result.onTimeout(() -> {
logger.info("[ZLM HOOK] 预览流自动点播, 等待超时");
msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
resultHolder.invokeAllResult(msg);
inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
storager.stopPlay(deviceId, channelId);
});
resultHolder.put(key, uuid, result);
if (!exist) {
playService.play(mediaInfo, deviceId, channelId, null, (code, message, data) -> {
msg.setData(new HookResult(code, message));
resultHolder.invokeResult(msg);
});
}
return result;
} else if (s.length == 4) {
// 此时为录像回放 录像回放格式为> 设备ID_通道ID_开始时间_结束时间
String startTimeStr = s[2];
String endTimeStr = s[3];
if (startTimeStr == null || endTimeStr == null || startTimeStr.length() != 14 || endTimeStr.length() != 14) {
defaultResult.setResult(HookResult.SUCCESS());
return defaultResult;
}
String startTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(startTimeStr);
String endTime = DateUtil.urlToyyyy_MM_dd_HH_mm_ss(endTimeStr);
logger.info("[ZLM HOOK] 回放流未找到, 发起自动点播:{}->{}->{}/{}-{}-{}",
param.getMediaServerId(), param.getSchema(),
param.getApp(), param.getStream(),
startTime, endTime
);
RequestMessage msg = new RequestMessage();
String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
boolean exist = resultHolder.exist(key, null);
msg.setKey(key);
String uuid = UUID.randomUUID().toString();
msg.setId(uuid);
DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
result.onTimeout(() -> {
logger.info("[ZLM HOOK] 回放流自动点播, 等待超时");
// 释放rtpserver
msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
resultHolder.invokeResult(msg);
});
resultHolder.put(key, uuid, result);
if (!exist) {
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaInfo, param.getStream(), null,
device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
playService.playBack(mediaInfo, ssrcInfo, deviceId, channelId, startTime, endTime, (code, message, data) -> {
msg.setData(new HookResult(code, message));
resultHolder.invokeResult(msg);
});
}
return result;
} else {
defaultResult.setResult(HookResult.SUCCESS());
return defaultResult;
}
} else {
// 拉流代理
StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnableDisableNoneReader()) {
streamProxyService.start(param.getApp(), param.getStream());
}
DeferredResult<HookResult> result = new DeferredResult<>();
result.setResult(HookResult.SUCCESS());
return result;
MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId());
if (!userSetting.isAutoApplyPlay() || mediaServer == null) {
return HookResult.SUCCESS();
}
MediaNotFoundEvent mediaNotFoundEvent = MediaNotFoundEvent.getInstance(this, param, mediaServer);
applicationEventPublisher.publishEvent(mediaNotFoundEvent);
return HookResult.SUCCESS();
}
/**
@ -800,15 +255,16 @@ public class ZLMHttpHookListener {
ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
zlmServerConfig.setIp(request.getRemoteAddr());
logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
taskExecutor.execute(() -> {
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, zlmServerConfig);
}
try {
HookZlmServerStartEvent event = new HookZlmServerStartEvent(this);
MediaServer mediaServerItem = mediaServerService.getOne(zlmServerConfig.getMediaServerId());
if (mediaServerItem != null) {
event.setMediaServerItem(mediaServerItem);
applicationEventPublisher.publishEvent(event);
}
mediaServerService.zlmServerOnline(zlmServerConfig);
});
}catch (Exception e) {
logger.info("[ZLM-HOOK-ZLM启动] 发送通知失败 ", e);
}
return HookResult.SUCCESS();
}
@ -826,22 +282,16 @@ public class ZLMHttpHookListener {
if (!"rtp".equals(param.getApp())) {
return HookResult.SUCCESS();
}
taskExecutor.execute(() -> {
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
if (sendRtpItems.size() > 0) {
for (SendRtpItem sendRtpItem : sendRtpItems) {
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
try {
commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
}
redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
sendRtpItem.getCallId(), sendRtpItem.getStream());
}
try {
MediaSendRtpStoppedEvent event = new MediaSendRtpStoppedEvent(this);
MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
if (mediaServerItem != null) {
event.setMediaServer(mediaServerItem);
applicationEventPublisher.publishEvent(event);
}
});
}catch (Exception e) {
logger.info("[ZLM-HOOK-rtp发送关闭] 发送通知失败 ", e);
}
return HookResult.SUCCESS();
}
@ -855,14 +305,17 @@ public class ZLMHttpHookListener {
param) {
logger.info("[ZLM HOOK] rtpServer收流超时{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
taskExecutor.execute(() -> {
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, param);
}
try {
MediaRtpServerTimeoutEvent event = new MediaRtpServerTimeoutEvent(this);
MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
if (mediaServerItem != null) {
event.setMediaServer(mediaServerItem);
event.setApp("rtp");
applicationEventPublisher.publishEvent(event);
}
});
}catch (Exception e) {
logger.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e);
}
return HookResult.SUCCESS();
}
@ -875,16 +328,16 @@ public class ZLMHttpHookListener {
public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
taskExecutor.execute(() -> {
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
if (subscribes != null && !subscribes.isEmpty()) {
for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
subscribe.response(null, param);
}
try {
MediaServer mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
if (mediaServerItem != null) {
MediaRecordMp4Event event = MediaRecordMp4Event.getInstance(this, param, mediaServerItem);
event.setMediaServer(mediaServerItem);
applicationEventPublisher.publishEvent(event);
}
cloudRecordService.addRecord(param);
});
}catch (Exception e) {
logger.info("[ZLM-HOOK-rtpServer收流超时] 发送通知失败 ", e);
}
return HookResult.SUCCESS();
}

View File

@ -2,15 +2,14 @@ package com.genersoft.iot.vmp.media.zlm;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.dto.*;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.zlm.dto.ChannelOnlineEvent;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.StreamPushMapper;
import com.genersoft.iot.vmp.utils.DateUtil;
import org.slf4j.Logger;
@ -19,7 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.util.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
@ -30,38 +29,22 @@ public class ZLMMediaListManager {
private Logger logger = LoggerFactory.getLogger("ZLMMediaListManager");
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IVideoManagerStorage storager;
@Autowired
private GbStreamMapper gbStreamMapper;
@Autowired
private PlatformGbStreamMapper platformGbStreamMapper;
@Autowired
private IStreamPushService streamPushService;
@Autowired
private IStreamProxyService streamProxyService;
@Autowired
private StreamPushMapper streamPushMapper;
@Autowired
private ZlmHttpHookSubscribe subscribe;
@Autowired
private UserSetting userSetting;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Autowired
private IMediaServerService mediaServerService;
@ -95,9 +78,9 @@ public class ZLMMediaListManager {
}
public void sendStreamEvent(String app, String stream, String mediaServerId) {
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
// 查看推流状态
Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, app, stream);
Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, app, stream);
if (streamReady != null && streamReady) {
ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream);
if (channelOnlineEventLister != null) {

View File

@ -0,0 +1,361 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.service.IMediaNodeServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
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 org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service("zlm")
public class ZLMMediaNodeServerService implements IMediaNodeServerService {
private final static Logger logger = LoggerFactory.getLogger(ZLMMediaNodeServerService.class);
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private ZLMServerFactory zlmServerFactory;
@Value("${sip.ip}")
private String sipIp;
@Override
public int createRTPServer(MediaServer mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) {
return zlmServerFactory.createRTPServer(mediaServer, streamId, ssrc, port, onlyAuto, reUsePort, tcpMode);
}
@Override
public void closeRtpServer(MediaServer mediaServer, String streamId) {
zlmServerFactory.closeRtpServer(mediaServer, streamId);
}
@Override
public void closeRtpServer(MediaServer mediaServer, String streamId, CommonCallback<Boolean> callback) {
zlmServerFactory.closeRtpServer(mediaServer, streamId, callback);
}
@Override
public void closeStreams(MediaServer mediaServer, String app, String stream) {
zlmresTfulUtils.closeStreams(mediaServer, app, stream);
}
@Override
public Boolean updateRtpServerSSRC(MediaServer mediaServer, String streamId, String ssrc) {
return zlmServerFactory.updateRtpServerSSRC(mediaServer, streamId, ssrc);
}
@Override
public boolean checkNodeId(MediaServer mediaServer) {
if (mediaServer == null) {
return false;
}
JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServer);
if (responseJSON != null) {
JSONArray data = responseJSON.getJSONArray("data");
if (data != null && !data.isEmpty()) {
ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
return zlmServerConfig.getGeneralMediaServerId().equals(mediaServer.getId());
}else {
return false;
}
}else {
return false;
}
}
@Override
public void online(MediaServer mediaServer) {
}
@Override
public MediaServer checkMediaServer(String ip, int port, String secret) {
MediaServer mediaServer = new MediaServer();
mediaServer.setIp(ip);
mediaServer.setHttpPort(port);
mediaServer.setSecret(secret);
JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServer);
if (responseJSON == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败");
}
JSONArray data = responseJSON.getJSONArray("data");
if (data == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败");
}
ZLMServerConfig zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
if (zlmServerConfig == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败");
}
mediaServer.setId(zlmServerConfig.getGeneralMediaServerId());
mediaServer.setHttpSSlPort(zlmServerConfig.getHttpPort());
mediaServer.setRtmpPort(zlmServerConfig.getRtmpPort());
mediaServer.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
mediaServer.setRtspPort(zlmServerConfig.getRtspPort());
mediaServer.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
mediaServer.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
mediaServer.setStreamIp(ip);
mediaServer.setHookIp(sipIp.split(",")[0]);
mediaServer.setSdpIp(ip);
mediaServer.setType("zlm");
return mediaServer;
}
@Override
public boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) {
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app);
param.put("stream", stream);
if (!ObjectUtils.isEmpty(ssrc)) {
param.put("ssrc", ssrc);
}
JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param);
return (jsonObject != null && jsonObject.getInteger("code") == 0);
}
@Override
public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) {
logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件, server: {} {}:{}->{}/{}", mediaServer.getId(), app, stream, date, fileName);
JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServer, app,
stream, date, fileName);
if (jsonObject.getInteger("code") == 0) {
return true;
}else {
logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件错误, server: {} {}:{}->{}/{}, 结果: {}", mediaServer.getId(), app, stream, date, fileName, jsonObject);
return false;
}
}
@Override
public List<StreamInfo> getMediaList(MediaServer mediaServer, String app, String stream, String callId) {
List<StreamInfo> streamInfoList = new ArrayList<>();
JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaServer, app, stream);
if (mediaList != null) {
if (mediaList.getInteger("code") == 0) {
JSONArray data = mediaList.getJSONArray("data");
if (data == null) {
return null;
}
JSONObject mediaJSON = data.getJSONObject(0);
MediaInfo mediaInfo = MediaInfo.getInstance(mediaJSON, mediaServer);
StreamInfo streamInfo = getStreamInfoByAppAndStream(mediaServer, app, stream, mediaInfo, callId, true);
if (streamInfo != null) {
streamInfoList.add(streamInfo);
}
}
}
return streamInfoList;
}
public StreamInfo getStreamInfoByAppAndStream(MediaServer mediaServer, String app, String stream, MediaInfo mediaInfo, String callId, boolean isPlay) {
StreamInfo streamInfoResult = new StreamInfo();
streamInfoResult.setStream(stream);
streamInfoResult.setApp(app);
String addr = mediaServer.getStreamIp();
streamInfoResult.setIp(addr);
streamInfoResult.setMediaServerId(mediaServer.getId());
String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId;
streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam);
streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam);
String flvFile = String.format("%s/%s.live.flv%s", app, stream, callIdParam);
streamInfoResult.setFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), flvFile);
streamInfoResult.setWsFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), flvFile);
streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam);
streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam);
streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam);
streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay);
streamInfoResult.setMediaInfo(mediaInfo);
streamInfoResult.setOriginType(mediaInfo.getOriginType());
return streamInfoResult;
}
@Override
public Boolean connectRtpServer(MediaServer mediaServer, String address, int port, String stream) {
JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServer, address, port, stream);
logger.info("[TCP主动连接对方] 结果: {}", jsonObject);
return jsonObject.getInteger("code") == 0;
}
@Override
public void getSnap(MediaServer mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) {
zlmresTfulUtils.getSnap(mediaServer, streamUrl, timeoutSec, expireSec, path, fileName);
}
@Override
public MediaInfo getMediaInfo(MediaServer mediaServer, String app, String stream) {
JSONObject jsonObject = zlmresTfulUtils.getMediaInfo(mediaServer, app, "rtsp", stream);
if (jsonObject.getInteger("code") != 0) {
return null;
}
return MediaInfo.getInstance(jsonObject, mediaServer);
}
@Override
public Boolean pauseRtpCheck(MediaServer mediaServer, String streamKey) {
JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServer, streamKey);
return jsonObject.getInteger("code") == 0;
}
@Override
public Boolean resumeRtpCheck(MediaServer mediaServer, String streamKey) {
JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServer, streamKey);
return jsonObject.getInteger("code") == 0;
}
@Override
public String getFfmpegCmd(MediaServer mediaServer, String cmdKey) {
JSONObject jsonObject = zlmresTfulUtils.getMediaServerConfig(mediaServer);
if (jsonObject.getInteger("code") != 0) {
logger.warn("[getFfmpegCmd] 获取流媒体配置失败");
throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取流媒体配置失败");
}
JSONArray dataArray = jsonObject.getJSONArray("data");
JSONObject mediaServerConfig = dataArray.getJSONObject(0);
if (ObjectUtils.isEmpty(cmdKey)) {
cmdKey = "ffmpeg.cmd";
}
return mediaServerConfig.getString(cmdKey);
}
@Override
public WVPResult<String> addFFmpegSource(MediaServer mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) {
JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs, enableAudio, enableMp4, ffmpegCmdKey);
if (jsonObject.getInteger("code") != 0) {
logger.warn("[getFfmpegCmd] 添加FFMPEG代理失败");
return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加FFMPEG代理失败");
}else {
JSONObject data = jsonObject.getJSONObject("data");
if (data == null) {
return WVPResult.fail(ErrorCode.ERROR100.getCode(), "代理结果异常: " + jsonObject);
}else {
return WVPResult.success(data.getString("key"));
}
}
}
@Override
public WVPResult<String> addStreamProxy(MediaServer mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) {
JSONObject jsonObject = zlmresTfulUtils.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType);
if (jsonObject.getInteger("code") != 0) {
return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加代理失败");
}else {
JSONObject data = jsonObject.getJSONObject("data");
if (data == null) {
return WVPResult.fail(ErrorCode.ERROR100.getCode(), "代理结果异常: " + jsonObject);
}else {
return WVPResult.success(data.getString("key"));
}
}
}
@Override
public Boolean delFFmpegSource(MediaServer mediaServer, String streamKey) {
JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaServer, streamKey);
return jsonObject.getInteger("code") == 0;
}
@Override
public Boolean delStreamProxy(MediaServer mediaServer, String streamKey) {
JSONObject jsonObject = zlmresTfulUtils.delStreamProxy(mediaServer, streamKey);
return jsonObject.getInteger("code") == 0;
}
@Override
public Map<String, String> getFFmpegCMDs(MediaServer mediaServer) {
Map<String, String> result = new HashMap<>();
JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServer);
if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0
&& mediaServerConfigResuly.getJSONArray("data").size() > 0){
JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0);
for (String key : mediaServerConfig.keySet()) {
if (key.startsWith("ffmpeg.cmd")){
result.put(key, mediaServerConfig.getString(key));
}
}
}
return result;
}
@Override
public void startSendRtpPassive(MediaServer mediaServer, SendRtpItem sendRtpItem, Integer timeout) {
Map<String, Object> param = new HashMap<>(12);
param.put("vhost","__defaultVhost__");
param.put("app", sendRtpItem.getApp());
param.put("stream", sendRtpItem.getStream());
param.put("ssrc", sendRtpItem.getSsrc());
param.put("src_port", sendRtpItem.getLocalPort());
param.put("pt", sendRtpItem.getPt());
param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1");
param.put("recv_stream_id", sendRtpItem.getReceiveStream());
if (timeout != null) {
param.put("close_delay_ms", timeout);
}
if (!sendRtpItem.isTcp()) {
// 开启rtcp保活
param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0");
}
if (!sendRtpItem.isTcpActive()) {
param.put("dst_url",sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
}
JSONObject jsonObject = zlmServerFactory.startSendRtpPassive(mediaServer, param, null);
if (jsonObject == null || jsonObject.getInteger("code") != 0 ) {
logger.error("启动监听TCP被动推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param));
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
}
logger.info("调用ZLM-TCP被动推流接口, 结果: {}", jsonObject);
logger.info("启动监听TCP被动推流成功[ {}/{} ]{}->{}:{}, " , sendRtpItem.getApp(), sendRtpItem.getStream(),
jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
}
@Override
public void startSendRtpStream(MediaServer mediaServer, SendRtpItem sendRtpItem) {
Map<String, Object> param = new HashMap<>(12);
param.put("vhost", "__defaultVhost__");
param.put("app", sendRtpItem.getApp());
param.put("stream", sendRtpItem.getStream());
param.put("ssrc", sendRtpItem.getSsrc());
param.put("src_port", sendRtpItem.getLocalPort());
param.put("pt", sendRtpItem.getPt());
param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0");
param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0");
param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1");
if (!sendRtpItem.isTcp()) {
// udp模式下开启rtcp保活
param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0");
}
param.put("dst_url", sendRtpItem.getIp());
param.put("dst_port", sendRtpItem.getPort());
JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServer, param);
if (jsonObject == null || jsonObject.getInteger("code") != 0 ) {
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
}
}
}

View File

@ -0,0 +1,306 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerKeepaliveEvent;
import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerStartEvent;
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.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 管理zlm流媒体节点的状态
*/
@Component
public class ZLMMediaServerStatusManger {
private final static Logger logger = LoggerFactory.getLogger(ZLMMediaServerStatusManger.class);
private final Map<Object, MediaServer> offlineZlmPrimaryMap = new ConcurrentHashMap<>();
private final Map<Object, MediaServer> offlineZlmsecondaryMap = new ConcurrentHashMap<>();
private final Map<Object, Long> offlineZlmTimeMap = new ConcurrentHashMap<>();
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private DynamicTask dynamicTask;
@Value("${server.ssl.enabled:false}")
private boolean sslEnabled;
@Value("${server.port}")
private Integer serverPort;
@Value("${server.servlet.context-path:}")
private String serverServletContextPath;
@Autowired
private UserSetting userSetting;
private final String type = "zlm";
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(MediaServerChangeEvent event) {
if (event.getMediaServerItemList() == null
|| event.getMediaServerItemList().isEmpty()) {
return;
}
for (MediaServer mediaServerItem : event.getMediaServerItemList()) {
if (!type.equals(mediaServerItem.getType())) {
continue;
}
logger.info("[ZLM-添加待上线节点] ID" + mediaServerItem.getId());
offlineZlmPrimaryMap.put(mediaServerItem.getId(), mediaServerItem);
offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
execute();
}
}
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(HookZlmServerStartEvent event) {
if (event.getMediaServerItem() == null
|| !type.equals(event.getMediaServerItem().getType())
|| event.getMediaServerItem().isStatus()) {
return;
}
MediaServer serverItem = mediaServerService.getOne(event.getMediaServerItem().getId());
if (serverItem == null) {
return;
}
logger.info("[ZLM-HOOK事件-服务启动] ID" + event.getMediaServerItem().getId());
online(serverItem, null);
}
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(HookZlmServerKeepaliveEvent event) {
if (event.getMediaServerItem() == null) {
return;
}
MediaServer serverItem = mediaServerService.getOne(event.getMediaServerItem().getId());
if (serverItem == null) {
return;
}
logger.info("[ZLM-HOOK事件-心跳] ID" + event.getMediaServerItem().getId());
online(serverItem, null);
}
@Async("taskExecutor")
@EventListener
public void onApplicationEvent(MediaServerDeleteEvent event) {
if (event.getMediaServerId() == null) {
return;
}
logger.info("[ZLM-节点被移除] ID" + event.getMediaServerId());
offlineZlmPrimaryMap.remove(event.getMediaServerId());
offlineZlmsecondaryMap.remove(event.getMediaServerId());
offlineZlmTimeMap.remove(event.getMediaServerId());
}
@Scheduled(fixedDelay = 10*1000) //每隔10秒检查一次
public void execute(){
// 初次加入的离线节点会在30分钟内每间隔十秒尝试一次30分钟后如果仍然没有上线则每隔30分钟尝试一次连接
if (offlineZlmPrimaryMap.isEmpty() && offlineZlmsecondaryMap.isEmpty()) {
return;
}
if (!offlineZlmPrimaryMap.isEmpty()) {
for (MediaServer mediaServerItem : offlineZlmPrimaryMap.values()) {
if (offlineZlmTimeMap.get(mediaServerItem.getId()) < System.currentTimeMillis() - 30*60*1000) {
offlineZlmsecondaryMap.put(mediaServerItem.getId(), mediaServerItem);
offlineZlmPrimaryMap.remove(mediaServerItem.getId());
continue;
}
logger.info("[ZLM-尝试连接] ID{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
ZLMServerConfig zlmServerConfig = null;
if (responseJson == null) {
logger.info("[ZLM-尝试连接]失败, ID{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
continue;
}
JSONArray data = responseJson.getJSONArray("data");
if (data == null || data.isEmpty()) {
logger.info("[ZLM-尝试连接]失败, ID{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
}else {
zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
initPort(mediaServerItem, zlmServerConfig);
online(mediaServerItem, zlmServerConfig);
}
}
}
if (!offlineZlmsecondaryMap.isEmpty()) {
for (MediaServer mediaServerItem : offlineZlmsecondaryMap.values()) {
if (offlineZlmTimeMap.get(mediaServerItem.getId()) < System.currentTimeMillis() - 30*60*1000) {
continue;
}
logger.info("[ZLM-尝试连接] ID{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
ZLMServerConfig zlmServerConfig = null;
if (responseJson == null) {
logger.info("[ZLM-尝试连接]失败, ID{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
continue;
}
JSONArray data = responseJson.getJSONArray("data");
if (data == null || data.isEmpty()) {
logger.info("[ZLM-尝试连接]失败, ID{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
}else {
zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
initPort(mediaServerItem, zlmServerConfig);
online(mediaServerItem, zlmServerConfig);
}
}
}
}
private void online(MediaServer mediaServerItem, ZLMServerConfig config) {
offlineZlmPrimaryMap.remove(mediaServerItem.getId());
offlineZlmsecondaryMap.remove(mediaServerItem.getId());
offlineZlmTimeMap.remove(mediaServerItem.getId());
if (!mediaServerItem.isStatus()) {
logger.info("[ZLM-连接成功] ID{}, 地址: {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
mediaServerItem.setStatus(true);
mediaServerItem.setHookAliveInterval(10F);
mediaServerService.update(mediaServerItem);
if(mediaServerItem.isAutoConfig()) {
if (config == null) {
JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
JSONArray data = responseJSON.getJSONArray("data");
if (data != null && !data.isEmpty()) {
config = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
}
}
if (config != null) {
initPort(mediaServerItem, config);
setZLMConfig(mediaServerItem, "0".equals(config.getHookEnable())
|| !Objects.equals(mediaServerItem.getHookAliveInterval(), config.getHookAliveInterval()));
}
}
mediaServerService.update(mediaServerItem);
}
// 设置两次心跳未收到则认为zlm离线
String key = "zlm-keepalive-" + mediaServerItem.getId();
dynamicTask.startDelay(key, ()->{
logger.warn("[ZLM-心跳超时] ID{}", mediaServerItem.getId());
mediaServerItem.setStatus(false);
offlineZlmPrimaryMap.put(mediaServerItem.getId(), mediaServerItem);
offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis());
// TODO 发送离线通知
mediaServerService.update(mediaServerItem);
}, (int)(mediaServerItem.getHookAliveInterval() * 2 * 1000));
}
private void initPort(MediaServer mediaServerItem, ZLMServerConfig zlmServerConfig) {
// 端口只会从配置中读取一次一旦自己配置或者读取过了将不在配置
if (mediaServerItem.getHttpSSlPort() == 0) {
mediaServerItem.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
}
if (mediaServerItem.getRtmpPort() == 0) {
mediaServerItem.setRtmpPort(zlmServerConfig.getRtmpPort());
}
if (mediaServerItem.getRtmpSSlPort() == 0) {
mediaServerItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
}
if (mediaServerItem.getRtspPort() == 0) {
mediaServerItem.setRtspPort(zlmServerConfig.getRtspPort());
}
if (mediaServerItem.getRtspSSLPort() == 0) {
mediaServerItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
}
if (mediaServerItem.getRtpProxyPort() == 0) {
mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
}
mediaServerItem.setHookAliveInterval(10F);
}
public void setZLMConfig(MediaServer mediaServerItem, boolean restart) {
logger.info("[媒体服务节点] 正在设置 {} -> {}:{}",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
String protocol = sslEnabled ? "https" : "http";
String hookPrefix = String.format("%s://%s:%s%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort, (serverServletContextPath == null || "/".equals(serverServletContextPath)) ? "" : serverServletContextPath);
Map<String, Object> param = new HashMap<>();
param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
if (mediaServerItem.getRtspPort() != 0) {
param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -frames:v 1 %s");
}
param.put("hook.enable","1");
param.put("hook.on_flow_report","");
param.put("hook.on_play",String.format("%s/on_play", hookPrefix));
param.put("hook.on_http_access","");
param.put("hook.on_publish", String.format("%s/on_publish", hookPrefix));
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", hookPrefix));
param.put("hook.on_shell_login","");
param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrefix));
param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrefix));
param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrefix));
param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrefix));
param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrefix));
param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrefix));
param.put("hook.on_record_mp4",String.format("%s/on_record_mp4", hookPrefix));
param.put("hook.timeoutSec","30");
param.put("hook.alive_interval", mediaServerItem.getHookAliveInterval());
// 推流断开后可以在超时时间内重新连接上继续推流这样播放器会接着播放
// 置0关闭此特性(推流断开会导致立即断开播放器)
// 此参数不应大于播放器超时时间
// 优化此消息以更快的收到流注销事件
param.put("protocol.continue_push_ms", "3000" );
// 最多等待未初始化的Track时间单位毫秒超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流
// 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) {
param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
}
if (!ObjectUtils.isEmpty(mediaServerItem.getRecordPath())) {
File recordPathFile = new File(mediaServerItem.getRecordPath());
param.put("protocol.mp4_save_path", recordPathFile.getParentFile().getPath());
param.put("protocol.downloadRoot", recordPathFile.getParentFile().getPath());
param.put("record.appName", recordPathFile.getName());
}
JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
if (responseJSON != null && responseJSON.getInteger("code") == 0) {
if (restart) {
logger.info("[媒体服务节点] 设置成功,开始重启以保证配置生效 {} -> {}:{}",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
zlmresTfulUtils.restartServer(mediaServerItem);
}else {
logger.info("[媒体服务节点] 设置成功 {} -> {}:{}",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
}
}else {
logger.info("[媒体服务节点] 设置媒体服务节点失败 {} -> {}:{}",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
}
}
}

View File

@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import org.jetbrains.annotations.NotNull;
@ -60,12 +60,12 @@ public class ZLMRESTfulUtils {
}
public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
public JSONObject sendPost(MediaServer mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
return sendPost(mediaServerItem, api, param, callback, null);
}
public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback, Integer readTimeOut) {
public JSONObject sendPost(MediaServer mediaServerItem, String api, Map<String, Object> param, RequestCallback callback, Integer readTimeOut) {
OkHttpClient client = getClient(readTimeOut);
if (mediaServerItem == null) {
@ -104,8 +104,6 @@ public class ZLMRESTfulUtils {
responseJSON = JSON.parseObject(responseStr);
}
}else {
System.out.println( 2222);
System.out.println( response.code());
response.close();
Objects.requireNonNull(response.body()).close();
}
@ -164,7 +162,7 @@ public class ZLMRESTfulUtils {
return responseJSON;
}
public void sendGetForImg(MediaServerItem mediaServerItem, String api, Map<String, Object> params, String targetPath, String fileName) {
public void sendGetForImg(MediaServer mediaServerItem, String api, Map<String, Object> params, String targetPath, String fileName) {
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
HttpUrl parseUrl = HttpUrl.parse(url);
if (parseUrl == null) {
@ -216,7 +214,7 @@ public class ZLMRESTfulUtils {
}
}
public JSONObject isMediaOnline(MediaServerItem mediaServerItem, String app, String stream, String schema){
public JSONObject isMediaOnline(MediaServer mediaServerItem, String app, String stream, String schema){
Map<String, Object> param = new HashMap<>();
if (app != null) {
param.put("app",app);
@ -231,7 +229,7 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "isMediaOnline", param, null);
}
public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){
public JSONObject getMediaList(MediaServer mediaServerItem, String app, String stream, String schema, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
if (app != null) {
param.put("app",app);
@ -246,15 +244,15 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "getMediaList",param, callback);
}
public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream){
public JSONObject getMediaList(MediaServer mediaServerItem, String app, String stream){
return getMediaList(mediaServerItem, app, stream,null, null);
}
public JSONObject getMediaList(MediaServerItem mediaServerItem, RequestCallback callback){
public JSONObject getMediaList(MediaServer mediaServerItem, RequestCallback callback){
return sendPost(mediaServerItem, "getMediaList",null, callback);
}
public JSONObject getMediaInfo(MediaServerItem mediaServerItem, String app, String schema, String stream){
public JSONObject getMediaInfo(MediaServer mediaServerItem, String app, String schema, String stream){
Map<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("schema",schema);
@ -263,13 +261,13 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "getMediaInfo",param, null);
}
public JSONObject getRtpInfo(MediaServerItem mediaServerItem, String stream_id){
public JSONObject getRtpInfo(MediaServer mediaServerItem, String stream_id){
Map<String, Object> param = new HashMap<>();
param.put("stream_id",stream_id);
return sendPost(mediaServerItem, "getRtpInfo",param, null);
}
public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms,
public JSONObject addFFmpegSource(MediaServer mediaServerItem, String src_url, String dst_url, Integer timeout_ms,
boolean enable_audio, boolean enable_mp4, String ffmpeg_cmd_key){
logger.info(src_url);
logger.info(dst_url);
@ -282,63 +280,63 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "addFFmpegSource",param, null);
}
public JSONObject delFFmpegSource(MediaServerItem mediaServerItem, String key){
public JSONObject delFFmpegSource(MediaServer mediaServerItem, String key){
Map<String, Object> param = new HashMap<>();
param.put("key", key);
return sendPost(mediaServerItem, "delFFmpegSource",param, null);
}
public JSONObject delStreamProxy(MediaServerItem mediaServerItem, String key){
public JSONObject delStreamProxy(MediaServer mediaServerItem, String key){
Map<String, Object> param = new HashMap<>();
param.put("key", key);
return sendPost(mediaServerItem, "delStreamProxy",param, null);
}
public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){
public JSONObject getMediaServerConfig(MediaServer mediaServerItem){
return sendPost(mediaServerItem, "getServerConfig",null, null);
}
public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map<String, Object> param){
public JSONObject setServerConfig(MediaServer mediaServerItem, Map<String, Object> param){
return sendPost(mediaServerItem,"setServerConfig",param, null);
}
public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param){
public JSONObject openRtpServer(MediaServer mediaServerItem, Map<String, Object> param){
return sendPost(mediaServerItem, "openRtpServer",param, null);
}
public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param) {
public JSONObject closeRtpServer(MediaServer mediaServerItem, Map<String, Object> param) {
return sendPost(mediaServerItem, "closeRtpServer",param, null);
}
public void closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param, RequestCallback callback) {
public void closeRtpServer(MediaServer mediaServerItem, Map<String, Object> param, RequestCallback callback) {
sendPost(mediaServerItem, "closeRtpServer",param, callback);
}
public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
public JSONObject listRtpServer(MediaServer mediaServerItem) {
return sendPost(mediaServerItem, "listRtpServer",null, null);
}
public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
public JSONObject startSendRtp(MediaServer mediaServerItem, Map<String, Object> param) {
return sendPost(mediaServerItem, "startSendRtp",param, null);
}
public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object> param) {
public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object> param) {
return sendPost(mediaServerItem, "startSendRtpPassive",param, null);
}
public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object> param, RequestCallback callback) {
public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object> param, RequestCallback callback) {
return sendPost(mediaServerItem, "startSendRtpPassive",param, callback);
}
public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
public JSONObject stopSendRtp(MediaServer mediaServerItem, Map<String, Object> param) {
return sendPost(mediaServerItem, "stopSendRtp",param, null);
}
public JSONObject restartServer(MediaServerItem mediaServerItem) {
public JSONObject restartServer(MediaServer mediaServerItem) {
return sendPost(mediaServerItem, "restartServer",null, null);
}
public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) {
public JSONObject addStreamProxy(MediaServer mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) {
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app);
@ -350,7 +348,7 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "addStreamProxy",param, null, 20);
}
public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) {
public JSONObject closeStreams(MediaServer mediaServerItem, String app, String stream) {
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app);
@ -359,17 +357,17 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "close_streams",param, null);
}
public JSONObject getAllSession(MediaServerItem mediaServerItem) {
public JSONObject getAllSession(MediaServer mediaServerItem) {
return sendPost(mediaServerItem, "getAllSession",null, null);
}
public void kickSessions(MediaServerItem mediaServerItem, String localPortSStr) {
public void kickSessions(MediaServer mediaServerItem, String localPortSStr) {
Map<String, Object> param = new HashMap<>();
param.put("local_port", localPortSStr);
sendPost(mediaServerItem, "kick_sessions",param, null);
}
public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
public void getSnap(MediaServer mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
Map<String, Object> param = new HashMap<>(3);
param.put("url", streamUrl);
param.put("timeout_sec", timeout_sec);
@ -377,19 +375,19 @@ public class ZLMRESTfulUtils {
sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName);
}
public JSONObject pauseRtpCheck(MediaServerItem mediaServerItem, String streamId) {
public JSONObject pauseRtpCheck(MediaServer mediaServerItem, String streamId) {
Map<String, Object> param = new HashMap<>(1);
param.put("stream_id", streamId);
return sendPost(mediaServerItem, "pauseRtpCheck",param, null);
}
public JSONObject resumeRtpCheck(MediaServerItem mediaServerItem, String streamId) {
public JSONObject resumeRtpCheck(MediaServer mediaServerItem, String streamId) {
Map<String, Object> param = new HashMap<>(1);
param.put("stream_id", streamId);
return sendPost(mediaServerItem, "resumeRtpCheck",param, null);
}
public JSONObject connectRtpServer(MediaServerItem mediaServerItem, String dst_url, int dst_port, String stream_id) {
public JSONObject connectRtpServer(MediaServer mediaServerItem, String dst_url, int dst_port, String stream_id) {
Map<String, Object> param = new HashMap<>(1);
param.put("dst_url", dst_url);
param.put("dst_port", dst_port);
@ -397,14 +395,14 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "connectRtpServer",param, null);
}
public JSONObject updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
public JSONObject updateRtpServerSSRC(MediaServer mediaServerItem, String streamId, String ssrc) {
Map<String, Object> param = new HashMap<>(1);
param.put("ssrc", ssrc);
param.put("stream_id", streamId);
return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null);
}
public JSONObject deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName) {
public JSONObject deleteRecordDirectory(MediaServer mediaServerItem, String app, String stream, String date, String fileName) {
Map<String, Object> param = new HashMap<>(1);
param.put("vhost", "__defaultVhost__");
param.put("app", app);

View File

@ -1,173 +0,0 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForServerStarted;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Component
@Order(value=12)
public class ZLMRunner implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
private Map<String, Boolean> startGetMedia;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private ZlmHttpHookSubscribe hookSubscribe;
@Autowired
private EventPublisher publisher;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private MediaConfig mediaConfig;
@Autowired
private DynamicTask dynamicTask;
@Override
public void run(String... strings) throws Exception {
mediaServerService.clearMediaServerForOnline();
MediaServerItem defaultMediaServer = mediaServerService.getDefaultMediaServer();
if (defaultMediaServer == null) {
mediaServerService.addToDatabase(mediaConfig.getMediaSerItem());
}else {
MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
mediaServerService.updateToDatabase(mediaSerItem);
}
mediaServerService.syncCatchFromDatabase();
HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started();
// 订阅 zlm启动事件, 新的zlm也会从这里进入系统
hookSubscribe.addSubscribe(hookSubscribeForServerStarted,
(mediaServerItem, hookParam)->{
ZLMServerConfig zlmServerConfig = (ZLMServerConfig)hookParam;
if (zlmServerConfig !=null ) {
if (startGetMedia != null) {
startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId());
if (startGetMedia.size() == 0) {
hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
}
}
}
});
// 获取zlm信息
logger.info("[zlm] 等待默认zlm中...");
// 获取所有的zlm 并开启主动连接
List<MediaServerItem> all = mediaServerService.getAllFromDatabase();
Map<String, MediaServerItem> allMap = new HashMap<>();
mediaServerService.updateVmServer(all);
if (all.size() == 0) {
all.add(mediaConfig.getMediaSerItem());
}
for (MediaServerItem mediaServerItem : all) {
if (startGetMedia == null) {
startGetMedia = new ConcurrentHashMap<>();
}
startGetMedia.put(mediaServerItem.getId(), true);
connectZlmServer(mediaServerItem);
allMap.put(mediaServerItem.getId(), mediaServerItem);
}
String taskKey = "zlm-connect-timeout";
dynamicTask.startDelay(taskKey, ()->{
if (startGetMedia != null && startGetMedia.size() > 0) {
Set<String> allZlmId = startGetMedia.keySet();
for (String id : allZlmId) {
logger.error("[ {} ]]主动连接失败,不再尝试连接", id);
}
startGetMedia = null;
}
// 获取redis中所有的zlm
List<MediaServerItem> allInRedis = mediaServerService.getAll();
for (MediaServerItem mediaServerItem : allInRedis) {
if (!allMap.containsKey(mediaServerItem.getId())) {
mediaServerService.delete(mediaServerItem.getId());
}
}
}, 60 * 1000 );
}
@Async("taskExecutor")
public void connectZlmServer(MediaServerItem mediaServerItem){
String connectZlmServerTaskKey = "connect-zlm-" + mediaServerItem.getId();
ZLMServerConfig zlmServerConfigFirst = getMediaServerConfig(mediaServerItem);
if (zlmServerConfigFirst != null) {
zlmServerConfigFirst.setIp(mediaServerItem.getIp());
zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort());
startGetMedia.remove(mediaServerItem.getId());
if (startGetMedia.size() == 0) {
hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
}
mediaServerService.zlmServerOnline(zlmServerConfigFirst);
}else {
logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
publisher.zlmOfflineEventPublish(mediaServerItem.getId());
}
dynamicTask.startCron(connectZlmServerTaskKey, ()->{
ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem);
if (zlmServerConfig != null) {
dynamicTask.stop(connectZlmServerTaskKey);
zlmServerConfig.setIp(mediaServerItem.getIp());
zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort());
startGetMedia.remove(mediaServerItem.getId());
if (startGetMedia.size() == 0) {
hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started());
}
mediaServerService.zlmServerOnline(zlmServerConfig);
}
}, 2000);
}
public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) {
if (startGetMedia == null) { return null;}
if (!mediaServerItem.isDefaultServer() && mediaServerService.getOne(mediaServerItem.getId()) == null) {
return null;
}
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);
}
} else {
logger.error("[ {} ]-[ {}:{} ]主动连接失败, 2s后重试",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
}
return zlmServerConfig;
}
}

View File

@ -5,7 +5,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -17,7 +17,7 @@ import java.util.Map;
@Component
public class ZLMServerFactory {
private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory");
private Logger logger = LoggerFactory.getLogger("ZLMServerFactory");
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@ -25,9 +25,6 @@ public class ZLMServerFactory {
@Autowired
private UserSetting userSetting;
@Autowired
private ZlmHttpHookSubscribe hookSubscribe;
@Autowired
private SendRtpPortManager sendRtpPortManager;
@ -42,7 +39,7 @@ public class ZLMServerFactory {
* @param tcpMode 0/null udp 模式1 tcp 被动模式, 2 tcp 主动模式
* @return
*/
public int createRTPServer(MediaServerItem mediaServerItem, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) {
public int createRTPServer(MediaServer mediaServerItem, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) {
int result = -1;
// 查询此rtp server 是否已经存在
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
@ -108,7 +105,7 @@ public class ZLMServerFactory {
return result;
}
public boolean closeRtpServer(MediaServerItem serverItem, String streamId) {
public boolean closeRtpServer(MediaServer serverItem, String streamId) {
boolean result = false;
if (serverItem !=null){
Map<String, Object> param = new HashMap<>();
@ -129,7 +126,7 @@ public class ZLMServerFactory {
return result;
}
public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback<Boolean> callback) {
public void closeRtpServer(MediaServer serverItem, String streamId, CommonCallback<Boolean> callback) {
if (serverItem == null) {
callback.run(false);
return;
@ -155,105 +152,28 @@ public class ZLMServerFactory {
}
/**
* 创建一个国标推流
* @param ip 推流ip
* @param port 推流端口
* @param ssrc 推流唯一标识
* @param platformId 平台id
* @param channelId 通道id
* @param tcp 是否为tcp
* @return SendRtpItem
*/
public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
String deviceId, String channelId, boolean tcp, boolean rtcp){
int localPort = sendRtpPortManager.getNextPort(serverItem);
if (localPort == 0) {
return null;
}
SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setIp(ip);
sendRtpItem.setPort(port);
sendRtpItem.setSsrc(ssrc);
sendRtpItem.setPlatformId(platformId);
sendRtpItem.setDeviceId(deviceId);
sendRtpItem.setChannelId(channelId);
sendRtpItem.setTcp(tcp);
sendRtpItem.setRtcp(rtcp);
sendRtpItem.setApp("rtp");
sendRtpItem.setLocalPort(localPort);
sendRtpItem.setServerId(userSetting.getServerId());
sendRtpItem.setMediaServerId(serverItem.getId());
return sendRtpItem;
}
/**
* 创建一个直播推流
* @param ip 推流ip
* @param port 推流端口
* @param ssrc 推流唯一标识
* @param platformId 平台id
* @param channelId 通道id
* @param tcp 是否为tcp
* @return SendRtpItem
*/
public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId,
String app, String stream, String channelId, boolean tcp, boolean rtcp){
int localPort = sendRtpPortManager.getNextPort(serverItem);
if (localPort == 0) {
return null;
}
SendRtpItem sendRtpItem = new SendRtpItem();
sendRtpItem.setIp(ip);
sendRtpItem.setPort(port);
sendRtpItem.setSsrc(ssrc);
sendRtpItem.setApp(app);
sendRtpItem.setStream(stream);
sendRtpItem.setPlatformId(platformId);
sendRtpItem.setChannelId(channelId);
sendRtpItem.setTcp(tcp);
sendRtpItem.setLocalPort(localPort);
sendRtpItem.setServerId(userSetting.getServerId());
sendRtpItem.setMediaServerId(serverItem.getId());
sendRtpItem.setRtcp(rtcp);
return sendRtpItem;
}
/**
* 调用zlm RESTFUL API startSendRtp
*/
public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
public JSONObject startSendRtpStream(MediaServer mediaServerItem, Map<String, Object>param) {
return zlmresTfulUtils.startSendRtp(mediaServerItem, param);
}
/**
* 调用zlm RESTFUL API startSendRtpPassive
*/
public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object>param) {
public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object>param) {
return zlmresTfulUtils.startSendRtpPassive(mediaServerItem, param);
}
public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map<String, Object>param, ZLMRESTfulUtils.RequestCallback callback) {
public JSONObject startSendRtpPassive(MediaServer mediaServerItem, Map<String, Object>param, ZLMRESTfulUtils.RequestCallback callback) {
return zlmresTfulUtils.startSendRtpPassive(mediaServerItem, param, callback);
}
/**
* 查询待转推的流是否就绪
*/
public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtsp", streamId);
if (mediaInfo.getInteger("code") == -2) {
return null;
}
return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
}
/**
* 查询待转推的流是否就绪
*/
public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
public Boolean isStreamReady(MediaServer mediaServerItem, String app, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId);
if (mediaInfo == null || (mediaInfo.getInteger("code") == -2)) {
return null;
@ -268,7 +188,7 @@ public class ZLMServerFactory {
* @param streamId
* @return
*/
public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
public int totalReaderCount(MediaServer mediaServerItem, String app, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId);
if (mediaInfo == null) {
return 0;
@ -285,28 +205,7 @@ public class ZLMServerFactory {
return mediaInfo.getInteger("totalReaderCount");
}
/**
* 调用zlm RESTful API stopSendRtp
*/
public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
if (mediaServerItem == null) {
logger.error("[停止RTP推流] 失败: 媒体节点为NULL");
return false;
}
Boolean result = false;
JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
if (jsonObject == null) {
logger.error("[停止RTP推流] 失败: 请检查ZLM服务");
} else if (jsonObject.getInteger("code") == 0) {
result= true;
logger.info("[停止RTP推流] 成功");
} else {
logger.warn("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"), JSON.toJSON(param), jsonObject);
}
return result;
}
public JSONObject startSendRtp(MediaServerItem mediaInfo, SendRtpItem sendRtpItem) {
public JSONObject startSendRtp(MediaServer mediaInfo, SendRtpItem sendRtpItem) {
String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
logger.info("rtp/{}开始推流, 目标={}:{}SSRC={}", sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc());
Map<String, Object> param = new HashMap<>(12);
@ -351,7 +250,7 @@ public class ZLMServerFactory {
return startSendRtpStreamResult;
}
public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
public Boolean updateRtpServerSSRC(MediaServer mediaServerItem, String streamId, String ssrc) {
boolean result = false;
JSONObject jsonObject = zlmresTfulUtils.updateRtpServerSSRC(mediaServerItem, streamId, ssrc);
if (jsonObject == null) {

View File

@ -1,161 +0,0 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.HookType;
import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* ZLMediaServer的hook事件订阅
* @author lin
*/
@Component
public class ZlmHttpHookSubscribe {
private final static Logger logger = LoggerFactory.getLogger(ZlmHttpHookSubscribe.class);
@FunctionalInterface
public interface Event{
void response(MediaServerItem mediaServerItem, HookParam hookParam);
}
private Map<HookType, Map<IHookSubscribe, ZlmHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();
public void addSubscribe(IHookSubscribe hookSubscribe, ZlmHttpHookSubscribe.Event event) {
if (hookSubscribe.getExpires() == null) {
// 默认5分钟过期
Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5));
hookSubscribe.setExpires(expiresInstant);
}
allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event);
System.out.println(allSubscribes);
}
public ZlmHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) {
ZlmHttpHookSubscribe.Event event= null;
Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type);
if (eventMap == null) {
return null;
}
for (IHookSubscribe key : eventMap.keySet()) {
Boolean result = null;
for (String s : key.getContent().keySet()) {
if (result == null) {
result = key.getContent().getString(s).equals(hookResponse.getString(s));
}else {
if (key.getContent().getString(s) == null) {
continue;
}
result = result && key.getContent().getString(s).equals(hookResponse.getString(s));
}
}
if (null != result && result) {
event = eventMap.get(key);
}
}
return event;
}
public void removeSubscribe(IHookSubscribe hookSubscribe) {
Map<IHookSubscribe, Event> eventMap = allSubscribes.get(hookSubscribe.getHookType());
if (eventMap == null) {
return;
}
Set<Map.Entry<IHookSubscribe, Event>> entries = eventMap.entrySet();
if (entries.size() > 0) {
List<Map.Entry<IHookSubscribe, ZlmHttpHookSubscribe.Event>> entriesToRemove = new ArrayList<>();
for (Map.Entry<IHookSubscribe, ZlmHttpHookSubscribe.Event> entry : entries) {
JSONObject content = entry.getKey().getContent();
if (content == null || content.size() == 0) {
entriesToRemove.add(entry);
continue;
}
Boolean result = null;
for (String s : content.keySet()) {
if (result == null) {
result = content.getString(s).equals(hookSubscribe.getContent().getString(s));
}else {
if (content.getString(s) == null) {
continue;
}
result = result && content.getString(s).equals(hookSubscribe.getContent().getString(s));
}
}
if (result){
entriesToRemove.add(entry);
}
}
if (!CollectionUtils.isEmpty(entriesToRemove)) {
for (Map.Entry<IHookSubscribe, ZlmHttpHookSubscribe.Event> entry : entriesToRemove) {
eventMap.remove(entry.getKey());
}
if (eventMap.size() == 0) {
allSubscribes.remove(hookSubscribe.getHookType());
}
}
}
}
/**
* 获取某个类型的所有的订阅
* @param type
* @return
*/
public List<ZlmHttpHookSubscribe.Event> getSubscribes(HookType type) {
Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type);
if (eventMap == null) {
return null;
}
List<ZlmHttpHookSubscribe.Event> result = new ArrayList<>();
for (IHookSubscribe key : eventMap.keySet()) {
result.add(eventMap.get(key));
}
return result;
}
public List<IHookSubscribe> getAll(){
ArrayList<IHookSubscribe> result = new ArrayList<>();
Collection<Map<IHookSubscribe, Event>> values = allSubscribes.values();
for (Map<IHookSubscribe, Event> value : values) {
result.addAll(value.keySet());
}
return result;
}
/**
* 对订阅数据进行过期清理
*/
// @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次
@Scheduled(fixedRate = 2 * 1000)
public void execute(){
Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5));
int total = 0;
for (HookType hookType : allSubscribes.keySet()) {
Map<IHookSubscribe, Event> hookSubscribeEventMap = allSubscribes.get(hookType);
if (hookSubscribeEventMap.size() > 0) {
for (IHookSubscribe hookSubscribe : hookSubscribeEventMap.keySet()) {
if (hookSubscribe.getExpires().isBefore(instant)) {
// 过期的
hookSubscribeEventMap.remove(hookSubscribe);
total ++;
}
}
}
}
}
}

View File

@ -1,71 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
/**
* hook 订阅工厂
* @author lin
*/
public class HookSubscribeFactory {
public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) {
HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange();
JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
subscribeKey.put("app", app);
subscribeKey.put("stream", stream);
subscribeKey.put("regist", regist);
if (scheam != null) {
subscribeKey.put("schema", scheam);
}
subscribeKey.put("mediaServerId", mediaServerId);
hookSubscribe.setContent(subscribeKey);
return hookSubscribe;
}
public static HookSubscribeForRtpServerTimeout on_rtp_server_timeout(String stream, String ssrc, String mediaServerId) {
HookSubscribeForRtpServerTimeout hookSubscribe = new HookSubscribeForRtpServerTimeout();
JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
subscribeKey.put("stream_id", stream);
subscribeKey.put("ssrc", ssrc);
subscribeKey.put("mediaServerId", mediaServerId);
hookSubscribe.setContent(subscribeKey);
return hookSubscribe;
}
public static HookSubscribeForStreamPush on_publish(String app, String stream, String scheam, String mediaServerId) {
HookSubscribeForStreamPush hookSubscribe = new HookSubscribeForStreamPush();
JSONObject subscribeKey = new JSONObject();
subscribeKey.put("app", app);
subscribeKey.put("stream", stream);
if (scheam != null) {
subscribeKey.put("schema", scheam);
}
subscribeKey.put("mediaServerId", mediaServerId);
hookSubscribe.setContent(subscribeKey);
return hookSubscribe;
}
public static HookSubscribeForServerStarted on_server_started() {
HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted();
hookSubscribe.setContent(new JSONObject());
return hookSubscribe;
}
public static HookSubscribeForRecordMp4 on_record_mp4(String mediaServerId, String app, String stream) {
HookSubscribeForRecordMp4 hookSubscribe = new HookSubscribeForRecordMp4();
JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
subscribeKey.put("app", app);
subscribeKey.put("stream", stream);
subscribeKey.put("mediaServerId", mediaServerId);
hookSubscribe.setContent(subscribeKey);
return hookSubscribe;
}
}

View File

@ -1,44 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import java.time.Instant;
/**
* hook订阅-录像完成
* @author lin
*/
public class HookSubscribeForRecordMp4 implements IHookSubscribe{
private HookType hookType = HookType.on_record_mp4;
private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -1,44 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import java.time.Instant;
/**
* hook订阅-收流超时
* @author lin
*/
public class HookSubscribeForRtpServerTimeout implements IHookSubscribe{
private HookType hookType = HookType.on_rtp_server_timeout;
private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -1,44 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import java.time.Instant;
/**
* hook订阅-流变化
* @author lin
*/
public class HookSubscribeForServerStarted implements IHookSubscribe{
private HookType hookType = HookType.on_server_started;
private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -1,44 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import java.time.Instant;
/**
* hook订阅-流变化
* @author lin
*/
public class HookSubscribeForStreamChange implements IHookSubscribe{
private HookType hookType = HookType.on_stream_changed;
private JSONObject content;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -1,43 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import java.time.Instant;
/**
* hook订阅-开始推流
* @author lin
*/
public class HookSubscribeForStreamPush implements IHookSubscribe{
private HookType hookType = HookType.on_publish;
private JSONObject content;
private Instant expires;
@Override
public HookType getHookType() {
return hookType;
}
@Override
public JSONObject getContent() {
return content;
}
public void setContent(JSONObject content) {
this.content = content;
}
@Override
public Instant getExpires() {
return expires;
}
@Override
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -1,26 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
/**
* hook类型
* @author lin
*/
public enum HookType {
on_flow_report,
on_http_access,
on_play,
on_publish,
on_record_mp4,
on_rtsp_auth,
on_rtsp_realm,
on_shell_login,
on_stream_changed,
on_stream_none_reader,
on_stream_not_found,
on_server_started,
on_rtp_server_timeout,
on_server_keepalive,
on_send_rtp_stopped
}

View File

@ -1,36 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.JSONObject;
import java.time.Instant;
/**
* zlm hook事件的参数
* @author lin
*/
public interface IHookSubscribe {
/**
* 获取hook类型
* @return hook类型
*/
HookType getHookType();
/**
* 获取hook的具体内容
* @return hook的具体内容
*/
JSONObject getContent();
/**
* 设置过期时间
* @param instant 过期时间
*/
void setExpires(Instant instant);
/**
* 获取过期时间
* @return 过期时间
*/
Instant getExpires();
}

View File

@ -1,168 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
/**
* 精简的MediaServerItem信息方便给前端返回数据
*/
public class MediaServerItemLite {
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 String secret;
private int recordAssistPort;
public MediaServerItemLite(MediaServerItem mediaServerItem) {
this.id = mediaServerItem.getId();
this.ip = mediaServerItem.getIp();
this.hookIp = mediaServerItem.getHookIp();
this.sdpIp = mediaServerItem.getSdpIp();
this.streamIp = mediaServerItem.getStreamIp();
this.httpPort = mediaServerItem.getHttpPort();
this.httpSSlPort = mediaServerItem.getHttpSSlPort();
this.rtmpPort = mediaServerItem.getRtmpPort();
this.rtmpSSlPort = mediaServerItem.getRtmpSSlPort();
this.rtpProxyPort = mediaServerItem.getRtpProxyPort();
this.rtspPort = mediaServerItem.getRtspPort();
this.rtspSSLPort = mediaServerItem.getRtspSSLPort();
this.secret = mediaServerItem.getSecret();
this.recordAssistPort = mediaServerItem.getRecordAssistPort();
}
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 String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public int getRecordAssistPort() {
return recordAssistPort;
}
public void setRecordAssistPort(int recordAssistPort) {
this.recordAssistPort = recordAssistPort;
}
}

View File

@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnPublishHookParam;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
/**
* 流的鉴权信息
@ -97,21 +96,23 @@ public class StreamAuthorityInfo {
this.sign = sign;
}
public static StreamAuthorityInfo getInstanceByHook(OnPublishHookParam hookParam) {
public static StreamAuthorityInfo getInstanceByHook(String app, String stream, String id) {
StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo();
streamAuthorityInfo.setApp(hookParam.getApp());
streamAuthorityInfo.setStream(hookParam.getStream());
streamAuthorityInfo.setId(hookParam.getId());
streamAuthorityInfo.setApp(app);
streamAuthorityInfo.setStream(stream);
streamAuthorityInfo.setId(id);
return streamAuthorityInfo;
}
public static StreamAuthorityInfo getInstanceByHook(OnStreamChangedHookParam onStreamChangedHookParam) {
public static StreamAuthorityInfo getInstanceByHook(MediaArrivalEvent event) {
StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo();
streamAuthorityInfo.setApp(onStreamChangedHookParam.getApp());
streamAuthorityInfo.setStream(onStreamChangedHookParam.getStream());
streamAuthorityInfo.setId(onStreamChangedHookParam.getMediaServerId());
streamAuthorityInfo.setOriginType(onStreamChangedHookParam.getOriginType());
streamAuthorityInfo.setOriginTypeStr(onStreamChangedHookParam.getOriginTypeStr());
streamAuthorityInfo.setApp(event.getApp());
streamAuthorityInfo.setStream(event.getStream());
streamAuthorityInfo.setId(event.getMediaServer().getId());
if (event.getMediaInfo() != null) {
streamAuthorityInfo.setOriginType(event.getMediaInfo().getOriginType());
}
return streamAuthorityInfo;
}
}

View File

@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.utils.DateUtil;
import io.swagger.v3.oas.annotations.media.Schema;
@ -150,6 +152,47 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
- DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(streamPushItem.getCreateTime())).intValue();
}
public StreamPushItem getInstance(StreamInfo streamInfo) {
StreamPushItem streamPushItem = new StreamPushItem();
streamPushItem.setApp(streamInfo.getApp());
streamPushItem.setMediaServerId(streamInfo.getMediaServerId());
streamPushItem.setStream(streamInfo.getStream());
streamPushItem.setAliveSecond(streamInfo.getMediaInfo().getAliveSecond());
// streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock());
streamPushItem.setTotalReaderCount(streamInfo.getMediaInfo().getReaderCount() + "");
streamPushItem.setOriginType(streamInfo.getOriginType());
// streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr());
// streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl());
streamPushItem.setCreateTime(DateUtil.getNow());
streamPushItem.setAliveSecond(streamInfo.getMediaInfo().getAliveSecond());
streamPushItem.setStatus(true);
streamPushItem.setStreamType("push");
// streamPushItem.setVhost(streamInfo.getVhost());
streamPushItem.setServerId(streamInfo.getMediaServerId());
return streamPushItem;
}
public static StreamPushItem getInstance(MediaArrivalEvent event, String serverId){
StreamPushItem streamPushItem = new StreamPushItem();
streamPushItem.setApp(event.getApp());
streamPushItem.setMediaServerId(event.getMediaServer().getId());
streamPushItem.setStream(event.getStream());
streamPushItem.setAliveSecond(event.getMediaInfo().getAliveSecond());
// streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock());
streamPushItem.setTotalReaderCount(event.getMediaInfo().getReaderCount() + "");
streamPushItem.setOriginType(event.getMediaInfo().getOriginType());
// streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr());
// streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl());
streamPushItem.setCreateTime(DateUtil.getNow());
streamPushItem.setAliveSecond(event.getMediaInfo().getAliveSecond());
streamPushItem.setStatus(true);
streamPushItem.setStreamType("push");
// streamPushItem.setVhost(streamInfo.getVhost());
streamPushItem.setServerId(serverId);
return streamPushItem;
}
public static class MediaSchema {
private String schema;
private Long bytesSpeed;

View File

@ -1,4 +1,4 @@
package com.genersoft.iot.vmp.media.zlm;
package com.genersoft.iot.vmp.media.zlm.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;

View File

@ -7,6 +7,8 @@ package com.genersoft.iot.vmp.media.zlm.dto.hook;
public class HookParam {
private String mediaServerId;
public String getMediaServerId() {
return mediaServerId;
}

View File

@ -18,8 +18,8 @@ public class HookResult {
return new HookResult(0, "success");
}
public static HookResult Fail(){
return new HookResult(-1, "fail");
public static HookResultForOnPublish Fail(){
return new HookResultForOnPublish(-1, "fail");
}
public int getCode() {

View File

@ -1,5 +1,7 @@
package com.genersoft.iot.vmp.media.zlm.dto.hook;
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
public class HookResultForOnPublish extends HookResult{
private boolean enable_audio;
@ -16,6 +18,17 @@ public class HookResultForOnPublish extends HookResult{
return new HookResultForOnPublish(0, "success");
}
public static HookResultForOnPublish getInstance(ResultForOnPublish resultForOnPublish){
HookResultForOnPublish successResult = new HookResultForOnPublish(0, "success");
successResult.setEnable_audio(resultForOnPublish.isEnable_audio());
successResult.setEnable_mp4(resultForOnPublish.isEnable_mp4());
successResult.setModify_stamp(resultForOnPublish.getModify_stamp());
successResult.setStream_replace(resultForOnPublish.getStream_replace());
successResult.setMp4_max_second(resultForOnPublish.getMp4_max_second());
successResult.setMp4_save_path(resultForOnPublish.getMp4_save_path());
return successResult;
}
public HookResultForOnPublish(int code, String msg) {
setCode(code);
setMsg(msg);

View File

@ -32,7 +32,7 @@ public class OnStreamChangedHookParam extends HookParam{
/**
* 观看总人数包括hls/rtsp/rtmp/http-flv/ws-flv
*/
private String totalReaderCount;
private int totalReaderCount;
/**
* 协议 包括hls/rtsp/rtmp/http-flv/ws-flv
@ -374,11 +374,11 @@ public class OnStreamChangedHookParam extends HookParam{
this.stream = stream;
}
public String getTotalReaderCount() {
public int getTotalReaderCount() {
return totalReaderCount;
}
public void setTotalReaderCount(String totalReaderCount) {
public void setTotalReaderCount(int totalReaderCount) {
this.totalReaderCount = totalReaderCount;
}

View File

@ -0,0 +1,24 @@
package com.genersoft.iot.vmp.media.zlm.event;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import org.springframework.context.ApplicationEvent;
/**
* zlm 心跳事件
*/
public class HookZlmServerKeepaliveEvent extends ApplicationEvent {
public HookZlmServerKeepaliveEvent(Object source) {
super(source);
}
private MediaServer mediaServerItem;
public MediaServer getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServer mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}
}

View File

@ -0,0 +1,24 @@
package com.genersoft.iot.vmp.media.zlm.event;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import org.springframework.context.ApplicationEvent;
/**
* zlm server_start事件
*/
public class HookZlmServerStartEvent extends ApplicationEvent {
public HookZlmServerStartEvent(Object source) {
super(source);
}
private MediaServer mediaServerItem;
public MediaServer getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServer mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}
}

View File

@ -1,11 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.event;
/**
* zlm离线事件类
*/
public class ZLMOfflineEvent extends ZLMEventAbstract {
public ZLMOfflineEvent(Object source) {
super(source);
}
}

View File

@ -1,11 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.event;
/**
* zlm在线事件
*/
public class ZLMOnlineEvent extends ZLMEventAbstract {
public ZLMOnlineEvent(Object source) {
super(source);
}
}

View File

@ -1,8 +1,7 @@
package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson2.JSONArray;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.github.pagehelper.PageInfo;
@ -18,22 +17,17 @@ public interface ICloudRecordService {
/**
* 分页回去云端录像列表
*/
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
/**
* 根据hook消息增加一条记录
*/
void addRecord(OnRecordMp4HookParam param);
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems);
/**
* 获取所有的日期
*/
List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);
List<String> getDateList(String app, String stream, int year, int month, List<MediaServer> mediaServerItems);
/**
* 添加合并任务
*/
String addTask(String app, String stream, MediaServerItem mediaServerItem, String startTime,
String addTask(String app, String stream, MediaServer mediaServerItem, String startTime,
String endTime, String callId, String remoteHost, boolean filterMediaServer);

View File

@ -1,100 +0,0 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.common.CommonCallback;
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.ServerKeepaliveData;
import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.vmanager.bean.RecordFile;
import java.util.List;
/**
* 媒体服务节点
*/
public interface IMediaServerService {
List<MediaServerItem> getAll();
List<MediaServerItem> getAllFromDatabase();
List<MediaServerItem> getAllOnline();
MediaServerItem getOne(String generalMediaServerId);
void syncCatchFromDatabase();
/**
* 新的节点加入
* @param zlmServerConfig
* @return
*/
void zlmServerOnline(ZLMServerConfig zlmServerConfig);
/**
* 节点离线
* @param mediaServerId
* @return
*/
void zlmServerOffline(String mediaServerId);
MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist);
void setZLMConfig(MediaServerItem mediaServerItem, boolean restart);
void updateVmServer(List<MediaServerItem> mediaServerItemList);
SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
boolean isPlayback, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode);
SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port, Boolean onlyAuto);
void closeRTPServer(MediaServerItem mediaServerItem, String streamId);
void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback);
Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc);
void closeRTPServer(String mediaServerId, String streamId);
void clearRTPServer(MediaServerItem mediaServerItem);
void update(MediaServerItem mediaSerItem);
void addCount(String mediaServerId);
void removeCount(String mediaServerId);
void releaseSsrc(String mediaServerItemId, String ssrc);
void clearMediaServerForOnline();
void add(MediaServerItem mediaSerItem);
int addToDatabase(MediaServerItem mediaSerItem);
int updateToDatabase(MediaServerItem mediaSerItem);
void resetOnlineServerItem(MediaServerItem serverItem);
MediaServerItem checkMediaServer(String ip, int port, String secret);
boolean checkMediaRecordServer(String ip, int port);
void delete(String id);
void deleteDb(String id);
MediaServerItem getDefaultMediaServer();
void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data);
/**
* 获取负载信息
* @return
*/
MediaServerLoad getLoad(MediaServerItem mediaServerItem);
List<MediaServerItem> getAllWithAssistPort();
}

View File

@ -1,8 +1,7 @@
package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson2.JSONArray;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
import com.genersoft.iot.vmp.media.bean.MediaServer;
/**
* 媒体信息业务
@ -10,35 +9,11 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
public interface IMediaService {
/**
* 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在
* @param app
* @param stream
* @return
* 播放鉴权
*/
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr, boolean authority);
boolean authenticatePlay(String app, String stream, String callId);
ResultForOnPublish authenticatePublish(MediaServer mediaServer, String app, String stream, String params);
/**
* 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, Object tracks, String callId);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
* @param app
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr, String callId, boolean isPlay);
boolean closeStreamOnNoneReader(String mediaServerId, String app, String stream, String schema);
}

View File

@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.github.pagehelper.PageInfo;
@ -73,13 +73,13 @@ public interface IPlatformService {
* @param errorEvent 信令错误事件
* @param timeoutCallback 超时事件
*/
void broadcastInvite(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem, ZlmHttpHookSubscribe.Event hookEvent,
void broadcastInvite(ParentPlatform platform, String channelId, MediaServer mediaServerItem, HookSubscribe.Event hookEvent,
SipSubscribe.Event errorEvent, InviteTimeOutCallback timeoutCallback) throws InvalidArgumentException, ParseException, SipException;
/**
* 语音喊话回复BYE
*/
void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream,boolean sendBye, MediaServerItem mediaServerItem);
void stopBroadcast(ParentPlatform platform, DeviceChannel channel, String stream,boolean sendBye, MediaServer mediaServerItem);
void addSimulatedSubscribeInfo(ParentPlatform parentPlatform);
}

View File

@ -7,8 +7,8 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
@ -26,20 +26,20 @@ import java.util.Map;
*/
public interface IPlayService {
void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channelId,
void play(MediaServer mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channelId,
ErrorCallback<Object> callback);
SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback);
SSRCInfo play(MediaServer mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback<Object> callback);
StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, HookParam hookParam, String deviceId, String channelId);
StreamInfo onPublishHandlerForPlay(MediaServer mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId);
MediaServerItem getNewMediaServerItem(Device device);
MediaServer getNewMediaServerItem(Device device);
void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
void playBack(MediaServer mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
void zlmServerOffline(String mediaServerId);
void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback);
void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback);
void download(MediaServer mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback);
StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
@ -47,7 +47,7 @@ public interface IPlayService {
AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode);
boolean audioBroadcastCmd(Device device, String channelId, MediaServerItem mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException;
boolean audioBroadcastCmd(Device device, String channelId, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException;
boolean audioBroadcastInUse(Device device, String channelId);
@ -59,10 +59,9 @@ public interface IPlayService {
void startPushStream(SendRtpItem sendRtpItem, SIPResponse sipResponse, ParentPlatform platform, CallIdHeader callIdHeader);
void startSendRtpStreamHand(SendRtpItem sendRtpItem, Object correlationInfo,
JSONObject jsonObject, Map<String, Object> param, CallIdHeader callIdHeader);
void startSendRtpStreamFailHand(SendRtpItem sendRtpItem,ParentPlatform platform, CallIdHeader callIdHeader);
void talkCmd(Device device, String channelId, MediaServerItem mediaServerItem, String stream, AudioBroadcastEvent event);
void talkCmd(Device device, String channelId, MediaServer mediaServerItem, String stream, AudioBroadcastEvent event);
void stopTalk(Device device, String channelId, Boolean streamIsReady);

View File

@ -1,13 +1,15 @@
package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.GeneralCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo;
import java.util.Map;
public interface IStreamProxyService {
/**
@ -18,17 +20,19 @@ public interface IStreamProxyService {
/**
* 添加视频代理到zlm
*
* @param param
* @return
*/
JSONObject addStreamProxyToZlm(StreamProxyItem param);
WVPResult<String> addStreamProxyToZlm(StreamProxyItem param);
/**
* 从zlm移除视频代理
*
* @param param
* @return
*/
JSONObject removeStreamProxyFromZlm(StreamProxyItem param);
Boolean removeStreamProxyFromZlm(StreamProxyItem param);
/**
* 分页查询
@ -73,9 +77,10 @@ public interface IStreamProxyService {
/**
* 获取ffmpeg.cmd模板
*
* @return
*/
JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem);
Map<String, String> getFFmpegCMDs(MediaServer mediaServerItem);
/**
* 根据app与stream获取streamProxy

View File

@ -1,9 +1,8 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
import com.github.pagehelper.PageInfo;
@ -16,8 +15,6 @@ import java.util.Map;
*/
public interface IStreamPushService {
List<StreamPushItem> handleJSON(String json, MediaServerItem mediaServerItem);
/**
* 将应用名和流ID加入国标关联
* @param stream

View File

@ -0,0 +1,25 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
import com.github.pagehelper.PageInfo;
public interface IUserApiKeyService {
int addApiKey(UserApiKey userApiKey);
boolean isApiKeyExists(String apiKey);
PageInfo<UserApiKey> getUserApiKeys(int page, int count);
int enable(Integer id);
int disable(Integer id);
int remark(Integer id, String remark);
int delete(Integer id);
UserApiKey getUserApiKeyById(Integer id);
int reset(Integer id, String apiKey);
}

View File

@ -11,6 +11,8 @@ public interface IUserService {
boolean changePassword(int id, String password);
User getUserById(int id);
User getUserByUsername(String username);
int addUser(User user);

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.service.bean;
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
/**
@ -76,18 +77,18 @@ public class CloudRecordItem {
*/
private long timeLen;
public static CloudRecordItem getInstance(OnRecordMp4HookParam param) {
public static CloudRecordItem getInstance(MediaRecordMp4Event param) {
CloudRecordItem cloudRecordItem = new CloudRecordItem();
cloudRecordItem.setApp(param.getApp());
cloudRecordItem.setStream(param.getStream());
cloudRecordItem.setStartTime(param.getStart_time()*1000);
cloudRecordItem.setFileName(param.getFile_name());
cloudRecordItem.setFolder(param.getFolder());
cloudRecordItem.setFileSize(param.getFile_size());
cloudRecordItem.setFilePath(param.getFile_path());
cloudRecordItem.setMediaServerId(param.getMediaServerId());
cloudRecordItem.setTimeLen((long) param.getTime_len() * 1000);
cloudRecordItem.setEndTime((param.getStart_time() + (long)param.getTime_len()) * 1000);
cloudRecordItem.setStartTime(param.getRecordInfo().getStartTime()*1000);
cloudRecordItem.setFileName(param.getRecordInfo().getFileName());
cloudRecordItem.setFolder(param.getRecordInfo().getFolder());
cloudRecordItem.setFileSize(param.getRecordInfo().getFileSize());
cloudRecordItem.setFilePath(param.getRecordInfo().getFilePath());
cloudRecordItem.setMediaServerId(param.getMediaServer().getId());
cloudRecordItem.setTimeLen((long) param.getRecordInfo().getTimeLen() * 1000);
cloudRecordItem.setEndTime((param.getRecordInfo().getStartTime() + (long)param.getRecordInfo().getTimeLen()) * 1000);
return cloudRecordItem;
}

View File

@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.service.bean;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import java.util.EventObject;
@ -15,7 +15,7 @@ public class PlayBackResult<T> {
private String msg;
private T data;
private MediaServerItem mediaServerItem;
private MediaServer mediaServerItem;
private JSONObject response;
private SipSubscribe.EventResult<EventObject> event;
@ -35,11 +35,11 @@ public class PlayBackResult<T> {
this.data = data;
}
public MediaServerItem getMediaServerItem() {
public MediaServer getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServerItem mediaServerItem) {
public void setMediaServerItem(MediaServer mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}

View File

@ -1,5 +1,7 @@
package com.genersoft.iot.vmp.service.bean;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
/**
* redis消息请求下级推送流信息
* @author lin
@ -80,6 +82,22 @@ public class RequestPushStreamMsg {
return requestPushStreamMsg;
}
public static RequestPushStreamMsg getInstance(SendRtpItem sendRtpItem) {
RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg();
requestPushStreamMsg.setMediaServerId(sendRtpItem.getMediaServerId());
requestPushStreamMsg.setApp(sendRtpItem.getApp());
requestPushStreamMsg.setStream(sendRtpItem.getStream());
requestPushStreamMsg.setIp(sendRtpItem.getIp());
requestPushStreamMsg.setPort(sendRtpItem.getPort());
requestPushStreamMsg.setSsrc(sendRtpItem.getSsrc());
requestPushStreamMsg.setTcp(sendRtpItem.isTcp());
requestPushStreamMsg.setSrcPort(sendRtpItem.getLocalPort());
requestPushStreamMsg.setPt(sendRtpItem.getPt());
requestPushStreamMsg.setPs(sendRtpItem.isUsePs());
requestPushStreamMsg.setOnlyAudio(sendRtpItem.isOnlyAudio());
return requestPushStreamMsg;
}
public String getMediaServerId() {
return mediaServerId;
}

View File

@ -1,7 +1,7 @@
package com.genersoft.iot.vmp.service.bean;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.bean.MediaServer;
/**
* redis消息下级回复推送信息
@ -11,7 +11,7 @@ public class ResponseSendItemMsg {
private SendRtpItem sendRtpItem;
private MediaServerItem mediaServerItem;
private MediaServer mediaServerItem;
public SendRtpItem getSendRtpItem() {
return sendRtpItem;
@ -21,11 +21,11 @@ public class ResponseSendItemMsg {
this.sendRtpItem = sendRtpItem;
}
public MediaServerItem getMediaServerItem() {
public MediaServer getMediaServerItem() {
return mediaServerItem;
}
public void setMediaServerItem(MediaServerItem mediaServerItem) {
public void setMediaServerItem(MediaServer mediaServerItem) {
this.mediaServerItem = mediaServerItem;
}
}

Some files were not shown because too many files have changed in this diff Show More