diff --git a/pom.xml b/pom.xml index cb624c02..6d248b14 100644 --- a/pom.xml +++ b/pom.xml @@ -1,53 +1,54 @@ - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.7.2 - + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.17 + - com.genersoft - wvp-pro - 2.6.9 - web video platform - 国标28181视频平台 - ${project.packaging} + com.genersoft + wvp-pro + 2.6.9 + web video platform + 国标28181视频平台 + ${project.packaging} - - - nexus-aliyun - Nexus aliyun - https://maven.aliyun.com/repository/public - default - - false - - - true - - - - - - nexus-aliyun - Nexus aliyun - https://maven.aliyun.com/repository/public - - false - - - true - - - + + + nexus-aliyun + Nexus aliyun + https://maven.aliyun.com/repository/public + default + + false + + + true + + + - - UTF-8 - MMddHHmm - 3.1.1 + + + nexus-aliyun + Nexus aliyun + https://maven.aliyun.com/repository/public + + false + + + true + + + + + + UTF-8 + MMddHHmm + 3.1.1 ${project.build.directory}/generated-snippets @@ -59,90 +60,90 @@ 2.7.2 - - - jar - - true - - - jar - - - - war - - war - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-jetty - - - - - javax.servlet - javax.servlet-api - 3.1.0 - provided - - - - + + + jar + + true + + + jar + + + + war + + war + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-jetty + + + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + + - - - org.springframework.boot - spring-boot-starter-data-redis - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.mybatis.spring.boot - mybatis-spring-boot-starter - 2.2.2 - - - com.zaxxer - HikariCP - - - - - org.springframework.boot - spring-boot-starter-security - + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.2.2 + + + com.zaxxer + HikariCP + + + + + org.springframework.boot + spring-boot-starter-security + - - org.springframework.boot - spring-boot-starter-jdbc - + + org.springframework.boot + spring-boot-starter-jdbc + - - - mysql - mysql-connector-java - 8.0.30 - + + + com.mysql + mysql-connector-j + 8.2.0 + - - - org.postgresql - postgresql - 42.5.1 - + + + org.postgresql + postgresql + 42.5.1 + @@ -152,152 +153,168 @@ 8.6.0 - - - com.github.pagehelper - pagehelper-spring-boot-starter - 1.4.6 - + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.4.6 + - - - org.springdoc - springdoc-openapi-ui - 1.6.10 - + + + org.springdoc + springdoc-openapi-ui + 1.7.0 + + + org.yaml + snakeyaml + + + + + org.yaml + snakeyaml + 2.2 + - - com.github.xiaoymin - knife4j-springdoc-ui - 3.0.3 - + + com.github.xiaoymin + knife4j-springdoc-ui + 3.0.3 + - - - javax.validation - validation-api - + + + javax.validation + validation-api + - - - org.springframework.boot - spring-boot-starter-aop - + + + org.springframework.boot + spring-boot-starter-aop + - - - javax.sip - jain-sip-ri - 1.3.0-91 - + + + javax.sip + jain-sip-ri + 1.3.0-91 + - - - org.slf4j - log4j-over-slf4j - 1.7.36 - + + + org.slf4j + log4j-over-slf4j + 1.7.36 + - - - org.dom4j - dom4j - 2.1.3 - + + + org.dom4j + dom4j + 2.1.3 + - - com.google.guava - guava - 20.0 - + + + com.alibaba.fastjson2 + fastjson2 + 2.0.17 + + + com.alibaba.fastjson2 + fastjson2-extension + 2.0.17 + - - - com.alibaba.fastjson2 - fastjson2 - 2.0.17 - - - com.alibaba.fastjson2 - fastjson2-extension - 2.0.17 - + + + com.squareup.okhttp3 + okhttp + 4.10.0 + - - - com.squareup.okhttp3 - okhttp - 4.10.0 - + + + com.squareup.okhttp3 + logging-interceptor + 4.10.0 + - - - com.squareup.okhttp3 - logging-interceptor - 4.10.0 - + + + io.github.rburgst + okhttp-digest + 2.7 + - - - io.github.rburgst - okhttp-digest - 2.7 - + + + + + + - - - - - - + + + org.bitbucket.b_c + jose4j + 0.9.3 + - - - org.bitbucket.b_c - jose4j - 0.9.3 - + + + org.mitre.dsmiley.httpproxy + smiley-http-proxy-servlet + 1.12.1 + - - - org.mitre.dsmiley.httpproxy - smiley-http-proxy-servlet - 1.12.1 - + + + com.alibaba + easyexcel + 3.3.2 + + + org.apache.commons + commons-compress + + + + + org.apache.commons + commons-compress + 1.24.0 + - - - com.alibaba - easyexcel - 3.1.1 - + + + com.github.oshi + oshi-core + 6.2.2 + - - - com.github.oshi - oshi-core - 6.2.2 - + + org.springframework.session + spring-session-core + - - org.springframework.session - spring-session-core - + + + + + + + - - - - - - - - - - - com.google.guava - guava - 31.1-jre - + + + com.google.guava + guava + 32.1.3-jre + @@ -335,37 +352,36 @@ - - pl.project13.maven - git-commit-id-plugin - 3.0.1 - - true - false - yyyyMMdd - - + + pl.project13.maven + git-commit-id-plugin + 3.0.1 + + true + false + yyyyMMdd + + - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.2 - - true - - - - - - - src/main/resources - - - src/main/java - - **/*.xml - - - - + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + true + + + + + + src/main/resources + + + src/main/java + + **/*.xml + + + + diff --git a/sql/2.6.9更新.sql b/sql/2.6.9更新.sql index 769004d2..f8f44d95 100644 --- a/sql/2.6.9更新.sql +++ b/sql/2.6.9更新.sql @@ -5,4 +5,4 @@ alter table wvp_platform add auto_push_channel bool default false alter table wvp_stream_proxy - add stream_key varying(255) + add stream_key character varying(255) diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java index f35b5bd8..f45f89a1 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java @@ -78,6 +78,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录 User user = new User(); + user.setId(jwtUser.getUserId()); user.setUsername(jwtUser.getUserName()); user.setPassword(jwtUser.getPassword()); Role role = new Role(); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java index 65e9de37..3df75936 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java @@ -144,6 +144,7 @@ public class JwtUtils implements InitializingBean { jwtUser.setUserName(username); jwtUser.setPassword(user.getPassword()); jwtUser.setRoleId(user.getRole().getId()); + jwtUser.setUserId(user.getId()); return jwtUser; } catch (InvalidJwtException e) { diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index 6a247359..0431dcdf 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -1,12 +1,12 @@ package com.genersoft.iot.vmp.conf.security; import com.genersoft.iot.vmp.conf.UserSetting; -import org.springframework.core.annotation.Order; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -28,6 +28,7 @@ import java.util.Arrays; /** * 配置Spring Security + * * @author lin */ @Configuration @@ -75,7 +76,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { matchers.add("/js/**"); matchers.add("/api/device/query/snap/**"); matchers.add("/record_proxy/*/**"); - matchers.addAll(userSetting.getInterfaceAuthenticationExcludes()); + matchers.add("/api/emit"); // 可以直接访问的静态数据 web.ignoring().antMatchers(matchers.toArray(new String[0])); } @@ -83,6 +84,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * 配置认证方式 + * * @param auth * @throws Exception */ @@ -111,7 +113,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/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll() + .antMatchers("/api/user/login", "/index/hook/**").permitAll() .anyRequest().authenticated() // 异常处理器 .and() @@ -124,7 +126,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } - CorsConfigurationSource configurationSource(){ + CorsConfigurationSource configurationSource() { // 配置跨域 CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedHeaders(Arrays.asList("*")); @@ -135,7 +137,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader())); UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource(); - url.registerCorsConfiguration("/**",corsConfiguration); + url.registerCorsConfiguration("/**", corsConfiguration); return url; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java index 8921a308..df29c333 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java @@ -21,6 +21,7 @@ public class JwtUser { EXCEPTION } + private int userId; private String userName; private String password; @@ -29,6 +30,14 @@ public class JwtUser { private TokenStatus status; + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + public String getUserName() { return userName; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java index 5e67bdba..bab02856 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/StackLoggerImpl.java @@ -1,8 +1,8 @@ package com.genersoft.iot.vmp.gb28181.conf; import gov.nist.core.StackLogger; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.spi.LocationAwareLogger; import org.springframework.stereotype.Component; import java.util.Properties; @@ -10,100 +10,132 @@ import java.util.Properties; @Component public class StackLoggerImpl implements StackLogger { - private final static Logger logger = LoggerFactory.getLogger(StackLoggerImpl.class); + /** + * 完全限定类名(Fully Qualified Class Name),用于定位日志位置 + */ + private static final String FQCN = StackLoggerImpl.class.getName(); - @Override - public void logStackTrace() { + /** + * 获取栈中类信息(以便底层日志记录系统能够提取正确的位置信息(方法名、行号)) + * @return LocationAwareLogger + */ + private static LocationAwareLogger getLocationAwareLogger() { + return (LocationAwareLogger) LoggerFactory.getLogger(new Throwable().getStackTrace()[4].getClassName()); + } - } - @Override - public void logStackTrace(int traceLevel) { - System.out.println("traceLevel: " + traceLevel); - } + /** + * 封装打印日志的位置信息 + * @param level 日志级别 + * @param message 日志事件的消息 + */ + private static void log(int level, String message) { + LocationAwareLogger locationAwareLogger = getLocationAwareLogger(); + locationAwareLogger.log(null, FQCN, level, message, null, null); + } - @Override - public int getLineCount() { - return 0; - } + /** + * 封装打印日志的位置信息 + * @param level 日志级别 + * @param message 日志事件的消息 + */ + private static void log(int level, String message, Throwable throwable) { + LocationAwareLogger locationAwareLogger = getLocationAwareLogger(); + locationAwareLogger.log(null, FQCN, level, message, null, throwable); + } - @Override - public void logException(Throwable ex) { + @Override + public void logStackTrace() { - } + } - @Override - public void logDebug(String message) { -// logger.debug(message); - } + @Override + public void logStackTrace(int traceLevel) { + System.out.println("traceLevel: " + traceLevel); + } - @Override - public void logDebug(String message, Exception ex) { -// logger.debug(message); - } + @Override + public int getLineCount() { + return 0; + } - @Override - public void logTrace(String message) { - logger.trace(message); - } + @Override + public void logException(Throwable ex) { - @Override - public void logFatalError(String message) { -// logger.error(message); - } + } - @Override - public void logError(String message) { -// logger.error(message); - } + @Override + public void logDebug(String message) { + log(LocationAwareLogger.INFO_INT, message); + } - @Override - public boolean isLoggingEnabled() { - return true; - } + @Override + public void logDebug(String message, Exception ex) { + log(LocationAwareLogger.INFO_INT, message, ex); + } - @Override - public boolean isLoggingEnabled(int logLevel) { - return true; - } + @Override + public void logTrace(String message) { + log(LocationAwareLogger.INFO_INT, message); + } - @Override - public void logError(String message, Exception ex) { -// logger.error(message); - } + @Override + public void logFatalError(String message) { + log(LocationAwareLogger.INFO_INT, message); + } - @Override - public void logWarning(String message) { - logger.warn(message); - } + @Override + public void logError(String message) { + log(LocationAwareLogger.INFO_INT, message); + } - @Override - public void logInfo(String message) { - logger.info(message); - } + @Override + public boolean isLoggingEnabled() { + return true; + } - @Override - public void disableLogging() { + @Override + public boolean isLoggingEnabled(int logLevel) { + return true; + } - } + @Override + public void logError(String message, Exception ex) { + log(LocationAwareLogger.INFO_INT, message, ex); + } - @Override - public void enableLogging() { + @Override + public void logWarning(String message) { + log(LocationAwareLogger.INFO_INT, message); + } - } + @Override + public void logInfo(String message) { + log(LocationAwareLogger.INFO_INT, message); + } - @Override - public void setBuildTimeStamp(String buildTimeStamp) { + @Override + public void disableLogging() { - } + } - @Override - public void setStackProperties(Properties stackProperties) { + @Override + public void enableLogging() { - } + } - @Override - public String getLoggerName() { - return null; - } + @Override + public void setBuildTimeStamp(String buildTimeStamp) { + + } + + @Override + public void setStackProperties(Properties stackProperties) { + + } + + @Override + public String getLoggerName() { + return null; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java index 9ee64773..aef59076 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java @@ -1,55 +1,68 @@ package com.genersoft.iot.vmp.gb28181.event.alarm; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import java.io.IOException; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Map; - +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** - * @description: 报警事件监听 - * @author: lawrencehj - * @data: 2021-01-20 + * 报警事件监听器. + * + * @author lawrencehj + * @author xiaoQQya + * @since 2021/01/20 */ - @Component public class AlarmEventListener implements ApplicationListener { - private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); + private static final Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); - private static Map sseEmitters = new Hashtable<>(); + private static final Map SSE_CACHE = new ConcurrentHashMap<>(); - public void addSseEmitters(String browserId, SseEmitter sseEmitter) { - sseEmitters.put(browserId, sseEmitter); + public void addSseEmitter(String browserId, PrintWriter writer) { + SSE_CACHE.put(browserId, writer); + logger.info("SSE 在线数量: {}", SSE_CACHE.size()); + } + + public void removeSseEmitter(String browserId, PrintWriter writer) { + SSE_CACHE.remove(browserId, writer); + logger.info("SSE 在线数量: {}", SSE_CACHE.size()); } @Override - public void onApplicationEvent(AlarmEvent event) { + public void onApplicationEvent(@NotNull AlarmEvent event) { if (logger.isDebugEnabled()) { - logger.debug("设备报警事件触发,deviceId:" + event.getAlarmInfo().getDeviceId() + ", " - + event.getAlarmInfo().getAlarmDescription()); + logger.debug("设备报警事件触发, deviceId: {}, {}", event.getAlarmInfo().getDeviceId(), event.getAlarmInfo().getAlarmDescription()); } - String msg = "设备编码: " + event.getAlarmInfo().getDeviceId() + "" - + "
报警描述: " + event.getAlarmInfo().getAlarmDescription() + "" - + "
报警时间: " + event.getAlarmInfo().getAlarmTime() + "" - + "
报警位置: " + event.getAlarmInfo().getLongitude() + "" - + ", " + event.getAlarmInfo().getLatitude() + ""; - for (Iterator> it = sseEmitters.entrySet().iterator(); it.hasNext();) { - Map.Entry emitter = it.next(); - logger.info("推送到SSE连接,浏览器ID: " + emitter.getKey()); + String msg = "设备编号: " + event.getAlarmInfo().getDeviceId() + "" + + "
通道编号: " + event.getAlarmInfo().getChannelId() + "" + + "
报警描述: " + event.getAlarmInfo().getAlarmDescription() + "" + + "
报警时间: " + event.getAlarmInfo().getAlarmTime() + ""; + + for (Iterator> it = SSE_CACHE.entrySet().iterator(); it.hasNext(); ) { + Map.Entry response = it.next(); + logger.info("推送到 SSE 连接, 浏览器 ID: {}", response.getKey()); try { - emitter.getValue().send(msg); - } catch (IOException | IllegalStateException e) { - if (logger.isDebugEnabled()) { - logger.debug("SSE连接已关闭"); + PrintWriter writer = response.getValue(); + + if (writer.checkError()) { + it.remove(); + continue; } - // 移除已关闭的连接 + + String sseMsg = "event:message\n" + + "data:" + msg + "\n" + + "\n"; + writer.write(sseMsg); + writer.flush(); + } catch (Exception e) { it.remove(); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java index c46e38a9..1c00dc30 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java @@ -117,8 +117,19 @@ public class VideoStreamSessionManager { } public void remove(String deviceId, String channelId, String stream) { - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); - if (ssrcTransaction == null) { + List ssrcTransactionList = getSsrcTransactionForAll(deviceId, channelId, null, stream); + if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) { + return; + } + for (SsrcTransaction ssrcTransaction : ssrcTransactionList) { + redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + + deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream()); + } + } + + public void removeByCallId(String deviceId, String channelId, String callId) { + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null); + if (ssrcTransaction == null ) { return; } redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java index 39dff931..2ffbfe40 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java @@ -89,17 +89,17 @@ public class CatalogSubscribeTask implements ISubscribeTask { ResponseEvent event = (ResponseEvent) eventResult.event; if (event.getResponse().getRawContent() != null) { // 成功 - logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId()); + logger.info("[取消目录订阅]成功: {}", device.getDeviceId()); }else { // 成功 - logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId()); + logger.info("[取消目录订阅]成功: {}", device.getDeviceId()); } },eventResult -> { // 失败 - logger.warn("[取消目录订阅订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + logger.warn("[取消目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); }); } catch (InvalidArgumentException | SipException | ParseException e) { - logger.error("[命令发送失败] 取消目录订阅订阅: {}", e.getMessage()); + logger.error("[命令发送失败] 取消目录订阅: {}", e.getMessage()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java index 88f5f1a6..d4a25a1a 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java @@ -164,6 +164,7 @@ public class SIPRequestHeaderProvider { Request request = null; //请求行 SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); +// SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); @@ -174,6 +175,7 @@ public class SIPRequestHeaderProvider { FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); //to SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress()); +// SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(),device.getHostAddress()); Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 85809f8f..42d6006d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -40,6 +40,7 @@ import javax.sip.SipFactory; import javax.sip.header.CallIdHeader; import javax.sip.message.Request; import java.text.ParseException; +import java.util.List; /** * @description:设备能力接口,用于定义设备的控制、查询能力 @@ -373,7 +374,8 @@ public class SIPCommander implements ISIPCommander { }), e -> { ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); - streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, + String callId = response.getCallIdHeader().getCallId(); + streamSession.put(device.getDeviceId(), channelId, callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); okEvent.response(e); }); @@ -612,17 +614,21 @@ public class SIPCommander implements ISIPCommander { */ @Override public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callId, stream); - if (ssrcTransaction == null) { + List ssrcTransactionList = streamSession.getSsrcTransactionForAll(device.getDeviceId(), channelId, callId, stream); + if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) { + logger.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId); throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream); } - mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); - mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); - streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + for (SsrcTransaction ssrcTransaction : ssrcTransactionList) { + logger.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId()); + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); - Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo()); - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); + streamSession.removeByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getCallId()); + Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo()); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); + } } /** diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java index 23dac958..7837e77d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java @@ -178,7 +178,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In if (mediaServerItem != null) { mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc()); } - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getStream()); + streamSession.removeByCallId(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getCallId()); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java index dbe49d5d..d35c6a63 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java @@ -132,7 +132,6 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements if (CmdType.CATALOG.equals(cmd)) { logger.info("接收到Catalog通知"); - processNotifyCatalogList(take.getEvt()); notifyRequestForCatalogProcessor.process(take.getEvt()); } else if (CmdType.ALARM.equals(cmd)) { logger.info("接收到Alarm通知"); @@ -371,114 +370,6 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements } } - /*** - * 处理catalog设备目录列表Notify - * - * @param evt - */ - private void processNotifyCatalogList(RequestEvent evt) { - try { - FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); - String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); - - Device device = redisCatchStorage.getDevice(deviceId); - if (device == null || !device.isOnLine()) { - logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); - return; - } - Element rootElement = getRootElement(evt, device.getCharset()); - if (rootElement == null) { - logger.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest()); - return; - } - Element deviceListElement = rootElement.element("DeviceList"); - if (deviceListElement == null) { - return; - } - Iterator deviceListIterator = deviceListElement.elementIterator(); - if (deviceListIterator != null) { - - // 遍历DeviceList - while (deviceListIterator.hasNext()) { - Element itemDevice = deviceListIterator.next(); - Element channelDeviceElement = itemDevice.element("DeviceID"); - if (channelDeviceElement == null) { - continue; - } - Element eventElement = itemDevice.element("Event"); - String event; - if (eventElement == null) { - logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); - event = CatalogEvent.ADD; - }else { - event = eventElement.getText().toUpperCase(); - } - DeviceChannel channel = XmlUtil.channelContentHandler(itemDevice, device, event, civilCodeFileConf); - if (channel == null) { - logger.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); - continue; - } - if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { - channel.setParentId(null); - } - channel.setDeviceId(device.getDeviceId()); - logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); - switch (event) { - case CatalogEvent.ON: - // 上线 - logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - storager.deviceChannelOnline(deviceId, channel.getChannelId()); - break; - case CatalogEvent.OFF : - // 离线 - logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - if (userSetting.getRefuseChannelStatusChannelFormNotify()) { - storager.deviceChannelOffline(deviceId, channel.getChannelId()); - }else { - logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - } - break; - case CatalogEvent.VLOST: - // 视频丢失 - logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - if (userSetting.getRefuseChannelStatusChannelFormNotify()) { - storager.deviceChannelOffline(deviceId, channel.getChannelId()); - }else { - logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - } - break; - case CatalogEvent.DEFECT: - // 故障 - break; - case CatalogEvent.ADD: - // 增加 - logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - deviceChannelService.updateChannel(deviceId, channel); - break; - case CatalogEvent.DEL: - // 删除 - logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - storager.delChannel(deviceId, channel.getChannelId()); - break; - case CatalogEvent.UPDATE: - // 更新 - logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); - deviceChannelService.updateChannel(deviceId, channel); - break; - default: - logger.warn("[ NotifyCatalog ] event not found : {}", event ); - - } - // 转发变化信息 - eventPublisher.catalogEventPublish(null, channel, event); - - } - } - } catch (DocumentException e) { - logger.error("未处理的异常 ", e); - } - } - public void setCmder(SIPCommander cmder) { } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java index 7d94787e..389e4013 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java @@ -61,7 +61,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp return; } SIPRequest request = (SIPRequest) evt.getRequest(); - logger.info("[收到心跳], device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + logger.info("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); // 回复200 OK try { @@ -80,6 +80,11 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp device.setPort(remoteAddressInfo.getPort()); device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); device.setIp(remoteAddressInfo.getIp()); + // 设备地址变化会引起目录订阅任务失效,需要重新添加 + if (device.getSubscribeCycleForCatalog() > 0) { + deviceService.removeCatalogSubscribe(device); + deviceService.addCatalogSubscribe(device); + } } if (device.getKeepaliveTime() == null) { device.setKeepaliveIntervalTime(60); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index 3c76883a..55ab6090 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -262,29 +262,40 @@ public class ZLMHttpHookListener { } else { result.setEnable_mp4(userSetting.isRecordPushLive()); } - // 替换流地址 - if ("rtp".equals(param.getApp()) && !mediaInfo.isRtpEnable()) { - String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));; - InviteInfo inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc); - if (inviteInfo != null) { - result.setStream_replace(inviteInfo.getStream()); - logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream()); - } - } - List ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream()); - if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) { - 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) { - result.setMp4_max_second(10); - result.setEnable_mp4(true); + + // 国标流 + 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 ssrcTransactionForAll = (inviteInfo == null ? null : + sessionManager.getSsrcTransactionForAll(inviteInfo.getDeviceId(), inviteInfo.getChannelId(), null, null)); + if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) { + 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) { + result.setMp4_max_second(10); + result.setEnable_mp4(true); + } } } + if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) { logger.info("推流时发现尚未设置录像路径,从assist服务中读取"); JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null); @@ -524,11 +535,15 @@ public class ZLMHttpHookListener { 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(), @@ -593,7 +608,7 @@ public class ZLMHttpHookListener { String deviceId = s[0]; String channelId = s[1]; Device device = redisCatchStorage.getDevice(deviceId); - if (device == null) { + if (device == null || !device.isOnLine()) { defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); return defaultResult; } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java index 0c6e3269..17a65a70 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java @@ -215,6 +215,21 @@ public class ZLMRESTfulUtils { } } + public JSONObject isMediaOnline(MediaServerItem mediaServerItem, String app, String stream, String schema){ + Map param = new HashMap<>(); + if (app != null) { + param.put("app",app); + } + if (stream != null) { + param.put("stream",stream); + } + if (schema != null) { + param.put("schema",schema); + } + param.put("vhost","__defaultVhost__"); + return sendPost(mediaServerItem, "isMediaOnline", param, null); + } + public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){ Map param = new HashMap<>(); if (app != null) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index 6e594024..4a781f31 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -9,6 +9,7 @@ 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; diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java index 4587fb0f..ffca0d5a 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnStreamChangedHookParam.java @@ -120,17 +120,17 @@ public class OnStreamChangedHookParam extends HookParam{ /** * H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4 */ - private int codecId; + private int codec_id; /** * 编码类型名称 CodecAAC CodecH264 */ - private String codecIdName; + private String codec_id_name; /** * Video = 0, Audio = 1 */ - private int codecType; + private int codec_type; /** * 轨道是否准备就绪 @@ -140,17 +140,17 @@ public class OnStreamChangedHookParam extends HookParam{ /** * 音频采样位数 */ - private int sampleBit; + private int sample_bit; /** * 音频采样率 */ - private int sampleRate; + private int sample_rate; /** * 视频fps */ - private int fps; + private float fps; /** * 视频高 @@ -162,6 +162,31 @@ public class OnStreamChangedHookParam extends HookParam{ */ private int width; + /** + * 帧数 + */ + private int frames; + + /** + * 关键帧数 + */ + private int key_frames; + + /** + * GOP大小 + */ + private int gop_size; + + /** + * GOP间隔时长(ms) + */ + private int gop_interval_ms; + + /** + * 丢帧率 + */ + private float loss; + public int getChannels() { return channels; } @@ -170,28 +195,28 @@ public class OnStreamChangedHookParam extends HookParam{ this.channels = channels; } - public int getCodecId() { - return codecId; + public int getCodec_id() { + return codec_id; } - public void setCodecId(int codecId) { - this.codecId = codecId; + public void setCodec_id(int codec_id) { + this.codec_id = codec_id; } - public String getCodecIdName() { - return codecIdName; + public String getCodec_id_name() { + return codec_id_name; } - public void setCodecIdName(String codecIdName) { - this.codecIdName = codecIdName; + public void setCodec_id_name(String codec_id_name) { + this.codec_id_name = codec_id_name; } - public int getCodecType() { - return codecType; + public int getCodec_type() { + return codec_type; } - public void setCodecType(int codecType) { - this.codecType = codecType; + public void setCodec_type(int codec_type) { + this.codec_type = codec_type; } public boolean isReady() { @@ -202,27 +227,27 @@ public class OnStreamChangedHookParam extends HookParam{ this.ready = ready; } - public int getSampleBit() { - return sampleBit; + public int getSample_bit() { + return sample_bit; } - public void setSampleBit(int sampleBit) { - this.sampleBit = sampleBit; + public void setSample_bit(int sample_bit) { + this.sample_bit = sample_bit; } - public int getSampleRate() { - return sampleRate; + public int getSample_rate() { + return sample_rate; } - public void setSampleRate(int sampleRate) { - this.sampleRate = sampleRate; + public void setSample_rate(int sample_rate) { + this.sample_rate = sample_rate; } - public int getFps() { + public float getFps() { return fps; } - public void setFps(int fps) { + public void setFps(float fps) { this.fps = fps; } @@ -241,6 +266,46 @@ public class OnStreamChangedHookParam extends HookParam{ public void setWidth(int width) { this.width = width; } + + public int getFrames() { + return frames; + } + + public void setFrames(int frames) { + this.frames = frames; + } + + public int getKey_frames() { + return key_frames; + } + + public void setKey_frames(int key_frames) { + this.key_frames = key_frames; + } + + public int getGop_size() { + return gop_size; + } + + public void setGop_size(int gop_size) { + this.gop_size = gop_size; + } + + public int getGop_interval_ms() { + return gop_interval_ms; + } + + public void setGop_interval_ms(int gop_interval_ms) { + this.gop_interval_ms = gop_interval_ms; + } + + public float getLoss() { + return loss; + } + + public void setLoss(float loss) { + this.loss = loss; + } } public static class OriginSock{ diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java index 57de6310..9fbec1fa 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java @@ -243,6 +243,10 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { @Override public void batchUpdateChannel(List channels) { + String now = DateUtil.getNow(); + for (DeviceChannel channel : channels) { + channel.setUpdateTime(now); + } channelMapper.batchUpdate(channels); for (DeviceChannel channel : channels) { if (channel.getParentId() != null) { diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java index 1bd82b38..a5328d05 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java @@ -216,8 +216,8 @@ public class DeviceServiceImpl implements IDeviceService { if (ssrcTransactions != null && ssrcTransactions.size() > 0) { for (SsrcTransaction ssrcTransaction : ssrcTransactions) { mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); - // mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); - // streamSession.remove(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); + streamSession.remove(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); } } // 移除订阅 @@ -520,16 +520,18 @@ public class DeviceServiceImpl implements IDeviceService { // 目录订阅相关的信息 - if (device.getSubscribeCycleForCatalog() > 0) { - if (deviceInStore.getSubscribeCycleForCatalog() == 0 || deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { - deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); + if (deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { + if (device.getSubscribeCycleForCatalog() > 0) { + // 若已开启订阅,但订阅周期不同,则先取消 + if (deviceInStore.getSubscribeCycleForCatalog() != 0) { + removeCatalogSubscribe(deviceInStore); + } // 开启订阅 - addCatalogSubscribe(deviceInStore); - } - }else if (device.getSubscribeCycleForCatalog() == 0) { - if (deviceInStore.getSubscribeCycleForCatalog() != 0) { deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); + addCatalogSubscribe(deviceInStore); + }else if (device.getSubscribeCycleForCatalog() == 0) { // 取消订阅 + deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); removeCatalogSubscribe(deviceInStore); } } @@ -544,6 +546,8 @@ public class DeviceServiceImpl implements IDeviceService { } }else if (device.getSubscribeCycleForMobilePosition() == 0) { if (deviceInStore.getSubscribeCycleForMobilePosition() != 0) { + deviceInStore.setMobilePositionSubmissionInterval(device.getMobilePositionSubmissionInterval()); + deviceInStore.setSubscribeCycleForMobilePosition(device.getSubscribeCycleForMobilePosition()); // 取消订阅 removeMobilePositionSubscribe(deviceInStore); } @@ -564,7 +568,7 @@ public class DeviceServiceImpl implements IDeviceService { deviceInStore.setSsrcCheck(device.isSsrcCheck()); //作为消息通道 deviceInStore.setAsMessageChannel(device.isAsMessageChannel()); - + // 更新redis deviceMapper.updateCustom(deviceInStore); redisCatchStorage.removeDevice(deviceInStore.getDeviceId()); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java index d630a2c0..752d0631 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java @@ -257,7 +257,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService { ":" + inviteInfo.getDeviceId() + ":" + inviteInfo.getChannelId() + ":" + inviteInfo.getStream() + - ":" + inviteInfo.getSsrcInfo().getSsrc(); + ":" + ssrc; if (inviteInfoInDb.getSsrcInfo() != null) { inviteInfoInDb.getSsrcInfo().setSsrc(ssrc); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java index 4d46b5ce..72a12dc3 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java @@ -166,14 +166,13 @@ public class MediaServerServiceImpl implements IMediaServerService { if (streamId == null) { streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase(); } - int ssrcCheckParam = 0; - if (ssrcCheck && tcpMode > 1) { + if (ssrcCheck && tcpMode > 0) { // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验 - logger.warn("[openRTPServer] TCP被动/TCP主动收流时,默认关闭ssrc检验"); + logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验"); } int rtpServerPort; if (mediaServerItem.isRtpEnable()) { - rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, (ssrcCheck && tcpMode == 0) ? Long.parseLong(ssrc) : 0, port, reUsePort, tcpMode); + rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, reUsePort, tcpMode); } else { rtpServerPort = mediaServerItem.getRtpProxyPort(); } @@ -200,7 +199,10 @@ public class MediaServerServiceImpl implements IMediaServerService { @Override public void closeRTPServer(String mediaServerId, String streamId) { MediaServerItem mediaServerItem = this.getOne(mediaServerId); - closeRTPServer(mediaServerItem, streamId); + if (mediaServerItem.isRtpEnable()) { + closeRTPServer(mediaServerItem, streamId); + } + zlmresTfulUtils.closeStreams(mediaServerItem, "rtp", streamId); } @Override @@ -572,7 +574,7 @@ public class MediaServerServiceImpl implements IMediaServerService { Map 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 -t 0.001 %s"); + 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",""); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java index 7ede0921..9f2b5e05 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java @@ -162,7 +162,7 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService { return 0; } if (ObjectUtils.isEmpty(catalogId)) { - catalogId = platform.getDeviceGBId(); + catalogId = null; } if ((result = platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId)) > 0) { diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index 4e947b33..67b85f4f 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -238,6 +238,15 @@ public class PlayServiceImpl implements IPlayService { HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); subscribe.removeSubscribe(hookSubscribe); } + }else { + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}", + device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流", + ssrcInfo.getPort(), ssrcInfo.getSsrc()); + + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + mediaServerService.closeRTPServer(mediaServerItem.getId(), ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); } }, userSetting.getPlayTimeout()); @@ -268,6 +277,7 @@ public class PlayServiceImpl implements IPlayService { InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, timeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAY); }, (event) -> { + logger.info("[点播失败] deviceId: {}, channelId:{}, {}: {}", device.getDeviceId(), channelId, event.statusCode, event.msg); dynamicTask.stop(timeOutTaskKey); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); // 释放ssrc @@ -309,7 +319,13 @@ public class PlayServiceImpl implements IPlayService { if (!device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { return; } - String substring = contentString.substring(0, contentString.indexOf("y=")); + + String substring; + if (contentString.indexOf("y=") > 0) { + substring = contentString.substring(0, contentString.indexOf("y=")); + }else { + substring = contentString; + } try { SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); int port = -1; @@ -399,7 +415,7 @@ public class PlayServiceImpl implements IPlayService { deviceChannel.setStreamId(streamInfo.getStream()); storager.startPlay(deviceId, channelId, streamInfo.getStream()); } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, deviceId, channelId); + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, ((OnStreamChangedHookParam) param).getStream()); if (inviteInfo != null) { inviteInfo.setStatus(InviteSessionStatus.ok); @@ -553,7 +569,6 @@ public class PlayServiceImpl implements IPlayService { // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId, playBackTimeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAYBACK); - }, errorEvent); } catch (InvalidArgumentException | SipException | ParseException e) { logger.error("[命令发送失败] 录像回放: {}", e.getMessage()); @@ -574,6 +589,10 @@ public class PlayServiceImpl implements IPlayService { ResponseEvent responseEvent = (ResponseEvent) eventResult.event; String contentString = new String(responseEvent.getResponse().getRawContent()); String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); + // 兼容回复的消息中缺少ssrc(y字段)的情况 + if (ssrcInResponse == null) { + ssrcInResponse = ssrcInfo.getSsrc(); + } if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { // ssrc 一致 if (mediaServerItem.isRtpEnable()) { @@ -652,6 +671,7 @@ public class PlayServiceImpl implements IPlayService { + @Override public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback callback) { Device device = storager.queryVideoDevice(deviceId); diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java index 7fbe7691..69bae41b 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java @@ -35,15 +35,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; /** * 视频代理业务 @@ -403,6 +407,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService { logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"), streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl()); } + } else if (streamProxy != null && streamProxy.isEnable()) { + return true ; } return result; } @@ -560,4 +566,43 @@ public class StreamProxyServiceImpl implements IStreamProxyService { return new ResourceBaseInfo(total, online); } + + + @Scheduled(cron = "* 0/10 * * * ?") + public void asyncCheckStreamProxyStatus() { + + List all = mediaServerService.getAllOnline(); + + if (CollectionUtils.isEmpty(all)){ + return; + } + + Map serverItemMap = all.stream().collect(Collectors.toMap(MediaServerItem::getId, Function.identity(), (m1, m2) -> m1)); + + List list = videoManagerStorager.getStreamProxyListForEnable(true); + + if (CollectionUtils.isEmpty(list)){ + return; + } + + for (StreamProxyItem streamProxyItem : list) { + + MediaServerItem mediaServerItem = serverItemMap.get(streamProxyItem.getMediaServerId()); + + // TODO 支持其他 schema + JSONObject mediaInfo = zlmresTfulUtils.isMediaOnline(mediaServerItem, streamProxyItem.getApp(), streamProxyItem.getStream(), "rtsp"); + + if (mediaInfo == null){ + streamProxyItem.setStatus(false); + } else { + if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) { + streamProxyItem.setStatus(true); + } else { + streamProxyItem.setStatus(false); + } + } + + updateStreamProxy(streamProxyItem); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java index bc34162b..32e9bdbe 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java @@ -506,6 +506,9 @@ public class StreamPushServiceImpl implements IStreamPushService { stream.setUpdateTime(DateUtil.getNow()); stream.setCreateTime(DateUtil.getNow()); stream.setServerId(userSetting.getServerId()); + stream.setMediaServerId(mediaConfig.getId()); + stream.setSelf(true); + stream.setPushIng(true); // 放在事务内执行 boolean result = false; diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java index edd34e5d..62d61019 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java @@ -31,8 +31,8 @@ public interface PlatformCatalogMapper { @Update(value = {" "}) int update(@Param("platformCatalog") PlatformCatalog platformCatalog); @@ -51,4 +51,16 @@ public interface PlatformCatalogMapper { " from wvp_platform_catalog pc " + " WHERE pc.id=#{id} and pc.platform_id=#{platformId}") PlatformCatalog selectByPlatFormAndCatalogId(@Param("platformId") String platformId, @Param("id") String id); + + + @Delete("") + int deleteAll(String platformId, List ids); + + @Select("SELECT id from wvp_platform_catalog WHERE platform_id=#{platformId} and parent_id = #{id}") + List queryCatalogFromParent(@Param("id") String id, @Param("platformId") String platformId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java index 38263506..11aaa087 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java @@ -105,7 +105,8 @@ public interface PlatformChannelMapper { void delByPlatformId(String serverGBId); @Delete("") int delChannelForGBByCatalogId(@Param("platformId") String platformId, @Param("catalogId") String catalogId); @@ -116,6 +117,6 @@ public interface PlatformChannelMapper { "where dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}") List queryDeviceInfoByPlatformIdAndChannelId(@Param("platformId") String platformId, @Param("channelId") String channelId); - @Select("SELECT pgc.platform_id from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id='${channelId}'") - List queryParentPlatformByChannelId(String channelId); + @Select("SELECT pgc.platform_id from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id=#{channelId}") + List queryParentPlatformByChannelId(@Param("channelId") String channelId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java index a316a5cb..af9454d2 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java @@ -73,6 +73,9 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { @Autowired private PlatformChannelMapper platformChannelMapper; + @Autowired + private PlatformCatalogMapper platformCatalogMapper; + @Autowired private StreamProxyMapper streamProxyMapper; @@ -126,6 +129,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { List updateChannels = new ArrayList<>(); List addChannels = new ArrayList<>(); + List deleteChannels = new ArrayList<>(); StringBuilder stringBuilder = new StringBuilder(); Map subContMap = new HashMap<>(); @@ -156,6 +160,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { deviceChannel.setUpdateTime(DateUtil.getNow()); addChannels.add(deviceChannel); } + allChannelMap.remove(deviceChannel.getChannelId()); channels.add(deviceChannel); if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { if (subContMap.get(deviceChannel.getParentId()) == null) { @@ -166,7 +171,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { } } } - if (channels.size() > 0) { + deleteChannels.addAll(allChannelMap.values()); + if (!channels.isEmpty()) { for (DeviceChannel channel : channels) { if (subContMap.get(channel.getChannelId()) != null){ Integer count = subContMap.get(channel.getChannelId()); @@ -187,20 +193,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { } try { int limitCount = 50; - int cleanChannelsResult = 0; - if (channels.size() > limitCount) { - for (int i = 0; i < channels.size(); i += limitCount) { - int toIndex = i + limitCount; - if (i + limitCount > channels.size()) { - toIndex = channels.size(); - } - cleanChannelsResult += this.deviceChannelMapper.cleanChannelsNotInList(deviceId, channels.subList(i, toIndex)); - } - } else { - cleanChannelsResult = this.deviceChannelMapper.cleanChannelsNotInList(deviceId, channels); - } - boolean result = cleanChannelsResult < 0; - if (!result && addChannels.size() > 0) { + boolean result = false; + if (!result && !addChannels.isEmpty()) { if (addChannels.size() > limitCount) { for (int i = 0; i < addChannels.size(); i += limitCount) { int toIndex = i + limitCount; @@ -213,7 +207,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { result = result || deviceChannelMapper.batchAdd(addChannels) < 0; } } - if (!result && updateChannels.size() > 0) { + if (!result && !updateChannels.isEmpty()) { if (updateChannels.size() > limitCount) { for (int i = 0; i < updateChannels.size(); i += limitCount) { int toIndex = i + limitCount; @@ -226,6 +220,20 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0; } } + if (!result && !deleteChannels.isEmpty()) { + System.out.println("删除: " + deleteChannels.size()); + if (deleteChannels.size() > limitCount) { + for (int i = 0; i < deleteChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > deleteChannels.size()) { + toIndex = deleteChannels.size(); + } + result = result || deviceChannelMapper.batchDel(deleteChannels.subList(i, toIndex)) < 0; + } + }else { + result = result || deviceChannelMapper.batchDel(deleteChannels) < 0; + } + } if (result) { //事务回滚 @@ -910,9 +918,43 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage { eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); } int delChannelresult = platformChannelMapper.delByCatalogId(platformId, id); + // 查看是否存在子目录,如果存在一并删除 + List allChildCatalog = getAllChildCatalog(id, platformId); + if (!allChildCatalog.isEmpty()) { + int limitCount = 50; + if (allChildCatalog.size() > limitCount) { + for (int i = 0; i < allChildCatalog.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > allChildCatalog.size()) { + toIndex = allChildCatalog.size(); + } + delChannelresult += platformCatalogMapper.deleteAll(platformId, allChildCatalog.subList(i, toIndex)); + } + }else { + delChannelresult += platformCatalogMapper.deleteAll(platformId, allChildCatalog); + } + } return delresult + delChannelresult + delStreamresult; } + private List getAllChildCatalog(String id, String platformId) { + List catalogList = platformCatalogMapper.queryCatalogFromParent(id, platformId); + List catalogListChild = new ArrayList<>(); + if (catalogList != null && !catalogList.isEmpty()) { + for (String childId : catalogList) { + List allChildCatalog = getAllChildCatalog(childId, platformId); + if (allChildCatalog != null && !allChildCatalog.isEmpty()) { + catalogListChild.addAll(allChildCatalog); + } + + } + } + if (!catalogListChild.isEmpty()) { + catalogList.addAll(catalogListChild); + } + return catalogList; + } + @Override public int updateCatalog(PlatformCatalog platformCatalog) { diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java deleted file mode 100755 index b1ad3b9f..00000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.SseController; - -import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; - -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -/** - * @description: SSE推送 - * @author: lawrencehj - * @data: 2021-01-20 - */ -@Tag(name = "SSE推送") - -@Controller -@RequestMapping("/api") -public class SseController { - @Autowired - AlarmEventListener alarmEventListener; - - @GetMapping("/emit") - public SseEmitter emit(@RequestParam String browserId) { - final SseEmitter sseEmitter = new SseEmitter(0L); - try { - alarmEventListener.addSseEmitters(browserId, sseEmitter); - }catch (Exception e){ - sseEmitter.completeWithError(e); - } - return sseEmitter; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java new file mode 100644 index 00000000..575f22b3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java @@ -0,0 +1,55 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.sse; + +import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + + +/** + * SSE 推送. + * + * @author lawrencehj + * @author xiaoQQya + * @since 2021/01/20 + */ +@Tag(name = "SSE 推送") +@RestController +@RequestMapping("/api") +public class SseController { + + @Resource + private AlarmEventListener alarmEventListener; + + /** + * SSE 推送. + * + * @param response 响应 + * @param browserId 浏览器ID + * @throws IOException IOEXCEPTION + * @author xiaoQQya + * @since 2023/11/06 + */ + @GetMapping("/emit") + public void emit(HttpServletResponse response, @RequestParam String browserId) throws IOException, InterruptedException { + response.setContentType("text/event-stream"); + response.setCharacterEncoding("utf-8"); + + PrintWriter writer = response.getWriter(); + alarmEventListener.addSseEmitter(browserId, writer); + + while (!writer.checkError()) { + Thread.sleep(1000); + writer.write(":keep alive\n\n"); + writer.flush(); + } + alarmEventListener.removeSseEmitter(browserId, writer); + } +} diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml index 595f629c..dfc858f5 100644 --- a/src/main/resources/all-application.yml +++ b/src/main/resources/all-application.yml @@ -150,9 +150,9 @@ media: # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 record-assist-port: 0 -# [可选] 日志配置, 一般不需要改 +# [可选] 日志配置, 如果不需要在jar外修改日志内容那么可以不配置此项 logging: - config: classpath:logback-spring-local.xml + config: classpath:logback-spring.xml # [根据业务需求配置] user-settings: diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index eacc9763..02656da6 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -26,7 +26,7 @@ spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/test_gb-89wulian?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true username: root password: root hikari: diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 0e0c0ad3..f6f6f348 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -71,10 +71,6 @@ media: record-assist-port: 18081 sdp-ip: ${sip.ip} stream-ip: ${sip.ip} -# [可选] 日志配置, 一般不需要改 -# [可选] 日志配置, 一般不需要改 -logging: - config: classpath:logback-spring-local.xml # [根据业务需求配置] user-settings: diff --git a/src/main/resources/logback-spring-local.xml b/src/main/resources/logback-spring.xml similarity index 95% rename from src/main/resources/logback-spring-local.xml rename to src/main/resources/logback-spring.xml index d876a362..ceedcc08 100644 --- a/src/main/resources/logback-spring-local.xml +++ b/src/main/resources/logback-spring.xml @@ -4,8 +4,8 @@ - - + diff --git a/web_src/src/components/common/jessibuca.vue b/web_src/src/components/common/jessibuca.vue index c02960e6..5b48da4a 100755 --- a/web_src/src/components/common/jessibuca.vue +++ b/web_src/src/components/common/jessibuca.vue @@ -1,6 +1,6 @@