合并开源主线

This commit is contained in:
648540858
2023-04-04 09:09:07 +08:00
165 changed files with 3017 additions and 1006 deletions

View File

@@ -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);

View File

@@ -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);
}

View 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);
}
}

View File

@@ -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中返回的错误

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 {
}

View File

@@ -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());

View File

@@ -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 {
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View 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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}
/**

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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(), "等待消息超时");
}
}

View File

@@ -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计数

View File

@@ -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

View File

@@ -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;
/**

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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")

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
});

View File

@@ -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());
}

View File

@@ -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);
}
}
});

View File

@@ -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());

View File

@@ -142,7 +142,7 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
}
} catch (DocumentException e) {
e.printStackTrace();
logger.error("未处理的异常 ", e);
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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()) {

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
/**
* 添加目录订阅

View File

@@ -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);
/**
* 平台离线

View File

@@ -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;

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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 -> {

View File

@@ -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());

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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);
}
}
});

View File

@@ -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);
}
}
});

View File

@@ -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);
}
}
});

View File

@@ -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);
}
}
});

View File

@@ -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);
}
}
});

View File

@@ -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);
}
}
});

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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();

View File

@@ -45,7 +45,6 @@ public class DateUtil {
public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
return formatter.format(formatterCompatibleISO8601.parse(formatTime));
}
/**

View File

@@ -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) {

View File

@@ -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());
}

View File

@@ -30,7 +30,7 @@ import java.util.UUID;
* 位置信息管理
*/
@Tag(name = "位置信息管理")
@CrossOrigin
@RestController
@RequestMapping("/api/position")
public class MobilePositionController {

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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 {

View File

@@ -32,7 +32,7 @@ import java.text.ParseException;
import java.util.UUID;
@Tag(name = "国标设备控制")
@CrossOrigin
@RestController
@RequestMapping("/api/device/control")
public class DeviceControl {

View File

@@ -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);
}

View File

@@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "视频流关联到级联平台")
@CrossOrigin
@RestController
@RequestMapping("/api/gbStream")
public class GbStreamController {

View File

@@ -24,7 +24,7 @@ import javax.servlet.http.HttpServletRequest;
@Tag(name = "媒体流相关")
@Controller
@CrossOrigin
@RequestMapping(value = "/api/media")
public class MediaController {

View File

@@ -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());

View File

@@ -41,7 +41,7 @@ import java.util.UUID;
* @author lin
*/
@Tag(name = "国标设备点播")
@CrossOrigin
@RestController
@RequestMapping("/api/play")
public class PlayController {

View File

@@ -40,7 +40,7 @@ import java.util.UUID;
* @author lin
*/
@Tag(name = "视频回放")
@CrossOrigin
@RestController
@RequestMapping("/api/playback")
public class PlaybackController {

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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);

View File

@@ -14,7 +14,7 @@
//import org.springframework.web.bind.annotation.*;
//
//@Tag(name = "云端录像")
//@CrossOrigin
//
//@RestController
//@RequestMapping("/api/record")
//public class RecordController {

View File

@@ -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;

View File

@@ -25,7 +25,7 @@ import org.springframework.web.bind.annotation.*;
*/
@Tag(name = "拉流代理", description = "")
@Controller
@CrossOrigin
@RequestMapping(value = "/api/proxy")
public class StreamProxyController {

View File

@@ -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 {
//传入参数

View File

@@ -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 {

View File

@@ -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