合并开源主线
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package com.genersoft.iot.vmp;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
|
||||
import com.genersoft.iot.vmp.utils.GitUtil;
|
||||
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
|
||||
import org.slf4j.Logger;
|
||||
@@ -25,7 +24,6 @@ import java.util.Collections;
|
||||
@ServletComponentScan("com.genersoft.iot.vmp.conf")
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableDruidSupport
|
||||
public class VManageBootstrap extends SpringBootServletInitializer {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.common;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Schema(description = "流信息")
|
||||
public class StreamInfo implements Serializable, Cloneable{
|
||||
@@ -168,7 +169,7 @@ public class StreamInfo implements Serializable, Cloneable{
|
||||
}
|
||||
|
||||
public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s/%s", app, stream, callIdParam);
|
||||
String file = String.format("%s/%s%s", app, stream, callIdParam);
|
||||
if (port > 0) {
|
||||
this.rtmp = new StreamURL("rtmp", host, port, file);
|
||||
}
|
||||
@@ -178,7 +179,7 @@ public class StreamInfo implements Serializable, Cloneable{
|
||||
}
|
||||
|
||||
public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s/%s", app, stream, callIdParam);
|
||||
String file = String.format("%s/%s%s", app, stream, callIdParam);
|
||||
if (port > 0) {
|
||||
this.rtsp = new StreamURL("rtsp", host, port, file);
|
||||
}
|
||||
@@ -236,8 +237,11 @@ public class StreamInfo implements Serializable, Cloneable{
|
||||
}
|
||||
}
|
||||
|
||||
public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam, boolean isPlay) {
|
||||
String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, isPlay?"play":"push", callIdParam);
|
||||
public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) {
|
||||
if (callIdParam != null) {
|
||||
callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&");
|
||||
}
|
||||
String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam);
|
||||
if (port > 0) {
|
||||
this.rtc = new StreamURL("http", host, port, file);
|
||||
}
|
||||
|
||||
@@ -66,9 +66,7 @@ public class ApiAccessFilter extends OncePerRequestFilter {
|
||||
logDto.setUri(servletRequest.getRequestURI());
|
||||
logDto.setCreateTime(DateUtil.getNow());
|
||||
logService.add(logDto);
|
||||
// logger.warn("[Api Access] [{}] [{}] [{}] [{}] [{}] {}ms",
|
||||
// uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()),
|
||||
// System.currentTimeMillis() - start);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@@ -32,6 +33,28 @@ public class GlobalExceptionHandler {
|
||||
return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认异常处理
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(IllegalStateException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public WVPResult<String> exceptionHandler(IllegalStateException e) {
|
||||
return WVPResult.fail(ErrorCode.ERROR400);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认异常处理
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public WVPResult<String> exceptionHandler(HttpRequestMethodNotSupportedException e) {
|
||||
return WVPResult.fail(ErrorCode.ERROR400);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自定义异常处理, 处理controller中返回的错误
|
||||
|
||||
@@ -10,14 +10,11 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 全局统一返回结果
|
||||
* @author lin
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
@@ -169,13 +170,14 @@ public class ProxyServletConfig {
|
||||
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
|
||||
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
|
||||
MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
|
||||
String remoteHost = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
|
||||
if (mediaInfo != null) {
|
||||
if (!ObjectUtils.isEmpty(queryStr)) {
|
||||
queryStr += "&remoteHost=" + remoteHost;
|
||||
}else {
|
||||
queryStr = "remoteHost=" + remoteHost;
|
||||
}
|
||||
if (mediaInfo == null) {
|
||||
return null;
|
||||
}
|
||||
String remoteHost = String.format("http://%s:%s", mediaInfo.getStreamIp(), mediaInfo.getRecordAssistPort());
|
||||
if (!ObjectUtils.isEmpty(queryStr)) {
|
||||
queryStr += "&remoteHost=" + remoteHost;
|
||||
}else {
|
||||
queryStr = "remoteHost=" + remoteHost;
|
||||
}
|
||||
return queryStr;
|
||||
}
|
||||
@@ -192,6 +194,12 @@ public class ProxyServletConfig {
|
||||
} catch (IOException ioException) {
|
||||
if (ioException instanceof ConnectException) {
|
||||
logger.error("录像服务 连接失败");
|
||||
}else if (ioException instanceof ClientAbortException) {
|
||||
/**
|
||||
* TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常,
|
||||
* TODO 暂时去除异常处理。后续使用其他代理框架修改测试
|
||||
*/
|
||||
|
||||
}else {
|
||||
logger.error("录像服务 代理失败: ", e);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ package com.genersoft.iot.vmp.conf;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
|
||||
@@ -13,6 +12,8 @@ public class SipConfig {
|
||||
|
||||
private String ip;
|
||||
|
||||
private String showIp;
|
||||
|
||||
private Integer port;
|
||||
|
||||
private String domain;
|
||||
@@ -96,4 +97,14 @@ public class SipConfig {
|
||||
this.alarm = alarm;
|
||||
}
|
||||
|
||||
public String getShowIp() {
|
||||
if (this.showIp == null) {
|
||||
return this.ip;
|
||||
}
|
||||
return showIp;
|
||||
}
|
||||
|
||||
public void setShowIp(String showIp) {
|
||||
this.showIp = showIp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,17 +40,20 @@ public class SipPlatformRunner implements CommandLineRunner {
|
||||
List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true);
|
||||
|
||||
for (ParentPlatform parentPlatform : parentPlatforms) {
|
||||
|
||||
ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
|
||||
|
||||
// 更新缓存
|
||||
ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
|
||||
parentPlatformCatch.setParentPlatform(parentPlatform);
|
||||
parentPlatformCatch.setId(parentPlatform.getServerGBId());
|
||||
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
|
||||
// 设置所有平台离线
|
||||
platformService.offline(parentPlatform, true);
|
||||
// 取消订阅
|
||||
sipCommanderForPlatform.unregister(parentPlatform, null, (eventResult)->{
|
||||
sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
|
||||
platformService.login(parentPlatform);
|
||||
});
|
||||
// 设置所有平台离线
|
||||
platformService.offline(parentPlatform, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,15 +50,22 @@ public class UserSetting {
|
||||
private Boolean pushStreamAfterAck = Boolean.FALSE;
|
||||
|
||||
private Boolean sipLog = Boolean.FALSE;
|
||||
private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE;
|
||||
|
||||
private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
|
||||
|
||||
private String serverId = "000000";
|
||||
|
||||
private String recordPath = null;
|
||||
|
||||
private String thirdPartyGBIdReg = "[\\s\\S]*";
|
||||
|
||||
private String broadcastForPlatform = "UDP";
|
||||
|
||||
private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
|
||||
|
||||
private List<String> allowedOrigins = new ArrayList<>();
|
||||
|
||||
public Boolean getSavePositionHistory() {
|
||||
return savePositionHistory;
|
||||
}
|
||||
@@ -238,4 +245,36 @@ public class UserSetting {
|
||||
public void setSipLog(Boolean sipLog) {
|
||||
this.sipLog = sipLog;
|
||||
}
|
||||
|
||||
public List<String> getAllowedOrigins() {
|
||||
return allowedOrigins;
|
||||
}
|
||||
|
||||
public void setAllowedOrigins(List<String> allowedOrigins) {
|
||||
this.allowedOrigins = allowedOrigins;
|
||||
}
|
||||
|
||||
public Boolean getSendToPlatformsWhenIdLost() {
|
||||
return sendToPlatformsWhenIdLost;
|
||||
}
|
||||
|
||||
public void setSendToPlatformsWhenIdLost(Boolean sendToPlatformsWhenIdLost) {
|
||||
this.sendToPlatformsWhenIdLost = sendToPlatformsWhenIdLost;
|
||||
}
|
||||
|
||||
public Boolean getRefuseChannelStatusChannelFormNotify() {
|
||||
return refuseChannelStatusChannelFormNotify;
|
||||
}
|
||||
|
||||
public void setRefuseChannelStatusChannelFormNotify(Boolean refuseChannelStatusChannelFormNotify) {
|
||||
this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify;
|
||||
}
|
||||
|
||||
public String getRecordPath() {
|
||||
return recordPath;
|
||||
}
|
||||
|
||||
public void setRecordPath(String recordPath) {
|
||||
this.recordPath = recordPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.genersoft.iot.vmp.conf.druid;
|
||||
|
||||
import com.alibaba.druid.support.http.StatViewServlet;
|
||||
import com.alibaba.druid.support.http.WebStatFilter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
/**
|
||||
* druid监控配置
|
||||
* @author
|
||||
*/
|
||||
public class DruidConfiguration {
|
||||
|
||||
@Value("${rj-druid-manage.allow:127.0.0.1}")
|
||||
private String allow;
|
||||
|
||||
@Value("${rj-druid-manage.deny:}")
|
||||
private String deny;
|
||||
|
||||
@Value("${rj-druid-manage.loginUsername:admin}")
|
||||
private String loginUsername;
|
||||
|
||||
@Value("${rj-druid-manage.loginPassword:admin}")
|
||||
private String loginPassword;
|
||||
|
||||
@Value("${rj-druid-manage.resetEnable:false}")
|
||||
private String resetEnable;
|
||||
|
||||
/**
|
||||
* druid监控页面开启
|
||||
*/
|
||||
@Bean
|
||||
public ServletRegistrationBean druidServlet() {
|
||||
ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
|
||||
// IP白名单
|
||||
servletRegistrationBean.addInitParameter("allow", allow);
|
||||
// IP黑名单(共同存在时,deny优先于allow)
|
||||
servletRegistrationBean.addInitParameter("deny", deny);
|
||||
//控制台管理用户
|
||||
servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
|
||||
servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
|
||||
//是否能够重置数据 禁用HTML页面上的“Reset All”功能
|
||||
servletRegistrationBean.addInitParameter("resetEnable", resetEnable);
|
||||
return servletRegistrationBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* druid url监控配置
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean filterRegistrationBean() {
|
||||
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
|
||||
filterRegistrationBean.addUrlPatterns("/*");
|
||||
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.genersoft.iot.vmp.conf.druid;
|
||||
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* druid监控支持注解
|
||||
*
|
||||
* @author
|
||||
* {@link DruidConfiguration} druid监控页面安全配置支持
|
||||
* {@link ServletComponentScan} druid监控页面需要扫描servlet
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({
|
||||
DruidConfiguration.class,
|
||||
})
|
||||
@ServletComponentScan
|
||||
public @interface EnableDruidSupport {
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import org.apache.poi.hssf.eventmodel.ERFListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -18,17 +18,15 @@ import java.io.IOException;
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
|
||||
public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
||||
// 允许跨域
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
// 允许自定义请求头token(允许head跨域)
|
||||
response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");
|
||||
response.setHeader("Content-type", "application/json;charset=UTF-8");
|
||||
String jwt = request.getHeader(JwtUtils.getHeader());
|
||||
JwtUser jwtUser = JwtUtils.verifyToken(jwt);
|
||||
String username = jwtUser.getUserName();
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", ErrorCode.ERROR401.getCode());
|
||||
jsonObject.put("msg", ErrorCode.ERROR401.getMsg());
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||
import com.genersoft.iot.vmp.service.IUserService;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -10,10 +12,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||
import com.genersoft.iot.vmp.service.IUserService;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户登录认证逻辑
|
||||
@@ -45,4 +44,8 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 登录超时的处理
|
||||
*/
|
||||
public class InvalidSessionHandler implements InvalidSessionStrategy {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class);
|
||||
|
||||
@Override
|
||||
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException {
|
||||
String username = request.getParameter("username");
|
||||
logger.info("[登录超时] - [{}]", username);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* jwt token 过滤器
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
// 忽略登录请求的token验证
|
||||
String requestURI = request.getRequestURI();
|
||||
if (requestURI.equalsIgnoreCase("/api/user/login")) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
if (!userSetting.isInterfaceAuthentication()) {
|
||||
// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
String jwt = request.getHeader(JwtUtils.getHeader());
|
||||
// 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的
|
||||
// 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
jwt = request.getParameter(JwtUtils.getHeader());
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JwtUser jwtUser = JwtUtils.verifyToken(jwt);
|
||||
String username = jwtUser.getUserName();
|
||||
// TODO 处理各个状态
|
||||
switch (jwtUser.getStatus()){
|
||||
case EXPIRED:
|
||||
response.setStatus(400);
|
||||
chain.doFilter(request, response);
|
||||
// 异常
|
||||
return;
|
||||
case EXCEPTION:
|
||||
// 过期
|
||||
response.setStatus(400);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
case EXPIRING_SOON:
|
||||
// 即将过期
|
||||
// return;
|
||||
default:
|
||||
}
|
||||
|
||||
// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
138
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
Normal file
138
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
|
||||
import org.jose4j.json.JsonUtil;
|
||||
import org.jose4j.jwk.RsaJsonWebKey;
|
||||
import org.jose4j.jws.AlgorithmIdentifiers;
|
||||
import org.jose4j.jws.JsonWebSignature;
|
||||
import org.jose4j.jwt.JwtClaims;
|
||||
import org.jose4j.jwt.NumericDate;
|
||||
import org.jose4j.jwt.consumer.ErrorCodes;
|
||||
import org.jose4j.jwt.consumer.InvalidJwtException;
|
||||
import org.jose4j.jwt.consumer.JwtConsumer;
|
||||
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
|
||||
import org.jose4j.lang.JoseException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
public class JwtUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
|
||||
|
||||
private static final String HEADER = "access-token";
|
||||
private static final String AUDIENCE = "Audience";
|
||||
|
||||
private static final long EXPIRED_THRESHOLD = 10 * 60;
|
||||
|
||||
private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae";
|
||||
private static final String privateKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\",\"d\":\"ed7U_k3rJ4yTk70JtRSIfjKGiEb67BO1TabcymnljKO7RU8nage84zZYuSu_XpQsHk6P1f0Gzxkicghm_Er-FrfVn2pp70Xu52z3yRd6BJUgWLDFk97ngScIyw5OiULKU9SrZk2frDpftNCSUcIgb50F8m0QAnBa_CdPsQKbuuhLv8V8tBAV7F_lAwvSBgu56wRo3hPz5dWH8YeXM7XBfQ9viFMNEKd21sP_j5C7ueUnXT66nBxe3ZJEU3iuMYM6D6dB_KW2GfZC6WmTgvGhhxJD0h7aYmfjkD99MDleB7SkpbvoODOqiQ5Epb7Nyh6kv5u4KUv2CJYtATLZkUeMkQ\",\"p\":\"uBUjWPWtlGksmOqsqCNWksfqJvMcnP_8TDYN7e4-WnHL4N-9HjRuPDnp6kHvCIEi9SEfxm7gNxlRcWegvNQr3IZCz7TnCTexXc5NOklB9OavWFla6u-s3Thn6Tz45-EUjpJr0VJMxhO-KxGmuTwUXBBp4vN6K2qV6rQNFmgkWzk\",\"q\":\"tW_i7cCec56bHkhITL_79dXHz_PLC_f7xlynmlZJGU_d6mqOKmLBNBbTMLnYW8uAFiFzWxDeDHh1o5uF0mSQR-Z1Fg35OftnpbWpy0Cbc2la5WgXQjOwtG1eLYIY2BD3-wQ1VYDBCvowr4FDi-sngxwLqvwmrJ0xjhi99O-Gzcs\",\"dp\":\"q1d5jE85Hz_6M-eTh_lEluEf0NtPEc-vvhw-QO4V-cecNpbrCBdTWBmr4dE3NdpFeJc5ZVFEv-SACyei1MBEh0ItI_pFZi4BmMfy2ELh8ptaMMkTOESYyVy8U7veDq9RnBcr5i1Nqr0rsBkA77-9T6gzdvycBZdzLYAkAmwzEvk\",\"dq\":\"q29A2K08Crs-jmp2Bi8Q_8QzvIX6wSBbwZ4ir24AO-5_HNP56IrPS0yV2GCB0pqCOGb6_Hz_koDvhtuYoqdqvMVAtMoXR3YJBUaVXPt65p4RyNmFwIPe31zHs_BNUTsXVRMw4c16mci03-Af1sEm4HdLfxAp6sfM3xr5wcnhcek\",\"qi\":\"rHPgVTyHUHuYzcxfouyBfb1XAY8nshwn0ddo81o1BccD4Z7zo5It6SefDHjxCAbcmbiCcXBSooLcY-NF5FMv3fg19UE21VyLQltHcVjRRp2tRs4OHcM8yaXIU2x6N6Z6BP2tOksHb9MOBY1wAQzFOAKg_G4Sxev6-_6ud6RISuc\"}";
|
||||
private static final String publicKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\"}";
|
||||
|
||||
/**
|
||||
* token过期时间(分钟)
|
||||
*/
|
||||
public static final long expirationTime = 30;
|
||||
|
||||
public static String createToken(String username, String password) {
|
||||
try {
|
||||
/**
|
||||
* “iss” (issuer) 发行人
|
||||
*
|
||||
* “sub” (subject) 主题
|
||||
*
|
||||
* “aud” (audience) 接收方 用户
|
||||
*
|
||||
* “exp” (expiration time) 到期时间
|
||||
*
|
||||
* “nbf” (not before) 在此之前不可用
|
||||
*
|
||||
* “iat” (issued at) jwt的签发时间
|
||||
*/
|
||||
//Payload
|
||||
JwtClaims claims = new JwtClaims();
|
||||
claims.setGeneratedJwtId();
|
||||
claims.setIssuedAtToNow();
|
||||
// 令牌将过期的时间 分钟
|
||||
claims.setExpirationTimeMinutesInTheFuture(expirationTime);
|
||||
claims.setNotBeforeMinutesInThePast(0);
|
||||
claims.setSubject("login");
|
||||
claims.setAudience(AUDIENCE);
|
||||
//添加自定义参数,必须是字符串类型
|
||||
claims.setClaim("username", username);
|
||||
claims.setClaim("password", password);
|
||||
|
||||
//jws
|
||||
JsonWebSignature jws = new JsonWebSignature();
|
||||
//签名算法RS256
|
||||
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
|
||||
jws.setKeyIdHeaderValue(keyId);
|
||||
jws.setPayload(claims.toJson());
|
||||
|
||||
PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey();
|
||||
jws.setKey(privateKey);
|
||||
|
||||
//get token
|
||||
String idToken = jws.getCompactSerialization();
|
||||
return idToken;
|
||||
} catch (JoseException e) {
|
||||
logger.error("[Token生成失败]: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getHeader() {
|
||||
return HEADER;
|
||||
}
|
||||
|
||||
|
||||
public static JwtUser verifyToken(String token) {
|
||||
|
||||
JwtUser jwtUser = new JwtUser();
|
||||
|
||||
try {
|
||||
JwtConsumer consumer = new JwtConsumerBuilder()
|
||||
.setRequireExpirationTime()
|
||||
.setMaxFutureValidityInMinutes(5256000)
|
||||
.setAllowedClockSkewInSeconds(30)
|
||||
.setRequireSubject()
|
||||
//.setExpectedIssuer("")
|
||||
.setExpectedAudience(AUDIENCE)
|
||||
.setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey())
|
||||
.build();
|
||||
|
||||
JwtClaims claims = consumer.processToClaims(token);
|
||||
NumericDate expirationTime = claims.getExpirationTime();
|
||||
// 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
|
||||
// 剩余时间 (秒)
|
||||
long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
|
||||
if (timeRemaining < 5 * 60) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
|
||||
}else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
|
||||
}
|
||||
|
||||
String username = (String) claims.getClaimValue("username");
|
||||
String password = (String) claims.getClaimValue("password");
|
||||
jwtUser.setUserName(username);
|
||||
jwtUser.setPassword(password);
|
||||
|
||||
return jwtUser;
|
||||
} catch (InvalidJwtException e) {
|
||||
if (e.hasErrorCode(ErrorCodes.EXPIRED)) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
}else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION);
|
||||
}
|
||||
return jwtUser;
|
||||
}catch (Exception e) {
|
||||
logger.error("[Token解析失败]: {}", e.getMessage());
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
return jwtUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,16 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
|
||||
String username = request.getParameter("username");
|
||||
logger.info("[登录成功] - [{}]", username);
|
||||
// String username = request.getParameter("username");
|
||||
// httpServletResponse.setContentType("application/json;charset=UTF-8");
|
||||
// // 生成JWT,并放置到请求头中
|
||||
// String jwt = JwtUtils.createToken(authentication.getName(), );
|
||||
// httpServletResponse.setHeader(JwtUtils.getHeader(), jwt);
|
||||
// ServletOutputStream outputStream = httpServletResponse.getOutputStream();
|
||||
// outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8));
|
||||
// outputStream.flush();
|
||||
// outputStream.close();
|
||||
|
||||
// logger.info("[登录成功] - [{}]", username);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -9,6 +10,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SecurityUtils {
|
||||
|
||||
@@ -25,9 +27,12 @@ public class SecurityUtils {
|
||||
public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
|
||||
//使用security框架自带的验证token生成器 也可以自定义。
|
||||
UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
|
||||
//认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功
|
||||
Authentication authenticate = authenticationManager.authenticate(token);
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticate);
|
||||
LoginUser user = (LoginUser) authenticate.getPrincipal();
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -49,8 +54,13 @@ public class SecurityUtils {
|
||||
if(authentication!=null){
|
||||
Object principal = authentication.getPrincipal();
|
||||
if(principal!=null && !"anonymousUser".equals(principal)){
|
||||
LoginUser user = (LoginUser) authentication.getPrincipal();
|
||||
return user;
|
||||
// LoginUser user = (LoginUser) authentication.getPrincipal();
|
||||
|
||||
String username = (String) principal;
|
||||
User user = new User();
|
||||
user.setUsername(username);
|
||||
LoginUser loginUser = new LoginUser(user, LocalDateTime.now());
|
||||
return loginUser;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -15,9 +15,16 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 配置Spring Security
|
||||
@@ -56,22 +63,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
*/
|
||||
@Autowired
|
||||
private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
|
||||
// /**
|
||||
// * 超时处理
|
||||
// */
|
||||
// @Autowired
|
||||
// private InvalidSessionHandler invalidSessionHandler;
|
||||
|
||||
// /**
|
||||
// * 顶号处理
|
||||
// */
|
||||
// @Autowired
|
||||
// private SessionInformationExpiredHandler sessionInformationExpiredHandler;
|
||||
// /**
|
||||
// * 登录用户没有权限访问资源
|
||||
// */
|
||||
// @Autowired
|
||||
// private LoginUserAccessDeniedHandler accessDeniedHandler;
|
||||
@Autowired
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
|
||||
/**
|
||||
@@ -80,31 +73,21 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
|
||||
if (!userSetting.isInterfaceAuthentication()) {
|
||||
web.ignoring().antMatchers("**");
|
||||
}else {
|
||||
// 可以直接访问的静态数据
|
||||
web.ignoring()
|
||||
.antMatchers("/")
|
||||
.antMatchers("/#/**")
|
||||
.antMatchers("/static/**")
|
||||
.antMatchers("/index.html")
|
||||
.antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**"
|
||||
.antMatchers("/webjars/**")
|
||||
.antMatchers("/swagger-resources/**")
|
||||
.antMatchers("/v3/api-docs/**")
|
||||
.antMatchers("/favicon.ico")
|
||||
.antMatchers("/js/**");
|
||||
List<String> interfaceAuthenticationExcludes = userSetting.getInterfaceAuthenticationExcludes();
|
||||
for (String interfaceAuthenticationExclude : interfaceAuthenticationExcludes) {
|
||||
if (interfaceAuthenticationExclude.split("/").length < 4 ) {
|
||||
logger.warn("{}不满足两级目录,已忽略", interfaceAuthenticationExclude);
|
||||
}else {
|
||||
web.ignoring().antMatchers(interfaceAuthenticationExclude);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
ArrayList<String> matchers = new ArrayList<>();
|
||||
matchers.add("/");
|
||||
matchers.add("/#/**");
|
||||
matchers.add("/static/**");
|
||||
matchers.add("/index.html");
|
||||
matchers.add("/doc.html");
|
||||
matchers.add("/webjars/**");
|
||||
matchers.add("/swagger-resources/**");
|
||||
matchers.add("/v3/api-docs/**");
|
||||
matchers.add("/js/**");
|
||||
matchers.add("/api/device/query/snap/**");
|
||||
matchers.add("/record_proxy/*/**");
|
||||
matchers.addAll(userSetting.getInterfaceAuthenticationExcludes());
|
||||
// 可以直接访问的静态数据
|
||||
web.ignoring().antMatchers(matchers.toArray(new String[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,36 +109,43 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.cors().and().csrf().disable();
|
||||
// 设置允许添加静态文件
|
||||
http.headers().contentTypeOptions().disable();
|
||||
http.authorizeRequests()
|
||||
// 放行接口
|
||||
.antMatchers("/api/user/login","/index/hook/**").permitAll()
|
||||
// 除上面外的所有请求全部需要鉴权认证
|
||||
.anyRequest().authenticated()
|
||||
// 异常处理(权限拒绝、登录失效等)
|
||||
.and().exceptionHandling()
|
||||
//匿名用户访问无权限资源时的异常处理
|
||||
.authenticationEntryPoint(anonymousAuthenticationEntryPoint)
|
||||
// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
|
||||
// 登入 允许所有用户
|
||||
.and().formLogin().permitAll()
|
||||
//登录成功处理逻辑
|
||||
.successHandler(loginSuccessHandler)
|
||||
//登录失败处理逻辑
|
||||
.failureHandler(loginFailureHandler)
|
||||
// 登出
|
||||
.and().logout().logoutUrl("/api/user/logout").permitAll()
|
||||
//登出成功处理逻辑
|
||||
.logoutSuccessHandler(logoutHandler)
|
||||
.deleteCookies("JSESSIONID")
|
||||
// 会话管理
|
||||
// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
|
||||
// .maximumSessions(1)//同一账号同时登录最大用户数
|
||||
// .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理
|
||||
;
|
||||
http.headers().contentTypeOptions().disable()
|
||||
.and().cors().configurationSource(configurationSource())
|
||||
.and().csrf().disable()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
|
||||
// 配置拦截规则
|
||||
.and()
|
||||
.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()
|
||||
.anyRequest().authenticated()
|
||||
// 异常处理器
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(anonymousAuthenticationEntryPoint)
|
||||
.and().logout().logoutUrl("/api/user/logout").permitAll()
|
||||
.logoutSuccessHandler(logoutHandler)
|
||||
;
|
||||
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
}
|
||||
|
||||
CorsConfigurationSource configurationSource(){
|
||||
// 配置跨域
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
|
||||
corsConfiguration.setMaxAge(3600L);
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());
|
||||
corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader()));
|
||||
|
||||
UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource();
|
||||
url.registerCorsConfiguration("/**",corsConfiguration);
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.genersoft.iot.vmp.conf.security.dto;
|
||||
|
||||
public class JwtUser {
|
||||
|
||||
public enum TokenStatus{
|
||||
/**
|
||||
* 正常的使用状态
|
||||
*/
|
||||
NORMAL,
|
||||
/**
|
||||
* 过期而失效
|
||||
*/
|
||||
EXPIRED,
|
||||
/**
|
||||
* 即将过期
|
||||
*/
|
||||
EXPIRING_SOON,
|
||||
/**
|
||||
* 异常
|
||||
*/
|
||||
EXCEPTION
|
||||
}
|
||||
|
||||
private String userName;
|
||||
|
||||
private String password;
|
||||
|
||||
private TokenStatus status;
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public TokenStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(TokenStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ public class LoginUser implements UserDetails, CredentialsContainer {
|
||||
*/
|
||||
private User user;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
@@ -102,4 +104,11 @@ public class LoginUser implements UserDetails, CredentialsContainer {
|
||||
return user.getPushKey();
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public class DigestServerAuthenticationHelper {
|
||||
*/
|
||||
public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
|
||||
AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
|
||||
if ( authHeader == null ) {
|
||||
if ( authHeader == null || authHeader.getRealm() == null) {
|
||||
return false;
|
||||
}
|
||||
String realm = authHeader.getRealm().trim();
|
||||
|
||||
@@ -188,6 +188,13 @@ public class Device {
|
||||
@Schema(description = "SIP交互IP(设备访问平台的IP)")
|
||||
private String localIp;
|
||||
|
||||
@Schema(description = "是否作为消息通道")
|
||||
private boolean asMessageChannel;
|
||||
|
||||
@Schema(description = "设备注册的事务信息")
|
||||
private SipTransactionInfo sipTransactionInfo;
|
||||
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
@@ -428,4 +435,20 @@ public class Device {
|
||||
public void setKeepaliveIntervalTime(int keepaliveIntervalTime) {
|
||||
this.keepaliveIntervalTime = keepaliveIntervalTime;
|
||||
}
|
||||
|
||||
public boolean isAsMessageChannel() {
|
||||
return asMessageChannel;
|
||||
}
|
||||
|
||||
public void setAsMessageChannel(boolean asMessageChannel) {
|
||||
this.asMessageChannel = asMessageChannel;
|
||||
}
|
||||
|
||||
public SipTransactionInfo getSipTransactionInfo() {
|
||||
return sipTransactionInfo;
|
||||
}
|
||||
|
||||
public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
|
||||
this.sipTransactionInfo = sipTransactionInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,9 @@ public class ParentPlatform {
|
||||
@Schema(description = "树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGrou")
|
||||
private String treeType;
|
||||
|
||||
@Schema(description = "是否作为消息通道")
|
||||
private boolean asMessageChannel;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -428,4 +431,12 @@ public class ParentPlatform {
|
||||
public void setTreeType(String treeType) {
|
||||
this.treeType = treeType;
|
||||
}
|
||||
|
||||
public boolean isAsMessageChannel() {
|
||||
return asMessageChannel;
|
||||
}
|
||||
|
||||
public void setAsMessageChannel(boolean asMessageChannel) {
|
||||
this.asMessageChannel = asMessageChannel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ public class ParentPlatformCatch {
|
||||
|
||||
private ParentPlatform parentPlatform;
|
||||
|
||||
private SipTransactionInfo sipTransactionInfo;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -55,4 +57,12 @@ public class ParentPlatformCatch {
|
||||
public void setCallId(String callId) {
|
||||
this.callId = callId;
|
||||
}
|
||||
|
||||
public SipTransactionInfo getSipTransactionInfo() {
|
||||
return sipTransactionInfo;
|
||||
}
|
||||
|
||||
public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
|
||||
this.sipTransactionInfo = sipTransactionInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
@@ -9,22 +11,29 @@ import java.util.List;
|
||||
* @author: swwheihei
|
||||
* @date: 2020年5月8日 下午2:05:56
|
||||
*/
|
||||
@Schema(description = "设备录像查询结果信息")
|
||||
public class RecordInfo {
|
||||
|
||||
@Schema(description = "设备编号")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "通道编号")
|
||||
private String channelId;
|
||||
|
||||
@Schema(description = "命令序列号")
|
||||
private String sn;
|
||||
|
||||
@Schema(description = "设备名称")
|
||||
private String name;
|
||||
|
||||
|
||||
@Schema(description = "列表总数")
|
||||
private int sumNum;
|
||||
|
||||
private int count;
|
||||
|
||||
private Instant lastTime;
|
||||
|
||||
|
||||
@Schema(description = "")
|
||||
private List<RecordItem> recordList;
|
||||
|
||||
public String getDeviceId() {
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
|
||||
@@ -13,26 +13,37 @@ import java.time.temporal.TemporalAccessor;
|
||||
* @author: swwheihei
|
||||
* @date: 2020年5月8日 下午2:06:54
|
||||
*/
|
||||
@Schema(description = "设备录像详情")
|
||||
public class RecordItem implements Comparable<RecordItem>{
|
||||
|
||||
@Schema(description = "设备编号")
|
||||
private String deviceId;
|
||||
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
|
||||
@Schema(description = "文件路径名 (可选)")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "录像文件大小,单位:Byte(可选)")
|
||||
private String fileSize;
|
||||
|
||||
@Schema(description = "录像地址(可选)")
|
||||
private String address;
|
||||
|
||||
|
||||
@Schema(description = "录像开始时间(可选)")
|
||||
private String startTime;
|
||||
|
||||
|
||||
@Schema(description = "录像结束时间(可选)")
|
||||
private String endTime;
|
||||
|
||||
|
||||
@Schema(description = "保密属性(必选)缺省为0;0:不涉密,1:涉密")
|
||||
private int secrecy;
|
||||
|
||||
|
||||
@Schema(description = "录像产生类型(可选)time或alarm 或 manua")
|
||||
private String type;
|
||||
|
||||
|
||||
@Schema(description = "录像触发者ID(可选)")
|
||||
private String recorderId;
|
||||
|
||||
public String getDeviceId() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.genersoft.iot.vmp.gb28181.event.device;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
@@ -9,8 +8,6 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.ClientTransaction;
|
||||
import javax.sip.address.SipURI;
|
||||
import javax.sip.header.CallIdHeader;
|
||||
import javax.sip.header.ToHeader;
|
||||
import javax.sip.message.Request;
|
||||
|
||||
/**
|
||||
@@ -34,7 +31,7 @@ public class RequestTimeoutEventImpl implements ApplicationListener<RequestTimeo
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
deviceService.offline(device.getDeviceId());
|
||||
deviceService.offline(device.getDeviceId(), "等待消息超时");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ public class SipRunner implements CommandLineRunner {
|
||||
|
||||
for (Device device : deviceList) {
|
||||
if (deviceService.expire(device)){
|
||||
deviceService.offline(device.getDeviceId());
|
||||
deviceService.offline(device.getDeviceId(), "注册已过期");
|
||||
}else {
|
||||
deviceService.online(device);
|
||||
deviceService.online(device, null);
|
||||
}
|
||||
}
|
||||
// 重置cseq计数
|
||||
|
||||
@@ -120,7 +120,7 @@ public interface ISIPCommander {
|
||||
*/
|
||||
void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
|
||||
String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
|
||||
SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
|
||||
SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -217,7 +217,6 @@ public interface ISIPCommander {
|
||||
*
|
||||
* @param device 视频设备
|
||||
* @param channelId 通道id,非通道则是设备本身
|
||||
* @param frontCmd 上级平台的指令,如果存在则直接下发
|
||||
* @param enabled 看守位使能:1 = 开启,0 = 关闭
|
||||
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
|
||||
* @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
|
||||
|
||||
@@ -22,12 +22,10 @@ public interface ISIPCommanderForPlatform {
|
||||
* @param parentPlatform
|
||||
* @return
|
||||
*/
|
||||
void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
|
||||
throws InvalidArgumentException, ParseException, SipException;
|
||||
void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
|
||||
|
||||
void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www,
|
||||
SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister)
|
||||
throws SipException, InvalidArgumentException, ParseException;
|
||||
void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
|
||||
void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException;
|
||||
|
||||
/**
|
||||
* 向上级平台注销
|
||||
@@ -35,8 +33,7 @@ public interface ISIPCommanderForPlatform {
|
||||
* @param parentPlatform
|
||||
* @return
|
||||
*/
|
||||
void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
|
||||
throws InvalidArgumentException, ParseException, SipException;
|
||||
void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,7 +46,7 @@ public class SIPRequestHeaderPlarformProvider {
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String viaTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException {
|
||||
public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String toTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException {
|
||||
Request request = null;
|
||||
String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
|
||||
//请求行
|
||||
@@ -54,7 +54,8 @@ public class SIPRequestHeaderPlarformProvider {
|
||||
parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
|
||||
//via
|
||||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
|
||||
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), parentPlatform.getServerPort(), parentPlatform.getTransport(), viaTag);
|
||||
ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(),
|
||||
parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag());
|
||||
viaHeader.setRPort();
|
||||
viaHeaders.add(viaHeader);
|
||||
//from
|
||||
@@ -64,7 +65,7 @@ public class SIPRequestHeaderPlarformProvider {
|
||||
//to
|
||||
SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
|
||||
Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
|
||||
ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
|
||||
ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,toTag);
|
||||
|
||||
//Forwards
|
||||
MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
|
||||
@@ -86,15 +87,21 @@ public class SIPRequestHeaderPlarformProvider {
|
||||
return request;
|
||||
}
|
||||
|
||||
public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String viaTag,
|
||||
String callId, WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
|
||||
public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String toTag,
|
||||
WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
|
||||
|
||||
|
||||
Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, viaTag, callIdHeader, isRegister);
|
||||
Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, isRegister);
|
||||
SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
|
||||
if (www == null) {
|
||||
AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest");
|
||||
authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
|
||||
String username = parentPlatform.getUsername();
|
||||
if ( username == null || username == "" )
|
||||
{
|
||||
authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
|
||||
} else {
|
||||
authorizationHeader.setUsername(username);
|
||||
}
|
||||
authorizationHeader.setURI(requestURI);
|
||||
authorizationHeader.setAlgorithm("MD5");
|
||||
registerRequest.addHeader(authorizationHeader);
|
||||
@@ -108,8 +115,6 @@ public class SIPRequestHeaderPlarformProvider {
|
||||
// qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
|
||||
String qop = www.getQop();
|
||||
|
||||
callIdHeader.setCallId(callId);
|
||||
|
||||
String cNonce = null;
|
||||
String nc = "00000001";
|
||||
if (qop != null) {
|
||||
|
||||
@@ -276,7 +276,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
|
||||
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
|
||||
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
|
||||
if (event != null) {
|
||||
@@ -377,7 +377,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
|
||||
|
||||
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
String sdpIp;
|
||||
if (!ObjectUtils.isEmpty(device.getSdpIp())) {
|
||||
sdpIp = device.getSdpIp();
|
||||
@@ -479,10 +479,11 @@ public class SIPCommander implements ISIPCommander {
|
||||
*/
|
||||
@Override
|
||||
public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
|
||||
String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
|
||||
SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
|
||||
String startTime, String endTime, int downloadSpeed,
|
||||
InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
|
||||
SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
|
||||
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
|
||||
String sdpIp;
|
||||
if (!ObjectUtils.isEmpty(device.getSdpIp())) {
|
||||
sdpIp = device.getSdpIp();
|
||||
@@ -549,11 +550,14 @@ public class SIPCommander implements ISIPCommander {
|
||||
content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
|
||||
|
||||
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
|
||||
|
||||
logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
|
||||
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
|
||||
// 添加订阅
|
||||
CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
|
||||
String callId=newCallIdHeader.getCallId();
|
||||
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
|
||||
hookEvent.call(new InviteStreamInfo(mediaServerItem, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
|
||||
logger.debug("sipc 添加订阅===callId {}",callId);
|
||||
hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream()));
|
||||
subscribe.removeSubscribe(hookSubscribe);
|
||||
hookSubscribe.getContent().put("regist", false);
|
||||
hookSubscribe.getContent().put("schema", "rtsp");
|
||||
@@ -562,7 +566,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
(MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
|
||||
logger.info("[录像]下载结束, 发送BYE");
|
||||
try {
|
||||
streamByeCmd(device, channelId, ssrcInfo.getStream(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId());
|
||||
streamByeCmd(device, channelId, ssrcInfo.getStream(),callId);
|
||||
} catch (InvalidArgumentException | ParseException | SipException |
|
||||
SsrcTransactionNotFoundException e) {
|
||||
logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
|
||||
@@ -570,15 +574,24 @@ public class SIPCommander implements ISIPCommander {
|
||||
});
|
||||
});
|
||||
|
||||
Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
|
||||
Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
|
||||
if (inviteStreamCallback != null) {
|
||||
inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
|
||||
inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,callId, "rtp", ssrcInfo.getStream()));
|
||||
}
|
||||
|
||||
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent -> {
|
||||
ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
|
||||
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
|
||||
ResponseEvent responseEvent = (ResponseEvent) event.event;
|
||||
SIPResponse response = (SIPResponse) responseEvent.getResponse();
|
||||
streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
|
||||
String contentString =new String(response.getRawContent());
|
||||
int ssrcIndex = contentString.indexOf("y=");
|
||||
String ssrc=ssrcInfo.getSsrc();
|
||||
if (ssrcIndex >= 0) {
|
||||
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
|
||||
}
|
||||
logger.debug("接收到的下载响应ssrc====>{}",ssrcInfo.getSsrc());
|
||||
logger.debug("接收到的下载响应ssrc====>{}",ssrc);
|
||||
streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
|
||||
okEvent.response(event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -778,7 +791,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
|
||||
cmdXml.append("</Control>\r\n");
|
||||
|
||||
|
||||
|
||||
|
||||
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
|
||||
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
|
||||
@@ -854,7 +867,6 @@ public class SIPCommander implements ISIPCommander {
|
||||
*
|
||||
* @param device 视频设备
|
||||
* @param channelId 通道id,非通道则是设备本身
|
||||
* @param frontCmd 上级平台的指令,如果存在则直接下发
|
||||
* @param enabled 看守位使能:1 = 开启,0 = 关闭
|
||||
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
|
||||
* @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
|
||||
@@ -978,7 +990,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
|
||||
catalogXml.append("</Query>\r\n");
|
||||
|
||||
|
||||
|
||||
|
||||
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
|
||||
|
||||
@@ -1181,7 +1193,6 @@ public class SIPCommander implements ISIPCommander {
|
||||
cmdXml.append("</Query>\r\n");
|
||||
|
||||
|
||||
|
||||
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
|
||||
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
|
||||
}
|
||||
@@ -1427,7 +1438,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
|
||||
logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
|
||||
deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
|
||||
|
||||
String characterSet = device.getCharset();
|
||||
@@ -1439,7 +1450,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
|
||||
deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
|
||||
deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
|
||||
deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
|
||||
deviceStatusXml.append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n");
|
||||
deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
|
||||
deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
|
||||
deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.GitUtil;
|
||||
import gov.nist.javax.sip.message.MessageFactoryImpl;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.message.SIPResponse;
|
||||
@@ -85,26 +86,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
@Autowired
|
||||
private DynamicTask dynamicTask;
|
||||
|
||||
@Autowired
|
||||
private GitUtil gitUtil;
|
||||
|
||||
@Override
|
||||
public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
|
||||
register(parentPlatform, null, null, errorEvent, okEvent, false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
|
||||
register(parentPlatform, null, null, errorEvent, okEvent, false, false);
|
||||
public void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
|
||||
|
||||
register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www,
|
||||
public void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
|
||||
register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www,
|
||||
SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException {
|
||||
Request request;
|
||||
if (!registerAgain ) {
|
||||
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
|
||||
|
||||
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
|
||||
String fromTag = SipUtils.getNewFromTag();
|
||||
String toTag = null;
|
||||
if (sipTransactionInfo != null ) {
|
||||
if (sipTransactionInfo.getCallId() != null) {
|
||||
callIdHeader.setCallId(sipTransactionInfo.getCallId());
|
||||
}
|
||||
if (sipTransactionInfo.getFromTag() != null) {
|
||||
fromTag = sipTransactionInfo.getFromTag();
|
||||
}
|
||||
if (sipTransactionInfo.getToTag() != null) {
|
||||
toTag = sipTransactionInfo.getToTag();
|
||||
}
|
||||
}
|
||||
|
||||
if (!registerAgain ) {
|
||||
request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform,
|
||||
redisCatchStorage.getCSEQ(), SipUtils.getNewFromTag(),
|
||||
SipUtils.getNewViaTag(), callIdHeader, isRegister);
|
||||
redisCatchStorage.getCSEQ(), fromTag,
|
||||
toTag, callIdHeader, isRegister);
|
||||
// 将 callid 写入缓存, 等注册成功可以更新状态
|
||||
String callIdFromHeader = callIdHeader.getCallId();
|
||||
redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister));
|
||||
@@ -122,8 +146,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
});
|
||||
|
||||
}else {
|
||||
CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
|
||||
request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, SipUtils.getNewFromTag(), null, callId, www, callIdHeader, isRegister);
|
||||
request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister);
|
||||
}
|
||||
|
||||
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent);
|
||||
@@ -245,6 +268,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
|
||||
catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
|
||||
catalogXml.append("<Password>" + channel.getPort() + "</Password>\r\n");
|
||||
catalogXml.append("<PTZType>" + channel.getPTZType() + "</PTZType>\r\n");
|
||||
catalogXml.append("<Status>" + (channel.getStatus() == 1?"ON":"OFF") + "</Status>\r\n");
|
||||
catalogXml.append("<Longitude>" +
|
||||
(channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude())
|
||||
@@ -285,6 +309,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
|
||||
String callId = request.getCallIdHeader().getCallId();
|
||||
|
||||
logger.info("[命令发送] 国标级联{} 目录查询回复: 共{}条,已发送{}条", parentPlatform.getServerGBId(),
|
||||
channels.size(), Math.min(index + parentPlatform.getCatalogGroup(), channels.size()));
|
||||
logger.debug(catalogXml);
|
||||
if (sendAfterResponse) {
|
||||
// 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
|
||||
dynamicTask.startDelay(timeoutTaskKey, ()->{
|
||||
@@ -336,17 +363,22 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
if (parentPlatform == null) {
|
||||
return;
|
||||
}
|
||||
String deviceId = device == null ? parentPlatform.getDeviceGBId() : device.getDeviceId();
|
||||
String deviceName = device == null ? parentPlatform.getName() : device.getName();
|
||||
String manufacturer = device == null ? "WVP-28181-PRO" : device.getManufacturer();
|
||||
String model = device == null ? "platform" : device.getModel();
|
||||
String firmware = device == null ? gitUtil.getBuildVersion() : device.getFirmware();
|
||||
String characterSet = parentPlatform.getCharacterSet();
|
||||
StringBuffer deviceInfoXml = new StringBuffer(600);
|
||||
deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
|
||||
deviceInfoXml.append("<Response>\r\n");
|
||||
deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
|
||||
deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
|
||||
deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
|
||||
deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
|
||||
deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
|
||||
deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
|
||||
deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
|
||||
deviceInfoXml.append("<DeviceID>" + deviceId + "</DeviceID>\r\n");
|
||||
deviceInfoXml.append("<DeviceName>" + deviceName + "</DeviceName>\r\n");
|
||||
deviceInfoXml.append("<Manufacturer>" + manufacturer + "</Manufacturer>\r\n");
|
||||
deviceInfoXml.append("<Model>" + model + "</Model>\r\n");
|
||||
deviceInfoXml.append("<Firmware>" + firmware + "</Firmware>\r\n");
|
||||
deviceInfoXml.append("<Result>OK</Result>\r\n");
|
||||
deviceInfoXml.append("</Response>\r\n");
|
||||
|
||||
@@ -423,7 +455,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
if (parentPlatform == null) {
|
||||
return;
|
||||
}
|
||||
logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
|
||||
logger.info("[发送报警通知]平台: {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
|
||||
deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
|
||||
String characterSet = parentPlatform.getCharacterSet();
|
||||
StringBuffer deviceStatusXml = new StringBuffer(600);
|
||||
@@ -434,7 +466,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
|
||||
.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
|
||||
.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
|
||||
.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
|
||||
.append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n")
|
||||
.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
|
||||
.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
|
||||
.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class SIPRequestProcessorParent {
|
||||
try {
|
||||
return SipFactory.getInstance().createHeaderFactory();
|
||||
} catch (PeerUnavailableException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public abstract class SIPRequestProcessorParent {
|
||||
try {
|
||||
return SipFactory.getInstance().createMessageFactory();
|
||||
} catch (PeerUnavailableException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
|
||||
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
|
||||
@@ -457,12 +458,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
if (!userSetting.getPushStreamAfterAck()) {
|
||||
playService.startPushStream(sendRtpItem, sipResponse, platform, request.getCallIdHeader());
|
||||
}
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 国标级联 回复SdpAck", e);
|
||||
}
|
||||
};
|
||||
SipSubscribe.Event errorEvent = ((event) -> {
|
||||
@@ -471,7 +468,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
} catch (ParseException | SipException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
});
|
||||
sendRtpItem.setApp("rtp");
|
||||
@@ -543,6 +540,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
}
|
||||
} else if (gbStream != null) {
|
||||
if(ssrc.equals(ssrcDefault))
|
||||
{
|
||||
SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
|
||||
if(ssrcConfig != null)
|
||||
{
|
||||
ssrc = ssrcConfig.getPlaySsrc();
|
||||
ssrcConfig.releaseSsrc(ssrc);
|
||||
}
|
||||
}
|
||||
if("push".equals(gbStream.getStreamType())) {
|
||||
if (streamPushItem != null && streamPushItem.isPushIng()) {
|
||||
// 推流状态
|
||||
@@ -572,7 +578,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
} catch (SdpParseException e) {
|
||||
logger.error("sdp解析错误", e);
|
||||
} catch (SdpException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,11 +733,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
|
||||
responseAck(request, Response.REQUEST_TIMEOUT); // 超时
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
// 添加监听
|
||||
@@ -750,11 +756,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -812,11 +818,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -869,7 +875,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
content.append("s=Play\r\n");
|
||||
content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
||||
content.append("t=0 0\r\n");
|
||||
content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n");
|
||||
// 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
|
||||
int localPort = sendRtpItem.getLocalPort();
|
||||
if(localPort == 0)
|
||||
{
|
||||
localPort = new Random().nextInt(65535) + 1;
|
||||
}
|
||||
content.append("m=video " + localPort + " RTP/AVP 96\r\n");
|
||||
content.append("a=sendonly\r\n");
|
||||
content.append("a=rtpmap:96 PS/90000\r\n");
|
||||
if (sendRtpItem.isTcp()) {
|
||||
@@ -890,11 +902,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
return sipResponse;
|
||||
} catch (SipException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
try {
|
||||
responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
|
||||
}catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
boolean runed = !taskQueue.isEmpty();
|
||||
taskQueue.offer(new HandlerCatchData(evt, null, null));
|
||||
@@ -229,7 +229,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
jsonObject.put("speed", mobilePosition.getSpeed());
|
||||
redisCatchStorage.sendMobilePositionMsg(jsonObject);
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
publisher.deviceAlarmEventPublish(deviceAlarm);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,12 +397,20 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
case CatalogEvent.OFF :
|
||||
// 离线
|
||||
logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
storager.deviceChannelOffline(deviceId, 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());
|
||||
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||
if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
|
||||
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||
}else {
|
||||
logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
break;
|
||||
case CatalogEvent.DEFECT:
|
||||
// 故障
|
||||
@@ -432,7 +440,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
|
||||
@@ -18,6 +19,7 @@ import gov.nist.javax.sip.address.AddressImpl;
|
||||
import gov.nist.javax.sip.address.SipUri;
|
||||
import gov.nist.javax.sip.header.SIPDateHeader;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.message.SIPResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -34,6 +36,7 @@ import javax.sip.header.AuthorizationHeader;
|
||||
import javax.sip.header.ContactHeader;
|
||||
import javax.sip.header.FromHeader;
|
||||
import javax.sip.header.ViaHeader;
|
||||
import javax.sip.message.Request;
|
||||
import javax.sip.message.Response;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.ParseException;
|
||||
@@ -105,6 +108,30 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
SipUri uri = (SipUri) address.getURI();
|
||||
String deviceId = uri.getUser();
|
||||
Device device = deviceService.getDevice(deviceId);
|
||||
|
||||
RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
|
||||
userSetting.getSipUseSourceIpAsRemoteAddress());
|
||||
|
||||
if (device != null &&
|
||||
device.getSipTransactionInfo() != null &&
|
||||
request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
|
||||
logger.info("[注册请求] 注册续订: {}", device.getDeviceId());
|
||||
device.setExpires(request.getExpires().getExpires());
|
||||
device.setIp(remoteAddressInfo.getIp());
|
||||
device.setPort(remoteAddressInfo.getPort());
|
||||
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
|
||||
device.setLocalIp(request.getLocalAddress().getHostAddress());
|
||||
Response registerOkResponse = getRegisterOkResponse(request);
|
||||
// 判断TCP还是UDP
|
||||
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
|
||||
String transport = reqViaHeader.getTransport();
|
||||
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
|
||||
device.setRegisterTime(DateUtil.getNow());
|
||||
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
|
||||
deviceService.online(device, sipTransactionInfo);
|
||||
return;
|
||||
}
|
||||
String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
|
||||
AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
|
||||
if (authHead == null && !ObjectUtils.isEmpty(password)) {
|
||||
@@ -147,9 +174,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
// 添加Expires头
|
||||
response.addHeader(request.getExpires());
|
||||
|
||||
RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
|
||||
userSetting.getSipUseSourceIpAsRemoteAddress());
|
||||
|
||||
if (device == null) {
|
||||
device = new Device();
|
||||
device.setStreamMode("UDP");
|
||||
@@ -182,13 +206,33 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
if (registerFlag) {
|
||||
logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress);
|
||||
device.setRegisterTime(DateUtil.getNow());
|
||||
deviceService.online(device);
|
||||
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response);
|
||||
deviceService.online(device, sipTransactionInfo);
|
||||
} else {
|
||||
logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress);
|
||||
deviceService.offline(deviceId);
|
||||
deviceService.offline(deviceId, "主动注销");
|
||||
}
|
||||
} catch (SipException | NoSuchAlgorithmException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Response getRegisterOkResponse(Request request) throws ParseException {
|
||||
// 携带授权头并且密码正确
|
||||
Response response = getMessageFactory().createResponse(Response.OK, request);
|
||||
// 添加date头
|
||||
SIPDateHeader dateHeader = new SIPDateHeader();
|
||||
// 使用自己修改的
|
||||
WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
|
||||
dateHeader.setDate(wvpSipDate);
|
||||
response.addHeader(dateHeader);
|
||||
|
||||
// 添加Contact头
|
||||
response.addHeader(request.getHeader(ContactHeader.NAME));
|
||||
// 添加Expires头
|
||||
response.addHeader(request.getExpires());
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.CmdType;
|
||||
@@ -8,14 +9,11 @@ import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import gov.nist.javax.sip.SipProviderImpl;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.message.SIPResponse;
|
||||
import org.dom4j.DocumentException;
|
||||
@@ -26,7 +24,9 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.*;
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.RequestEvent;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.header.ExpiresHeader;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
@@ -93,7 +93,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
|
||||
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
|
||||
}
|
||||
} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -146,7 +146,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
|
||||
}
|
||||
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
|
||||
subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo);
|
||||
}
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,35 +73,38 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
|
||||
String channelId = getText(rootElement, "DeviceID");
|
||||
// 远程启动功能
|
||||
if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
|
||||
if (parentPlatform.getServerGBId().equals(targetGBId)) {
|
||||
// 远程启动本平台:需要在重新启动程序后先对SipStack解绑
|
||||
logger.info("执行远程启动本平台命令");
|
||||
try {
|
||||
cmderFroPlatform.unregister(parentPlatform, null, null);
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
|
||||
}
|
||||
taskExecutor.execute(() -> {
|
||||
// 远程启动
|
||||
// try {
|
||||
// Thread.sleep(3000);
|
||||
// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
|
||||
// SipStackImpl stack = (SipStackImpl)up.getSipStack();
|
||||
// stack.stop();
|
||||
// Iterator listener = stack.getListeningPoints();
|
||||
// while (listener.hasNext()) {
|
||||
// stack.deleteListeningPoint((ListeningPoint) listener.next());
|
||||
// }
|
||||
// Iterator providers = stack.getSipProviders();
|
||||
// while (providers.hasNext()) {
|
||||
// stack.deleteSipProvider((SipProvider) providers.next());
|
||||
// }
|
||||
// VManageBootstrap.restart();
|
||||
// } catch (InterruptedException | ObjectInUseException e) {
|
||||
// logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
|
||||
// }
|
||||
});
|
||||
}
|
||||
// TODO 拒绝远程启动命令
|
||||
logger.warn("[国标级联]收到平台的远程启动命令, 不处理");
|
||||
|
||||
// if (parentPlatform.getServerGBId().equals(targetGBId)) {
|
||||
// // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
|
||||
// logger.info("执行远程启动本平台命令");
|
||||
// try {
|
||||
// cmderFroPlatform.unregister(parentPlatform, null, null);
|
||||
// } catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
// logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
|
||||
// }
|
||||
// taskExecutor.execute(() -> {
|
||||
// // 远程启动
|
||||
//// try {
|
||||
//// Thread.sleep(3000);
|
||||
//// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
|
||||
//// SipStackImpl stack = (SipStackImpl)up.getSipStack();
|
||||
//// stack.stop();
|
||||
//// Iterator listener = stack.getListeningPoints();
|
||||
//// while (listener.hasNext()) {
|
||||
//// stack.deleteListeningPoint((ListeningPoint) listener.next());
|
||||
//// }
|
||||
//// Iterator providers = stack.getSipProviders();
|
||||
//// while (providers.hasNext()) {
|
||||
//// stack.deleteSipProvider((SipProvider) providers.next());
|
||||
//// }
|
||||
//// VManageBootstrap.restart();
|
||||
//// } catch (InterruptedException | ObjectInUseException e) {
|
||||
//// logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
|
||||
//// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
|
||||
logger.info("[接受deviceControl命令] 命令: {}", deviceControlType);
|
||||
|
||||
@@ -186,9 +186,13 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
// 发送给平台的报警信息。 发送redis通知
|
||||
logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
|
||||
AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
|
||||
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
|
||||
if (deviceAlarm.getAlarmMethod() != null) {
|
||||
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
|
||||
}
|
||||
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
|
||||
alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
|
||||
if (deviceAlarm.getAlarmType() != null) {
|
||||
alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
|
||||
}
|
||||
alarmChannelMessage.setGbId(channelId);
|
||||
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
|
||||
continue;
|
||||
@@ -204,6 +208,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
publisher.deviceAlarmEventPublish(deviceAlarm);
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
|
||||
}
|
||||
}
|
||||
@@ -264,12 +269,15 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
if (channelId.equals(parentPlatform.getDeviceGBId())) {
|
||||
// 发送给平台的报警信息。 发送redis通知
|
||||
AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
|
||||
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
|
||||
if (deviceAlarm.getAlarmMethod() != null) {
|
||||
alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
|
||||
}
|
||||
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
|
||||
alarmChannelMessage.setGbId(channelId);
|
||||
alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
|
||||
if (deviceAlarm.getAlarmType() != null) {
|
||||
alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
|
||||
}
|
||||
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,13 +88,13 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
|
||||
// 对于已经离线的设备判断他的注册是否已经过期
|
||||
if (!deviceService.expire(device)){
|
||||
device.setOnline(0);
|
||||
deviceService.online(device);
|
||||
deviceService.online(device, null);
|
||||
}
|
||||
}
|
||||
// 刷新过期任务
|
||||
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
|
||||
// 如果三次心跳失败,则设置设备离线
|
||||
dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId()), device.getKeepaliveIntervalTime()*1000*3);
|
||||
dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -149,9 +149,10 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
|
||||
redisCatchStorage.sendMobilePositionMsg(jsonObject);
|
||||
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (Exception e) {
|
||||
logger.warn("[移动位置通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
|
||||
logger.warn("[移动位置通知] 发现未处理的异常, \r\n{}", evt.getRequest());
|
||||
logger.error("[移动位置通知] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -60,17 +60,24 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
|
||||
return;
|
||||
}
|
||||
String sn = rootElement.element("SN").getText();
|
||||
|
||||
/*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理
|
||||
大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。
|
||||
我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/
|
||||
|
||||
String channelId = getText(rootElement, "DeviceID");
|
||||
Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
|
||||
if (device ==null){
|
||||
logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
|
||||
return;
|
||||
// 查询这是通道id还是设备id
|
||||
Device device = null;
|
||||
// 如果id指向平台的国标编号,那么就是查询平台的信息
|
||||
if (!parentPlatform.getDeviceGBId().equals(channelId)) {
|
||||
device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
|
||||
if (device ==null){
|
||||
logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
|
||||
cmderFroPlatform.deviceInfoResponse(parentPlatform, device, sn, fromHeader.getTag());
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
|
||||
}
|
||||
|
||||
@@ -132,7 +132,8 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[收到通道] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
|
||||
logger.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest());
|
||||
logger.error("[收到通道] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
@@ -26,7 +24,6 @@ import javax.sip.RequestEvent;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
|
||||
@@ -74,9 +71,9 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
|
||||
}
|
||||
String text = onlineElement.getText();
|
||||
if ("ONLINE".equalsIgnoreCase(text.trim())) {
|
||||
deviceService.online(device);
|
||||
deviceService.online(device, null);
|
||||
}else {
|
||||
deviceService.offline(device.getDeviceId());
|
||||
deviceService.offline(device.getDeviceId(), "设备状态查询结果:" + text.trim());
|
||||
}
|
||||
RequestMessage msg = new RequestMessage();
|
||||
msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
|
||||
|
||||
@@ -142,7 +142,7 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
|
||||
}
|
||||
|
||||
} catch (DocumentException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,8 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("[国标录像] 发现未处理的异常, "+e.getMessage(), e);
|
||||
logger.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest());
|
||||
logger.error("[国标录像] 异常内容: ", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -163,7 +164,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
|
||||
public void releaseRequest(String deviceId, String sn,RecordInfo recordInfo){
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
|
||||
// 对数据进行排序
|
||||
Collections.sort(recordInfo.getRecordList());
|
||||
if(recordInfo!=null && recordInfo.getRecordList()!=null) {
|
||||
Collections.sort(recordInfo.getRecordList());
|
||||
}else{
|
||||
recordInfo.setRecordList(new ArrayList<>());
|
||||
}
|
||||
|
||||
RequestMessage msg = new RequestMessage();
|
||||
msg.setKey(key);
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
@@ -10,6 +11,7 @@ import com.genersoft.iot.vmp.service.IPlatformService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
|
||||
import gov.nist.javax.sip.message.SIPResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -18,7 +20,6 @@ import org.springframework.stereotype.Component;
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.ResponseEvent;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.header.CallIdHeader;
|
||||
import javax.sip.header.WWWAuthenticateHeader;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
@@ -65,9 +66,8 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
*/
|
||||
@Override
|
||||
public void process(ResponseEvent evt) {
|
||||
Response response = evt.getResponse();
|
||||
CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
|
||||
String callId = callIdHeader.getCallId();
|
||||
SIPResponse response = (SIPResponse)evt.getResponse();
|
||||
String callId = response.getCallIdHeader().getCallId();
|
||||
PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId);
|
||||
if (platformRegisterInfo == null) {
|
||||
logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId ));
|
||||
@@ -90,15 +90,17 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
|
||||
|
||||
if (response.getStatusCode() == Response.UNAUTHORIZED) {
|
||||
WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
|
||||
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
|
||||
try {
|
||||
sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true, platformRegisterInfo.isRegister());
|
||||
sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, true, platformRegisterInfo.isRegister());
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage());
|
||||
}
|
||||
}else if (response.getStatusCode() == Response.OK){
|
||||
|
||||
if (platformRegisterInfo.isRegister()) {
|
||||
platformService.online(parentPlatform);
|
||||
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
|
||||
platformService.online(parentPlatform, sipTransactionInfo);
|
||||
}else {
|
||||
platformService.offline(parentPlatform, false);
|
||||
}
|
||||
|
||||
@@ -9,17 +9,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class AssistRESTfulUtils {
|
||||
@@ -137,6 +132,11 @@ public class AssistRESTfulUtils {
|
||||
return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
|
||||
}
|
||||
|
||||
public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
return sendGet(mediaServerItem, "api/record/info",param, callback);
|
||||
}
|
||||
|
||||
public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("app",app);
|
||||
|
||||
@@ -119,10 +119,11 @@ public class ZLMHttpHookListener {
|
||||
* 服务器定时上报时间,上报间隔可配置,默认10s上报一次
|
||||
*/
|
||||
@ResponseBody
|
||||
|
||||
@PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
|
||||
public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
|
||||
|
||||
logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
|
||||
// logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
|
||||
|
||||
taskExecutor.execute(() -> {
|
||||
List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
|
||||
@@ -142,6 +143,7 @@ public class ZLMHttpHookListener {
|
||||
* 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
|
||||
*/
|
||||
@ResponseBody
|
||||
|
||||
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
|
||||
public HookResult onPlay(@RequestBody OnPlayHookParam param) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -264,9 +266,28 @@ public class ZLMHttpHookListener {
|
||||
}
|
||||
|
||||
}
|
||||
if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
|
||||
logger.info("推流时发现尚未设置录像路径,从assist服务中读取");
|
||||
JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
|
||||
if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
|
||||
JSONObject dataJson = info.getJSONObject("data");
|
||||
if (dataJson != null) {
|
||||
String recordPath = dataJson.getString("record");
|
||||
userSetting.setRecordPath(recordPath);
|
||||
result.setMp4_save_path(recordPath);
|
||||
// 修改zlm中的录像路径
|
||||
if (mediaInfo.isAutoConfig()) {
|
||||
taskExecutor.execute(() -> {
|
||||
mediaServerService.setZLMConfig(mediaInfo, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
|
||||
*/
|
||||
@@ -293,8 +314,12 @@ public class ZLMHttpHookListener {
|
||||
subscribe.response(mediaInfo, json);
|
||||
}
|
||||
}
|
||||
// 流消失移除redis play
|
||||
|
||||
List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
|
||||
// TODO 重构此处逻辑
|
||||
|
||||
if (param.isRegist()) {
|
||||
// 处理流注册的鉴权信息
|
||||
if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|
||||
|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|
||||
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
|
||||
|
||||
@@ -334,9 +334,9 @@ public class ZLMRESTfulUtils {
|
||||
sendPost(mediaServerItem, "kick_sessions",param, null);
|
||||
}
|
||||
|
||||
public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
|
||||
public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
|
||||
Map<String, Object> param = new HashMap<>(3);
|
||||
param.put("url", flvUrl);
|
||||
param.put("url", streamUrl);
|
||||
param.put("timeout_sec", timeout_sec);
|
||||
param.put("expire_sec", expire_sec);
|
||||
sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName);
|
||||
|
||||
@@ -262,8 +262,11 @@ public class ZLMRTPServerFactory {
|
||||
logger.info("[保持端口] {}->监听端口到期继续保持监听", ssrc);
|
||||
keepPort(serverItem, ssrc);
|
||||
});
|
||||
}
|
||||
logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
|
||||
logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
|
||||
}else {
|
||||
logger.info("[保持端口] 监听端口失败: {}", ssrc);
|
||||
}
|
||||
return localPort;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,21 +10,87 @@ public class ZLMServerConfig {
|
||||
@JSONField(name = "api.secret")
|
||||
private String apiSecret;
|
||||
|
||||
@JSONField(name = "api.snapRoot")
|
||||
private String apiSnapRoot;
|
||||
|
||||
@JSONField(name = "api.defaultSnap")
|
||||
private String apiDefaultSnap;
|
||||
|
||||
@JSONField(name = "ffmpeg.bin")
|
||||
private String ffmpegBin;
|
||||
|
||||
@JSONField(name = "ffmpeg.cmd")
|
||||
private String ffmpegCmd;
|
||||
|
||||
@JSONField(name = "ffmpeg.snap")
|
||||
private String ffmpegSnap;
|
||||
|
||||
@JSONField(name = "ffmpeg.log")
|
||||
private String ffmpegLog;
|
||||
|
||||
@JSONField(name = "ffmpeg.restart_sec")
|
||||
private String ffmpegRestartSec;
|
||||
|
||||
@JSONField(name = "protocol.modify_stamp")
|
||||
private String protocolModifyStamp;
|
||||
|
||||
@JSONField(name = "protocol.enable_audio")
|
||||
private String protocolEnableAudio;
|
||||
|
||||
@JSONField(name = "protocol.add_mute_audio")
|
||||
private String protocolAddMuteAudio;
|
||||
|
||||
@JSONField(name = "protocol.continue_push_ms")
|
||||
private String protocolContinuePushMs;
|
||||
|
||||
@JSONField(name = "protocol.enable_hls")
|
||||
private String protocolEnableHls;
|
||||
|
||||
@JSONField(name = "protocol.enable_mp4")
|
||||
private String protocolEnableMp4;
|
||||
|
||||
@JSONField(name = "protocol.enable_rtsp")
|
||||
private String protocolEnableRtsp;
|
||||
|
||||
@JSONField(name = "protocol.enable_rtmp")
|
||||
private String protocolEnableRtmp;
|
||||
|
||||
@JSONField(name = "protocol.enable_ts")
|
||||
private String protocolEnableTs;
|
||||
|
||||
@JSONField(name = "protocol.enable_fmp4")
|
||||
private String protocolEnableFmp4;
|
||||
|
||||
@JSONField(name = "protocol.mp4_as_player")
|
||||
private String protocolMp4AsPlayer;
|
||||
|
||||
@JSONField(name = "protocol.mp4_max_second")
|
||||
private String protocolMp4MaxSecond;
|
||||
|
||||
@JSONField(name = "protocol.mp4_save_path")
|
||||
private String protocolMp4SavePath;
|
||||
|
||||
@JSONField(name = "protocol.hls_save_path")
|
||||
private String protocolHlsSavePath;
|
||||
|
||||
@JSONField(name = "protocol.hls_demand")
|
||||
private String protocolHlsDemand;
|
||||
|
||||
@JSONField(name = "protocol.rtsp_demand")
|
||||
private String protocolRtspDemand;
|
||||
|
||||
@JSONField(name = "protocol.rtmp_demand")
|
||||
private String protocolRtmpDemand;
|
||||
|
||||
@JSONField(name = "protocol.ts_demand")
|
||||
private String protocolTsDemand;
|
||||
|
||||
@JSONField(name = "protocol.fmp4_demand")
|
||||
private String protocolFmp4Demand;
|
||||
|
||||
@JSONField(name = "general.enableVhost")
|
||||
private String generalEnableVhost;
|
||||
|
||||
@JSONField(name = "general.mediaServerId")
|
||||
private String generalMediaServerId;
|
||||
|
||||
@JSONField(name = "general.flowThreshold")
|
||||
private String generalFlowThreshold;
|
||||
|
||||
@@ -34,6 +100,25 @@ public class ZLMServerConfig {
|
||||
@JSONField(name = "general.streamNoneReaderDelayMS")
|
||||
private int generalStreamNoneReaderDelayMS;
|
||||
|
||||
@JSONField(name = "general.resetWhenRePlay")
|
||||
private String generalResetWhenRePlay;
|
||||
|
||||
@JSONField(name = "general.mergeWriteMS")
|
||||
private String generalMergeWriteMS;
|
||||
|
||||
@JSONField(name = "general.mediaServerId")
|
||||
private String generalMediaServerId;
|
||||
|
||||
@JSONField(name = "general.wait_track_ready_ms")
|
||||
private String generalWaitTrackReadyMs;
|
||||
|
||||
@JSONField(name = "general.wait_add_track_ms")
|
||||
private String generalWaitAddTrackMs;
|
||||
|
||||
@JSONField(name = "general.unready_frame_cache")
|
||||
private String generalUnreadyFrameCache;
|
||||
|
||||
|
||||
@JSONField(name = "ip")
|
||||
private String ip;
|
||||
|
||||
@@ -59,6 +144,18 @@ public class ZLMServerConfig {
|
||||
@JSONField(name = "hls.segNum")
|
||||
private String hlsSegNum;
|
||||
|
||||
@JSONField(name = "hls.segRetain")
|
||||
private String hlsSegRetain;
|
||||
|
||||
@JSONField(name = "hls.broadcastRecordTs")
|
||||
private String hlsBroadcastRecordTs;
|
||||
|
||||
@JSONField(name = "hls.deleteDelaySec")
|
||||
private String hlsDeleteDelaySec;
|
||||
|
||||
@JSONField(name = "hls.segKeep")
|
||||
private String hlsSegKeep;
|
||||
|
||||
@JSONField(name = "hook.access_file_except_hls")
|
||||
private String hookAccessFileExceptHLS;
|
||||
|
||||
@@ -104,6 +201,18 @@ public class ZLMServerConfig {
|
||||
@JSONField(name = "hook.on_stream_not_found")
|
||||
private String hookOnStreamNotFound;
|
||||
|
||||
@JSONField(name = "hook.on_server_started")
|
||||
private String hookOnServerStarted;
|
||||
|
||||
@JSONField(name = "hook.on_server_keepalive")
|
||||
private String hookOnServerKeepalive;
|
||||
|
||||
@JSONField(name = "hook.on_send_rtp_stopped")
|
||||
private String hookOnSendRtpStopped;
|
||||
|
||||
@JSONField(name = "hook.on_rtp_server_timeout")
|
||||
private String hookOnRtpServerTimeout;
|
||||
|
||||
@JSONField(name = "hook.timeoutSec")
|
||||
private String hookTimeoutSec;
|
||||
|
||||
@@ -813,4 +922,292 @@ public class ZLMServerConfig {
|
||||
public void setPortRange(String portRange) {
|
||||
this.portRange = portRange;
|
||||
}
|
||||
|
||||
public String getApiSnapRoot() {
|
||||
return apiSnapRoot;
|
||||
}
|
||||
|
||||
public void setApiSnapRoot(String apiSnapRoot) {
|
||||
this.apiSnapRoot = apiSnapRoot;
|
||||
}
|
||||
|
||||
public String getApiDefaultSnap() {
|
||||
return apiDefaultSnap;
|
||||
}
|
||||
|
||||
public void setApiDefaultSnap(String apiDefaultSnap) {
|
||||
this.apiDefaultSnap = apiDefaultSnap;
|
||||
}
|
||||
|
||||
public String getFfmpegSnap() {
|
||||
return ffmpegSnap;
|
||||
}
|
||||
|
||||
public void setFfmpegSnap(String ffmpegSnap) {
|
||||
this.ffmpegSnap = ffmpegSnap;
|
||||
}
|
||||
|
||||
public String getFfmpegRestartSec() {
|
||||
return ffmpegRestartSec;
|
||||
}
|
||||
|
||||
public void setFfmpegRestartSec(String ffmpegRestartSec) {
|
||||
this.ffmpegRestartSec = ffmpegRestartSec;
|
||||
}
|
||||
|
||||
public String getProtocolModifyStamp() {
|
||||
return protocolModifyStamp;
|
||||
}
|
||||
|
||||
public void setProtocolModifyStamp(String protocolModifyStamp) {
|
||||
this.protocolModifyStamp = protocolModifyStamp;
|
||||
}
|
||||
|
||||
public String getProtocolEnableAudio() {
|
||||
return protocolEnableAudio;
|
||||
}
|
||||
|
||||
public void setProtocolEnableAudio(String protocolEnableAudio) {
|
||||
this.protocolEnableAudio = protocolEnableAudio;
|
||||
}
|
||||
|
||||
public String getProtocolAddMuteAudio() {
|
||||
return protocolAddMuteAudio;
|
||||
}
|
||||
|
||||
public void setProtocolAddMuteAudio(String protocolAddMuteAudio) {
|
||||
this.protocolAddMuteAudio = protocolAddMuteAudio;
|
||||
}
|
||||
|
||||
public String getProtocolContinuePushMs() {
|
||||
return protocolContinuePushMs;
|
||||
}
|
||||
|
||||
public void setProtocolContinuePushMs(String protocolContinuePushMs) {
|
||||
this.protocolContinuePushMs = protocolContinuePushMs;
|
||||
}
|
||||
|
||||
public String getProtocolEnableHls() {
|
||||
return protocolEnableHls;
|
||||
}
|
||||
|
||||
public void setProtocolEnableHls(String protocolEnableHls) {
|
||||
this.protocolEnableHls = protocolEnableHls;
|
||||
}
|
||||
|
||||
public String getProtocolEnableMp4() {
|
||||
return protocolEnableMp4;
|
||||
}
|
||||
|
||||
public void setProtocolEnableMp4(String protocolEnableMp4) {
|
||||
this.protocolEnableMp4 = protocolEnableMp4;
|
||||
}
|
||||
|
||||
public String getProtocolEnableRtsp() {
|
||||
return protocolEnableRtsp;
|
||||
}
|
||||
|
||||
public void setProtocolEnableRtsp(String protocolEnableRtsp) {
|
||||
this.protocolEnableRtsp = protocolEnableRtsp;
|
||||
}
|
||||
|
||||
public String getProtocolEnableRtmp() {
|
||||
return protocolEnableRtmp;
|
||||
}
|
||||
|
||||
public void setProtocolEnableRtmp(String protocolEnableRtmp) {
|
||||
this.protocolEnableRtmp = protocolEnableRtmp;
|
||||
}
|
||||
|
||||
public String getProtocolEnableTs() {
|
||||
return protocolEnableTs;
|
||||
}
|
||||
|
||||
public void setProtocolEnableTs(String protocolEnableTs) {
|
||||
this.protocolEnableTs = protocolEnableTs;
|
||||
}
|
||||
|
||||
public String getProtocolEnableFmp4() {
|
||||
return protocolEnableFmp4;
|
||||
}
|
||||
|
||||
public void setProtocolEnableFmp4(String protocolEnableFmp4) {
|
||||
this.protocolEnableFmp4 = protocolEnableFmp4;
|
||||
}
|
||||
|
||||
public String getProtocolMp4AsPlayer() {
|
||||
return protocolMp4AsPlayer;
|
||||
}
|
||||
|
||||
public void setProtocolMp4AsPlayer(String protocolMp4AsPlayer) {
|
||||
this.protocolMp4AsPlayer = protocolMp4AsPlayer;
|
||||
}
|
||||
|
||||
public String getProtocolMp4MaxSecond() {
|
||||
return protocolMp4MaxSecond;
|
||||
}
|
||||
|
||||
public void setProtocolMp4MaxSecond(String protocolMp4MaxSecond) {
|
||||
this.protocolMp4MaxSecond = protocolMp4MaxSecond;
|
||||
}
|
||||
|
||||
public String getProtocolMp4SavePath() {
|
||||
return protocolMp4SavePath;
|
||||
}
|
||||
|
||||
public void setProtocolMp4SavePath(String protocolMp4SavePath) {
|
||||
this.protocolMp4SavePath = protocolMp4SavePath;
|
||||
}
|
||||
|
||||
public String getProtocolHlsSavePath() {
|
||||
return protocolHlsSavePath;
|
||||
}
|
||||
|
||||
public void setProtocolHlsSavePath(String protocolHlsSavePath) {
|
||||
this.protocolHlsSavePath = protocolHlsSavePath;
|
||||
}
|
||||
|
||||
public String getProtocolHlsDemand() {
|
||||
return protocolHlsDemand;
|
||||
}
|
||||
|
||||
public void setProtocolHlsDemand(String protocolHlsDemand) {
|
||||
this.protocolHlsDemand = protocolHlsDemand;
|
||||
}
|
||||
|
||||
public String getProtocolRtspDemand() {
|
||||
return protocolRtspDemand;
|
||||
}
|
||||
|
||||
public void setProtocolRtspDemand(String protocolRtspDemand) {
|
||||
this.protocolRtspDemand = protocolRtspDemand;
|
||||
}
|
||||
|
||||
public String getProtocolRtmpDemand() {
|
||||
return protocolRtmpDemand;
|
||||
}
|
||||
|
||||
public void setProtocolRtmpDemand(String protocolRtmpDemand) {
|
||||
this.protocolRtmpDemand = protocolRtmpDemand;
|
||||
}
|
||||
|
||||
public String getProtocolTsDemand() {
|
||||
return protocolTsDemand;
|
||||
}
|
||||
|
||||
public void setProtocolTsDemand(String protocolTsDemand) {
|
||||
this.protocolTsDemand = protocolTsDemand;
|
||||
}
|
||||
|
||||
public String getProtocolFmp4Demand() {
|
||||
return protocolFmp4Demand;
|
||||
}
|
||||
|
||||
public void setProtocolFmp4Demand(String protocolFmp4Demand) {
|
||||
this.protocolFmp4Demand = protocolFmp4Demand;
|
||||
}
|
||||
|
||||
public String getGeneralResetWhenRePlay() {
|
||||
return generalResetWhenRePlay;
|
||||
}
|
||||
|
||||
public void setGeneralResetWhenRePlay(String generalResetWhenRePlay) {
|
||||
this.generalResetWhenRePlay = generalResetWhenRePlay;
|
||||
}
|
||||
|
||||
public String getGeneralMergeWriteMS() {
|
||||
return generalMergeWriteMS;
|
||||
}
|
||||
|
||||
public void setGeneralMergeWriteMS(String generalMergeWriteMS) {
|
||||
this.generalMergeWriteMS = generalMergeWriteMS;
|
||||
}
|
||||
|
||||
public String getGeneralWaitTrackReadyMs() {
|
||||
return generalWaitTrackReadyMs;
|
||||
}
|
||||
|
||||
public void setGeneralWaitTrackReadyMs(String generalWaitTrackReadyMs) {
|
||||
this.generalWaitTrackReadyMs = generalWaitTrackReadyMs;
|
||||
}
|
||||
|
||||
public String getGeneralWaitAddTrackMs() {
|
||||
return generalWaitAddTrackMs;
|
||||
}
|
||||
|
||||
public void setGeneralWaitAddTrackMs(String generalWaitAddTrackMs) {
|
||||
this.generalWaitAddTrackMs = generalWaitAddTrackMs;
|
||||
}
|
||||
|
||||
public String getGeneralUnreadyFrameCache() {
|
||||
return generalUnreadyFrameCache;
|
||||
}
|
||||
|
||||
public void setGeneralUnreadyFrameCache(String generalUnreadyFrameCache) {
|
||||
this.generalUnreadyFrameCache = generalUnreadyFrameCache;
|
||||
}
|
||||
|
||||
public String getHlsSegRetain() {
|
||||
return hlsSegRetain;
|
||||
}
|
||||
|
||||
public void setHlsSegRetain(String hlsSegRetain) {
|
||||
this.hlsSegRetain = hlsSegRetain;
|
||||
}
|
||||
|
||||
public String getHlsBroadcastRecordTs() {
|
||||
return hlsBroadcastRecordTs;
|
||||
}
|
||||
|
||||
public void setHlsBroadcastRecordTs(String hlsBroadcastRecordTs) {
|
||||
this.hlsBroadcastRecordTs = hlsBroadcastRecordTs;
|
||||
}
|
||||
|
||||
public String getHlsDeleteDelaySec() {
|
||||
return hlsDeleteDelaySec;
|
||||
}
|
||||
|
||||
public void setHlsDeleteDelaySec(String hlsDeleteDelaySec) {
|
||||
this.hlsDeleteDelaySec = hlsDeleteDelaySec;
|
||||
}
|
||||
|
||||
public String getHlsSegKeep() {
|
||||
return hlsSegKeep;
|
||||
}
|
||||
|
||||
public void setHlsSegKeep(String hlsSegKeep) {
|
||||
this.hlsSegKeep = hlsSegKeep;
|
||||
}
|
||||
|
||||
public String getHookOnServerStarted() {
|
||||
return hookOnServerStarted;
|
||||
}
|
||||
|
||||
public void setHookOnServerStarted(String hookOnServerStarted) {
|
||||
this.hookOnServerStarted = hookOnServerStarted;
|
||||
}
|
||||
|
||||
public String getHookOnServerKeepalive() {
|
||||
return hookOnServerKeepalive;
|
||||
}
|
||||
|
||||
public void setHookOnServerKeepalive(String hookOnServerKeepalive) {
|
||||
this.hookOnServerKeepalive = hookOnServerKeepalive;
|
||||
}
|
||||
|
||||
public String getHookOnSendRtpStopped() {
|
||||
return hookOnSendRtpStopped;
|
||||
}
|
||||
|
||||
public void setHookOnSendRtpStopped(String hookOnSendRtpStopped) {
|
||||
this.hookOnSendRtpStopped = hookOnSendRtpStopped;
|
||||
}
|
||||
|
||||
public String getHookOnRtpServerTimeout() {
|
||||
return hookOnRtpServerTimeout;
|
||||
}
|
||||
|
||||
public void setHookOnRtpServerTimeout(String hookOnRtpServerTimeout) {
|
||||
this.hookOnRtpServerTimeout = hookOnRtpServerTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ public class HookResultForOnPublish extends HookResult{
|
||||
private boolean enable_audio;
|
||||
private boolean enable_mp4;
|
||||
private int mp4_max_second;
|
||||
private String mp4_save_path;
|
||||
|
||||
public HookResultForOnPublish() {
|
||||
}
|
||||
@@ -41,4 +42,12 @@ public class HookResultForOnPublish extends HookResult{
|
||||
public void setMp4_max_second(int mp4_max_second) {
|
||||
this.mp4_max_second = mp4_max_second;
|
||||
}
|
||||
|
||||
public String getMp4_save_path() {
|
||||
return mp4_save_path;
|
||||
}
|
||||
|
||||
public void setMp4_save_path(String mp4_save_path) {
|
||||
this.mp4_save_path = mp4_save_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.BaseTree;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
|
||||
@@ -18,13 +19,13 @@ public interface IDeviceService {
|
||||
* 设备上线
|
||||
* @param device 设备信息
|
||||
*/
|
||||
void online(Device device);
|
||||
void online(Device device, SipTransactionInfo sipTransactionInfo);
|
||||
|
||||
/**
|
||||
* 设备下线
|
||||
* @param deviceId 设备编号
|
||||
*/
|
||||
void offline(String deviceId);
|
||||
void offline(String deviceId, String reason);
|
||||
|
||||
/**
|
||||
* 添加目录订阅
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
@@ -34,11 +35,17 @@ public interface IPlatformService {
|
||||
*/
|
||||
boolean add(ParentPlatform parentPlatform);
|
||||
|
||||
/**
|
||||
* 添加级联平台
|
||||
* @param parentPlatform 级联平台
|
||||
*/
|
||||
boolean update(ParentPlatform parentPlatform);
|
||||
|
||||
/**
|
||||
* 平台上线
|
||||
* @param parentPlatform 平台信息
|
||||
*/
|
||||
void online(ParentPlatform parentPlatform);
|
||||
void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo);
|
||||
|
||||
/**
|
||||
* 平台离线
|
||||
|
||||
@@ -96,7 +96,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||
|
||||
@Override
|
||||
public void online(Device device) {
|
||||
public void online(Device device, SipTransactionInfo sipTransactionInfo) {
|
||||
logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
|
||||
Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId());
|
||||
Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId());
|
||||
@@ -111,6 +111,14 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
// 默认心跳间隔60
|
||||
device.setKeepaliveIntervalTime(60);
|
||||
}
|
||||
if (sipTransactionInfo != null) {
|
||||
device.setSipTransactionInfo(sipTransactionInfo);
|
||||
}else {
|
||||
if (deviceInRedis != null) {
|
||||
device.setSipTransactionInfo(deviceInRedis.getSipTransactionInfo());
|
||||
}
|
||||
}
|
||||
|
||||
// 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询
|
||||
if (device.getCreateTime() == null) {
|
||||
device.setOnline(1);
|
||||
@@ -163,12 +171,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
// 刷新过期任务
|
||||
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
|
||||
// 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
|
||||
dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getKeepaliveIntervalTime() * 1000 * 3);
|
||||
dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offline(String deviceId) {
|
||||
logger.error("[设备离线], device:{}", deviceId);
|
||||
public void offline(String deviceId, String reason) {
|
||||
logger.error("[设备离线],{}, device:{}", reason, deviceId);
|
||||
Device device = deviceMapper.getDeviceByDeviceId(deviceId);
|
||||
if (device == null) {
|
||||
return;
|
||||
|
||||
@@ -40,6 +40,9 @@ public class GbStreamServiceImpl implements IGbStreamService {
|
||||
@Autowired
|
||||
private PlatformGbStreamMapper platformGbStreamMapper;
|
||||
|
||||
@Autowired
|
||||
private SubscribeHolder subscribeHolder;
|
||||
|
||||
@Autowired
|
||||
private ParentPlatformMapper platformMapper;
|
||||
|
||||
@@ -73,16 +76,23 @@ public class GbStreamServiceImpl implements IGbStreamService {
|
||||
}
|
||||
try {
|
||||
List<DeviceChannel> deviceChannelList = new ArrayList<>();
|
||||
for (GbStream gbStream : gbStreams) {
|
||||
|
||||
|
||||
for (int i = 0; i < gbStreams.size(); i++) {
|
||||
GbStream gbStream = gbStreams.get(i);
|
||||
gbStream.setCatalogId(catalogId);
|
||||
gbStream.setPlatformId(platformId);
|
||||
// TODO 修改为批量提交
|
||||
platformGbStreamMapper.add(gbStream);
|
||||
logger.info("[关联通道]直播流通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, gbStreams.size(), i + 1);
|
||||
DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform);
|
||||
deviceChannelList.add(deviceChannelListByStream);
|
||||
}
|
||||
dataSourceTransactionManager.commit(transactionStatus); //手动提交
|
||||
eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
|
||||
if (subscribeHolder.getCatalogSubscribe(platformId) != null) {
|
||||
eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
|
||||
}
|
||||
|
||||
result = true;
|
||||
}catch (Exception e) {
|
||||
logger.error("批量保存流与平台的关系时错误", e);
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
|
||||
@@ -38,6 +39,7 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@@ -63,6 +65,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private AssistRESTfulUtils assistRESTfulUtils;
|
||||
|
||||
@Autowired
|
||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||
|
||||
@@ -409,13 +414,27 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
}
|
||||
RedisUtil.set(key, serverItem);
|
||||
resetOnlineServerItem(serverItem);
|
||||
|
||||
|
||||
if (serverItem.isAutoConfig()) {
|
||||
// 查看assist服务的录像路径配置
|
||||
if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
|
||||
JSONObject info = assistRESTfulUtils.getInfo(serverItem, null);
|
||||
if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
|
||||
JSONObject dataJson = info.getJSONObject("data");
|
||||
if (dataJson != null) {
|
||||
String recordPath = dataJson.getString("record");
|
||||
userSetting.setRecordPath(recordPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
|
||||
}
|
||||
final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
|
||||
dynamicTask.stop(zlmKeepaliveKey);
|
||||
dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000);
|
||||
publisher.zlmOnlineEventPublish(serverItem.getId());
|
||||
|
||||
logger.info("[ZLM] 连接成功 {} - {}:{} ",
|
||||
zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
|
||||
}
|
||||
@@ -549,6 +568,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
|
||||
if (mediaServerItem.getRtspPort() != 0) {
|
||||
param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %s");
|
||||
}
|
||||
param.put("hook.enable","1");
|
||||
param.put("hook.on_flow_report","");
|
||||
param.put("hook.on_play",String.format("%s/on_play", hookPrex));
|
||||
@@ -583,6 +605,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
|
||||
}
|
||||
|
||||
if (userSetting.getRecordPath() != null) {
|
||||
File recordPathFile = new File(userSetting.getRecordPath());
|
||||
File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile();
|
||||
param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile());
|
||||
param.put("record.appName", recordPathFile.getName());
|
||||
}
|
||||
|
||||
JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
|
||||
|
||||
if (responseJSON != null && responseJSON.getInteger("code") == 0) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.TreeType;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
|
||||
import com.genersoft.iot.vmp.service.IPlatformChannelService;
|
||||
@@ -15,7 +12,10 @@ import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -34,6 +34,16 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
|
||||
@Autowired
|
||||
private PlatformChannelMapper platformChannelMapper;
|
||||
|
||||
@Autowired
|
||||
TransactionDefinition transactionDefinition;
|
||||
|
||||
@Autowired
|
||||
DataSourceTransactionManager dataSourceTransactionManager;
|
||||
|
||||
@Autowired
|
||||
private SubscribeHolder subscribeHolder;
|
||||
|
||||
|
||||
@Autowired
|
||||
private DeviceChannelMapper deviceChannelMapper;
|
||||
|
||||
@@ -69,17 +79,47 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
|
||||
}
|
||||
List<ChannelReduce> channelReducesToAdd = new ArrayList<>(deviceAndChannels.values());
|
||||
// 对剩下的数据进行存储
|
||||
int result = 0;
|
||||
int allCount = 0;
|
||||
boolean result = false;
|
||||
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
|
||||
int limitCount = 300;
|
||||
if (channelReducesToAdd.size() > 0) {
|
||||
result = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
|
||||
// TODO 后续给平台增加控制开关以控制是否响应目录订阅
|
||||
List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
|
||||
if (deviceChannelList != null) {
|
||||
eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
|
||||
if (channelReducesToAdd.size() > limitCount) {
|
||||
for (int i = 0; i < channelReducesToAdd.size(); i += limitCount) {
|
||||
int toIndex = i + limitCount;
|
||||
if (i + limitCount > channelReducesToAdd.size()) {
|
||||
toIndex = channelReducesToAdd.size();
|
||||
}
|
||||
int count = platformChannelMapper.addChannels(platformId, channelReducesToAdd.subList(i, toIndex));
|
||||
result = result || count < 0;
|
||||
allCount += count;
|
||||
logger.info("[关联通道]国标通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, channelReducesToAdd.size(), toIndex);
|
||||
}
|
||||
}else {
|
||||
allCount = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
|
||||
result = result || allCount < 0;
|
||||
logger.info("[关联通道]国标通道 平台:{}, 关联通道数:{}", platformId, channelReducesToAdd.size());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
if (result) {
|
||||
//事务回滚
|
||||
dataSourceTransactionManager.rollback(transactionStatus);
|
||||
allCount = 0;
|
||||
}else {
|
||||
logger.info("[关联通道]国标通道 平台:{}, 正在存入数据库", platformId);
|
||||
dataSourceTransactionManager.commit(transactionStatus);
|
||||
|
||||
}
|
||||
SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platformId);
|
||||
if (catalogSubscribe != null) {
|
||||
List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
|
||||
if (deviceChannelList != null) {
|
||||
eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
|
||||
}
|
||||
}
|
||||
logger.info("[关联通道]国标通道 平台:{}, 存入数据库成功", platformId);
|
||||
}
|
||||
return allCount;
|
||||
}
|
||||
|
||||
private List<DeviceChannel> getDeviceChannelListByChannelReduceList(List<ChannelReduce> channelReduces, String catalogId, ParentPlatform platform) {
|
||||
|
||||
@@ -21,8 +21,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
|
||||
import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
|
||||
import com.genersoft.iot.vmp.storager.dao.*;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
@@ -53,6 +53,15 @@ public class PlatformServiceImpl implements IPlatformService {
|
||||
@Autowired
|
||||
private ParentPlatformMapper platformMapper;
|
||||
|
||||
@Autowired
|
||||
private PlatformCatalogMapper catalogMapper;
|
||||
|
||||
@Autowired
|
||||
private PlatformChannelMapper platformChannelMapper;
|
||||
|
||||
@Autowired
|
||||
private PlatformGbStreamMapper platformGbStreamMapper;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@@ -135,36 +144,106 @@ public class PlatformServiceImpl implements IPlatformService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void online(ParentPlatform parentPlatform) {
|
||||
logger.info("[国标级联]:{}, 平台上线/更新注册", parentPlatform.getServerGBId());
|
||||
public boolean update(ParentPlatform parentPlatform) {
|
||||
logger.info("[国标级联]更新平台 {}", parentPlatform.getDeviceGBId());
|
||||
parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
|
||||
ParentPlatform parentPlatformOld = platformMapper.getParentPlatById(parentPlatform.getId());
|
||||
ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatformOld.getServerGBId());
|
||||
parentPlatform.setUpdateTime(DateUtil.getNow());
|
||||
if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
|
||||
// 目录结构发生变化,清空之前的关联关系
|
||||
logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
|
||||
catalogMapper.delByPlatformId(parentPlatformOld.getServerGBId());
|
||||
platformChannelMapper.delByPlatformId(parentPlatformOld.getServerGBId());
|
||||
platformGbStreamMapper.delByPlatformId(parentPlatformOld.getServerGBId());
|
||||
}
|
||||
|
||||
|
||||
// 停止心跳定时
|
||||
final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatformOld.getServerGBId();
|
||||
dynamicTask.stop(keepaliveTaskKey);
|
||||
// 停止注册定时
|
||||
final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatformOld.getServerGBId();
|
||||
dynamicTask.stop(registerTaskKey);
|
||||
// 注销旧的
|
||||
try {
|
||||
if (parentPlatformOld.isStatus()) {
|
||||
logger.info("保存平台{}时发现救平台在线,发送注销命令", parentPlatform.getDeviceGBId());
|
||||
commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> {
|
||||
logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId());
|
||||
});
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
if (parentPlatform.getCatalogGroup() == 0) {
|
||||
parentPlatform.setCatalogGroup(1);
|
||||
}
|
||||
if (parentPlatform.getAdministrativeDivision() == null) {
|
||||
parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision());
|
||||
}
|
||||
|
||||
platformMapper.updateParentPlatform(parentPlatform);
|
||||
// 更新redis
|
||||
redisCatchStorage.delPlatformCatchInfo(parentPlatformOld.getServerGBId());
|
||||
ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
|
||||
parentPlatformCatch.setParentPlatform(parentPlatform);
|
||||
parentPlatformCatch.setId(parentPlatform.getServerGBId());
|
||||
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
|
||||
// 注册
|
||||
if (parentPlatform.isEnable()) {
|
||||
// 保存时启用就发送注册
|
||||
// 注册成功时由程序直接调用了online方法
|
||||
try {
|
||||
commanderForPlatform.register(parentPlatform, eventResult -> {
|
||||
logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId());
|
||||
}, null);
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
// 重新开启定时注册, 使用续订消息
|
||||
// 重新开始心跳保活
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) {
|
||||
logger.info("[国标级联]:{}, 平台上线", parentPlatform.getServerGBId());
|
||||
platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true);
|
||||
ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
|
||||
if (parentPlatformCatch != null) {
|
||||
parentPlatformCatch.getParentPlatform().setStatus(true);
|
||||
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
|
||||
}else {
|
||||
if (parentPlatformCatch == null) {
|
||||
parentPlatformCatch = new ParentPlatformCatch();
|
||||
parentPlatformCatch.setParentPlatform(parentPlatform);
|
||||
parentPlatformCatch.setId(parentPlatform.getServerGBId());
|
||||
parentPlatform.setStatus(true);
|
||||
parentPlatformCatch.setParentPlatform(parentPlatform);
|
||||
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
|
||||
}
|
||||
|
||||
parentPlatformCatch.getParentPlatform().setStatus(true);
|
||||
parentPlatformCatch.setSipTransactionInfo(sipTransactionInfo);
|
||||
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
|
||||
|
||||
final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId();
|
||||
if (!dynamicTask.isAlive(registerTaskKey)) {
|
||||
logger.info("[国标级联]:{}, 添加定时注册任务", parentPlatform.getServerGBId());
|
||||
// 添加注册任务
|
||||
dynamicTask.startCron(registerTaskKey,
|
||||
// 注册失败(注册成功时由程序直接调用了online方法)
|
||||
()-> {
|
||||
registerTask(parentPlatform);
|
||||
},
|
||||
(parentPlatform.getExpires() - 10) *1000);
|
||||
()-> registerTask(parentPlatform, sipTransactionInfo),
|
||||
parentPlatform.getExpires() * 1000);
|
||||
}
|
||||
|
||||
|
||||
final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId();
|
||||
if (!dynamicTask.contains(keepaliveTaskKey)) {
|
||||
logger.info("[国标级联]:{}, 添加定时心跳任务", parentPlatform.getServerGBId());
|
||||
// 添加心跳任务
|
||||
dynamicTask.startCron(keepaliveTaskKey,
|
||||
()-> {
|
||||
@@ -205,11 +284,11 @@ public class PlatformServiceImpl implements IPlatformService {
|
||||
logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage());
|
||||
}
|
||||
},
|
||||
(parentPlatform.getKeepTimeout() - 10)*1000);
|
||||
(parentPlatform.getKeepTimeout())*1000);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTask(ParentPlatform parentPlatform){
|
||||
private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){
|
||||
try {
|
||||
// 设置超时重发, 后续从底层支持消息重发
|
||||
String key = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId() + "_timeout";
|
||||
@@ -217,10 +296,10 @@ public class PlatformServiceImpl implements IPlatformService {
|
||||
return;
|
||||
}
|
||||
dynamicTask.startDelay(key, ()->{
|
||||
registerTask(parentPlatform);
|
||||
registerTask(parentPlatform, sipTransactionInfo);
|
||||
}, 1000);
|
||||
logger.info("[国标级联] 平台:{}注册即将到期,重新注册", parentPlatform.getServerGBId());
|
||||
commanderForPlatform.register(parentPlatform, eventResult -> {
|
||||
logger.info("[国标级联] 平台:{}注册即将到期,开始续订", parentPlatform.getServerGBId());
|
||||
commanderForPlatform.register(parentPlatform, sipTransactionInfo, eventResult -> {
|
||||
dynamicTask.stop(key);
|
||||
offline(parentPlatform, false);
|
||||
},eventResult -> {
|
||||
|
||||
@@ -438,7 +438,12 @@ public class PlayServiceImpl implements IPlayService {
|
||||
onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
|
||||
hookEvent.response(mediaServerItemInuse, response);
|
||||
logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
String streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.flv", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
|
||||
String streamUrl;
|
||||
if (mediaServerItemInuse.getRtspPort() != 0) {
|
||||
streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream());
|
||||
}else {
|
||||
streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
|
||||
}
|
||||
String path = "snap";
|
||||
String fileName = device.getDeviceId() + "_" + channelId + ".jpg";
|
||||
// 请求截图
|
||||
@@ -806,23 +811,75 @@ public class PlayServiceImpl implements IPlayService {
|
||||
hookCallBack.call(downloadResult);
|
||||
streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
|
||||
};
|
||||
|
||||
InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
|
||||
logger.info("收到订阅消息: " + inviteStreamInfo.getCallId());
|
||||
dynamicTask.stop(downLoadTimeOutTaskKey);
|
||||
StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
|
||||
streamInfo.setStartTime(startTime);
|
||||
streamInfo.setEndTime(endTime);
|
||||
redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
|
||||
downloadResult.setCode(ErrorCode.SUCCESS.getCode());
|
||||
downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
|
||||
downloadResult.setData(streamInfo);
|
||||
downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
|
||||
downloadResult.setResponse(inviteStreamInfo.getResponse());
|
||||
hookCallBack.call(downloadResult);
|
||||
};
|
||||
try {
|
||||
cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
|
||||
inviteStreamInfo -> {
|
||||
logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
|
||||
dynamicTask.stop(downLoadTimeOutTaskKey);
|
||||
StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
|
||||
streamInfo.setStartTime(startTime);
|
||||
streamInfo.setEndTime(endTime);
|
||||
redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
|
||||
downloadResult.setCode(ErrorCode.SUCCESS.getCode());
|
||||
downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
|
||||
downloadResult.setData(streamInfo);
|
||||
downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
|
||||
downloadResult.setResponse(inviteStreamInfo.getResponse());
|
||||
hookCallBack.call(downloadResult);
|
||||
}, errorEvent);
|
||||
hookEvent, errorEvent, eventResult ->
|
||||
{
|
||||
if (eventResult.type == SipSubscribe.EventResultType.response) {
|
||||
ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
|
||||
String contentString = new String(responseEvent.getResponse().getRawContent());
|
||||
// 获取ssrc
|
||||
int ssrcIndex = contentString.indexOf("y=");
|
||||
// 检查是否有y字段
|
||||
if (ssrcIndex >= 0) {
|
||||
//ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
|
||||
String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
|
||||
// 查询到ssrc不一致且开启了ssrc校验则需要针对处理
|
||||
if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
|
||||
return;
|
||||
}
|
||||
logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
|
||||
if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
|
||||
logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
|
||||
|
||||
if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
|
||||
// ssrc 不可用
|
||||
// 释放ssrc
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
|
||||
streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
|
||||
eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
|
||||
eventResult.statusCode = 400;
|
||||
errorEvent.response(eventResult);
|
||||
return;
|
||||
}
|
||||
|
||||
// 单端口模式streamId也有变化,需要重新设置监听
|
||||
if (!mediaServerItem.isRtpEnable()) {
|
||||
// 添加订阅
|
||||
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
|
||||
subscribe.removeSubscribe(hookSubscribe);
|
||||
hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
|
||||
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
|
||||
logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
|
||||
dynamicTask.stop(downLoadTimeOutTaskKey);
|
||||
// hook响应
|
||||
onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, hookCallBack);
|
||||
hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
|
||||
});
|
||||
}
|
||||
// 关闭rtp server
|
||||
mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
|
||||
// 重新开启ssrc server
|
||||
mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
} catch (InvalidArgumentException | SipException | ParseException e) {
|
||||
logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
||||
dataSourceTransactionManager.commit(transactionStatus); //手动提交
|
||||
result = true;
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
dataSourceTransactionManager.rollback(transactionStatus);
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.service.redisMsg;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
@@ -44,13 +45,17 @@ public class RedisAlarmMsgListener implements MessageListener {
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Override
|
||||
public void onMessage(@NotNull Message message, byte[] bytes) {
|
||||
// 消息示例: PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }'
|
||||
logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody()));
|
||||
boolean isEmpty = taskQueue.isEmpty();
|
||||
taskQueue.offer(message);
|
||||
if (isEmpty) {
|
||||
logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
|
||||
// logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
|
||||
taskExecutor.execute(() -> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
Message msg = taskQueue.poll();
|
||||
@@ -69,22 +74,52 @@ public class RedisAlarmMsgListener implements MessageListener {
|
||||
deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
|
||||
deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
|
||||
deviceAlarm.setAlarmPriority("1");
|
||||
deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
|
||||
deviceAlarm.setAlarmTime(DateUtil.getNow());
|
||||
deviceAlarm.setLongitude(0);
|
||||
deviceAlarm.setLatitude(0);
|
||||
|
||||
if (ObjectUtils.isEmpty(gbId)) {
|
||||
// 发送给所有的上级
|
||||
List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
|
||||
if (parentPlatforms.size() > 0) {
|
||||
for (ParentPlatform parentPlatform : parentPlatforms) {
|
||||
if (userSetting.getSendToPlatformsWhenIdLost()) {
|
||||
// 发送给所有的上级
|
||||
List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
|
||||
if (parentPlatforms.size() > 0) {
|
||||
for (ParentPlatform parentPlatform : parentPlatforms) {
|
||||
try {
|
||||
deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
|
||||
commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
// 获取开启了消息推送的设备和平台
|
||||
List<ParentPlatform> parentPlatforms = storage.queryEnablePlatformListWithAsMessageChannel();
|
||||
if (parentPlatforms.size() > 0) {
|
||||
for (ParentPlatform parentPlatform : parentPlatforms) {
|
||||
try {
|
||||
deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
|
||||
commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// 获取开启了消息推送的设备和平台
|
||||
List<Device> devices = storage.queryDeviceWithAsMessageChannel();
|
||||
if (devices.size() > 0) {
|
||||
for (Device device : devices) {
|
||||
try {
|
||||
commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
|
||||
deviceAlarm.setChannelId(device.getDeviceId());
|
||||
commander.sendAlarmMessage(device, deviceAlarm);
|
||||
} catch (InvalidArgumentException | SipException | ParseException e) {
|
||||
logger.error("[命令发送失败] 发送报警: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
Device device = storage.queryVideoDevice(gbId);
|
||||
ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
|
||||
@@ -105,6 +140,7 @@ public class RedisAlarmMsgListener implements MessageListener {
|
||||
}
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
|
||||
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[RedisGbPlayMsg] 发现未处理的异常, {}",e.getMessage());
|
||||
logger.warn("[RedisGbPlayMsg] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[RedisGbPlayMsg] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,7 +53,8 @@ public class RedisGpsMsgListener implements MessageListener {
|
||||
// 只是放入redis缓存起来
|
||||
redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
|
||||
}catch (Exception e) {
|
||||
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
|
||||
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[REDIS的ALARM通知] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,7 +58,8 @@ public class RedisPushStreamResponseListener implements MessageListener {
|
||||
responseEvents.get(response.getApp() + response.getStream()).run(response);
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
|
||||
logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[REDIS消息-请求推流结果] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -95,7 +95,8 @@ public class RedisPushStreamStatusListMsgListener implements MessageListener {
|
||||
gbStreamService.updateGbIdOrName(streamPushItemForUpdate);
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
|
||||
logger.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[REDIS消息-推流设备列表更新] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -79,7 +79,8 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
|
||||
streamPushService.online(statusChangeFromPushStream.getOnlineStreams());
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
|
||||
logger.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[REDIS消息-推流设备状态变化] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -82,7 +82,8 @@ public class RedisStreamMsgListener implements MessageListener {
|
||||
zlmMediaListManager.removeMedia(app, stream);
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
|
||||
logger.warn("[REDIS消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[REDIS消息-流变化] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||
import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
import java.util.List;
|
||||
@@ -58,7 +59,7 @@ public interface IVideoManagerStorage {
|
||||
*/
|
||||
public PageInfo<DeviceChannel> queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count);
|
||||
|
||||
public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds);
|
||||
public List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List<String> channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit);
|
||||
|
||||
|
||||
/**
|
||||
@@ -374,4 +375,10 @@ public interface IVideoManagerStorage {
|
||||
void cleanContentForPlatform(String serverGBId);
|
||||
|
||||
List<DeviceChannel> queryChannelWithCatalog(String serverGBId);
|
||||
|
||||
List<DeviceChannelExtend> queryChannelsByDeviceId(String serial, List<String> channelIds, Boolean online);
|
||||
|
||||
List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel();
|
||||
|
||||
List<Device> queryDeviceWithAsMessageChannel();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||
import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@@ -82,7 +83,56 @@ public interface DeviceChannelMapper {
|
||||
"</foreach> </if>" +
|
||||
"ORDER BY dc.channelId " +
|
||||
" </script>"})
|
||||
List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online,List<String> channelIds);
|
||||
List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List<String> channelIds);
|
||||
|
||||
@Select(value = {" <script>" +
|
||||
"SELECT " +
|
||||
"dc.*, " +
|
||||
"de.name as deviceName, " +
|
||||
"de.online as deviceOnline " +
|
||||
"from " +
|
||||
"device_channel dc " +
|
||||
"LEFT JOIN device de ON dc.deviceId = de.deviceId " +
|
||||
"WHERE 1=1" +
|
||||
" <if test='deviceId != null'> AND dc.deviceId = #{deviceId} </if> " +
|
||||
" <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
|
||||
" <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " +
|
||||
" <if test='online == true' > AND dc.status=1</if>" +
|
||||
" <if test='online == false' > AND dc.status=0</if>" +
|
||||
" <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" +
|
||||
" <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" +
|
||||
"<if test='channelIds != null'> AND dc.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
|
||||
"#{item} " +
|
||||
"</foreach> </if>" +
|
||||
"ORDER BY dc.channelId ASC" +
|
||||
" </script>"})
|
||||
List<DeviceChannelExtend> queryChannelsWithDeviceInfo(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List<String> channelIds);
|
||||
|
||||
|
||||
@Select(value = {" <script>" +
|
||||
"SELECT " +
|
||||
"dc.*, " +
|
||||
"de.name as deviceName, " +
|
||||
"de.online as deviceOnline " +
|
||||
"from " +
|
||||
"device_channel dc " +
|
||||
"LEFT JOIN device de ON dc.deviceId = de.deviceId " +
|
||||
"WHERE 1=1" +
|
||||
" <if test='deviceId != null'> AND dc.deviceId = #{deviceId} </if> " +
|
||||
" <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
|
||||
" <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " +
|
||||
" <if test='online == true' > AND dc.status=1</if>" +
|
||||
" <if test='online == false' > AND dc.status=0</if>" +
|
||||
" <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" +
|
||||
" <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" +
|
||||
"<if test='channelIds != null'> AND dc.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
|
||||
"#{item} " +
|
||||
"</foreach> </if>" +
|
||||
"ORDER BY dc.channelId ASC " +
|
||||
"Limit #{limit} OFFSET #{start}" +
|
||||
" </script>"})
|
||||
List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId,List<String> channelIds, String parentChannelId, String query,
|
||||
Boolean hasSubChannel, Boolean online, int start, int limit);
|
||||
|
||||
@Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}")
|
||||
DeviceChannel queryChannel(String deviceId, String channelId);
|
||||
@@ -245,28 +295,6 @@ public interface DeviceChannelMapper {
|
||||
int batchUpdate(List<DeviceChannel> updateChannels);
|
||||
|
||||
|
||||
@Select(value = {" <script>" +
|
||||
"SELECT " +
|
||||
"dc1.* " +
|
||||
"from " +
|
||||
"device_channel dc1 " +
|
||||
"WHERE " +
|
||||
"dc1.deviceId = #{deviceId} " +
|
||||
" <if test='query != null'> AND (dc1.channelId LIKE concat('%',#{query},'%') OR dc1.name LIKE concat('%',#{query},'%') OR dc1.name LIKE concat('%',#{query},'%'))</if> " +
|
||||
" <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " +
|
||||
" <if test='online == true' > AND dc1.status=1</if>" +
|
||||
" <if test='online == false' > AND dc1.status=0</if>" +
|
||||
" <if test='hasSubChannel == true' > AND dc1.subCount >0</if>" +
|
||||
" <if test='hasSubChannel == false' > AND dc1.subCount=0</if>" +
|
||||
"<if test='channelIds != null'> AND dc1.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
|
||||
"#{item} " +
|
||||
"</foreach> </if>" +
|
||||
"ORDER BY dc1.channelId ASC " +
|
||||
"Limit #{limit} OFFSET #{start}" +
|
||||
" </script>"})
|
||||
List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String parentChannelId, String query,
|
||||
Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds);
|
||||
|
||||
@Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
|
||||
List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId);
|
||||
|
||||
@@ -316,10 +344,10 @@ public interface DeviceChannelMapper {
|
||||
"select * " +
|
||||
"from device_channel " +
|
||||
"where deviceId=#{deviceId}" +
|
||||
" <if test='parentId != null and length != null' > and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} and length(channelId)=#{length} </if>" +
|
||||
" <if test='parentId != null and length != null' > and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} and length(channelId)=#{length} </if>" +
|
||||
" <if test='parentId == null and length != null' > and parentId = #{parentId} or length(channelId)=#{length} </if>" +
|
||||
" <if test='parentId == null and length == null' > and parentId = #{parentId} </if>" +
|
||||
" <if test='parentId != null and length == null' > and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} </if>" +
|
||||
" <if test='parentId != null and length == null' > and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} </if>" +
|
||||
" </script>"})
|
||||
List<DeviceChannel> getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length);
|
||||
|
||||
|
||||
@@ -39,9 +39,12 @@ public interface DeviceMapper {
|
||||
"mobilePositionSubmissionInterval," +
|
||||
"subscribeCycleForAlarm," +
|
||||
"ssrcCheck," +
|
||||
"asMessageChannel," +
|
||||
"geoCoordSys," +
|
||||
"treeType," +
|
||||
"online" +
|
||||
"online," +
|
||||
"mediaServerId," +
|
||||
"(SELECT count(0) FROM device_channel WHERE deviceId=device.deviceId) as channelCount "+
|
||||
" FROM device WHERE deviceId = #{deviceId}")
|
||||
Device getDeviceByDeviceId(String deviceId);
|
||||
|
||||
@@ -70,6 +73,7 @@ public interface DeviceMapper {
|
||||
"mobilePositionSubmissionInterval," +
|
||||
"subscribeCycleForAlarm," +
|
||||
"ssrcCheck," +
|
||||
"asMessageChannel," +
|
||||
"geoCoordSys," +
|
||||
"treeType," +
|
||||
"online" +
|
||||
@@ -98,6 +102,7 @@ public interface DeviceMapper {
|
||||
"#{mobilePositionSubmissionInterval}," +
|
||||
"#{subscribeCycleForAlarm}," +
|
||||
"#{ssrcCheck}," +
|
||||
"#{asMessageChannel}," +
|
||||
"#{geoCoordSys}," +
|
||||
"#{treeType}," +
|
||||
"#{online}" +
|
||||
@@ -152,9 +157,11 @@ public interface DeviceMapper {
|
||||
"mobilePositionSubmissionInterval," +
|
||||
"subscribeCycleForAlarm," +
|
||||
"ssrcCheck," +
|
||||
"asMessageChannel," +
|
||||
"geoCoordSys," +
|
||||
"treeType," +
|
||||
"online," +
|
||||
"mediaServerId," +
|
||||
"(SELECT count(0) FROM device_channel WHERE deviceId=de.deviceId) as channelCount FROM device de" +
|
||||
"<if test=\"online != null\"> where online=${online}</if>"+
|
||||
" </script>"
|
||||
@@ -164,9 +171,6 @@ public interface DeviceMapper {
|
||||
@Delete("DELETE FROM device WHERE deviceId=#{deviceId}")
|
||||
int del(String deviceId);
|
||||
|
||||
@Update("UPDATE device SET online=0")
|
||||
int outlineForAll();
|
||||
|
||||
@Select("SELECT " +
|
||||
"deviceId, " +
|
||||
"coalesce(custom_name, name) as name, " +
|
||||
@@ -192,6 +196,7 @@ public interface DeviceMapper {
|
||||
"mobilePositionSubmissionInterval," +
|
||||
"subscribeCycleForAlarm," +
|
||||
"ssrcCheck," +
|
||||
"asMessageChannel," +
|
||||
"geoCoordSys," +
|
||||
"treeType," +
|
||||
"online " +
|
||||
@@ -222,6 +227,7 @@ public interface DeviceMapper {
|
||||
"mobilePositionSubmissionInterval," +
|
||||
"subscribeCycleForAlarm," +
|
||||
"ssrcCheck," +
|
||||
"asMessageChannel," +
|
||||
"geoCoordSys," +
|
||||
"treeType," +
|
||||
"online" +
|
||||
@@ -243,12 +249,13 @@ public interface DeviceMapper {
|
||||
"<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=#{mobilePositionSubmissionInterval}</if>" +
|
||||
"<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=#{subscribeCycleForAlarm}</if>" +
|
||||
"<if test=\"ssrcCheck != null\">, ssrcCheck=#{ssrcCheck}</if>" +
|
||||
"<if test=\"asMessageChannel != null\">, asMessageChannel=#{asMessageChannel}</if>" +
|
||||
"<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
|
||||
"<if test=\"treeType != null\">, treeType=#{treeType}</if>" +
|
||||
"<if test=\"mediaServerId != null\">, mediaServerId=#{mediaServerId}</if>" +
|
||||
"WHERE deviceId=#{deviceId}"+
|
||||
" </script>"})
|
||||
int updateCustom(Device device);
|
||||
void updateCustom(Device device);
|
||||
|
||||
@Insert("INSERT INTO device (" +
|
||||
"deviceId, " +
|
||||
@@ -259,9 +266,11 @@ public interface DeviceMapper {
|
||||
"updateTime," +
|
||||
"charset," +
|
||||
"ssrcCheck," +
|
||||
"asMessageChannel," +
|
||||
"geoCoordSys," +
|
||||
"treeType," +
|
||||
"online" +
|
||||
"online," +
|
||||
"mediaServerId" +
|
||||
") VALUES (" +
|
||||
"#{deviceId}," +
|
||||
"#{name}," +
|
||||
@@ -271,9 +280,11 @@ public interface DeviceMapper {
|
||||
"#{updateTime}," +
|
||||
"#{charset}," +
|
||||
"#{ssrcCheck}," +
|
||||
"#{asMessageChannel}," +
|
||||
"#{geoCoordSys}," +
|
||||
"#{treeType}," +
|
||||
"#{online}" +
|
||||
"#{online}," +
|
||||
"#{mediaServerId}" +
|
||||
")")
|
||||
void addCustomDevice(Device device);
|
||||
|
||||
@@ -282,4 +293,7 @@ public interface DeviceMapper {
|
||||
|
||||
@Select("select * from device")
|
||||
List<Device> getAll();
|
||||
|
||||
@Select("select * from device where asMessageChannel = 1")
|
||||
List<Device> queryDeviceWithAsMessageChannel();
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ import java.util.List;
|
||||
public interface ParentPlatformMapper {
|
||||
|
||||
@Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " +
|
||||
" devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " +
|
||||
" devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, asMessageChannel, " +
|
||||
" status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " +
|
||||
" VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " +
|
||||
" #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, " +
|
||||
" #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, " +
|
||||
" #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})")
|
||||
int addParentPlatform(ParentPlatform parentPlatform);
|
||||
|
||||
@@ -40,6 +40,7 @@ public interface ParentPlatformMapper {
|
||||
"characterSet=#{characterSet}, " +
|
||||
"ptz=#{ptz}, " +
|
||||
"rtcp=#{rtcp}, " +
|
||||
"asMessageChannel=#{asMessageChannel}, " +
|
||||
"status=#{status}, " +
|
||||
"startOfflinePush=#{startOfflinePush}, " +
|
||||
"catalogGroup=#{catalogGroup}, " +
|
||||
@@ -68,9 +69,12 @@ public interface ParentPlatformMapper {
|
||||
"FROM parent_platform pp ")
|
||||
List<ParentPlatform> getParentPlatformList();
|
||||
|
||||
@Select("SELECT * FROM parent_platform WHERE enable=#{enable}")
|
||||
@Select("SELECT * FROM parent_platform WHERE enable=#{enable} ")
|
||||
List<ParentPlatform> getEnableParentPlatformList(boolean enable);
|
||||
|
||||
@Select("SELECT * FROM parent_platform WHERE enable=1 and asMessageChannel = 1")
|
||||
List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel();
|
||||
|
||||
@Select("SELECT * FROM parent_platform WHERE serverGBId=#{platformGbId}")
|
||||
ParentPlatform getParentPlatByServerGBId(String platformGbId);
|
||||
|
||||
|
||||
@@ -177,12 +177,14 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
@Override
|
||||
public boolean startDownload(StreamInfo stream, String callId) {
|
||||
boolean result;
|
||||
String key=String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
|
||||
userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId);
|
||||
if (stream.getProgress() == 1) {
|
||||
result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
|
||||
userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream);
|
||||
logger.debug("添加下载缓存==已完成下载=》{}",key);
|
||||
result = RedisUtil.set(key, stream);
|
||||
}else {
|
||||
result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
|
||||
userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream, 60*60);
|
||||
logger.debug("添加下载缓存==未完成下载=》{}",key);
|
||||
result = RedisUtil.set(key, stream, 60*60);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -617,7 +619,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
stream,
|
||||
callId
|
||||
);
|
||||
List<Object> streamInfoScan = RedisUtil.scan(key);
|
||||
List<Object> streamInfoScan = RedisUtil.scan2(key);
|
||||
if (streamInfoScan.size() > 0) {
|
||||
return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0));
|
||||
}else {
|
||||
@@ -855,7 +857,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
|
||||
@Override
|
||||
public void sendAlarmMsg(AlarmChannelMessage msg) {
|
||||
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE;
|
||||
// 此消息用于对接第三方服务下级来的消息内容
|
||||
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
|
||||
logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
|
||||
RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.storager.dao.*;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||
import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
@@ -189,7 +190,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
dataSourceTransactionManager.commit(transactionStatus); //手动提交
|
||||
return true;
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
dataSourceTransactionManager.rollback(transactionStatus);
|
||||
return false;
|
||||
}
|
||||
@@ -305,7 +306,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
}
|
||||
return true;
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
dataSourceTransactionManager.rollback(transactionStatus);
|
||||
return false;
|
||||
}
|
||||
@@ -359,8 +360,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds) {
|
||||
return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit,channelIds);
|
||||
public List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List<String> channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit) {
|
||||
return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, channelIds, null, query, hasSubChannel, online, start, limit);
|
||||
}
|
||||
|
||||
|
||||
@@ -369,6 +370,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
return deviceChannelMapper.queryChannels(deviceId, null,null, null, online,channelIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceChannelExtend> queryChannelsByDeviceId(String deviceId, List<String> channelIds, Boolean online) {
|
||||
return deviceChannelMapper.queryChannelsWithDeviceInfo(deviceId, null,null, null, online,channelIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<DeviceChannel> querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) {
|
||||
PageHelper.startPage(page, count);
|
||||
@@ -511,6 +517,16 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
return platformMapper.getEnableParentPlatformList(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel() {
|
||||
return platformMapper.queryEnablePlatformListWithAsMessageChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Device> queryDeviceWithAsMessageChannel() {
|
||||
return deviceMapper.queryDeviceWithAsMessageChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outlineForAllParentPlatform() {
|
||||
platformMapper.outlineForAllParentPlatform();
|
||||
|
||||
@@ -45,7 +45,6 @@ public class DateUtil {
|
||||
|
||||
public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
|
||||
return formatter.format(formatterCompatibleISO8601.parse(formatTime));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -881,7 +881,13 @@ public class RedisUtil {
|
||||
|
||||
return new ArrayList<>(resultKeys);
|
||||
}
|
||||
|
||||
public static List<Object> scan2(String query) {
|
||||
if (redisTemplate == null) {
|
||||
redisTemplate = SpringBeanFactory.getBean("redisTemplate");
|
||||
}
|
||||
Set<String> keys = redisTemplate.keys(query);
|
||||
return new ArrayList<>(keys);
|
||||
}
|
||||
// ============================== 消息发送与订阅 ==============================
|
||||
public static void convertAndSend(String channel, JSONObject msg) {
|
||||
if (redisTemplate == null) {
|
||||
|
||||
@@ -28,6 +28,10 @@ public class WVPResult<T> implements Cloneable{
|
||||
return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t);
|
||||
}
|
||||
|
||||
public static WVPResult success() {
|
||||
return new WVPResult<>(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), null);
|
||||
}
|
||||
|
||||
public static <T> WVPResult<T> success(T t) {
|
||||
return success(t, ErrorCode.SUCCESS.getMsg());
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.UUID;
|
||||
* 位置信息管理
|
||||
*/
|
||||
@Tag(name = "位置信息管理")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/position")
|
||||
public class MobilePositionController {
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
* @data: 2021-01-20
|
||||
*/
|
||||
@Tag(name = "SSE推送")
|
||||
@CrossOrigin
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/api")
|
||||
public class SseController {
|
||||
|
||||
@@ -6,35 +6,28 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookListener;
|
||||
import com.genersoft.iot.vmp.service.IDeviceAlarmService;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "报警信息管理")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/alarm")
|
||||
public class AlarmController {
|
||||
@@ -78,11 +71,11 @@ public class AlarmController {
|
||||
if (ObjectUtils.isEmpty(deviceIds)) {
|
||||
deviceIds = null;
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(time)) {
|
||||
time = null;
|
||||
}
|
||||
if (!DateUtil.verification(time, DateUtil.formatter) ){
|
||||
return null;
|
||||
}else if (!DateUtil.verification(time, DateUtil.formatter) ){
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "time格式为" + DateUtil.PATTERN);
|
||||
}
|
||||
List<String> deviceIdList = null;
|
||||
if (deviceIds != null) {
|
||||
@@ -110,7 +103,7 @@ public class AlarmController {
|
||||
deviceAlarm.setAlarmDescription("test");
|
||||
deviceAlarm.setAlarmMethod("1");
|
||||
deviceAlarm.setAlarmPriority("1");
|
||||
deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now()));
|
||||
deviceAlarm.setAlarmTime(DateUtil.getNow());
|
||||
deviceAlarm.setAlarmType("1");
|
||||
deviceAlarm.setLongitude(115.33333);
|
||||
deviceAlarm.setLatitude(39.33333);
|
||||
@@ -177,16 +170,17 @@ public class AlarmController {
|
||||
if (ObjectUtils.isEmpty(alarmType)) {
|
||||
alarmType = null;
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(startTime)) {
|
||||
startTime = null;
|
||||
}else if (!DateUtil.verification(startTime, DateUtil.formatter) ){
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN);
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(endTime)) {
|
||||
endTime = null;
|
||||
}
|
||||
|
||||
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间或结束时间格式有误");
|
||||
}else if (!DateUtil.verification(endTime, DateUtil.formatter) ){
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN);
|
||||
}
|
||||
|
||||
return deviceAlarmService.getAllAlarm(page, count, deviceId, alarmPriority, alarmMethod,
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -22,9 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
@@ -34,7 +31,6 @@ import java.text.ParseException;
|
||||
import java.util.UUID;
|
||||
|
||||
@Tag(name = "国标设备配置")
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequestMapping("/api/device/config")
|
||||
public class DeviceConfig {
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.text.ParseException;
|
||||
import java.util.UUID;
|
||||
|
||||
@Tag(name = "国标设备控制")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/device/control")
|
||||
public class DeviceControl {
|
||||
|
||||
@@ -24,6 +24,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.ibatis.annotations.Options;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -46,7 +47,7 @@ import java.util.*;
|
||||
|
||||
@Tag(name = "国标设备查询", description = "国标设备查询")
|
||||
@SuppressWarnings("rawtypes")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/device/query")
|
||||
public class DeviceQuery {
|
||||
@@ -97,8 +98,10 @@ public class DeviceQuery {
|
||||
@Parameter(name = "page", description = "当前页", required = true)
|
||||
@Parameter(name = "count", description = "每页查询数量", required = true)
|
||||
@GetMapping("/devices")
|
||||
@Options()
|
||||
public PageInfo<Device> devices(int page, int count){
|
||||
|
||||
// if (page == null) page = 0;
|
||||
// if (count == null) count = 20;
|
||||
return storager.queryVideoDeviceList(page, count,null);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "视频流关联到级联平台")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/gbStream")
|
||||
public class GbStreamController {
|
||||
|
||||
@@ -24,7 +24,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@Tag(name = "媒体流相关")
|
||||
@Controller
|
||||
@CrossOrigin
|
||||
|
||||
@RequestMapping(value = "/api/media")
|
||||
public class MediaController {
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
@@ -37,7 +38,7 @@ import java.util.List;
|
||||
* 级联平台管理
|
||||
*/
|
||||
@Tag(name = "级联平台管理")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/platform")
|
||||
public class PlatformController {
|
||||
@@ -205,58 +206,8 @@ public class PlatformController {
|
||||
) {
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
}
|
||||
parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
|
||||
ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId());
|
||||
parentPlatform.setUpdateTime(DateUtil.getNow());
|
||||
if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
|
||||
// 目录结构发生变化,清空之前的关联关系
|
||||
logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
|
||||
storager.cleanContentForPlatform(parentPlatform.getServerGBId());
|
||||
|
||||
}
|
||||
boolean updateResult = storager.updateParentPlatform(parentPlatform);
|
||||
|
||||
if (updateResult) {
|
||||
// 保存时启用就发送注册
|
||||
if (parentPlatform.isEnable()) {
|
||||
if (parentPlatformOld != null && parentPlatformOld.isStatus()) {
|
||||
try {
|
||||
commanderForPlatform.unregister(parentPlatformOld, null, null);
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
|
||||
}
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("[线程休眠失败] : {}", e.getMessage());
|
||||
}
|
||||
// 只要保存就发送注册
|
||||
try {
|
||||
commanderForPlatform.register(parentPlatform, null, null);
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
|
||||
}
|
||||
|
||||
} else {
|
||||
// 只要保存就发送注册
|
||||
try {
|
||||
commanderForPlatform.register(parentPlatform, null, null);
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
} else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销
|
||||
try {
|
||||
commanderForPlatform.unregister(parentPlatformOld, null, null);
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
|
||||
}
|
||||
// 停止订阅相关的定时任务
|
||||
subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
|
||||
}
|
||||
} else {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(),"写入数据库失败");
|
||||
}
|
||||
platformService.update(parentPlatform);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,12 +230,16 @@ public class PlatformController {
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
}
|
||||
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId);
|
||||
ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(serverGBId);
|
||||
if (parentPlatform == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
|
||||
}
|
||||
if (parentPlatformCatch == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
|
||||
}
|
||||
// 发送离线消息,无论是否成功都删除缓存
|
||||
try {
|
||||
commanderForPlatform.unregister(parentPlatform, (event -> {
|
||||
commanderForPlatform.unregister(parentPlatform, parentPlatformCatch.getSipTransactionInfo(), (event -> {
|
||||
// 清空redis缓存
|
||||
redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
|
||||
redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());
|
||||
|
||||
@@ -41,7 +41,7 @@ import java.util.UUID;
|
||||
* @author lin
|
||||
*/
|
||||
@Tag(name = "国标设备点播")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/play")
|
||||
public class PlayController {
|
||||
|
||||
@@ -40,7 +40,7 @@ import java.util.UUID;
|
||||
* @author lin
|
||||
*/
|
||||
@Tag(name = "视频回放")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/playback")
|
||||
public class PlaybackController {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.genersoft.iot.vmp.vmanager.gb28181.ptz;
|
||||
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -10,23 +15,16 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import java.util.UUID;
|
||||
|
||||
@Tag(name = "云台控制")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/ptz")
|
||||
public class PtzController {
|
||||
|
||||
@@ -3,40 +3,37 @@ package com.genersoft.iot.vmp.vmanager.gb28181.record;
|
||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.service.IPlayService;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import java.util.UUID;
|
||||
|
||||
@Tag(name = "国标录像")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/gb_record")
|
||||
public class GBRecordController {
|
||||
@@ -74,10 +71,10 @@ public class GBRecordController {
|
||||
}
|
||||
DeferredResult<WVPResult<RecordInfo>> result = new DeferredResult<>();
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter)){
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime error, format is " + DateUtil.PATTERN);
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime格式为" + DateUtil.PATTERN);
|
||||
}
|
||||
if (!DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime error, format is " + DateUtil.PATTERN);
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN);
|
||||
}
|
||||
|
||||
Device device = storager.queryVideoDevice(deviceId);
|
||||
|
||||
@@ -6,25 +6,18 @@ import com.genersoft.iot.vmp.service.ILogService;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.LogDto;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
@Tag(name = "日志管理")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/log")
|
||||
public class LogController {
|
||||
@@ -67,18 +60,21 @@ public class LogController {
|
||||
if (ObjectUtils.isEmpty(query)) {
|
||||
query = null;
|
||||
}
|
||||
if (ObjectUtils.isEmpty(startTime)) {
|
||||
startTime = null;
|
||||
}
|
||||
if (ObjectUtils.isEmpty(endTime)) {
|
||||
endTime = null;
|
||||
}
|
||||
|
||||
if (!userSetting.getLogInDatebase()) {
|
||||
logger.warn("自动记录日志功能已关闭,查询结果可能不完整。");
|
||||
}
|
||||
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
if (ObjectUtils.isEmpty(startTime)) {
|
||||
startTime = null;
|
||||
}else if (!DateUtil.verification(startTime, DateUtil.formatter) ){
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN);
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(endTime)) {
|
||||
endTime = null;
|
||||
}else if (!DateUtil.verification(endTime, DateUtil.formatter) ){
|
||||
throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN);
|
||||
}
|
||||
|
||||
return logService.getAll(page, count, query, type, startTime, endTime);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//import org.springframework.web.bind.annotation.*;
|
||||
//
|
||||
//@Tag(name = "云端录像")
|
||||
//@CrossOrigin
|
||||
//
|
||||
//@RestController
|
||||
//@RequestMapping("/api/record")
|
||||
//public class RecordController {
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.vmanager.server;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.VManageBootstrap;
|
||||
import com.genersoft.iot.vmp.common.SystemAllInfo;
|
||||
import com.genersoft.iot.vmp.common.VersionPo;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
@@ -15,30 +14,24 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||
import com.genersoft.iot.vmp.service.*;
|
||||
import com.genersoft.iot.vmp.service.bean.MediaServerLoad;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ResourceInfo;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.SystemConfigInfo;
|
||||
import gov.nist.javax.sip.SipStackImpl;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.sip.ListeningPoint;
|
||||
import javax.sip.ObjectInUseException;
|
||||
import javax.sip.SipProvider;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Tag(name = "服务控制")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/server")
|
||||
public class ServerController {
|
||||
@@ -76,9 +69,6 @@ public class ServerController {
|
||||
private int serverPort;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
*/
|
||||
@Tag(name = "拉流代理", description = "")
|
||||
@Controller
|
||||
@CrossOrigin
|
||||
|
||||
@RequestMapping(value = "/api/proxy")
|
||||
public class StreamProxyController {
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import java.util.UUID;
|
||||
|
||||
@Tag(name = "推流信息管理")
|
||||
@Controller
|
||||
@CrossOrigin
|
||||
|
||||
@RequestMapping(value = "/api/push")
|
||||
public class StreamPushController {
|
||||
|
||||
@@ -181,7 +181,7 @@ public class StreamPushController {
|
||||
String name = file.getName();
|
||||
inputStream = file.getInputStream();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
try {
|
||||
//传入参数
|
||||
|
||||
@@ -6,20 +6,16 @@ import com.genersoft.iot.vmp.service.IRoleService;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.Role;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "角色管理")
|
||||
@CrossOrigin
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/role")
|
||||
public class RoleController {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.vmanager.user;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.conf.security.SecurityUtils;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||
import com.genersoft.iot.vmp.service.IRoleService;
|
||||
@@ -21,10 +22,11 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "用户管理")
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
public class UserController {
|
||||
@@ -40,11 +42,13 @@ public class UserController {
|
||||
|
||||
@GetMapping("/login")
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "登录")
|
||||
@Operation(summary = "登录", description = "登录成功后返回AccessToken, 可以从返回值获取到也可以从响应头中获取到," +
|
||||
"后续的请求需要添加请求头 'access-token'或者放在参数里")
|
||||
|
||||
@Parameter(name = "username", description = "用户名", required = true)
|
||||
@Parameter(name = "password", description = "密码(32位md5加密)", required = true)
|
||||
public LoginUser login(@RequestParam String username, @RequestParam String password){
|
||||
LoginUser user = null;
|
||||
public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){
|
||||
LoginUser user;
|
||||
try {
|
||||
user = SecurityUtils.login(username, password, authenticationManager);
|
||||
} catch (AuthenticationException e) {
|
||||
@@ -52,10 +56,15 @@ public class UserController {
|
||||
}
|
||||
if (user == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误");
|
||||
}else {
|
||||
String jwt = JwtUtils.createToken(username, password);
|
||||
response.setHeader(JwtUtils.getHeader(), jwt);
|
||||
user.setAccessToken(jwt);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/changePassword")
|
||||
@Operation(summary = "修改密码")
|
||||
@Parameter(name = "username", description = "用户名", required = true)
|
||||
@@ -74,8 +83,8 @@ public class UserController {
|
||||
if (user == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100);
|
||||
}
|
||||
int userId = SecurityUtils.getUserId();
|
||||
boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes()));
|
||||
//int userId = SecurityUtils.getUserId();
|
||||
boolean result = userService.changePassword(user.getId(), DigestUtils.md5DigestAsHex(password.getBytes()));
|
||||
if (!result) {
|
||||
throw new ControllerException(ErrorCode.ERROR100);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user