diff --git a/pom.xml b/pom.xml index ce12f3bd..5b3e945d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,366 +1,382 @@ - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.7.9 - + + 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 + + + - - ${project.build.directory}/generated-snippets - ${project.basedir}/docs/asciidoc - ${project.build.directory}/asciidoc - ${project.build.directory}/asciidoc/html - ${project.build.directory}/asciidoc/pdf - + + UTF-8 + MMddHHmm + 3.1.1 - - - 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 - - - - + + ${project.build.directory}/generated-snippets + ${project.basedir}/docs/asciidoc + ${project.build.directory}/asciidoc + ${project.build.directory}/asciidoc/html + ${project.build.directory}/asciidoc/pdf + - - - 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 - + + + 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-jdbc - + + + 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 + - - - mysql - mysql-connector-java - 8.0.30 - + + org.springframework.boot + spring-boot-starter-jdbc + - - - org.postgresql - postgresql - 42.5.1 - + + + com.mysql + mysql-connector-j + 8.2.0 + - - - - - com.kingbase - kingbase8 - 8.6.0 - system - ${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar - + + + org.postgresql + postgresql + 42.5.1 + - - - com.github.pagehelper - pagehelper-spring-boot-starter - 1.4.6 - + + + + + com.kingbase + kingbase8 + 8.6.0 + system + ${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar + - - - org.springdoc - springdoc-openapi-ui - 1.6.10 - + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.4.6 + - - com.github.xiaoymin - knife4j-springdoc-ui - 3.0.3 - + + + org.springdoc + springdoc-openapi-ui + 1.7.0 + + + org.yaml + snakeyaml + + + + + org.yaml + snakeyaml + 2.2 + - - - javax.validation - validation-api - + + com.github.xiaoymin + knife4j-springdoc-ui + 3.0.3 + - - - org.springframework.boot - spring-boot-starter-aop - + + + javax.validation + validation-api + - - - javax.sip - jain-sip-ri - 1.3.0-91 - + + + org.springframework.boot + spring-boot-starter-aop + - - - org.slf4j - log4j-over-slf4j - 1.7.36 - + + + javax.sip + jain-sip-ri + 1.3.0-91 + - - - org.dom4j - dom4j - 2.1.3 - + + + org.slf4j + log4j-over-slf4j + 1.7.36 + - - com.google.guava - guava - 20.0 - + + + org.dom4j + dom4j + 2.1.3 + - - - 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.1.1 - + + + com.alibaba + easyexcel + 3.3.2 + + + org.apache.commons + commons-compress + + + + + org.apache.commons + commons-compress + 1.24.0 + - - - 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 + + + org.springframework.boot + spring-boot-starter-test + test + + - - org.springframework.boot - spring-boot-starter-test - - + + ${project.artifactId}-${project.version}-${maven.build.timestamp} + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.2 + + true + + - + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + pl.project13.maven + git-commit-id-plugin + 3.0.1 + + true + false + yyyyMMdd + + - - ${project.artifactId}-${project.version}-${maven.build.timestamp} - - - org.springframework.boot - spring-boot-maven-plugin - 2.7.2 - - true - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - - 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/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..9cb3a1ff 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,6 +76,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { matchers.add("/js/**"); matchers.add("/api/device/query/snap/**"); matchers.add("/record_proxy/*/**"); + matchers.add("/api/emit"); matchers.addAll(userSetting.getInterfaceAuthenticationExcludes()); // 可以直接访问的静态数据 web.ignoring().antMatchers(matchers.toArray(new String[0])); @@ -83,6 +85,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * 配置认证方式 + * * @param auth * @throws Exception */ @@ -111,7 +114,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 +127,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } - CorsConfigurationSource configurationSource(){ + CorsConfigurationSource configurationSource() { // 配置跨域 CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedHeaders(Arrays.asList("*")); @@ -135,7 +138,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/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 8317de2d..fe2ebd1a 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 @@ -374,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); }); @@ -1000,7 +1001,7 @@ public class SIPCommander implements ISIPCommander { catalogXml.append("" + device.getDeviceId() + "\r\n"); catalogXml.append("\r\n"); - + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); 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 161480b9..4ea7a144 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 @@ -251,7 +251,6 @@ public class ZLMHttpHookListener { HookResultForOnPublish result = HookResultForOnPublish.SUCCESS(); - result.setEnable_audio(true); taskExecutor.execute(() -> { ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json); if (subscribe != null) { @@ -269,35 +268,42 @@ 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) { + + // 国标流 + 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); 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()); + // 设置音频信息及录制信息 + 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); + } + // 如果是talk对讲,则默认获取声音 + if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) { + result.setEnable_audio(true); + } } - // 如果是录像下载就设置视频间隔十秒 - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) { - result.setMp4_max_second(10); - result.setEnable_mp4(true); - } - // 如果是talk对讲,则默认获取声音 - if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) { - result.setEnable_audio(true); - } - } + if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) { logger.info("推流时发现尚未设置录像路径,从assist服务中读取"); JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null); @@ -684,7 +690,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/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/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java index 0556a845..6e251b37 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 @@ -578,7 +578,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/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java index b705b5eb..a9573bcd 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 @@ -568,7 +568,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); 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/logback-spring.xml b/src/main/resources/logback-spring.xml index 76b4ba3e..e2b03ee0 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -4,8 +4,8 @@ - - + @@ -86,7 +86,7 @@ - + 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 @@