mirror of
https://gitee.com/pan648540858/wvp-GB28181-pro.git
synced 2026-06-24 11:47:50 +08:00
Merge branch 'refs/heads/master' into dev/abl支持
This commit is contained in:
commit
271bb19c99
2
pom.xml
2
pom.xml
@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>com.genersoft</groupId>
|
||||
<artifactId>wvp-pro</artifactId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.2</version>
|
||||
<name>web video platform</name>
|
||||
<description>国标28181视频平台</description>
|
||||
<packaging>${project.packaging}</packaging>
|
||||
|
||||
@ -72,6 +72,9 @@ public class VideoManagerConstants {
|
||||
|
||||
public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_";
|
||||
public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_";
|
||||
public static final String WAITE_SEND_PUSH_STREAM = "VMP_WAITE_SEND_PUSH_STREAM:";
|
||||
public static final String START_SEND_PUSH_STREAM = "VMP_START_SEND_PUSH_STREAM:";
|
||||
public static final String PUSH_STREAM_ONLINE = "VMP_PUSH_STREAM_ONLINE:";
|
||||
|
||||
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springdoc.core.GroupedOpenApi;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -18,6 +19,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Configuration
|
||||
@Order(1)
|
||||
@ConditionalOnProperty(value = "user-settings.doc-enable", havingValue = "true", matchIfMissing = true)
|
||||
public class SpringDocConfig {
|
||||
|
||||
@Value("${doc.enabled: true}")
|
||||
|
||||
@ -54,6 +54,8 @@ public class UserSetting {
|
||||
private Boolean deviceStatusNotify = Boolean.TRUE;
|
||||
private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
|
||||
|
||||
private Boolean docEnable = Boolean.TRUE;
|
||||
|
||||
private String serverId = "000000";
|
||||
|
||||
private String thirdPartyGBIdReg = "[\\s\\S]*";
|
||||
@ -66,7 +68,7 @@ public class UserSetting {
|
||||
|
||||
private List<String> allowedOrigins = new ArrayList<>();
|
||||
|
||||
private int maxNotifyCountQueue = 10000;
|
||||
private int maxNotifyCountQueue = 100000;
|
||||
|
||||
private int registerAgainAfterTime = 60;
|
||||
|
||||
@ -315,4 +317,12 @@ public class UserSetting {
|
||||
public void setRegisterKeepIntDialog(boolean registerKeepIntDialog) {
|
||||
this.registerKeepIntDialog = registerKeepIntDialog;
|
||||
}
|
||||
|
||||
public Boolean getDocEnable() {
|
||||
return docEnable;
|
||||
}
|
||||
|
||||
public void setDocEnable(Boolean docEnable) {
|
||||
this.docEnable = docEnable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,26 +28,22 @@ public class RedisMsgListenConfig {
|
||||
@Autowired
|
||||
private RedisAlarmMsgListener redisAlarmMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisStreamMsgListener redisStreamMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamStatusListMsgListener redisPushStreamListMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamResponseListener redisPushStreamResponseListener;
|
||||
|
||||
@Autowired
|
||||
private RedisCloseStreamMsgListener redisCloseStreamMsgListener;
|
||||
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamCloseResponseListener redisPushStreamCloseResponseListener;
|
||||
private RedisRpcConfig redisRpcConfig;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamResponseListener redisPushStreamCloseResponseListener;
|
||||
|
||||
|
||||
/**
|
||||
@ -64,13 +60,11 @@ public class RedisMsgListenConfig {
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
|
||||
container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
|
||||
container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH"));
|
||||
container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY));
|
||||
container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE));
|
||||
container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE));
|
||||
container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE));
|
||||
container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE));
|
||||
container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED));
|
||||
container.addMessageListener(redisRpcConfig, new PatternTopic(RedisRpcConfig.REDIS_REQUEST_CHANNEL_KEY));
|
||||
container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE));
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,221 @@
|
||||
package com.genersoft.iot.vmp.conf.redis;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.common.CommonCallback;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.control.RedisRpcController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class RedisRpcConfig implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisRpcConfig.class);
|
||||
|
||||
public final static String REDIS_REQUEST_CHANNEL_KEY = "WVP_REDIS_REQUEST_CHANNEL_KEY";
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private RedisRpcController redisRpcController;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
boolean isEmpty = taskQueue.isEmpty();
|
||||
taskQueue.offer(message);
|
||||
if (isEmpty) {
|
||||
taskExecutor.execute(() -> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
Message msg = taskQueue.poll();
|
||||
try {
|
||||
RedisRpcMessage redisRpcMessage = JSON.parseObject(new String(msg.getBody()), RedisRpcMessage.class);
|
||||
if (redisRpcMessage.getRequest() != null) {
|
||||
handlerRequest(redisRpcMessage.getRequest());
|
||||
} else if (redisRpcMessage.getResponse() != null){
|
||||
handlerResponse(redisRpcMessage.getResponse());
|
||||
} else {
|
||||
logger.error("[redis rpc 解析失败] {}", JSON.toJSONString(redisRpcMessage));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("[redis rpc 解析异常] ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void handlerResponse(RedisRpcResponse response) {
|
||||
if (userSetting.getServerId().equals(response.getToId())) {
|
||||
return;
|
||||
}
|
||||
logger.info("[redis-rpc] << {}", response);
|
||||
response(response);
|
||||
}
|
||||
|
||||
private void handlerRequest(RedisRpcRequest request) {
|
||||
try {
|
||||
if (userSetting.getServerId().equals(request.getFromId())) {
|
||||
return;
|
||||
}
|
||||
logger.info("[redis-rpc] << {}", request);
|
||||
Method method = getMethod(request.getUri());
|
||||
// 没有携带目标ID的可以理解为哪个wvp有结果就哪个回复,携带目标ID,但是如果是不存在的uri则直接回复404
|
||||
if (userSetting.getServerId().equals(request.getToId())) {
|
||||
if (method == null) {
|
||||
// 回复404结果
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(404);
|
||||
sendResponse(response);
|
||||
return;
|
||||
}
|
||||
RedisRpcResponse response = (RedisRpcResponse)method.invoke(redisRpcController, request);
|
||||
if(response != null) {
|
||||
sendResponse(response);
|
||||
}
|
||||
}else {
|
||||
if (method == null) {
|
||||
return;
|
||||
}
|
||||
RedisRpcResponse response = (RedisRpcResponse)method.invoke(redisRpcController, request);
|
||||
if (response != null) {
|
||||
sendResponse(response);
|
||||
}
|
||||
}
|
||||
}catch (InvocationTargetException | IllegalAccessException e) {
|
||||
logger.error("[redis rpc ] 处理请求失败 ", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Method getMethod(String name) {
|
||||
// 启动后扫描所有的路径注解
|
||||
Method[] methods = redisRpcController.getClass().getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(name)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void sendResponse(RedisRpcResponse response){
|
||||
logger.info("[redis-rpc] >> {}", response);
|
||||
response.setToId(userSetting.getServerId());
|
||||
RedisRpcMessage message = new RedisRpcMessage();
|
||||
message.setResponse(response);
|
||||
redisTemplate.convertAndSend(REDIS_REQUEST_CHANNEL_KEY, message);
|
||||
}
|
||||
|
||||
private void sendRequest(RedisRpcRequest request){
|
||||
logger.info("[redis-rpc] >> {}", request);
|
||||
RedisRpcMessage message = new RedisRpcMessage();
|
||||
message.setRequest(request);
|
||||
redisTemplate.convertAndSend(REDIS_REQUEST_CHANNEL_KEY, message);
|
||||
}
|
||||
|
||||
|
||||
private final Map<Long, SynchronousQueue<RedisRpcResponse>> topicSubscribers = new ConcurrentHashMap<>();
|
||||
private final Map<Long, CommonCallback<RedisRpcResponse>> callbacks = new ConcurrentHashMap<>();
|
||||
|
||||
public RedisRpcResponse request(RedisRpcRequest request, int timeOut) {
|
||||
request.setSn((long) random.nextInt(1000) + 1);
|
||||
SynchronousQueue<RedisRpcResponse> subscribe = subscribe(request.getSn());
|
||||
|
||||
try {
|
||||
sendRequest(request);
|
||||
return subscribe.poll(timeOut, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.warn("[redis rpc timeout] uri: {}, sn: {}", request.getUri(), request.getSn(), e);
|
||||
} finally {
|
||||
this.unsubscribe(request.getSn());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void request(RedisRpcRequest request, CommonCallback<RedisRpcResponse> callback) {
|
||||
request.setSn((long) random.nextInt(1000) + 1);
|
||||
setCallback(request.getSn(), callback);
|
||||
sendRequest(request);
|
||||
}
|
||||
|
||||
public Boolean response(RedisRpcResponse response) {
|
||||
SynchronousQueue<RedisRpcResponse> queue = topicSubscribers.get(response.getSn());
|
||||
CommonCallback<RedisRpcResponse> callback = callbacks.get(response.getSn());
|
||||
if (queue != null) {
|
||||
try {
|
||||
return queue.offer(response, 2, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("{}", e.getMessage(), e);
|
||||
}
|
||||
}else if (callback != null) {
|
||||
callback.run(response);
|
||||
callbacks.remove(response.getSn());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void unsubscribe(long key) {
|
||||
topicSubscribers.remove(key);
|
||||
}
|
||||
|
||||
|
||||
private SynchronousQueue<RedisRpcResponse> subscribe(long key) {
|
||||
SynchronousQueue<RedisRpcResponse> queue = null;
|
||||
if (!topicSubscribers.containsKey(key))
|
||||
topicSubscribers.put(key, queue = new SynchronousQueue<>());
|
||||
return queue;
|
||||
}
|
||||
|
||||
private void setCallback(long key, CommonCallback<RedisRpcResponse> callback) {
|
||||
// TODO 如果多个上级点播同一个通道会有问题
|
||||
callbacks.put(key, callback);
|
||||
}
|
||||
|
||||
public void removeCallback(long key) {
|
||||
callbacks.remove(key);
|
||||
}
|
||||
|
||||
|
||||
public int getCallbackCount(){
|
||||
return callbacks.size();
|
||||
}
|
||||
|
||||
// @Scheduled(fixedRate = 1000) //每1秒执行一次
|
||||
// public void execute(){
|
||||
// logger.info("callbacks的长度: " + callbacks.size());
|
||||
// logger.info("队列的长度: " + topicSubscribers.size());
|
||||
// logger.info("HOOK监听的长度: " + hookSubscribe.size());
|
||||
// logger.info("");
|
||||
// }
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.conf.redis;
|
||||
|
||||
import com.alibaba.fastjson2.support.spring.data.redis.GenericFastJsonRedisSerializer;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
@ -25,4 +26,20 @@ public class RedisTemplateConfig {
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, MobilePosition> getRedisTemplateForMobilePosition(RedisConnectionFactory redisConnectionFactory) {
|
||||
RedisTemplate<String, MobilePosition> redisTemplate = new RedisTemplate<>();
|
||||
// 使用fastJson序列化
|
||||
GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
|
||||
// value值的序列化采用fastJsonRedisSerializer
|
||||
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
|
||||
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
|
||||
|
||||
// key的序列化采用StringRedisSerializer
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
return redisTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.genersoft.iot.vmp.conf.redis.bean;
|
||||
|
||||
public class RedisRpcMessage {
|
||||
|
||||
private RedisRpcRequest request;
|
||||
|
||||
private RedisRpcResponse response;
|
||||
|
||||
public RedisRpcRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public void setRequest(RedisRpcRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public RedisRpcResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(RedisRpcResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
package com.genersoft.iot.vmp.conf.redis.bean;
|
||||
|
||||
/**
|
||||
* 通过redis发送请求
|
||||
*/
|
||||
public class RedisRpcRequest {
|
||||
|
||||
/**
|
||||
* 来自的WVP ID
|
||||
*/
|
||||
private String fromId;
|
||||
|
||||
|
||||
/**
|
||||
* 目标的WVP ID
|
||||
*/
|
||||
private String toId;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private long sn;
|
||||
|
||||
/**
|
||||
* 访问的路径
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private Object param;
|
||||
|
||||
public String getFromId() {
|
||||
return fromId;
|
||||
}
|
||||
|
||||
public void setFromId(String fromId) {
|
||||
this.fromId = fromId;
|
||||
}
|
||||
|
||||
public String getToId() {
|
||||
return toId;
|
||||
}
|
||||
|
||||
public void setToId(String toId) {
|
||||
this.toId = toId;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Object getParam() {
|
||||
return param;
|
||||
}
|
||||
|
||||
public void setParam(Object param) {
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public long getSn() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public void setSn(long sn) {
|
||||
this.sn = sn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RedisRpcRequest{" +
|
||||
"uri='" + uri + '\'' +
|
||||
", fromId='" + fromId + '\'' +
|
||||
", toId='" + toId + '\'' +
|
||||
", sn=" + sn +
|
||||
", param=" + param +
|
||||
'}';
|
||||
}
|
||||
|
||||
public RedisRpcResponse getResponse() {
|
||||
RedisRpcResponse response = new RedisRpcResponse();
|
||||
response.setFromId(fromId);
|
||||
response.setToId(toId);
|
||||
response.setSn(sn);
|
||||
response.setUri(uri);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
package com.genersoft.iot.vmp.conf.redis.bean;
|
||||
|
||||
/**
|
||||
* 通过redis发送回复
|
||||
*/
|
||||
public class RedisRpcResponse {
|
||||
|
||||
/**
|
||||
* 来自的WVP ID
|
||||
*/
|
||||
private String fromId;
|
||||
|
||||
|
||||
/**
|
||||
* 目标的WVP ID
|
||||
*/
|
||||
private String toId;
|
||||
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private long sn;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private int statusCode;
|
||||
|
||||
/**
|
||||
* 访问的路径
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private Object body;
|
||||
|
||||
public String getFromId() {
|
||||
return fromId;
|
||||
}
|
||||
|
||||
public void setFromId(String fromId) {
|
||||
this.fromId = fromId;
|
||||
}
|
||||
|
||||
public String getToId() {
|
||||
return toId;
|
||||
}
|
||||
|
||||
public void setToId(String toId) {
|
||||
this.toId = toId;
|
||||
}
|
||||
|
||||
public long getSn() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public void setSn(long sn) {
|
||||
this.sn = sn;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public void setStatusCode(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Object getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(Object body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RedisRpcResponse{" +
|
||||
"uri='" + uri + '\'' +
|
||||
", fromId='" + fromId + '\'' +
|
||||
", toId='" + toId + '\'' +
|
||||
", sn=" + sn +
|
||||
", statusCode=" + statusCode +
|
||||
", body=" + body +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -35,10 +35,15 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
// 忽略登录请求的token验证
|
||||
String requestURI = request.getRequestURI();
|
||||
if ((requestURI.startsWith("/doc.html") || requestURI.startsWith("/swagger-ui") ) && !userSetting.getDocEnable()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
if (requestURI.equalsIgnoreCase("/api/user/login")) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userSetting.isInterfaceAuthentication()) {
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
|
||||
@ -117,7 +117,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
.authorizeRequests()
|
||||
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
|
||||
.antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
|
||||
.antMatchers("/api/user/login", "/index/hook/**","/index/hook/abl/**", "/swagger-ui/**", "/doc.html").permitAll()
|
||||
.antMatchers("/api/user/login", "/index/hook/**","/index/hook/abl/**", "/swagger-ui/**", "/doc.html#/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
// 异常处理器
|
||||
.and()
|
||||
|
||||
@ -186,6 +186,18 @@ public class DeviceChannel {
|
||||
@Schema(description = "纬度")
|
||||
private double latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
@Schema(description = "自定义经度")
|
||||
private double customLongitude;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
@Schema(description = "自定义纬度")
|
||||
private double customLatitude;
|
||||
|
||||
/**
|
||||
* 经度 GCJ02
|
||||
*/
|
||||
@ -226,7 +238,7 @@ public class DeviceChannel {
|
||||
* 是否含有音频
|
||||
*/
|
||||
@Schema(description = "是否含有音频")
|
||||
private boolean hasAudio;
|
||||
private Boolean hasAudio;
|
||||
|
||||
/**
|
||||
* 标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划
|
||||
@ -523,11 +535,11 @@ public class DeviceChannel {
|
||||
this.subCount = subCount;
|
||||
}
|
||||
|
||||
public boolean isHasAudio() {
|
||||
public Boolean getHasAudio() {
|
||||
return hasAudio;
|
||||
}
|
||||
|
||||
public void setHasAudio(boolean hasAudio) {
|
||||
public void setHasAudio(Boolean hasAudio) {
|
||||
this.hasAudio = hasAudio;
|
||||
}
|
||||
|
||||
@ -586,4 +598,20 @@ public class DeviceChannel {
|
||||
public void setStreamIdentification(String streamIdentification) {
|
||||
this.streamIdentification = streamIdentification;
|
||||
}
|
||||
|
||||
public double getCustomLongitude() {
|
||||
return customLongitude;
|
||||
}
|
||||
|
||||
public void setCustomLongitude(double customLongitude) {
|
||||
this.customLongitude = customLongitude;
|
||||
}
|
||||
|
||||
public double getCustomLatitude() {
|
||||
return customLatitude;
|
||||
}
|
||||
|
||||
public void setCustomLatitude(double customLatitude) {
|
||||
this.customLatitude = customLatitude;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
|
||||
public class SendRtpItem {
|
||||
|
||||
/**
|
||||
@ -24,6 +26,11 @@ public class SendRtpItem {
|
||||
*/
|
||||
private String platformId;
|
||||
|
||||
/**
|
||||
* 平台名称
|
||||
*/
|
||||
private String platformName;
|
||||
|
||||
/**
|
||||
* 对应设备id
|
||||
*/
|
||||
@ -63,6 +70,11 @@ public class SendRtpItem {
|
||||
*/
|
||||
private boolean tcpActive;
|
||||
|
||||
/**
|
||||
* 自己推流使用的IP
|
||||
*/
|
||||
private String localIp;
|
||||
|
||||
/**
|
||||
* 自己推流使用的端口
|
||||
*/
|
||||
@ -81,7 +93,7 @@ public class SendRtpItem {
|
||||
/**
|
||||
* invite 的 callId
|
||||
*/
|
||||
private String CallId;
|
||||
private String callId;
|
||||
|
||||
/**
|
||||
* invite 的 fromTag
|
||||
@ -124,6 +136,11 @@ public class SendRtpItem {
|
||||
*/
|
||||
private String receiveStream;
|
||||
|
||||
/**
|
||||
* 上级的点播类型
|
||||
*/
|
||||
private String sessionName;
|
||||
|
||||
public static SendRtpItem getInstance(RequestPushStreamMsg requestPushStreamMsg) {
|
||||
SendRtpItem sendRtpItem = new SendRtpItem();
|
||||
sendRtpItem.setMediaServerId(requestPushStreamMsg.getMediaServerId());
|
||||
@ -262,11 +279,11 @@ public class SendRtpItem {
|
||||
}
|
||||
|
||||
public String getCallId() {
|
||||
return CallId;
|
||||
return callId;
|
||||
}
|
||||
|
||||
public void setCallId(String callId) {
|
||||
CallId = callId;
|
||||
this.callId = callId;
|
||||
}
|
||||
|
||||
public InviteStreamType getPlayType() {
|
||||
@ -341,6 +358,30 @@ public class SendRtpItem {
|
||||
this.receiveStream = receiveStream;
|
||||
}
|
||||
|
||||
public String getPlatformName() {
|
||||
return platformName;
|
||||
}
|
||||
|
||||
public void setPlatformName(String platformName) {
|
||||
this.platformName = platformName;
|
||||
}
|
||||
|
||||
public String getLocalIp() {
|
||||
return localIp;
|
||||
}
|
||||
|
||||
public void setLocalIp(String localIp) {
|
||||
this.localIp = localIp;
|
||||
}
|
||||
|
||||
public String getSessionName() {
|
||||
return sessionName;
|
||||
}
|
||||
|
||||
public void setSessionName(String sessionName) {
|
||||
this.sessionName = sessionName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SendRtpItem{" +
|
||||
@ -348,6 +389,7 @@ public class SendRtpItem {
|
||||
", port=" + port +
|
||||
", ssrc='" + ssrc + '\'' +
|
||||
", platformId='" + platformId + '\'' +
|
||||
", platformName='" + platformName + '\'' +
|
||||
", deviceId='" + deviceId + '\'' +
|
||||
", app='" + app + '\'' +
|
||||
", channelId='" + channelId + '\'' +
|
||||
@ -355,10 +397,11 @@ public class SendRtpItem {
|
||||
", stream='" + stream + '\'' +
|
||||
", tcp=" + tcp +
|
||||
", tcpActive=" + tcpActive +
|
||||
", localIp='" + localIp + '\'' +
|
||||
", localPort=" + localPort +
|
||||
", mediaServerId='" + mediaServerId + '\'' +
|
||||
", serverId='" + serverId + '\'' +
|
||||
", CallId='" + CallId + '\'' +
|
||||
", CallId='" + callId + '\'' +
|
||||
", fromTag='" + fromTag + '\'' +
|
||||
", toTag='" + toTag + '\'' +
|
||||
", pt=" + pt +
|
||||
@ -367,6 +410,18 @@ public class SendRtpItem {
|
||||
", rtcp=" + rtcp +
|
||||
", playType=" + playType +
|
||||
", receiveStream='" + receiveStream + '\'' +
|
||||
", sessionName='" + sessionName + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getRedisKey() {
|
||||
String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX +
|
||||
serverId + "_"
|
||||
+ mediaServerId + "_"
|
||||
+ platformId + "_"
|
||||
+ channelId + "_"
|
||||
+ stream + "_"
|
||||
+ callId;
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
|
||||
ParentPlatform parentPlatform = null;
|
||||
|
||||
Map<String, List<ParentPlatform>> parentPlatformMap = new HashMap<>();
|
||||
Map<String, DeviceChannel> channelMap = new HashMap<>();
|
||||
if (!ObjectUtils.isEmpty(event.getPlatformId())) {
|
||||
subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
|
||||
if (subscribe == null) {
|
||||
@ -67,6 +68,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
|
||||
for (DeviceChannel deviceChannel : event.getDeviceChannels()) {
|
||||
List<ParentPlatform> parentPlatformsForGB = storager.queryPlatFormListForGBWithGBId(deviceChannel.getChannelId(), platforms);
|
||||
parentPlatformMap.put(deviceChannel.getChannelId(), parentPlatformsForGB);
|
||||
channelMap.put(deviceChannel.getChannelId(), deviceChannel);
|
||||
}
|
||||
}
|
||||
}else if (event.getGbStreams() != null) {
|
||||
@ -174,7 +176,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
|
||||
}
|
||||
logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId);
|
||||
List<DeviceChannel> deviceChannelList = new ArrayList<>();
|
||||
DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(platform.getServerGBId(), gbId);
|
||||
DeviceChannel deviceChannel = channelMap.get(gbId);
|
||||
deviceChannelList.add(deviceChannel);
|
||||
GbStream gbStream = storager.queryStreamInParentPlatform(platform.getServerGBId(), gbId);
|
||||
if(gbStream != null){
|
||||
|
||||
@ -104,7 +104,7 @@ public class SipRunner implements CommandLineRunner {
|
||||
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStream());
|
||||
if (mediaServerItem != null) {
|
||||
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
|
||||
boolean stopResult = mediaServerService.stopSendRtp(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
|
||||
boolean stopResult = mediaServerService.initStopSendRtp(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
|
||||
if (stopResult) {
|
||||
ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
|
||||
if (platform != null) {
|
||||
|
||||
@ -168,6 +168,7 @@ public class SIPRequestHeaderProvider {
|
||||
// via
|
||||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
|
||||
ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
|
||||
// viaHeader.setRPort();
|
||||
viaHeaders.add(viaHeader);
|
||||
//from
|
||||
SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
|
||||
|
||||
@ -515,9 +515,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
if (parentPlatform == null) {
|
||||
return;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("[发送 移动位置订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), gpsMsgInfo.getId(), gpsMsgInfo.getLng(), gpsMsgInfo.getLat());
|
||||
}
|
||||
logger.info("[发送 移动位置订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), gpsMsgInfo.getId(), gpsMsgInfo.getLng(), gpsMsgInfo.getLat());
|
||||
|
||||
String characterSet = parentPlatform.getCharacterSet();
|
||||
StringBuffer deviceStatusXml = new StringBuffer(600);
|
||||
@ -592,6 +590,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
Integer finalIndex = index;
|
||||
String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels,
|
||||
deviceChannels.size(), type, subscribeInfo);
|
||||
System.out.println(catalogXmlContent);
|
||||
logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
|
||||
sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
|
||||
logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
|
||||
@ -621,7 +620,6 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
|
||||
private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
|
||||
StringBuffer catalogXml = new StringBuffer(600);
|
||||
|
||||
String characterSet = parentPlatform.getCharacterSet();
|
||||
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
|
||||
.append("<Notify>\r\n")
|
||||
@ -660,6 +658,8 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
|
||||
.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n")
|
||||
.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n")
|
||||
.append("<Address>" + channel.getAddress() + "</Address>\r\n");
|
||||
catalogXml.append("<Longitude>" + channel.getLongitude() + "</Longitude>\r\n");
|
||||
catalogXml.append("<Latitude>" + channel.getLatitude() + "</Latitude>\r\n");
|
||||
}
|
||||
if (!"presence".equals(subscribeInfo.getEventType())) {
|
||||
catalogXml.append("<Event>" + type + "</Event>\r\n");
|
||||
|
||||
@ -6,7 +6,6 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import gov.nist.javax.sip.message.SIPResponse;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.Element;
|
||||
@ -172,6 +171,7 @@ public abstract class SIPRequestProcessorParent {
|
||||
return getRootElement(evt, "gb2312");
|
||||
}
|
||||
public Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
|
||||
|
||||
if (charset == null) {
|
||||
charset = "gb2312";
|
||||
}
|
||||
|
||||
@ -13,10 +13,11 @@ import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.IDeviceService;
|
||||
import com.genersoft.iot.vmp.service.IPlayService;
|
||||
import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@ -51,6 +52,8 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
@Autowired
|
||||
private IRedisRpcService redisRpcService;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
@ -67,9 +70,6 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
@Autowired
|
||||
private DynamicTask dynamicTask;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
@Autowired
|
||||
private IPlayService playService;
|
||||
|
||||
@ -86,7 +86,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
logger.info("[收到ACK]: 来自->{}", fromUserId);
|
||||
SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
|
||||
if (sendRtpItem == null) {
|
||||
logger.warn("[收到ACK]:未找到来自{},目标为({})的推流信息",fromUserId, toUserId);
|
||||
logger.warn("[收到ACK]:未找到来自{},callId: {}", fromUserId, callIdHeader.getCallId());
|
||||
return;
|
||||
}
|
||||
// tcp主动时,此时是级联下级平台,在回复200ok时,本地已经请求zlm开启监听,跳过下面步骤
|
||||
@ -106,10 +106,14 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
|
||||
if (parentPlatform != null) {
|
||||
if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) {
|
||||
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(sendRtpItem);
|
||||
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, () -> {
|
||||
playService.startSendRtpStreamFailHand(sendRtpItem, parentPlatform, callIdHeader);
|
||||
});
|
||||
WVPResult wvpResult = redisRpcService.startSendRtp(sendRtpItem.getRedisKey(), sendRtpItem);
|
||||
if (wvpResult.getCode() == 0) {
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(),
|
||||
sendRtpItem.getChannelId(), parentPlatform.getServerGBId(), parentPlatform.getName(), userSetting.getServerId(),
|
||||
sendRtpItem.getMediaServerId());
|
||||
messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
|
||||
redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (sendRtpItem.isTcpActive()) {
|
||||
|
||||
@ -15,11 +15,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||
import com.genersoft.iot.vmp.service.*;
|
||||
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
|
||||
import com.genersoft.iot.vmp.service.bean.RequestStopPushStreamMsg;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
@ -91,7 +88,8 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
private IStreamPushService pushService;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
private IRedisRpcService redisRpcService;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@ -121,35 +119,24 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
logger.info("[收到bye] 停止推流:{}, 媒体节点: {}", streamId, sendRtpItem.getMediaServerId());
|
||||
|
||||
if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
|
||||
// 查询这路流是否是本平台的
|
||||
StreamPushItem push = pushService.getPush(sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
if (push!= null && !push.isSelf()) {
|
||||
// 不是本平台的就发送redis消息让其他wvp停止发流
|
||||
ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
|
||||
if (platform != null) {
|
||||
RequestStopPushStreamMsg streamMsg = RequestStopPushStreamMsg.getInstance(sendRtpItem, platform.getName(), platform.getId());
|
||||
redisGbPlayMsgListener.sendMsgForStopSendRtpStream(sendRtpItem.getServerId(), streamMsg);
|
||||
}
|
||||
redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem, platform);
|
||||
if (!userSetting.getServerId().equals(sendRtpItem.getServerId())) {
|
||||
redisRpcService.stopSendRtp(sendRtpItem.getRedisKey());
|
||||
redisCatchStorage.deleteSendRTPServer(null, null, sendRtpItem.getCallId(), null);
|
||||
}else {
|
||||
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
|
||||
callIdHeader.getCallId(), null);
|
||||
mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
|
||||
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
redisCatchStorage.deleteSendRTPServer(null, null, callIdHeader.getCallId(), null);
|
||||
mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
|
||||
if (userSetting.getUseCustomSsrcForParentInvite()) {
|
||||
mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
|
||||
mediaServerService.releaseSsrc(mediaServer.getId(), sendRtpItem.getSsrc());
|
||||
}
|
||||
}
|
||||
|
||||
ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
|
||||
if (platform != null) {
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
|
||||
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getPlatformId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
|
||||
messageForPushChannel.setPlatFormIndex(platform.getId());
|
||||
redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
|
||||
}else {
|
||||
logger.info("[上级平台停止观看] 未找到平台{}的信息,发送redis消息失败", sendRtpItem.getPlatformId());
|
||||
}
|
||||
}
|
||||
}else {
|
||||
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),
|
||||
@ -188,13 +175,13 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(callIdHeader.getCallId());
|
||||
// 可能是设备发送的停止
|
||||
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransactionByCallId(callIdHeader.getCallId());
|
||||
if (ssrcTransaction == null) {
|
||||
return;
|
||||
}
|
||||
logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
|
||||
logger.info("[收到bye] 来自设备:{}, 通道: {}, 类型: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getType());
|
||||
|
||||
ParentPlatform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId());
|
||||
if (platform != null ) {
|
||||
@ -223,13 +210,35 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
logger.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
|
||||
return;
|
||||
}
|
||||
storager.stopPlay(device.getDeviceId(), channel.getChannelId());
|
||||
switch (ssrcTransaction.getType()){
|
||||
case PLAY:
|
||||
case PLAYBACK:
|
||||
case DOWNLOAD:
|
||||
InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channel.getChannelId());
|
||||
if (inviteInfo != null) {
|
||||
storager.stopPlay(device.getDeviceId(), channel.getChannelId());
|
||||
inviteStreamService.removeInviteInfo(inviteInfo);
|
||||
if (inviteInfo.getStreamInfo() != null) {
|
||||
mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStreamInfo().getStream());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BROADCAST:
|
||||
case TALK:
|
||||
// 查找来源的对讲设备,发送停止
|
||||
Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
|
||||
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(ssrcTransaction.getDeviceId(), channel.getChannelId());
|
||||
if (sourceDevice != null) {
|
||||
playService.stopAudioBroadcast(sourceDevice.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
if (audioBroadcastCatch != null) {
|
||||
// 来自上级平台的停止对讲
|
||||
logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", ssrcTransaction.getDeviceId(), channel.getChannelId());
|
||||
audioBroadcastManager.del(ssrcTransaction.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
// 释放ssrc
|
||||
MediaServer mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
|
||||
@ -237,19 +246,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
||||
mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
|
||||
}
|
||||
streamSession.removeByCallId(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getCallId());
|
||||
if (ssrcTransaction.getType() == InviteSessionType.BROADCAST) {
|
||||
// 查找来源的对讲设备,发送停止
|
||||
Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
|
||||
if (sourceDevice != null) {
|
||||
playService.stopAudioBroadcast(sourceDevice.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
}
|
||||
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(ssrcTransaction.getDeviceId(), channel.getChannelId());
|
||||
if (audioBroadcastCatch != null) {
|
||||
// 来自上级平台的停止对讲
|
||||
logger.info("[停止对讲] 来自上级,平台:{}, 通道:{}", ssrcTransaction.getDeviceId(), channel.getChannelId());
|
||||
audioBroadcastManager.del(ssrcTransaction.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,23 +19,23 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.hook.Hook;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookType;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||
import com.genersoft.iot.vmp.service.IInviteStreamService;
|
||||
import com.genersoft.iot.vmp.service.IPlayService;
|
||||
import com.genersoft.iot.vmp.service.IStreamProxyService;
|
||||
import com.genersoft.iot.vmp.service.IStreamPushService;
|
||||
import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
|
||||
import com.genersoft.iot.vmp.service.bean.ErrorCallback;
|
||||
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
|
||||
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
|
||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.RedisPushStreamResponseListener;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
@ -51,6 +51,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sdp.*;
|
||||
@ -64,6 +65,7 @@ import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Vector;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* SIP命令类型: INVITE请求
|
||||
@ -92,7 +94,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IInviteStreamService inviteStreamService;
|
||||
private IRedisRpcService redisRpcService;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private SSRCFactory ssrcFactory;
|
||||
@ -100,9 +105,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Autowired
|
||||
private DynamicTask dynamicTask;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamResponseListener redisPushStreamResponseListener;
|
||||
|
||||
@Autowired
|
||||
private IPlayService playService;
|
||||
|
||||
@ -124,19 +126,18 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager mediaListManager;
|
||||
|
||||
@Autowired
|
||||
private SipConfig config;
|
||||
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
@Autowired
|
||||
private VideoStreamSessionManager streamSession;
|
||||
|
||||
@Autowired
|
||||
private SendRtpPortManager sendRtpPortManager;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamResponseListener redisPushStreamResponseListener;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@ -334,8 +335,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
return;
|
||||
}
|
||||
String username = sdp.getOrigin().getUsername();
|
||||
String addressStr = sdp.getConnection().getAddress();
|
||||
|
||||
String addressStr;
|
||||
if(StringUtils.isEmpty(platform.getSendStreamIp())){
|
||||
addressStr = sdp.getConnection().getAddress();
|
||||
}else {
|
||||
addressStr = platform.getSendStreamIp();
|
||||
}
|
||||
|
||||
Device device = null;
|
||||
// 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
|
||||
@ -484,7 +489,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
String startTimeStr = DateUtil.urlFormatter.format(start);
|
||||
String endTimeStr = DateUtil.urlFormatter.format(end);
|
||||
String stream = device.getDeviceId() + "_" + channelId + "_" + startTimeStr + "_" + endTimeStr;
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0,false,!channel.isHasAudio(), false, device.getStreamModeForParam());
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, stream, null, device.isSsrcCheck(), true, 0,false,!channel.getHasAudio(), false, device.getStreamModeForParam());
|
||||
sendRtpItem.setStream(stream);
|
||||
// 写入redis, 超时时回复
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
@ -514,7 +519,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
|
||||
sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD);
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.isHasAudio(), false, device.getStreamModeForParam());
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.getHasAudio(), false, device.getStreamModeForParam());
|
||||
sendRtpItem.setStream(ssrcInfo.getStream());
|
||||
// 写入redis, 超时时回复
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
@ -552,43 +557,78 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
|
||||
}
|
||||
} else if (gbStream != null) {
|
||||
|
||||
String ssrc;
|
||||
if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) {
|
||||
// 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
|
||||
}else {
|
||||
ssrc = gb28181Sdp.getSsrc();
|
||||
SendRtpItem sendRtpItem = new SendRtpItem();
|
||||
if (!userSetting.getUseCustomSsrcForParentInvite() && gb28181Sdp.getSsrc() != null) {
|
||||
sendRtpItem.setSsrc(gb28181Sdp.getSsrc());
|
||||
}
|
||||
|
||||
if (tcpActive != null) {
|
||||
sendRtpItem.setTcpActive(tcpActive);
|
||||
}
|
||||
sendRtpItem.setTcp(mediaTransmissionTCP);
|
||||
sendRtpItem.setRtcp(platform.isRtcp());
|
||||
sendRtpItem.setPlatformName(platform.getName());
|
||||
sendRtpItem.setPlatformId(platform.getServerGBId());
|
||||
sendRtpItem.setMediaServerId(mediaServerItem.getId());
|
||||
sendRtpItem.setChannelId(channelId);
|
||||
sendRtpItem.setIp(addressStr);
|
||||
sendRtpItem.setPort(port);
|
||||
sendRtpItem.setUsePs(true);
|
||||
sendRtpItem.setApp(gbStream.getApp());
|
||||
sendRtpItem.setStream(gbStream.getStream());
|
||||
sendRtpItem.setCallId(callIdHeader.getCallId());
|
||||
sendRtpItem.setFromTag(request.getFromTag());
|
||||
sendRtpItem.setOnlyAudio(false);
|
||||
sendRtpItem.setStatus(0);
|
||||
sendRtpItem.setSessionName(sessionName);
|
||||
// 清理可能存在的缓存避免用到旧的数据
|
||||
List<SendRtpItem> sendRtpItemList = redisCatchStorage.querySendRTPServer(platform.getServerGBId(), channelId, gbStream.getStream());
|
||||
if (!sendRtpItemList.isEmpty()) {
|
||||
for (SendRtpItem rtpItem : sendRtpItemList) {
|
||||
redisCatchStorage.deleteSendRTPServer(rtpItem);
|
||||
}
|
||||
}
|
||||
if ("push".equals(gbStream.getStreamType())) {
|
||||
sendRtpItem.setPlayType(InviteStreamType.PUSH);
|
||||
if (streamPushItem != null) {
|
||||
// 从redis查询是否正在接收这个推流
|
||||
StreamPushItem pushListItem = redisCatchStorage.getPushListItem(gbStream.getApp(), gbStream.getStream());
|
||||
if (pushListItem != null) {
|
||||
sendRtpItem.setServerId(pushListItem.getServerId());
|
||||
sendRtpItem.setMediaServerId(pushListItem.getMediaServerId());
|
||||
|
||||
pushListItem.setSelf(userSetting.getServerId().equals(pushListItem.getServerId()));
|
||||
// 推流状态
|
||||
pushStream(evt, request, gbStream, pushListItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
// 开始推流
|
||||
sendPushStream(sendRtpItem, mediaServerItem, platform, request);
|
||||
}else {
|
||||
// 未推流 拉起
|
||||
notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
if (!platform.isStartOfflinePush()) {
|
||||
// 平台设置中关闭了拉起离线的推流则直接回复
|
||||
try {
|
||||
logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", sendRtpItem.getChannelId(), sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request);
|
||||
}
|
||||
}
|
||||
} else if ("proxy".equals(gbStream.getStreamType())) {
|
||||
if (null != proxyByAppAndStream) {
|
||||
if (sendRtpItem.getSsrc() == null) {
|
||||
// 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
String ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
|
||||
sendRtpItem.setSsrc(ssrc);
|
||||
}
|
||||
if (proxyByAppAndStream.isStatus()) {
|
||||
pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
sendProxyStream(sendRtpItem, mediaServerItem, platform, request);
|
||||
} else {
|
||||
//开启代理拉流
|
||||
notifyStreamOnline(evt, request, gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
notifyProxyStreamOnline(sendRtpItem, mediaServerItem, platform, request);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -614,18 +654,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
/**
|
||||
* 安排推流
|
||||
*/
|
||||
private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform,
|
||||
CallIdHeader callIdHeader, MediaServer mediaServer,
|
||||
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
|
||||
String channelId, String addressStr, String ssrc, String requesterId) {
|
||||
Boolean streamReady = mediaServerService.isStreamReady(mediaServer, gbStream.getApp(), gbStream.getStream());
|
||||
if (streamReady != null && streamReady) {
|
||||
private void sendProxyStream(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) {
|
||||
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
|
||||
if (mediaInfo != null) {
|
||||
|
||||
// 自平台内容
|
||||
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServer, addressStr, port, ssrc, requesterId,
|
||||
gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
|
||||
|
||||
if (sendRtpItem == null) {
|
||||
int localPort = sendRtpPortManager.getNextPort(mediaServerItem);
|
||||
if (localPort == 0) {
|
||||
logger.warn("服务器端口资源不足");
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
@ -634,38 +670,27 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (tcpActive != null) {
|
||||
sendRtpItem.setTcpActive(tcpActive);
|
||||
}
|
||||
sendRtpItem.setPlayType(InviteStreamType.PUSH);
|
||||
sendRtpItem.setPlayType(InviteStreamType.PROXY);
|
||||
// 写入redis, 超时时回复
|
||||
sendRtpItem.setStatus(1);
|
||||
sendRtpItem.setCallId(callIdHeader.getCallId());
|
||||
sendRtpItem.setFromTag(request.getFromTag());
|
||||
sendRtpItem.setLocalIp(mediaServerItem.getSdpIp());
|
||||
|
||||
SIPResponse response = sendStreamAck(mediaServer, request, sendRtpItem, platform, evt);
|
||||
SIPResponse response = sendStreamAck(request, sendRtpItem, platform);
|
||||
if (response != null) {
|
||||
sendRtpItem.setToTag(response.getToTag());
|
||||
}
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
|
||||
CallIdHeader callIdHeader, MediaServer mediaServerItem,
|
||||
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
|
||||
String channelId, String addressStr, String ssrc, String requesterId) {
|
||||
private void sendPushStream(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) {
|
||||
// 推流
|
||||
if (streamPushItem.isSelf()) {
|
||||
Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
|
||||
if (streamReady != null && streamReady) {
|
||||
if (sendRtpItem.getServerId().equals(userSetting.getServerId())) {
|
||||
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
if (mediaInfo != null ) {
|
||||
// 自平台内容
|
||||
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
|
||||
gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
|
||||
|
||||
if (sendRtpItem == null) {
|
||||
int localPort = sendRtpPortManager.getNextPort(mediaServerItem);
|
||||
if (localPort == 0) {
|
||||
logger.warn("服务器端口资源不足");
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
@ -674,146 +699,135 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (tcpActive != null) {
|
||||
sendRtpItem.setTcpActive(tcpActive);
|
||||
}
|
||||
sendRtpItem.setPlayType(InviteStreamType.PUSH);
|
||||
// 写入redis, 超时时回复
|
||||
sendRtpItem.setStatus(1);
|
||||
sendRtpItem.setCallId(callIdHeader.getCallId());
|
||||
|
||||
sendRtpItem.setFromTag(request.getFromTag());
|
||||
SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
|
||||
SIPResponse response = sendStreamAck(request, sendRtpItem, platform);
|
||||
if (response != null) {
|
||||
sendRtpItem.setToTag(response.getToTag());
|
||||
}
|
||||
if (sendRtpItem.getSsrc() == null) {
|
||||
// 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
|
||||
sendRtpItem.setSsrc(ssrc);
|
||||
}
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
|
||||
} else {
|
||||
// 不在线 拉起
|
||||
notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
notifyPushStreamOnline(sendRtpItem, mediaServerItem, platform, request);
|
||||
}
|
||||
|
||||
} else {
|
||||
// 其他平台内容
|
||||
otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
otherWvpPushStream(sendRtpItem, request, platform);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知流上线
|
||||
*/
|
||||
private void notifyStreamOnline(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
|
||||
CallIdHeader callIdHeader, MediaServer mediaServerItem,
|
||||
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
|
||||
String channelId, String addressStr, String ssrc, String requesterId) {
|
||||
if ("proxy".equals(gbStream.getStreamType())) {
|
||||
private void notifyProxyStreamOnline(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) {
|
||||
// TODO 控制启用以使设备上线
|
||||
logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
|
||||
logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
// 监听流上线
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, gbStream.getApp(), gbStream.getStream(), mediaServerItem.getId());
|
||||
this.hookSubscribe.addSubscribe(hook, (hookData) -> {
|
||||
logger.info("[上级点播]拉流代理已经就绪, {}/{}", hookData.getApp(), hookData.getStream());
|
||||
dynamicTask.stop(callIdHeader.getCallId());
|
||||
pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), mediaServerItem.getId());
|
||||
hookSubscribe.addSubscribe(hook, (hookData)->{
|
||||
logger.info("[上级点播]拉流代理已经就绪, {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
dynamicTask.stop(sendRtpItem.getCallId());
|
||||
sendProxyStream(sendRtpItem, mediaServerItem, platform, request);
|
||||
});
|
||||
dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
|
||||
logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream());
|
||||
this.hookSubscribe.removeSubscribe(hook);
|
||||
dynamicTask.startDelay(sendRtpItem.getCallId(), () -> {
|
||||
logger.info("[ app={}, stream={} ] 等待拉流代理流超时", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
hookSubscribe.removeSubscribe(hook);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream());
|
||||
boolean start = streamProxyService.start(sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
if (!start) {
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE, "channel [" + gbStream.getGbId() + "] offline");
|
||||
responseAck(request, Response.BUSY_HERE, "channel [" + sendRtpItem.getChannelId() + "] offline");
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
|
||||
}
|
||||
this.hookSubscribe.removeSubscribe(hook);
|
||||
dynamicTask.stop(callIdHeader.getCallId());
|
||||
hookSubscribe.removeSubscribe(hook);
|
||||
dynamicTask.stop(sendRtpItem.getCallId());
|
||||
}
|
||||
} else if ("push".equals(gbStream.getStreamType())) {
|
||||
if (!platform.isStartOfflinePush()) {
|
||||
// 平台设置中关闭了拉起离线的推流则直接回复
|
||||
try {
|
||||
logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream());
|
||||
responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 发送redis消息以使设备上线
|
||||
logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream());
|
||||
|
||||
/**
|
||||
* 通知流上线
|
||||
*/
|
||||
private void notifyPushStreamOnline(SendRtpItem sendRtpItem, MediaServer mediaServerItem, ParentPlatform platform, SIPRequest request) {
|
||||
// 发送redis消息以使设备上线,流上线后被
|
||||
logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1,
|
||||
gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(),
|
||||
platform.getName(), null, gbStream.getMediaServerId());
|
||||
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(), sendRtpItem.getPlatformId(),
|
||||
platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
|
||||
redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
|
||||
// 设置超时
|
||||
dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
|
||||
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
|
||||
dynamicTask.startDelay(sendRtpItem.getCallId(), () -> {
|
||||
redisRpcService.stopWaitePushStreamOnline(sendRtpItem);
|
||||
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
try {
|
||||
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
|
||||
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
|
||||
responseAck(request, Response.REQUEST_TIMEOUT); // 超时
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
// 添加监听
|
||||
int finalPort = port;
|
||||
Boolean finalTcpActive = tcpActive;
|
||||
|
||||
// 添加在本机上线的通知
|
||||
mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> {
|
||||
dynamicTask.stop(callIdHeader.getCallId());
|
||||
redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
|
||||
if (serverId.equals(userSetting.getServerId())) {
|
||||
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
|
||||
app, stream, channelId, mediaTransmissionTCP, platform.isRtcp());
|
||||
|
||||
if (sendRtpItem == null) {
|
||||
logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足");
|
||||
//
|
||||
long key = redisRpcService.waitePushStreamOnline(sendRtpItem, (sendRtpItemKey) -> {
|
||||
dynamicTask.stop(sendRtpItem.getCallId());
|
||||
if (sendRtpItemKey == null) {
|
||||
logger.warn("[级联点播] 等待推流得到结果未空: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
} catch (SipException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (ParseException e) {
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (finalTcpActive != null) {
|
||||
sendRtpItem.setTcpActive(finalTcpActive);
|
||||
SendRtpItem sendRtpItemFromRedis = (SendRtpItem)redisTemplate.opsForValue().get(sendRtpItemKey);
|
||||
if (sendRtpItemFromRedis == null) {
|
||||
logger.warn("[级联点播] 等待推流, 未找到redis中缓存的发流信息: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
sendRtpItem.setPlayType(InviteStreamType.PUSH);
|
||||
return;
|
||||
}
|
||||
if (sendRtpItemFromRedis.getServerId().equals(userSetting.getServerId())) {
|
||||
logger.info("[级联点播] 等待的推流在本平台上线 {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
int localPort = sendRtpPortManager.getNextPort(mediaServerItem);
|
||||
if (localPort == 0) {
|
||||
logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足");
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sendRtpItem.setLocalPort(localPort);
|
||||
if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) {
|
||||
sendRtpItem.setLocalIp(platform.getSendStreamIp());
|
||||
}
|
||||
|
||||
// 写入redis, 超时时回复
|
||||
sendRtpItem.setStatus(1);
|
||||
sendRtpItem.setCallId(callIdHeader.getCallId());
|
||||
|
||||
sendRtpItem.setFromTag(request.getFromTag());
|
||||
SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
|
||||
SIPResponse response = sendStreamAck(request, sendRtpItem, platform);
|
||||
if (response != null) {
|
||||
sendRtpItem.setToTag(response.getToTag());
|
||||
}
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
} else {
|
||||
// 其他平台内容
|
||||
otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
otherWvpPushStream(sendRtpItemFromRedis, request, platform);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加回复的拒绝或者错误的通知
|
||||
redisPushStreamResponseListener.addEvent(gbStream.getApp(), gbStream.getStream(), response -> {
|
||||
// redis消息例如: PUBLISH VM_MSG_STREAM_PUSH_RESPONSE '{"code":1,"msg":"失败","app":"1","stream":"2"}'
|
||||
redisPushStreamResponseListener.addEvent(sendRtpItem.getApp(), sendRtpItem.getStream(), response -> {
|
||||
if (response.getCode() != 0) {
|
||||
dynamicTask.stop(callIdHeader.getCallId());
|
||||
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
|
||||
dynamicTask.stop(sendRtpItem.getCallId());
|
||||
redisRpcService.stopWaitePushStreamOnline(sendRtpItem);
|
||||
redisRpcService.removeCallback(key);
|
||||
try {
|
||||
responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg());
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
@ -822,78 +836,30 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 来自其他wvp的推流
|
||||
*/
|
||||
private void otherWvpPushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
|
||||
CallIdHeader callIdHeader, MediaServer mediaServerItem,
|
||||
int port, Boolean tcpActive, boolean mediaTransmissionTCP,
|
||||
String channelId, String addressStr, String ssrc, String requesterId) {
|
||||
logger.info("[级联点播]直播流来自其他平台,发送redis消息");
|
||||
// 发送redis消息
|
||||
redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(),
|
||||
streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId,
|
||||
channelId, mediaTransmissionTCP, platform.isRtcp(),platform.getName(), responseSendItemMsg -> {
|
||||
SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem();
|
||||
if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) {
|
||||
logger.warn("服务器端口资源不足");
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
} catch (SipException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (InvalidArgumentException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
} catch (ParseException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
private void otherWvpPushStream(SendRtpItem sendRtpItem, SIPRequest request, ParentPlatform platform) {
|
||||
logger.info("[级联点播] 来自其他wvp的推流 {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
sendRtpItem = redisRpcService.getSendRtpItem(sendRtpItem.getRedisKey());
|
||||
if (sendRtpItem == null) {
|
||||
return;
|
||||
}
|
||||
// 收到sendItem
|
||||
if (tcpActive != null) {
|
||||
sendRtpItem.setTcpActive(tcpActive);
|
||||
}
|
||||
sendRtpItem.setPlayType(InviteStreamType.PUSH);
|
||||
// 写入redis, 超时时回复
|
||||
sendRtpItem.setStatus(1);
|
||||
sendRtpItem.setCallId(callIdHeader.getCallId());
|
||||
|
||||
sendRtpItem.setFromTag(request.getFromTag());
|
||||
SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), request, sendRtpItem, platform, evt);
|
||||
SIPResponse response = sendStreamAck(request, sendRtpItem, platform);
|
||||
if (response != null) {
|
||||
sendRtpItem.setToTag(response.getToTag());
|
||||
}
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
}, (wvpResult) -> {
|
||||
|
||||
// 错误
|
||||
if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) {
|
||||
// 离线
|
||||
// 查询是否在本机上线了
|
||||
StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream());
|
||||
if (currentStreamPushItem.isPushIng()) {
|
||||
// 在线状态
|
||||
pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
|
||||
} else {
|
||||
// 不在线 拉起
|
||||
notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
|
||||
mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
|
||||
}
|
||||
}
|
||||
try {
|
||||
responseAck(request, Response.BUSY_HERE);
|
||||
} catch (InvalidArgumentException | ParseException | SipException e) {
|
||||
logger.error("[命令发送失败] 国标级联 点播回复 BUSY_HERE: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public SIPResponse sendStreamAck(MediaServer mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
|
||||
public SIPResponse sendStreamAck(SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform) {
|
||||
|
||||
String sdpIp = mediaServerItem.getSdpIp();
|
||||
String sdpIp = sendRtpItem.getLocalIp();
|
||||
if (!ObjectUtils.isEmpty(platform.getSendStreamIp())) {
|
||||
sdpIp = platform.getSendStreamIp();
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.CivilCodeFileConf;
|
||||
import com.genersoft.iot.vmp.conf.DynamicTask;
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.HandlerCatchData;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
@ -19,7 +19,9 @@ import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.sip.RequestEvent;
|
||||
import javax.sip.header.FromHeader;
|
||||
@ -28,6 +30,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
@ -46,6 +49,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
private final Map<String, DeviceChannel> addChannelMap = new ConcurrentHashMap<>();
|
||||
private final List<DeviceChannel> deleteChannelList = new CopyOnWriteArrayList<>();
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
@ -62,15 +66,28 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
@Autowired
|
||||
private DynamicTask dynamicTask;
|
||||
|
||||
@Autowired
|
||||
private CivilCodeFileConf civilCodeFileConf;
|
||||
|
||||
@Autowired
|
||||
private SipConfig sipConfig;
|
||||
|
||||
private final static String talkKey = "notify-request-for-catalog-task";
|
||||
|
||||
@Transactional
|
||||
public void process(RequestEvent evt) {
|
||||
if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) {
|
||||
logger.error("[notify-目录订阅] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue());
|
||||
return;
|
||||
}
|
||||
taskQueue.offer(new HandlerCatchData(evt, null, null));
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 400) //每400毫秒执行一次
|
||||
public void executeTaskQueue(){
|
||||
if (taskQueue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (HandlerCatchData take : taskQueue) {
|
||||
if (take == null) {
|
||||
continue;
|
||||
}
|
||||
RequestEvent evt = take.getEvt();
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
|
||||
@ -78,7 +95,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
if (device == null || !device.isOnLine()) {
|
||||
logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" ));
|
||||
logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId() : ""));
|
||||
return;
|
||||
}
|
||||
Element rootElement = getRootElement(evt, device.getCharset());
|
||||
@ -96,16 +113,12 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
// 遍历DeviceList
|
||||
while (deviceListIterator.hasNext()) {
|
||||
Element itemDevice = deviceListIterator.next();
|
||||
Element channelDeviceElement = itemDevice.element("DeviceID");
|
||||
if (channelDeviceElement == null) {
|
||||
continue;
|
||||
}
|
||||
Element eventElement = itemDevice.element("Event");
|
||||
String event;
|
||||
if (eventElement == null) {
|
||||
logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
|
||||
logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId() : ""));
|
||||
event = CatalogEvent.ADD;
|
||||
}else {
|
||||
} else {
|
||||
event = eventElement.getText().toUpperCase();
|
||||
}
|
||||
DeviceChannel channel = XmlUtil.channelContentHandler(itemDevice, device, event);
|
||||
@ -123,25 +136,18 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
// 上线
|
||||
logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
updateChannelOnlineList.add(channel);
|
||||
if (updateChannelOnlineList.size() > 300) {
|
||||
executeSaveForOnline();
|
||||
}
|
||||
if (userSetting.getDeviceStatusNotify()) {
|
||||
// 发送redis消息
|
||||
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), true);
|
||||
}
|
||||
|
||||
break;
|
||||
case CatalogEvent.OFF :
|
||||
case CatalogEvent.OFF:
|
||||
// 离线
|
||||
logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
|
||||
logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
}else {
|
||||
} else {
|
||||
updateChannelOfflineList.add(channel);
|
||||
if (updateChannelOfflineList.size() > 300) {
|
||||
executeSaveForOffline();
|
||||
}
|
||||
if (userSetting.getDeviceStatusNotify()) {
|
||||
// 发送redis消息
|
||||
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false);
|
||||
@ -153,11 +159,8 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
|
||||
logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
}else {
|
||||
} else {
|
||||
updateChannelOfflineList.add(channel);
|
||||
if (updateChannelOfflineList.size() > 300) {
|
||||
executeSaveForOffline();
|
||||
}
|
||||
if (userSetting.getDeviceStatusNotify()) {
|
||||
// 发送redis消息
|
||||
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false);
|
||||
@ -169,11 +172,8 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
logger.info("[收到通道视频故障通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
|
||||
logger.info("[收到通道视频故障通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
}else {
|
||||
} else {
|
||||
updateChannelOfflineList.add(channel);
|
||||
if (updateChannelOfflineList.size() > 300) {
|
||||
executeSaveForOffline();
|
||||
}
|
||||
if (userSetting.getDeviceStatusNotify()) {
|
||||
// 发送redis消息
|
||||
redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false);
|
||||
@ -188,20 +188,14 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
if (deviceChannel != null) {
|
||||
logger.info("[增加通道] 已存在,不发送通知只更新,设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
|
||||
channel.setId(deviceChannel.getId());
|
||||
channel.setHasAudio(null);
|
||||
updateChannelMap.put(channel.getChannelId(), channel);
|
||||
if (updateChannelMap.keySet().size() > 300) {
|
||||
executeSaveForUpdate();
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
addChannelMap.put(channel.getChannelId(), channel);
|
||||
if (userSetting.getDeviceStatusNotify()) {
|
||||
// 发送redis消息
|
||||
redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true);
|
||||
}
|
||||
|
||||
if (addChannelMap.keySet().size() > 300) {
|
||||
executeSaveForAdd();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@ -213,9 +207,6 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
// 发送redis消息
|
||||
redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), false);
|
||||
}
|
||||
if (deleteChannelList.size() > 300) {
|
||||
executeSaveForDelete();
|
||||
}
|
||||
break;
|
||||
case CatalogEvent.UPDATE:
|
||||
// 更新
|
||||
@ -225,15 +216,10 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
if (deviceChannelForUpdate != null) {
|
||||
channel.setId(deviceChannelForUpdate.getId());
|
||||
channel.setUpdateTime(DateUtil.getNow());
|
||||
channel.setHasAudio(null);
|
||||
updateChannelMap.put(channel.getChannelId(), channel);
|
||||
if (updateChannelMap.keySet().size() > 300) {
|
||||
executeSaveForUpdate();
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
addChannelMap.put(channel.getChannelId(), channel);
|
||||
if (addChannelMap.keySet().size() > 300) {
|
||||
executeSaveForAdd();
|
||||
}
|
||||
if (userSetting.getDeviceStatusNotify()) {
|
||||
// 发送redis消息
|
||||
redisCatchStorage.sendChannelAddOrDelete(device.getDeviceId(), channel.getChannelId(), true);
|
||||
@ -241,45 +227,34 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("[ NotifyCatalog ] event not found : {}", event );
|
||||
logger.warn("[ NotifyCatalog ] event not found : {}", event);
|
||||
|
||||
}
|
||||
// 转发变化信息
|
||||
eventPublisher.catalogEventPublish(null, channel, event);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (DocumentException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
taskQueue.clear();
|
||||
if (!updateChannelMap.keySet().isEmpty()
|
||||
|| !addChannelMap.keySet().isEmpty()
|
||||
|| !updateChannelOnlineList.isEmpty()
|
||||
|| !updateChannelOfflineList.isEmpty()
|
||||
|| !deleteChannelList.isEmpty()) {
|
||||
|
||||
if (!dynamicTask.contains(talkKey)) {
|
||||
dynamicTask.startDelay(talkKey, this::executeSave, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
executeSave();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSave(){
|
||||
public void executeSave(){
|
||||
try {
|
||||
executeSaveForAdd();
|
||||
} catch (Exception e) {
|
||||
logger.error("[存储收到的增加通道] 异常: ", e );
|
||||
}
|
||||
try {
|
||||
executeSaveForUpdate();
|
||||
} catch (Exception e) {
|
||||
logger.error("[存储收到的更新通道] 异常: ", e );
|
||||
}
|
||||
try {
|
||||
executeSaveForDelete();
|
||||
} catch (Exception e) {
|
||||
logger.error("[存储收到的删除通道] 异常: ", e );
|
||||
}
|
||||
try {
|
||||
executeSaveForOnline();
|
||||
} catch (Exception e) {
|
||||
@ -290,16 +265,25 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
} catch (Exception e) {
|
||||
logger.error("[存储收到的通道离线] 异常: ", e );
|
||||
}
|
||||
dynamicTask.stop(talkKey);
|
||||
try {
|
||||
executeSaveForUpdate();
|
||||
} catch (Exception e) {
|
||||
logger.error("[存储收到的更新通道] 异常: ", e );
|
||||
}
|
||||
try {
|
||||
executeSaveForDelete();
|
||||
} catch (Exception e) {
|
||||
logger.error("[存储收到的删除通道] 异常: ", e );
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSaveForUpdate(){
|
||||
if (!updateChannelMap.values().isEmpty()) {
|
||||
logger.info("[存储收到的更新通道], 数量: {}", updateChannelMap.size());
|
||||
ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values());
|
||||
updateChannelMap.clear();
|
||||
deviceChannelService.batchUpdateChannel(deviceChannels);
|
||||
updateChannelMap.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void executeSaveForAdd(){
|
||||
@ -331,4 +315,8 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 10000) //每1秒执行一次
|
||||
public void execute(){
|
||||
logger.info("[待处理Notify-目录订阅消息数量]: {}", taskQueue.size());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,195 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.HandlerCatchData;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.service.IDeviceChannelService;
|
||||
import com.genersoft.iot.vmp.service.IMobilePositionService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.sip.RequestEvent;
|
||||
import javax.sip.header.FromHeader;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* SIP命令类型: NOTIFY请求中的移动位置请求处理
|
||||
*/
|
||||
@Component
|
||||
public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessorParent {
|
||||
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(NotifyRequestForMobilePositionProcessor.class);
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private EventPublisher eventPublisher;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IDeviceChannelService deviceChannelService;
|
||||
|
||||
@Autowired
|
||||
private IMobilePositionService mobilePositionService;
|
||||
|
||||
public void process(RequestEvent evt) {
|
||||
|
||||
if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) {
|
||||
logger.error("[notify-移动位置] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue());
|
||||
return;
|
||||
}
|
||||
taskQueue.offer(new HandlerCatchData(evt, null, null));
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 200) //每200毫秒执行一次
|
||||
public void executeTaskQueue() {
|
||||
if (taskQueue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (HandlerCatchData take : taskQueue) {
|
||||
if (take == null) {
|
||||
continue;
|
||||
}
|
||||
RequestEvent evt = take.getEvt();
|
||||
try {
|
||||
FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
|
||||
String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 回复 200 OK
|
||||
Element rootElement = getRootElement(evt);
|
||||
if (rootElement == null) {
|
||||
logger.error("处理MobilePosition移动位置Notify时未获取到消息体,{}", evt.getRequest());
|
||||
return;
|
||||
}
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
if (device == null) {
|
||||
logger.error("处理MobilePosition移动位置Notify时未获取到device,{}", deviceId);
|
||||
return;
|
||||
}
|
||||
MobilePosition mobilePosition = new MobilePosition();
|
||||
mobilePosition.setDeviceId(device.getDeviceId());
|
||||
mobilePosition.setDeviceName(device.getName());
|
||||
mobilePosition.setCreateTime(DateUtil.getNow());
|
||||
List<Element> elements = rootElement.elements();
|
||||
for (Element element : elements) {
|
||||
switch (element.getName()){
|
||||
case "DeviceID":
|
||||
String channelId = element.getStringValue();
|
||||
if (!deviceId.equals(channelId)) {
|
||||
mobilePosition.setChannelId(channelId);
|
||||
}
|
||||
continue;
|
||||
case "Time":
|
||||
String timeVal = element.getStringValue();
|
||||
if (ObjectUtils.isEmpty(timeVal)) {
|
||||
mobilePosition.setTime(DateUtil.getNow());
|
||||
} else {
|
||||
mobilePosition.setTime(SipUtils.parseTime(timeVal));
|
||||
}
|
||||
continue;
|
||||
case "Longitude":
|
||||
mobilePosition.setLongitude(Double.parseDouble(element.getStringValue()));
|
||||
continue;
|
||||
case "Latitude":
|
||||
mobilePosition.setLatitude(Double.parseDouble(element.getStringValue()));
|
||||
continue;
|
||||
case "Speed":
|
||||
String speedVal = element.getStringValue();
|
||||
if (NumericUtil.isDouble(speedVal)) {
|
||||
mobilePosition.setSpeed(Double.parseDouble(speedVal));
|
||||
} else {
|
||||
mobilePosition.setSpeed(0.0);
|
||||
}
|
||||
continue;
|
||||
case "Direction":
|
||||
String directionVal = element.getStringValue();
|
||||
if (NumericUtil.isDouble(directionVal)) {
|
||||
mobilePosition.setDirection(Double.parseDouble(directionVal));
|
||||
} else {
|
||||
mobilePosition.setDirection(0.0);
|
||||
}
|
||||
continue;
|
||||
case "Altitude":
|
||||
String altitudeVal = element.getStringValue();
|
||||
if (NumericUtil.isDouble(altitudeVal)) {
|
||||
mobilePosition.setAltitude(Double.parseDouble(altitudeVal));
|
||||
} else {
|
||||
mobilePosition.setAltitude(0.0);
|
||||
}
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("[收到移动位置订阅通知]:{}/{}->{}.{}, 时间: {}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(),
|
||||
mobilePosition.getLongitude(), mobilePosition.getLatitude(), System.currentTimeMillis() - startTime);
|
||||
mobilePosition.setReportSource("Mobile Position");
|
||||
|
||||
mobilePositionService.add(mobilePosition);
|
||||
// 向关联了该通道并且开启移动位置订阅的上级平台发送移动位置订阅消息
|
||||
try {
|
||||
eventPublisher.mobilePositionEventPublish(mobilePosition);
|
||||
}catch (Exception e) {
|
||||
logger.error("[向上级转发移动位置失败] ", e);
|
||||
}
|
||||
if (mobilePosition.getChannelId() == null || mobilePosition.getChannelId().equals(mobilePosition.getDeviceId())) {
|
||||
List<DeviceChannel> channels = deviceChannelService.queryChaneListByDeviceId(mobilePosition.getDeviceId());
|
||||
channels.forEach(channel -> {
|
||||
// 发送redis消息。 通知位置信息的变化
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime()));
|
||||
jsonObject.put("serial", channel.getDeviceId());
|
||||
jsonObject.put("code", channel.getChannelId());
|
||||
jsonObject.put("longitude", mobilePosition.getLongitude());
|
||||
jsonObject.put("latitude", mobilePosition.getLatitude());
|
||||
jsonObject.put("altitude", mobilePosition.getAltitude());
|
||||
jsonObject.put("direction", mobilePosition.getDirection());
|
||||
jsonObject.put("speed", mobilePosition.getSpeed());
|
||||
redisCatchStorage.sendMobilePositionMsg(jsonObject);
|
||||
});
|
||||
}else {
|
||||
// 发送redis消息。 通知位置信息的变化
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("time", DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(mobilePosition.getTime()));
|
||||
jsonObject.put("serial", mobilePosition.getDeviceId());
|
||||
jsonObject.put("code", mobilePosition.getChannelId());
|
||||
jsonObject.put("longitude", mobilePosition.getLongitude());
|
||||
jsonObject.put("latitude", mobilePosition.getLatitude());
|
||||
jsonObject.put("altitude", mobilePosition.getAltitude());
|
||||
jsonObject.put("direction", mobilePosition.getDirection());
|
||||
jsonObject.put("speed", mobilePosition.getSpeed());
|
||||
redisCatchStorage.sendMobilePositionMsg(jsonObject);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
}
|
||||
taskQueue.clear();
|
||||
}
|
||||
@Scheduled(fixedRate = 10000)
|
||||
public void execute(){
|
||||
logger.info("[待处理Notify-移动位置订阅消息数量]: {}", taskQueue.size());
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
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.NumericUtil;
|
||||
@ -14,9 +11,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import com.genersoft.iot.vmp.service.IDeviceChannelService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
import gov.nist.javax.sip.message.SIPRequest;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.Element;
|
||||
@ -24,10 +19,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.RequestEvent;
|
||||
@ -35,8 +27,6 @@ import javax.sip.SipException;
|
||||
import javax.sip.header.FromHeader;
|
||||
import javax.sip.message.Response;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* SIP命令类型: NOTIFY请求,这是作为上级发送订阅请求后,设备才会响应的
|
||||
@ -47,15 +37,6 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(NotifyRequestProcessor.class);
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private EventPublisher eventPublisher;
|
||||
|
||||
@Autowired
|
||||
private SipConfig sipConfig;
|
||||
|
||||
@ -76,13 +57,8 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Autowired
|
||||
private NotifyRequestForCatalogProcessor notifyRequestForCatalogProcessor;
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
private int maxQueueCount = 30000;
|
||||
private NotifyRequestForMobilePositionProcessor notifyRequestForMobilePositionProcessor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@ -93,152 +69,33 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
@Override
|
||||
public void process(RequestEvent evt) {
|
||||
try {
|
||||
if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) {
|
||||
responseAck((SIPRequest) evt.getRequest(), Response.BUSY_HERE, null, null);
|
||||
logger.error("[notify] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue());
|
||||
return;
|
||||
}else {
|
||||
responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
|
||||
}
|
||||
|
||||
}catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
boolean runed = !taskQueue.isEmpty();
|
||||
logger.info("[notify] 待处理消息数量: {}", taskQueue.size());
|
||||
taskQueue.offer(new HandlerCatchData(evt, null, null));
|
||||
if (!runed) {
|
||||
taskExecutor.execute(()-> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
try {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
if (take == null) {
|
||||
continue;
|
||||
}
|
||||
Element rootElement = getRootElement(take.getEvt());
|
||||
Element rootElement = getRootElement(evt);
|
||||
if (rootElement == null) {
|
||||
logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest());
|
||||
continue;
|
||||
logger.error("处理NOTIFY消息时未获取到消息体,{}", evt.getRequest());
|
||||
responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
|
||||
return;
|
||||
}
|
||||
String cmd = XmlUtil.getText(rootElement, "CmdType");
|
||||
|
||||
if (CmdType.CATALOG.equals(cmd)) {
|
||||
logger.info("接收到Catalog通知");
|
||||
notifyRequestForCatalogProcessor.process(take.getEvt());
|
||||
notifyRequestForCatalogProcessor.process(evt);
|
||||
} else if (CmdType.ALARM.equals(cmd)) {
|
||||
logger.info("接收到Alarm通知");
|
||||
processNotifyAlarm(take.getEvt());
|
||||
processNotifyAlarm(evt);
|
||||
} else if (CmdType.MOBILE_POSITION.equals(cmd)) {
|
||||
logger.info("接收到MobilePosition通知");
|
||||
processNotifyMobilePosition(take.getEvt());
|
||||
notifyRequestForMobilePositionProcessor.process(evt);
|
||||
} else {
|
||||
logger.info("接收到消息:" + cmd);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
logger.error("处理NOTIFY消息时错误", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理MobilePosition移动位置Notify
|
||||
*
|
||||
* @param evt
|
||||
*/
|
||||
private void processNotifyMobilePosition(RequestEvent evt) {
|
||||
try {
|
||||
FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
|
||||
String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
|
||||
|
||||
// 回复 200 OK
|
||||
Element rootElement = getRootElement(evt);
|
||||
if (rootElement == null) {
|
||||
logger.error("处理MobilePosition移动位置Notify时未获取到消息体,{}", evt.getRequest());
|
||||
return;
|
||||
}
|
||||
|
||||
MobilePosition mobilePosition = new MobilePosition();
|
||||
mobilePosition.setCreateTime(DateUtil.getNow());
|
||||
|
||||
Element deviceIdElement = rootElement.element("DeviceID");
|
||||
String channelId = deviceIdElement.getTextTrim().toString();
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
|
||||
if (device == null) {
|
||||
device = redisCatchStorage.getDevice(channelId);
|
||||
if (device == null) {
|
||||
// 根据通道id查询设备Id
|
||||
List<Device> deviceList = deviceChannelService.getDeviceByChannelId(channelId);
|
||||
if (deviceList.size() > 0) {
|
||||
device = deviceList.get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (device == null) {
|
||||
logger.warn("[mobilePosition移动位置Notify] 未找到通道{}所属的设备", channelId);
|
||||
return;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(device.getName())) {
|
||||
mobilePosition.setDeviceName(device.getName());
|
||||
}
|
||||
|
||||
mobilePosition.setDeviceId(device.getDeviceId());
|
||||
mobilePosition.setChannelId(channelId);
|
||||
String time = XmlUtil.getText(rootElement, "Time");
|
||||
if (ObjectUtils.isEmpty(time)){
|
||||
mobilePosition.setTime(DateUtil.getNow());
|
||||
}else {
|
||||
mobilePosition.setTime(SipUtils.parseTime(time));
|
||||
}
|
||||
|
||||
mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
|
||||
mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
|
||||
if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) {
|
||||
mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed")));
|
||||
} else {
|
||||
mobilePosition.setSpeed(0.0);
|
||||
}
|
||||
if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) {
|
||||
mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction")));
|
||||
} else {
|
||||
mobilePosition.setDirection(0.0);
|
||||
}
|
||||
if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) {
|
||||
mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude")));
|
||||
} else {
|
||||
mobilePosition.setAltitude(0.0);
|
||||
}
|
||||
logger.info("[收到移动位置订阅通知]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(),
|
||||
mobilePosition.getLongitude(), mobilePosition.getLatitude());
|
||||
mobilePosition.setReportSource("Mobile Position");
|
||||
|
||||
// 更新device channel 的经纬度
|
||||
DeviceChannel deviceChannel = new DeviceChannel();
|
||||
deviceChannel.setDeviceId(device.getDeviceId());
|
||||
deviceChannel.setChannelId(channelId);
|
||||
deviceChannel.setLongitude(mobilePosition.getLongitude());
|
||||
deviceChannel.setLatitude(mobilePosition.getLatitude());
|
||||
deviceChannel.setGpsTime(mobilePosition.getTime());
|
||||
deviceChannel = deviceChannelService.updateGps(deviceChannel, device);
|
||||
|
||||
mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84());
|
||||
mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84());
|
||||
mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02());
|
||||
mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02());
|
||||
|
||||
deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition);
|
||||
|
||||
} catch (DocumentException e) {
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("未处理的异常 ", e);
|
||||
}
|
||||
} catch (DocumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
/***
|
||||
* 处理alarm设备报警Notify
|
||||
*
|
||||
* @param evt
|
||||
*/
|
||||
private void processNotifyAlarm(RequestEvent evt) {
|
||||
if (!sipConfig.isAlarm()) {
|
||||
@ -329,28 +186,5 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
||||
}
|
||||
}
|
||||
|
||||
public void setCmder(SIPCommander cmder) {
|
||||
}
|
||||
|
||||
public void setStorager(IVideoManagerStorage storager) {
|
||||
this.storager = storager;
|
||||
}
|
||||
|
||||
public void setPublisher(EventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
public void setRedis(RedisUtil redis) {
|
||||
}
|
||||
|
||||
public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
|
||||
}
|
||||
|
||||
public IRedisCatchStorage getRedisCatchStorage() {
|
||||
return redisCatchStorage;
|
||||
}
|
||||
|
||||
public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
|
||||
this.redisCatchStorage = redisCatchStorage;
|
||||
}
|
||||
}
|
||||
|
||||
@ -532,16 +532,17 @@ public class XmlUtil {
|
||||
String status = getText(itemDevice, "Status");
|
||||
if (status != null) {
|
||||
// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
|
||||
if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) {
|
||||
if (status.equalsIgnoreCase("ON") || status.equalsIgnoreCase("On") || status.equalsIgnoreCase("ONLINE") || status.equalsIgnoreCase("OK")) {
|
||||
deviceChannel.setStatus(true);
|
||||
}
|
||||
if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
|
||||
if (status.equalsIgnoreCase("OFF") || status.equalsIgnoreCase("Off") || status.equalsIgnoreCase("OFFLINE")) {
|
||||
deviceChannel.setStatus(false);
|
||||
}
|
||||
}else {
|
||||
deviceChannel.setStatus(true);
|
||||
}
|
||||
|
||||
// logger.info("状态字符串: {}", status);
|
||||
// logger.info("状态结果: {}", deviceChannel.isStatus());
|
||||
// 经度
|
||||
String longitude = getText(itemDevice, "Longitude");
|
||||
if (NumericUtil.isDouble(longitude)) {
|
||||
|
||||
@ -101,6 +101,9 @@ public class MediaServer {
|
||||
@Schema(description = "类型: zlm/abl")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "转码的前缀")
|
||||
private String transcodeSuffix;
|
||||
|
||||
public MediaServer() {
|
||||
}
|
||||
|
||||
@ -127,6 +130,7 @@ public class MediaServer {
|
||||
rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
|
||||
rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号
|
||||
recordAssistPort = 0; // 默认关闭
|
||||
transcodeSuffix = zlmServerConfig.getTranscodeSuffix();
|
||||
|
||||
}
|
||||
|
||||
@ -403,4 +407,12 @@ public class MediaServer {
|
||||
public void setWsFlvSSLPort(int wsFlvSSLPort) {
|
||||
this.wsFlvSSLPort = wsFlvSSLPort;
|
||||
}
|
||||
|
||||
public String getTranscodeSuffix() {
|
||||
return transcodeSuffix;
|
||||
}
|
||||
|
||||
public void setTranscodeSuffix(String transcodeSuffix) {
|
||||
this.transcodeSuffix = transcodeSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ public class RecordInfo {
|
||||
private String url;
|
||||
private long startTime;
|
||||
private double timeLen;
|
||||
private String params;
|
||||
|
||||
public static RecordInfo getInstance(OnRecordMp4HookParam hookParam) {
|
||||
RecordInfo recordInfo = new RecordInfo();
|
||||
@ -86,6 +87,14 @@ public class RecordInfo {
|
||||
this.timeLen = timeLen;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(String params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RecordInfo{" +
|
||||
@ -94,6 +103,7 @@ public class RecordInfo {
|
||||
", 文件大小=" + fileSize +
|
||||
", 开始时间=" + startTime +
|
||||
", 时长=" + timeLen +
|
||||
", params=" + params +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,8 @@ public interface IMediaNodeServerService {
|
||||
|
||||
boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
|
||||
|
||||
boolean initStopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
|
||||
|
||||
boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName);
|
||||
|
||||
List<StreamInfo> getMediaList(MediaServer mediaServer, String app, String stream, String callId);
|
||||
|
||||
@ -76,6 +76,8 @@ public interface IMediaServerService {
|
||||
|
||||
boolean stopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
|
||||
|
||||
boolean initStopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc);
|
||||
|
||||
boolean deleteRecordDirectory(MediaServer mediaServerItem, String app, String stream, String date, String fileName);
|
||||
|
||||
List<StreamInfo> getMediaList(MediaServer mediaInfo, String app, String stream, String callId);
|
||||
@ -147,4 +149,6 @@ public interface IMediaServerService {
|
||||
|
||||
SendRtpItem createSendRtpItem(MediaServer serverItem, String ip, int port, String ssrc, String platformId,
|
||||
String app, String stream, String channelId, boolean tcp, boolean rtcp);
|
||||
|
||||
MediaServer getMediaServerByAppAndStream(String app, String stream);
|
||||
}
|
||||
|
||||
@ -115,6 +115,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
removeCount(event.getMediaServer().getId());
|
||||
MediaInfo mediaInfo = redisCatchStorage.getStreamInfo(
|
||||
event.getApp(), event.getStream(), event.getMediaServer().getId());
|
||||
if (mediaInfo == null) {
|
||||
return;
|
||||
}
|
||||
String type = OriginType.values()[mediaInfo.getOriginType()].getType();
|
||||
redisCatchStorage.removeStream(mediaInfo.getMediaServer().getId(), type, event.getApp(), event.getStream());
|
||||
}
|
||||
@ -399,6 +402,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
logger.info("[添加媒体节点] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
|
||||
return;
|
||||
}
|
||||
|
||||
mediaServerMapper.add(mediaServer);
|
||||
if (mediaServer.isStatus()) {
|
||||
mediaNodeServerService.online(mediaServer);
|
||||
@ -591,6 +595,16 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
return mediaNodeServerService.stopSendRtp(mediaInfo, app, stream, ssrc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initStopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType());
|
||||
if (mediaNodeServerService == null) {
|
||||
logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType());
|
||||
return false;
|
||||
}
|
||||
return mediaNodeServerService.initStopSendRtp(mediaInfo, app, stream, ssrc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) {
|
||||
IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
|
||||
@ -895,4 +909,16 @@ public class MediaServerServiceImpl implements IMediaServerService {
|
||||
sendRtpItem.setRtcp(rtcp);
|
||||
return sendRtpItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaServer getMediaServerByAppAndStream(String app, String stream) {
|
||||
List<MediaServer> mediaServerList = getAll();
|
||||
for (MediaServer mediaServer : mediaServerList) {
|
||||
MediaInfo mediaInfo = getMediaInfo(mediaServer, app, stream);
|
||||
if (mediaInfo != null) {
|
||||
return mediaServer;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,6 @@ public class SendRtpPortManager {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface CheckPortCallback{
|
||||
|
||||
@ -10,19 +10,21 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.event.media.*;
|
||||
import com.genersoft.iot.vmp.media.event.mediaServer.MediaSendRtpStoppedEvent;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.ZLMServerConfig;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
|
||||
import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerKeepaliveEvent;
|
||||
import com.genersoft.iot.vmp.media.zlm.event.HookZlmServerStartEvent;
|
||||
import com.genersoft.iot.vmp.service.*;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.MediaServerUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -34,7 +36,6 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -66,6 +67,10 @@ public class ZLMHttpHookListener {
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
|
||||
@Autowired
|
||||
private IRedisRpcService redisRpcService;
|
||||
|
||||
@Autowired
|
||||
private IInviteStreamService inviteStreamService;
|
||||
|
||||
@ -87,9 +92,6 @@ public class ZLMHttpHookListener {
|
||||
@Autowired
|
||||
private EventPublisher eventPublisher;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager zlmMediaListManager;
|
||||
|
||||
@Autowired
|
||||
private HookSubscribe subscribe;
|
||||
|
||||
@ -118,6 +120,9 @@ public class ZLMHttpHookListener {
|
||||
@Autowired
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@Autowired
|
||||
private IStreamPushService streamPushService;
|
||||
|
||||
/**
|
||||
* 服务器定时上报时间,上报间隔可配置,默认10s上报一次
|
||||
*/
|
||||
@ -144,7 +149,7 @@ public class ZLMHttpHookListener {
|
||||
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
|
||||
public HookResult onPlay(@RequestBody OnPlayHookParam param) {
|
||||
|
||||
Map<String, String> paramMap = urlParamToMap(param.getParams());
|
||||
Map<String, String> paramMap = MediaServerUtils.urlParamToMap(param.getParams());
|
||||
// 对于播放流进行鉴权
|
||||
boolean authenticateResult = mediaService.authenticatePlay(param.getApp(), param.getStream(), paramMap.get("callId"));
|
||||
if (!authenticateResult) {
|
||||
@ -197,7 +202,12 @@ public class ZLMHttpHookListener {
|
||||
if (mediaServer == null) {
|
||||
return HookResult.SUCCESS();
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(mediaServer.getTranscodeSuffix())
|
||||
&& !"null".equalsIgnoreCase(mediaServer.getTranscodeSuffix())
|
||||
&& param.getStream().endsWith(mediaServer.getTranscodeSuffix()) ) {
|
||||
return HookResult.SUCCESS();
|
||||
}
|
||||
if (param.getSchema().equalsIgnoreCase("rtsp")) {
|
||||
if (param.isRegist()) {
|
||||
logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
|
||||
MediaArrivalEvent mediaArrivalEvent = MediaArrivalEvent.getInstance(this, param, mediaServer);
|
||||
@ -207,6 +217,8 @@ public class ZLMHttpHookListener {
|
||||
MediaDepartureEvent mediaDepartureEvent = MediaDepartureEvent.getInstance(this, param, mediaServer);
|
||||
applicationEventPublisher.publishEvent(mediaDepartureEvent);
|
||||
}
|
||||
}
|
||||
|
||||
return HookResult.SUCCESS();
|
||||
}
|
||||
|
||||
@ -219,10 +231,23 @@ public class ZLMHttpHookListener {
|
||||
|
||||
logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(),
|
||||
param.getApp(), param.getStream());
|
||||
JSONObject ret = new JSONObject();
|
||||
|
||||
MediaServer mediaInfo = mediaServerService.getOne(param.getMediaServerId());
|
||||
if (mediaInfo == null) {
|
||||
JSONObject ret = new JSONObject();
|
||||
ret.put("code", 0);
|
||||
return ret;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(mediaInfo.getTranscodeSuffix())
|
||||
&& !"null".equalsIgnoreCase(mediaInfo.getTranscodeSuffix())
|
||||
&& param.getStream().endsWith(mediaInfo.getTranscodeSuffix()) ) {
|
||||
param.setStream(param.getStream().substring(0, param.getStream().lastIndexOf(mediaInfo.getTranscodeSuffix()) -1 ));
|
||||
}
|
||||
|
||||
JSONObject ret = new JSONObject();
|
||||
boolean close = mediaService.closeStreamOnNoneReader(param.getMediaServerId(), param.getApp(), param.getStream(), param.getSchema());
|
||||
ret.put("code", close);
|
||||
ret.put("code", 0);
|
||||
ret.put("close", close);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -341,22 +366,4 @@ public class ZLMHttpHookListener {
|
||||
|
||||
return HookResult.SUCCESS();
|
||||
}
|
||||
|
||||
private Map<String, String> urlParamToMap(String params) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
if (ObjectUtils.isEmpty(params)) {
|
||||
return map;
|
||||
}
|
||||
String[] paramsArray = params.split("&");
|
||||
if (paramsArray.length == 0) {
|
||||
return map;
|
||||
}
|
||||
for (String param : paramsArray) {
|
||||
String[] paramArray = param.split("=");
|
||||
if (paramArray.length == 2) {
|
||||
map.put(paramArray[0], paramArray[1]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,121 +0,0 @@
|
||||
package com.genersoft.iot.vmp.media.zlm;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.ChannelOnlineEvent;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
|
||||
import com.genersoft.iot.vmp.service.IStreamPushService;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
|
||||
import com.genersoft.iot.vmp.storager.dao.StreamPushMapper;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class ZLMMediaListManager {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger("ZLMMediaListManager");
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private GbStreamMapper gbStreamMapper;
|
||||
|
||||
@Autowired
|
||||
private IStreamPushService streamPushService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private StreamPushMapper streamPushMapper;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
private Map<String, ChannelOnlineEvent> channelOnPublishEvents = new ConcurrentHashMap<>();
|
||||
|
||||
public StreamPushItem addPush(OnStreamChangedHookParam onStreamChangedHookParam) {
|
||||
StreamPushItem transform = streamPushService.transform(onStreamChangedHookParam);
|
||||
StreamPushItem pushInDb = streamPushService.getPush(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream());
|
||||
transform.setPushIng(onStreamChangedHookParam.isRegist());
|
||||
transform.setUpdateTime(DateUtil.getNow());
|
||||
transform.setPushTime(DateUtil.getNow());
|
||||
transform.setSelf(userSetting.getServerId().equals(onStreamChangedHookParam.getSeverId()));
|
||||
if (pushInDb == null) {
|
||||
transform.setCreateTime(DateUtil.getNow());
|
||||
streamPushMapper.add(transform);
|
||||
}else {
|
||||
streamPushMapper.update(transform);
|
||||
gbStreamMapper.updateMediaServer(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), onStreamChangedHookParam.getMediaServerId());
|
||||
}
|
||||
ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(transform.getApp(), transform.getStream());
|
||||
if ( channelOnlineEventLister != null) {
|
||||
try {
|
||||
channelOnlineEventLister.run(transform.getApp(), transform.getStream(), transform.getServerId());;
|
||||
} catch (ParseException e) {
|
||||
logger.error("addPush: ", e);
|
||||
}
|
||||
removedChannelOnlineEventLister(transform.getApp(), transform.getStream());
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
public void sendStreamEvent(String app, String stream, String mediaServerId) {
|
||||
MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||
// 查看推流状态
|
||||
Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, app, stream);
|
||||
if (streamReady != null && streamReady) {
|
||||
ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream);
|
||||
if (channelOnlineEventLister != null) {
|
||||
try {
|
||||
channelOnlineEventLister.run(app, stream, mediaServerId);
|
||||
} catch (ParseException e) {
|
||||
logger.error("sendStreamEvent: ", e);
|
||||
}
|
||||
removedChannelOnlineEventLister(app, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int removeMedia(String app, String streamId) {
|
||||
// 查找是否关联了国标, 关联了不删除, 置为离线
|
||||
GbStream gbStream = gbStreamMapper.selectOne(app, streamId);
|
||||
int result;
|
||||
if (gbStream == null) {
|
||||
result = storager.removeMedia(app, streamId);
|
||||
}else {
|
||||
result =storager.mediaOffline(app, streamId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addChannelOnlineEventLister(String app, String stream, ChannelOnlineEvent callback) {
|
||||
this.channelOnPublishEvents.put(app + "_" + stream, callback);
|
||||
}
|
||||
|
||||
public void removedChannelOnlineEventLister(String app, String stream) {
|
||||
this.channelOnPublishEvents.remove(app + "_" + stream);
|
||||
}
|
||||
|
||||
public ChannelOnlineEvent getChannelOnlineEventLister(String app, String stream) {
|
||||
return this.channelOnPublishEvents.get(app + "_" + stream);
|
||||
}
|
||||
|
||||
}
|
||||
@ -94,6 +94,8 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
MediaServer mediaServer = new MediaServer();
|
||||
mediaServer.setIp(ip);
|
||||
mediaServer.setHttpPort(port);
|
||||
mediaServer.setFlvPort(port);
|
||||
mediaServer.setWsFlvPort(port);
|
||||
mediaServer.setSecret(secret);
|
||||
JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServer);
|
||||
if (responseJSON == null) {
|
||||
@ -109,12 +111,15 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
}
|
||||
mediaServer.setId(zlmServerConfig.getGeneralMediaServerId());
|
||||
mediaServer.setHttpSSlPort(zlmServerConfig.getHttpPort());
|
||||
mediaServer.setFlvSSLPort(zlmServerConfig.getHttpPort());
|
||||
mediaServer.setWsFlvSSLPort(zlmServerConfig.getHttpPort());
|
||||
mediaServer.setRtmpPort(zlmServerConfig.getRtmpPort());
|
||||
mediaServer.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
|
||||
mediaServer.setRtspPort(zlmServerConfig.getRtspPort());
|
||||
mediaServer.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
|
||||
mediaServer.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
|
||||
mediaServer.setStreamIp(ip);
|
||||
|
||||
mediaServer.setHookIp(sipIp.split(",")[0]);
|
||||
mediaServer.setSdpIp(ip);
|
||||
mediaServer.setType("zlm");
|
||||
@ -131,10 +136,31 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService {
|
||||
param.put("ssrc", ssrc);
|
||||
}
|
||||
JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param);
|
||||
return (jsonObject != null && jsonObject.getInteger("code") == 0);
|
||||
if (jsonObject == null || jsonObject.getInteger("code") != 0 ) {
|
||||
logger.error("停止发流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param));
|
||||
throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg"));
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initStopSendRtp(MediaServer mediaInfo, String app, String stream, String ssrc) {
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", app);
|
||||
param.put("stream", stream);
|
||||
if (!ObjectUtils.isEmpty(ssrc)) {
|
||||
param.put("ssrc", ssrc);
|
||||
}
|
||||
JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param);
|
||||
if (jsonObject == null || jsonObject.getInteger("code") != 0 ) {
|
||||
logger.error("停止发流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteRecordDirectory(MediaServer mediaServer, String app, String stream, String date, String fileName) {
|
||||
logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件, server: {} {}:{}->{}/{}", mediaServer.getId(), app, stream, date, fileName);
|
||||
|
||||
@ -264,4 +264,13 @@ public class ZLMServerFactory {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public JSONObject stopSendRtpStream(MediaServer mediaServerItem, SendRtpItem sendRtpItem) {
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("vhost", "__defaultVhost__");
|
||||
param.put("app", sendRtpItem.getApp());
|
||||
param.put("stream", sendRtpItem.getStream());
|
||||
param.put("ssrc", sendRtpItem.getSsrc());
|
||||
return zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.genersoft.iot.vmp.media.zlm.dto;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
@ -7,5 +9,5 @@ import java.text.ParseException;
|
||||
*/
|
||||
public interface ChannelOnlineEvent {
|
||||
|
||||
void run(String app, String stream, String serverId) throws ParseException;
|
||||
void run(SendRtpItem sendRtpItem) throws ParseException;
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
* 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv
|
||||
*/
|
||||
@Schema(description = "观看总人数")
|
||||
private String totalReaderCount;
|
||||
private Integer totalReaderCount;
|
||||
|
||||
/**
|
||||
* 协议 包括hls/rtsp/rtmp/http-flv/ws-flv
|
||||
@ -159,7 +159,7 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
streamPushItem.setStream(streamInfo.getStream());
|
||||
streamPushItem.setAliveSecond(streamInfo.getMediaInfo().getAliveSecond());
|
||||
// streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock());
|
||||
streamPushItem.setTotalReaderCount(streamInfo.getMediaInfo().getReaderCount() + "");
|
||||
streamPushItem.setTotalReaderCount(streamInfo.getMediaInfo().getReaderCount());
|
||||
streamPushItem.setOriginType(streamInfo.getOriginType());
|
||||
// streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr());
|
||||
// streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl());
|
||||
@ -180,7 +180,7 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
streamPushItem.setStream(event.getStream());
|
||||
streamPushItem.setAliveSecond(event.getMediaInfo().getAliveSecond());
|
||||
// streamPushItem.setOriginSock(streamInfo.getMediaInfo().getOriginSock());
|
||||
streamPushItem.setTotalReaderCount(event.getMediaInfo().getReaderCount() + "");
|
||||
streamPushItem.setTotalReaderCount(event.getMediaInfo().getReaderCount());
|
||||
streamPushItem.setOriginType(event.getMediaInfo().getOriginType());
|
||||
// streamPushItem.setOriginTypeStr(streamInfo.getMediaInfo().getOriginTypeStr());
|
||||
// streamPushItem.setOriginUrl(streamInfo.getMediaInfo().getOriginUrl());
|
||||
@ -242,11 +242,11 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public String getTotalReaderCount() {
|
||||
public Integer getTotalReaderCount() {
|
||||
return totalReaderCount;
|
||||
}
|
||||
|
||||
public void setTotalReaderCount(String totalReaderCount) {
|
||||
public void setTotalReaderCount(Integer totalReaderCount) {
|
||||
this.totalReaderCount = totalReaderCount;
|
||||
}
|
||||
|
||||
|
||||
@ -331,6 +331,9 @@ public class ZLMServerConfig extends HookParam {
|
||||
@JSONField(name = "shell.shell")
|
||||
private String shellPhell;
|
||||
|
||||
@JSONField(name = "transcode.suffix")
|
||||
private String transcodeSuffix;
|
||||
|
||||
|
||||
public String getHookIp() {
|
||||
return hookIp;
|
||||
@ -1211,4 +1214,12 @@ public class ZLMServerConfig extends HookParam {
|
||||
public void setHookOnRtpServerTimeout(String hookOnRtpServerTimeout) {
|
||||
this.hookOnRtpServerTimeout = hookOnRtpServerTimeout;
|
||||
}
|
||||
|
||||
public String getTranscodeSuffix() {
|
||||
return transcodeSuffix;
|
||||
}
|
||||
|
||||
public void setTranscodeSuffix(String transcodeSuffix) {
|
||||
this.transcodeSuffix = transcodeSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ public class OnRecordMp4HookParam extends HookParam{
|
||||
private String vhost;
|
||||
private long start_time;
|
||||
private double time_len;
|
||||
private String params;
|
||||
|
||||
public String getApp() {
|
||||
return app;
|
||||
@ -96,6 +97,14 @@ public class OnRecordMp4HookParam extends HookParam{
|
||||
this.time_len = time_len;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(String params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnRecordMp4HookParam{" +
|
||||
@ -109,6 +118,7 @@ public class OnRecordMp4HookParam extends HookParam{
|
||||
", vhost='" + vhost + '\'' +
|
||||
", start_time=" + start_time +
|
||||
", time_len=" + time_len +
|
||||
", params=" + params +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.media.zlm.dto.hook;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
@ -98,6 +99,16 @@ public class OnStreamChangedHookParam extends HookParam{
|
||||
*/
|
||||
private String vhost;
|
||||
|
||||
/**
|
||||
* 额外的参数字符串
|
||||
*/
|
||||
private String params;
|
||||
|
||||
/**
|
||||
* 额外的参数
|
||||
*/
|
||||
private Map<String, String> paramMap;
|
||||
|
||||
public boolean isRegist() {
|
||||
return regist;
|
||||
}
|
||||
@ -496,6 +507,23 @@ public class OnStreamChangedHookParam extends HookParam{
|
||||
this.callId = callId;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, String> getParamMap() {
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
public void setParamMap(Map<String, String> paramMap) {
|
||||
this.paramMap = paramMap;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(String params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnStreamChangedHookParam{" +
|
||||
|
||||
@ -17,7 +17,7 @@ public interface ICloudRecordService {
|
||||
/**
|
||||
* 分页回去云端录像列表
|
||||
*/
|
||||
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems);
|
||||
PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId);
|
||||
|
||||
/**
|
||||
* 获取所有的日期
|
||||
@ -50,4 +50,6 @@ public interface ICloudRecordService {
|
||||
* 获取播放地址
|
||||
*/
|
||||
DownloadFileInfo getPlayUrlPath(Integer recordId);
|
||||
|
||||
List<CloudRecordItem> getAllList(String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, List<Integer> ids);
|
||||
}
|
||||
|
||||
@ -99,4 +99,13 @@ public interface IDeviceChannelService {
|
||||
void updateChannelGPS(Device device, DeviceChannel deviceChannel, MobilePosition mobilePosition);
|
||||
|
||||
void stopPlay(String deviceId, String channelId);
|
||||
void batchUpdateChannelGPS(List<DeviceChannel> channelList);
|
||||
|
||||
void batchAddMobilePosition(List<MobilePosition> addMobilePositionList);
|
||||
|
||||
void online(DeviceChannel channel);
|
||||
|
||||
void offline(DeviceChannel channel);
|
||||
|
||||
void delete(DeviceChannel channel);
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package com.genersoft.iot.vmp.service;
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IMobilePositionService {
|
||||
|
||||
void add(List<MobilePosition> mobilePositionList);
|
||||
|
||||
void add(MobilePosition mobilePosition);
|
||||
}
|
||||
@ -115,4 +115,5 @@ public interface IStreamPushService {
|
||||
Map<String, StreamPushItem> getAllAppAndStreamMap();
|
||||
|
||||
|
||||
void updatePush(OnStreamChangedHookParam param);
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.genersoft.iot.vmp.service.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
|
||||
import com.genersoft.iot.vmp.utils.MediaServerUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 云端录像数据
|
||||
@ -89,6 +91,10 @@ public class CloudRecordItem {
|
||||
cloudRecordItem.setMediaServerId(param.getMediaServer().getId());
|
||||
cloudRecordItem.setTimeLen((long) param.getRecordInfo().getTimeLen() * 1000);
|
||||
cloudRecordItem.setEndTime((param.getRecordInfo().getStartTime() + (long)param.getRecordInfo().getTimeLen()) * 1000);
|
||||
Map<String, String> paramsMap = MediaServerUtils.urlParamToMap(param.getRecordInfo().getParams());
|
||||
if (paramsMap.get("callId") != null) {
|
||||
cloudRecordItem.setCallId(paramsMap.get("callId"));
|
||||
}
|
||||
return cloudRecordItem;
|
||||
}
|
||||
|
||||
|
||||
@ -61,6 +61,7 @@ public class MessageForPushChannel {
|
||||
messageForPushChannel.setGbId(gbId);
|
||||
messageForPushChannel.setApp(app);
|
||||
messageForPushChannel.setStream(stream);
|
||||
messageForPushChannel.setServerId(serverId);
|
||||
messageForPushChannel.setMediaServerId(mediaServerId);
|
||||
messageForPushChannel.setPlatFormId(platFormId);
|
||||
messageForPushChannel.setPlatFormName(platFormName);
|
||||
|
||||
@ -5,12 +5,12 @@ import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
|
||||
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaRecordMp4Event;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
||||
import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
@ -28,13 +28,18 @@ import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
@DS("share")
|
||||
public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(CloudRecordServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
@ -53,7 +58,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
private VideoStreamSessionManager streamSession;
|
||||
|
||||
@Override
|
||||
public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems) {
|
||||
public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId) {
|
||||
// 开始时间和结束时间在数据库中都是以秒为单位的
|
||||
Long startTimeStamp = null;
|
||||
Long endTimeStamp = null;
|
||||
@ -73,7 +78,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
}
|
||||
PageHelper.startPage(page, count);
|
||||
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
|
||||
null, mediaServerItems);
|
||||
callId, mediaServerItems, null);
|
||||
return new PageInfo<>(all);
|
||||
}
|
||||
|
||||
@ -86,10 +91,10 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
}else {
|
||||
endDate = LocalDate.of(year, month + 1, 1);
|
||||
}
|
||||
long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
|
||||
long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
|
||||
long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond() * 1000;
|
||||
long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond() * 1000;
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
|
||||
endTimeStamp, null, mediaServerItems);
|
||||
endTimeStamp, null, mediaServerItems, null);
|
||||
if (cloudRecordItemList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@ -105,11 +110,13 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
@EventListener
|
||||
public void onApplicationEvent(MediaRecordMp4Event event) {
|
||||
CloudRecordItem cloudRecordItem = CloudRecordItem.getInstance(event);
|
||||
if (ObjectUtils.isEmpty(cloudRecordItem.getCallId())) {
|
||||
StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(event.getApp(), event.getStream());
|
||||
if (streamAuthorityInfo != null) {
|
||||
cloudRecordItem.setCallId(streamAuthorityInfo.getCallId());
|
||||
}
|
||||
logger.info("[添加录像记录] {}/{} 内容:{}", event.getApp(), event.getStream(), event.getRecordInfo());
|
||||
}
|
||||
logger.info("[添加录像记录] {}/{}, callId: {}, 内容:{}", event.getApp(), event.getStream(), cloudRecordItem.getCallId(), event.getRecordInfo());
|
||||
cloudRecordServiceMapper.add(cloudRecordItem);
|
||||
}
|
||||
|
||||
@ -199,7 +206,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
}
|
||||
|
||||
List<CloudRecordItem> all = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems);
|
||||
callId, mediaServerItems, null);
|
||||
if (all.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到待收藏的视频");
|
||||
}
|
||||
@ -235,4 +242,27 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
|
||||
MediaServer mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId());
|
||||
return CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CloudRecordItem> getAllList(String query, String app, String stream, String startTime, String endTime, List<MediaServer> mediaServerItems, String callId, List<Integer> ids) {
|
||||
// 开始时间和结束时间在数据库中都是以秒为单位的
|
||||
Long startTimeStamp = null;
|
||||
Long endTimeStamp = null;
|
||||
if (startTime != null ) {
|
||||
if (!DateUtil.verification(startTime, DateUtil.formatter)) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间格式错误,正确格式为: " + DateUtil.formatter);
|
||||
}
|
||||
startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(startTime);
|
||||
|
||||
}
|
||||
if (endTime != null ) {
|
||||
if (!DateUtil.verification(endTime, DateUtil.formatter)) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "结束时间格式错误,正确格式为: " + DateUtil.formatter);
|
||||
}
|
||||
endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(endTime);
|
||||
|
||||
}
|
||||
return cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
|
||||
callId, mediaServerItems, ids);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -247,26 +248,50 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
|
||||
return channelMapper.batchOnline(channels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void online(DeviceChannel channel) {
|
||||
channelMapper.online(channel.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int channelsOffline(List<DeviceChannel> channels) {
|
||||
return channelMapper.batchOffline(channels);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void offline(DeviceChannel channel) {
|
||||
channelMapper.offline(channel.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(DeviceChannel channel) {
|
||||
channelMapper.del(channel.getDeviceId(), channel.getChannelId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceChannel getOne(String deviceId, String channelId){
|
||||
return channelMapper.queryChannel(deviceId, channelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void batchUpdateChannel(List<DeviceChannel> channels) {
|
||||
public synchronized void batchUpdateChannel(List<DeviceChannel> channels) {
|
||||
String now = DateUtil.getNow();
|
||||
for (DeviceChannel channel : channels) {
|
||||
channel.setUpdateTime(now);
|
||||
}
|
||||
int limitCount = 1000;
|
||||
if (!channels.isEmpty()) {
|
||||
if (channels.size() > limitCount) {
|
||||
for (int i = 0; i < channels.size(); i += limitCount) {
|
||||
int toIndex = i + limitCount;
|
||||
if (i + limitCount > channels.size()) {
|
||||
toIndex = channels.size();
|
||||
}
|
||||
channelMapper.batchUpdate(channels.subList(i, toIndex));
|
||||
}
|
||||
}else {
|
||||
channelMapper.batchUpdate(channels);
|
||||
for (DeviceChannel channel : channels) {
|
||||
if (channel.getParentId() != null) {
|
||||
channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -358,4 +383,47 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
|
||||
public void stopPlay(String deviceId, String channelId) {
|
||||
channelMapper.stopPlay(deviceId, channelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void batchUpdateChannelGPS(List<DeviceChannel> channelList) {
|
||||
for (DeviceChannel deviceChannel : channelList) {
|
||||
deviceChannel.setUpdateTime(DateUtil.getNow());
|
||||
if (deviceChannel.getGpsTime() == null) {
|
||||
deviceChannel.setGpsTime(DateUtil.getNow());
|
||||
}
|
||||
}
|
||||
int count = 1000;
|
||||
if (channelList.size() > count) {
|
||||
for (int i = 0; i < channelList.size(); i+=count) {
|
||||
int toIndex = i+count;
|
||||
if ( i + count > channelList.size()) {
|
||||
toIndex = channelList.size();
|
||||
}
|
||||
List<DeviceChannel> channels = channelList.subList(i, toIndex);
|
||||
channelMapper.batchUpdatePosition(channels);
|
||||
}
|
||||
}else {
|
||||
channelMapper.batchUpdatePosition(channelList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void batchAddMobilePosition(List<MobilePosition> mobilePositions) {
|
||||
// int count = 500;
|
||||
// if (mobilePositions.size() > count) {
|
||||
// for (int i = 0; i < mobilePositions.size(); i+=count) {
|
||||
// int toIndex = i+count;
|
||||
// if ( i + count > mobilePositions.size()) {
|
||||
// toIndex = mobilePositions.size();
|
||||
// }
|
||||
// List<MobilePosition> mobilePositionsSub = mobilePositions.subList(i, toIndex);
|
||||
// deviceMobilePositionMapper.batchadd(mobilePositionsSub);
|
||||
// }
|
||||
// }else {
|
||||
// deviceMobilePositionMapper.batchadd(mobilePositions);
|
||||
// }
|
||||
deviceMobilePositionMapper.batchadd(mobilePositions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -558,6 +558,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
removeMobilePositionSubscribe(deviceInStore, result->{
|
||||
// 开启订阅
|
||||
deviceInStore.setSubscribeCycleForMobilePosition(device.getSubscribeCycleForMobilePosition());
|
||||
deviceInStore.setMobilePositionSubmissionInterval(device.getMobilePositionSubmissionInterval());
|
||||
addMobilePositionSubscribe(deviceInStore);
|
||||
// 因为是异步执行,需要在这里更新下数据
|
||||
deviceMapper.updateCustom(deviceInStore);
|
||||
@ -566,12 +567,14 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}else {
|
||||
// 开启订阅
|
||||
deviceInStore.setSubscribeCycleForMobilePosition(device.getSubscribeCycleForMobilePosition());
|
||||
deviceInStore.setMobilePositionSubmissionInterval(device.getMobilePositionSubmissionInterval());
|
||||
addMobilePositionSubscribe(deviceInStore);
|
||||
}
|
||||
|
||||
}else if (device.getSubscribeCycleForMobilePosition() == 0) {
|
||||
// 取消订阅
|
||||
deviceInStore.setSubscribeCycleForMobilePosition(0);
|
||||
deviceInStore.setMobilePositionSubmissionInterval(0);
|
||||
removeMobilePositionSubscribe(deviceInStore, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,16 +10,15 @@ import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.bean.ResultForOnPublish;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||
import com.genersoft.iot.vmp.service.*;
|
||||
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.MediaServerUtils;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.OtherPsSendInfo;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.OtherRtpSendInfo;
|
||||
@ -28,12 +27,10 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -66,9 +63,6 @@ public class MediaServiceImpl implements IMediaService {
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager zlmMediaListManager;
|
||||
|
||||
@Autowired
|
||||
private IDeviceService deviceService;
|
||||
|
||||
@ -106,7 +100,7 @@ public class MediaServiceImpl implements IMediaService {
|
||||
}
|
||||
if (userSetting.getPushAuthority()) {
|
||||
// 对于推流进行鉴权
|
||||
Map<String, String> paramMap = urlParamToMap(params);
|
||||
Map<String, String> paramMap = MediaServerUtils.urlParamToMap(params);
|
||||
// 推流鉴权
|
||||
if (params == null) {
|
||||
logger.info("推流鉴权失败: 缺少必要参数:sign=md5(user表的pushKey)");
|
||||
@ -132,8 +126,6 @@ public class MediaServiceImpl implements IMediaService {
|
||||
// 鉴权通过
|
||||
redisCatchStorage.updateStreamAuthorityInfo(app, stream, streamAuthorityInfo);
|
||||
}
|
||||
} else {
|
||||
zlmMediaListManager.sendStreamEvent(app, stream, mediaServer.getId());
|
||||
}
|
||||
|
||||
|
||||
@ -178,7 +170,7 @@ public class MediaServiceImpl implements IMediaService {
|
||||
String channelId = ssrcTransactionForAll.get(0).getChannelId();
|
||||
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
|
||||
if (deviceChannel != null) {
|
||||
result.setEnable_audio(deviceChannel.isHasAudio());
|
||||
result.setEnable_audio(deviceChannel.getHasAudio());
|
||||
}
|
||||
// 如果是录像下载就设置视频间隔十秒
|
||||
if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
|
||||
@ -217,24 +209,6 @@ public class MediaServiceImpl implements IMediaService {
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, String> urlParamToMap(String params) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
if (ObjectUtils.isEmpty(params)) {
|
||||
return map;
|
||||
}
|
||||
String[] paramsArray = params.split("&");
|
||||
if (paramsArray.length == 0) {
|
||||
return map;
|
||||
}
|
||||
for (String param : paramsArray) {
|
||||
String[] paramArray = param.split("=");
|
||||
if (paramArray.length == 2) {
|
||||
map.put(paramArray[0], paramArray[1]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closeStreamOnNoneReader(String mediaServerId, String app, String stream, String schema) {
|
||||
boolean result = false;
|
||||
@ -264,11 +238,7 @@ public class MediaServiceImpl implements IMediaService {
|
||||
redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getCallId(), sendRtpItem.getStream());
|
||||
if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
|
||||
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
|
||||
messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
|
||||
redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
|
||||
redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem,parentPlatform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
package com.genersoft.iot.vmp.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
|
||||
import com.genersoft.iot.vmp.service.IMobilePositionService;
|
||||
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
|
||||
import com.genersoft.iot.vmp.storager.dao.DeviceMobilePositionMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Service
|
||||
public class MobilePositionServiceImpl implements IMobilePositionService {
|
||||
|
||||
@Autowired
|
||||
private DeviceChannelMapper channelMapper;
|
||||
|
||||
@Autowired
|
||||
private DeviceMobilePositionMapper mobilePositionMapper;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, MobilePosition> redisTemplate;
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(MobilePositionServiceImpl.class);
|
||||
|
||||
private final String REDIS_MOBILE_POSITION_LIST = "redis_mobile_position_list";
|
||||
|
||||
@Override
|
||||
public void add(MobilePosition mobilePosition) {
|
||||
List<MobilePosition> list = new ArrayList<>();
|
||||
list.add(mobilePosition);
|
||||
add(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(List<MobilePosition> mobilePositionList) {
|
||||
redisTemplate.opsForList().leftPushAll(REDIS_MOBILE_POSITION_LIST, mobilePositionList);
|
||||
}
|
||||
|
||||
private List<MobilePosition> get(int length) {
|
||||
Long size = redisTemplate.opsForList().size(REDIS_MOBILE_POSITION_LIST);
|
||||
if (size == null || size == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<MobilePosition> mobilePositions;
|
||||
if (size > length) {
|
||||
mobilePositions = redisTemplate.opsForList().rightPop(REDIS_MOBILE_POSITION_LIST, length);
|
||||
}else {
|
||||
mobilePositions = redisTemplate.opsForList().rightPop(REDIS_MOBILE_POSITION_LIST, size);
|
||||
}
|
||||
return mobilePositions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Scheduled(fixedRate = 1000)
|
||||
@Transactional
|
||||
public void executeTaskQueue() {
|
||||
int countLimit = 3000;
|
||||
List<MobilePosition> mobilePositions = get(countLimit);
|
||||
if (mobilePositions == null || mobilePositions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (userSetting.getSavePositionHistory()) {
|
||||
mobilePositionMapper.batchadd(mobilePositions);
|
||||
}
|
||||
logger.info("[移动位置订阅]更新通道位置: {}", mobilePositions.size());
|
||||
Map<String, DeviceChannel> updateChannelMap = new HashMap<>();
|
||||
for (MobilePosition mobilePosition : mobilePositions) {
|
||||
DeviceChannel deviceChannel = new DeviceChannel();
|
||||
deviceChannel.setDeviceId(mobilePosition.getDeviceId());
|
||||
deviceChannel.setLongitude(mobilePosition.getLongitude());
|
||||
deviceChannel.setLatitude(mobilePosition.getLatitude());
|
||||
deviceChannel.setGpsTime(mobilePosition.getTime());
|
||||
updateChannelMap.put(mobilePosition.getDeviceId() + mobilePosition.getChannelId(), deviceChannel);
|
||||
}
|
||||
List<DeviceChannel> channels = new ArrayList<>(updateChannelMap.values());
|
||||
channelMapper.batchUpdatePosition(channels);
|
||||
}
|
||||
|
||||
}
|
||||
@ -34,7 +34,6 @@ import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.service.*;
|
||||
import com.genersoft.iot.vmp.service.bean.*;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.utils.CloudRecordUtils;
|
||||
@ -118,9 +117,6 @@ public class PlayServiceImpl implements IPlayService {
|
||||
@Autowired
|
||||
private ISIPCommanderForPlatform commanderForPlatform;
|
||||
|
||||
@Autowired
|
||||
private RedisGbPlayMsgListener redisGbPlayMsgListener;
|
||||
|
||||
@Autowired
|
||||
private SSRCFactory ssrcFactory;
|
||||
|
||||
@ -263,7 +259,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
);
|
||||
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(event.getMediaServer(), event.getStream(), null,
|
||||
device.isSsrcCheck(), true, 0, false, !deviceChannel.isHasAudio(), false, device.getStreamModeForParam());
|
||||
device.isSsrcCheck(), true, 0, false, !deviceChannel.getHasAudio(), false, device.getStreamModeForParam());
|
||||
playBack(event.getMediaServer(), ssrcInfo, deviceId, channelId, startTime, endTime, null);
|
||||
}
|
||||
}
|
||||
@ -275,7 +271,6 @@ public class PlayServiceImpl implements IPlayService {
|
||||
logger.warn("[点播] 未找到可用的zlm deviceId: {},channelId:{}", deviceId, channelId);
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
|
||||
}
|
||||
|
||||
Device device = redisCatchStorage.getDevice(deviceId);
|
||||
if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE") && !mediaServerItem.isRtpEnable()) {
|
||||
logger.warn("[点播] 单端口收流时不支持TCP主动方式收流 deviceId: {},channelId:{}", deviceId, channelId);
|
||||
@ -289,6 +284,8 @@ public class PlayServiceImpl implements IPlayService {
|
||||
InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
|
||||
if (inviteInfo != null ) {
|
||||
if (inviteInfo.getStreamInfo() == null) {
|
||||
// 释放生成的ssrc,使用上一次申请的
|
||||
ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);
|
||||
// 点播发起了但是尚未成功, 仅注册回调等待结果即可
|
||||
inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
|
||||
logger.info("[点播开始] 已经请求中,等待结果, deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
|
||||
@ -324,7 +321,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
}
|
||||
}
|
||||
String streamId = String.format("%s_%s", device.getDeviceId(), channelId);
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, ssrc, device.isSsrcCheck(), false, 0, false, !channel.isHasAudio(), false, device.getStreamModeForParam());
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, ssrc, device.isSsrcCheck(), false, 0, false, !channel.getHasAudio(), false, device.getStreamModeForParam());
|
||||
if (ssrcInfo == null) {
|
||||
callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null);
|
||||
inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
|
||||
@ -763,7 +760,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
.replace(":", "")
|
||||
.replace(" ", "");
|
||||
String stream = deviceId + "_" + channelId + "_" + startTimeStr + "_" + endTimeTimeStr;
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, stream, null, device.isSsrcCheck(), true, 0, false, !channel.isHasAudio(), false, device.getStreamModeForParam());
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, stream, null, device.isSsrcCheck(), true, 0, false, !channel.getHasAudio(), false, device.getStreamModeForParam());
|
||||
playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback);
|
||||
}
|
||||
|
||||
@ -960,7 +957,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
return;
|
||||
}
|
||||
// 录像下载不使用固定流地址,固定流地址会导致如果开始时间与结束时间一致时文件错误的叠加在一起
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.isHasAudio(), false, device.getStreamModeForParam());
|
||||
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,!channel.getHasAudio(), false, device.getStreamModeForParam());
|
||||
download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, callback);
|
||||
}
|
||||
|
||||
@ -1419,12 +1416,7 @@ public class PlayServiceImpl implements IPlayService {
|
||||
// 开始发流
|
||||
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
|
||||
if (mediaInfo == null) {
|
||||
RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance(sendRtpItem);
|
||||
redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, () -> {
|
||||
startSendRtpStreamFailHand(sendRtpItem, platform, callIdHeader);
|
||||
});
|
||||
} else {
|
||||
if (mediaInfo != null) {
|
||||
try {
|
||||
if (sendRtpItem.isTcpActive()) {
|
||||
mediaServerService.startSendRtpPassive(mediaInfo, platform, sendRtpItem, null);
|
||||
|
||||
@ -349,6 +349,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
||||
}
|
||||
String msgResult;
|
||||
if ("ffmpeg".equalsIgnoreCase(param.getType())){
|
||||
if (param.getTimeoutMs() == 0) {
|
||||
param.setTimeoutMs(15);
|
||||
}
|
||||
result = mediaServerService.addFFmpegSource(mediaServer, param.getSrcUrl().trim(), param.getDstUrl(),
|
||||
param.getTimeoutMs(), param.isEnableAudio(), param.isEnableMp4(),
|
||||
param.getFfmpegCmdKey());
|
||||
@ -406,6 +409,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
||||
gbStreamMapper.del(app, stream);
|
||||
videoManagerStorager.deleteStreamProxy(app, stream);
|
||||
redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream);
|
||||
redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PUSH", app, stream);
|
||||
Boolean result = removeStreamProxyFromZlm(streamProxyItem);
|
||||
if (result != null && result) {
|
||||
logger.info("[移除代理]: 代理: {}/{}, 从zlm移除成功", app, stream);
|
||||
|
||||
@ -217,7 +217,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
streamPushItem.setStream(item.getStream());
|
||||
streamPushItem.setAliveSecond(item.getAliveSecond());
|
||||
streamPushItem.setOriginSock(item.getOriginSock());
|
||||
streamPushItem.setTotalReaderCount(item.getTotalReaderCount() + "");
|
||||
streamPushItem.setTotalReaderCount(item.getTotalReaderCount());
|
||||
streamPushItem.setOriginType(item.getOriginType());
|
||||
streamPushItem.setOriginTypeStr(item.getOriginTypeStr());
|
||||
streamPushItem.setOriginUrl(item.getOriginUrl());
|
||||
@ -633,4 +633,21 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
||||
public Map<String, StreamPushItem> getAllAppAndStreamMap() {
|
||||
return streamPushMapper.getAllAppAndStreamMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePush(OnStreamChangedHookParam param) {
|
||||
StreamPushItem transform = transform(param);
|
||||
StreamPushItem pushInDb = getPush(param.getApp(), param.getStream());
|
||||
transform.setPushIng(param.isRegist());
|
||||
transform.setUpdateTime(DateUtil.getNow());
|
||||
transform.setPushTime(DateUtil.getNow());
|
||||
transform.setSelf(userSetting.getServerId().equals(param.getSeverId()));
|
||||
if (pushInDb == null) {
|
||||
transform.setCreateTime(DateUtil.getNow());
|
||||
streamPushMapper.add(transform);
|
||||
}else {
|
||||
streamPushMapper.update(transform);
|
||||
gbStreamMapper.updateMediaServer(param.getApp(), param.getStream(), param.getMediaServerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus
|
||||
streamPushItem.setName(streamPushExcelDto.getName());
|
||||
streamPushItem.setOriginType(2);
|
||||
streamPushItem.setOriginTypeStr("rtsp_push");
|
||||
streamPushItem.setTotalReaderCount("0");
|
||||
streamPushItem.setTotalReaderCount(0);
|
||||
streamPushItem.setPlatformId(streamPushExcelDto.getPlatformId());
|
||||
streamPushItem.setCatalogId(streamPushExcelDto.getCatalogId());
|
||||
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.genersoft.iot.vmp.service.redisMsg;
|
||||
|
||||
import com.genersoft.iot.vmp.common.CommonCallback;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
|
||||
public interface IRedisRpcService {
|
||||
|
||||
SendRtpItem getSendRtpItem(String sendRtpItemKey);
|
||||
|
||||
WVPResult startSendRtp(String sendRtpItemKey, SendRtpItem sendRtpItem);
|
||||
|
||||
WVPResult stopSendRtp(String sendRtpItemKey);
|
||||
|
||||
long waitePushStreamOnline(SendRtpItem sendRtpItem, CommonCallback<String> callback);
|
||||
|
||||
void stopWaitePushStreamOnline(SendRtpItem sendRtpItem);
|
||||
|
||||
void rtpSendStopped(String sendRtpItemKey);
|
||||
|
||||
void removeCallback(long key);
|
||||
}
|
||||
@ -1,451 +0,0 @@
|
||||
package com.genersoft.iot.vmp.service.redisMsg;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
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.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.hook.Hook;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookType;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.*;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
|
||||
/**
|
||||
* 监听下级发送推送信息,并发送国标推流消息上级
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisGbPlayMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class);
|
||||
|
||||
public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM";
|
||||
|
||||
/**
|
||||
* 流媒体不存在的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1;
|
||||
|
||||
/**
|
||||
* 离线的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_OFFLINE = -2;
|
||||
|
||||
/**
|
||||
* 超时的错误玛
|
||||
*/
|
||||
public static final int ERROR_CODE_TIMEOUT = -3;
|
||||
|
||||
private final Map<String, PlayMsgCallback> callbacks = new ConcurrentHashMap<>();
|
||||
private final Map<String, PlayMsgCallbackForStartSendRtpStream> callbacksForStartSendRtpStream = new ConcurrentHashMap<>();
|
||||
private final Map<String, PlayMsgErrorCallback> callbacksForError = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
|
||||
@Autowired
|
||||
private DynamicTask dynamicTask;
|
||||
|
||||
|
||||
@Autowired
|
||||
private HookSubscribe subscribe;
|
||||
|
||||
private final ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
|
||||
public interface PlayMsgCallback{
|
||||
void handler(ResponseSendItemMsg responseSendItemMsg) throws ParseException;
|
||||
}
|
||||
|
||||
public interface PlayMsgCallbackForStartSendRtpStream{
|
||||
void handler();
|
||||
}
|
||||
|
||||
public interface PlayMsgErrorCallback{
|
||||
void handler(WVPResult wvpResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
boolean isEmpty = taskQueue.isEmpty();
|
||||
taskQueue.offer(message);
|
||||
if (isEmpty) {
|
||||
taskExecutor.execute(() -> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
Message msg = taskQueue.poll();
|
||||
try {
|
||||
WvpRedisMsg wvpRedisMsg = JSON.parseObject(msg.getBody(), WvpRedisMsg.class);
|
||||
logger.info("[收到REDIS通知] 消息: {}", JSON.toJSONString(wvpRedisMsg));
|
||||
if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) {
|
||||
continue;
|
||||
}
|
||||
if (WvpRedisMsg.isRequest(wvpRedisMsg)) {
|
||||
logger.info("[收到REDIS通知] 请求: {}", new String(msg.getBody()));
|
||||
|
||||
switch (wvpRedisMsg.getCmd()){
|
||||
case WvpRedisMsgCmd.GET_SEND_ITEM:
|
||||
RequestSendItemMsg content = JSON.parseObject(wvpRedisMsg.getContent(), RequestSendItemMsg.class);
|
||||
requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
|
||||
break;
|
||||
case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
|
||||
RequestPushStreamMsg param = JSON.to(RequestPushStreamMsg.class, wvpRedisMsg.getContent());
|
||||
requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
|
||||
break;
|
||||
case WvpRedisMsgCmd.REQUEST_STOP_PUSH_STREAM:
|
||||
RequestStopPushStreamMsg streamMsg = JSON.to(RequestStopPushStreamMsg.class, wvpRedisMsg.getContent());
|
||||
requestStopPushStreamMsgHand(streamMsg, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}else {
|
||||
logger.info("[收到REDIS通知] 回复: {}", new String(msg.getBody()));
|
||||
switch (wvpRedisMsg.getCmd()){
|
||||
case WvpRedisMsgCmd.GET_SEND_ITEM:
|
||||
|
||||
WVPResult content = JSON.to(WVPResult.class, wvpRedisMsg.getContent());
|
||||
|
||||
String key = wvpRedisMsg.getSerial();
|
||||
switch (content.getCode()) {
|
||||
case 0:
|
||||
ResponseSendItemMsg responseSendItemMsg =JSON.to(ResponseSendItemMsg.class, content.getData());
|
||||
PlayMsgCallback playMsgCallback = callbacks.get(key);
|
||||
if (playMsgCallback != null) {
|
||||
callbacksForError.remove(key);
|
||||
try {
|
||||
playMsgCallback.handler(responseSendItemMsg);
|
||||
} catch (ParseException e) {
|
||||
logger.error("[REDIS消息处理异常] ", e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
|
||||
case ERROR_CODE_OFFLINE:
|
||||
case ERROR_CODE_TIMEOUT:
|
||||
PlayMsgErrorCallback errorCallback = callbacksForError.get(key);
|
||||
if (errorCallback != null) {
|
||||
callbacks.remove(key);
|
||||
errorCallback.handler(content);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
|
||||
WVPResult wvpResult = JSON.to(WVPResult.class, wvpRedisMsg.getContent());
|
||||
String serial = wvpRedisMsg.getSerial();
|
||||
switch (wvpResult.getCode()) {
|
||||
case 0:
|
||||
PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial);
|
||||
if (playMsgCallback != null) {
|
||||
callbacksForError.remove(serial);
|
||||
playMsgCallback.handler();
|
||||
}
|
||||
break;
|
||||
case ERROR_CODE_MEDIA_SERVER_NOT_FOUND:
|
||||
case ERROR_CODE_OFFLINE:
|
||||
case ERROR_CODE_TIMEOUT:
|
||||
PlayMsgErrorCallback errorCallback = callbacksForError.get(serial);
|
||||
if (errorCallback != null) {
|
||||
callbacks.remove(serial);
|
||||
errorCallback.handler(wvpResult);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[RedisGbPlayMsg] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[RedisGbPlayMsg] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理收到的请求推流的请求
|
||||
*/
|
||||
private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) {
|
||||
MediaServer mediaServer = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId());
|
||||
if (mediaServer == null) {
|
||||
// TODO 回复错误
|
||||
return;
|
||||
}
|
||||
SendRtpItem sendRtpItem = SendRtpItem.getInstance(requestPushStreamMsg);
|
||||
|
||||
try {
|
||||
mediaServerService.startSendRtp(mediaServer, null, sendRtpItem);
|
||||
}catch (ControllerException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 回复消息
|
||||
WVPResult<JSONObject> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), fromId,
|
||||
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, JSON.toJSONString(result));
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理收到的请求sendItem的请求
|
||||
*/
|
||||
private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) {
|
||||
MediaServer mediaServerItem = mediaServerService.getOne(content.getMediaServerId());
|
||||
if (mediaServerItem == null) {
|
||||
logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId());
|
||||
|
||||
WVPResult<SendRtpItem> result = new WVPResult<>();
|
||||
result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND);
|
||||
result.setMsg("流媒体不存在");
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
|
||||
WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result));
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
return;
|
||||
}
|
||||
// 确定流是否在线
|
||||
Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
|
||||
if (streamReady != null && streamReady) {
|
||||
logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream());
|
||||
responseSendItem(mediaServerItem, content, toId, serial);
|
||||
}else {
|
||||
// 流已经离线
|
||||
// 发送redis消息以使设备上线
|
||||
logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",content.getApp(), content.getStream());
|
||||
|
||||
String taskKey = UUID.randomUUID().toString();
|
||||
// 设置超时
|
||||
dynamicTask.startDelay(taskKey, ()->{
|
||||
logger.info("[ app={}, stream={} ] 等待设备开始推流超时", content.getApp(), content.getStream());
|
||||
WVPResult<SendRtpItem> result = new WVPResult<>();
|
||||
result.setCode(ERROR_CODE_TIMEOUT);
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
|
||||
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result)
|
||||
);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
|
||||
// 添加订阅
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, content.getApp(), content.getStream(), content.getMediaServerId());
|
||||
subscribe.addSubscribe(hook, (hookData)->{
|
||||
dynamicTask.stop(taskKey);
|
||||
responseSendItem(mediaServerItem, content, toId, serial);
|
||||
});
|
||||
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(),
|
||||
content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(),
|
||||
content.getMediaServerId());
|
||||
|
||||
String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
|
||||
logger.info("[redis发送通知] 推流被请求 {}: {}/{}", key, messageForPushChannel.getApp(), messageForPushChannel.getStream());
|
||||
redisTemplate.convertAndSend(key, JSON.toJSON(messageForPushChannel));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将获取到的sendItem发送出去
|
||||
*/
|
||||
private void responseSendItem(MediaServer mediaServerItem, RequestSendItemMsg content, String toId, String serial) {
|
||||
SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, content.getIp(),
|
||||
content.getPort(), content.getSsrc(), content.getPlatformId(),
|
||||
content.getApp(), content.getStream(), content.getChannelId(),
|
||||
content.getTcp(), content.getRtcp());
|
||||
|
||||
WVPResult<ResponseSendItemMsg> result = new WVPResult<>();
|
||||
result.setCode(0);
|
||||
ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg();
|
||||
responseSendItemMsg.setSendRtpItem(sendRtpItem);
|
||||
responseSendItemMsg.setMediaServerItem(mediaServerItem);
|
||||
result.setData(responseSendItemMsg);
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
|
||||
WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
|
||||
userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result)
|
||||
);
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
|
||||
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息要求下级生成推流信息
|
||||
* @param serverId 下级服务ID
|
||||
* @param app 应用名
|
||||
* @param stream 流ID
|
||||
* @param ip 目标IP
|
||||
* @param port 目标端口
|
||||
* @param ssrc ssrc
|
||||
* @param platformId 平台国标编号
|
||||
* @param channelId 通道ID
|
||||
* @param isTcp 是否使用TCP
|
||||
* @param callback 得到信息的回调
|
||||
*/
|
||||
public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc,
|
||||
String platformId, String channelId, boolean isTcp, boolean rtcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) {
|
||||
RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance(
|
||||
serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, rtcp, platformName);
|
||||
requestSendItemMsg.setServerId(serverId);
|
||||
String key = UUID.randomUUID().toString();
|
||||
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM,
|
||||
key, JSON.toJSONString(requestSendItemMsg));
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
|
||||
logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject);
|
||||
callbacks.put(key, callback);
|
||||
callbacksForError.put(key, errorCallback);
|
||||
dynamicTask.startDelay(key, ()->{
|
||||
callbacks.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
WVPResult<Object> wvpResult = new WVPResult<>();
|
||||
wvpResult.setCode(ERROR_CODE_TIMEOUT);
|
||||
wvpResult.setMsg("timeout");
|
||||
errorCallback.handler(wvpResult);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求推流的消息
|
||||
* @param param 推流参数
|
||||
* @param callback 回调
|
||||
*/
|
||||
public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) {
|
||||
String key = UUID.randomUUID().toString();
|
||||
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
|
||||
WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, JSON.toJSONString(param));
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
|
||||
logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject);
|
||||
dynamicTask.startDelay(key, ()->{
|
||||
callbacksForStartSendRtpStream.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
}, userSetting.getPlatformPlayTimeout());
|
||||
callbacksForStartSendRtpStream.put(key, callback);
|
||||
callbacksForError.put(key, (wvpResult)->{
|
||||
logger.info("[REDIS 请求其他平台推流] 失败: {}", wvpResult.getMsg());
|
||||
callbacksForStartSendRtpStream.remove(key);
|
||||
callbacksForError.remove(key);
|
||||
});
|
||||
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求推流的消息
|
||||
*/
|
||||
public void sendMsgForStopSendRtpStream(String serverId, RequestStopPushStreamMsg streamMsg) {
|
||||
String key = UUID.randomUUID().toString();
|
||||
WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
|
||||
WvpRedisMsgCmd.REQUEST_STOP_PUSH_STREAM, key, JSON.toJSONString(streamMsg));
|
||||
|
||||
JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
|
||||
logger.info("[REDIS 请求其他平台停止推流] {}: {}", serverId, jsonObject);
|
||||
redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
|
||||
}
|
||||
|
||||
private SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId) {
|
||||
if (platformGbId == null) {
|
||||
platformGbId = "*";
|
||||
}
|
||||
if (channelId == null) {
|
||||
channelId = "*";
|
||||
}
|
||||
if (streamId == null) {
|
||||
streamId = "*";
|
||||
}
|
||||
if (callId == null) {
|
||||
callId = "*";
|
||||
}
|
||||
String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX
|
||||
+ userSetting.getServerId() + "_*_"
|
||||
+ platformGbId + "_"
|
||||
+ channelId + "_"
|
||||
+ streamId + "_"
|
||||
+ callId;
|
||||
List<Object> scan = RedisUtil.scan(redisTemplate, key);
|
||||
if (scan.size() > 0) {
|
||||
return (SendRtpItem)redisTemplate.opsForValue().get(scan.get(0));
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理收到的请求推流的请求
|
||||
*/
|
||||
private void requestStopPushStreamMsgHand(RequestStopPushStreamMsg streamMsg, String fromId, String serial) {
|
||||
SendRtpItem sendRtpItem = streamMsg.getSendRtpItem();
|
||||
if (sendRtpItem == null) {
|
||||
logger.info("[REDIS 执行其他平台的请求停止推流] 失败: sendRtpItem为NULL");
|
||||
return;
|
||||
}
|
||||
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
if (mediaInfo == null) {
|
||||
// TODO 回复错误
|
||||
return;
|
||||
}
|
||||
|
||||
if (mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc())) {
|
||||
logger.info("[REDIS 执行其他平台的请求停止推流] 成功: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
// 发送redis消息
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
|
||||
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getPlatformId(), streamMsg.getPlatformName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
|
||||
messageForPushChannel.setPlatFormIndex(streamMsg.getPlatFormIndex());
|
||||
redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
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.InviteStreamType;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||
import com.genersoft.iot.vmp.service.IStreamPushService;
|
||||
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
|
||||
import com.genersoft.iot.vmp.service.bean.MessageForPushChannelResponse;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 接收redis发送的结束推流请求
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisPushStreamCloseResponseListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamCloseResponseListener.class);
|
||||
|
||||
@Autowired
|
||||
private IStreamPushService streamPushService;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
@Autowired
|
||||
private ISIPCommanderForPlatform commanderFroPlatform;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
|
||||
private Map<String, PushStreamResponseEvent> responseEvents = new ConcurrentHashMap<>();
|
||||
|
||||
public interface PushStreamResponseEvent{
|
||||
void run(MessageForPushChannelResponse response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
logger.info("[REDIS消息-推流结束]: {}", new String(message.getBody()));
|
||||
MessageForPushChannel pushChannel = JSON.parseObject(message.getBody(), MessageForPushChannel.class);
|
||||
StreamPushItem push = streamPushService.getPush(pushChannel.getApp(), pushChannel.getStream());
|
||||
if (push != null) {
|
||||
List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChannelId(
|
||||
push.getGbId());
|
||||
if (!sendRtpItems.isEmpty()) {
|
||||
for (SendRtpItem sendRtpItem : sendRtpItems) {
|
||||
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
|
||||
if (parentPlatform != null) {
|
||||
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStream());
|
||||
try {
|
||||
commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
if (push.isSelf()) {
|
||||
// 停止向上级推流
|
||||
logger.info("[REDIS消息-推流结束] 停止向上级推流:{}", sendRtpItem.getStream());
|
||||
MediaServer mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStream());
|
||||
mediaServerService.stopSendRtp(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
|
||||
if (InviteStreamType.PUSH == sendRtpItem.getPlayType()) {
|
||||
MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
|
||||
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getPlatformId(), parentPlatform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
|
||||
messageForPushChannel.setPlatFormIndex(parentPlatform.getId());
|
||||
redisCatchStorage.sendPlatformStopPlayMsg(messageForPushChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void addEvent(String app, String stream, PushStreamResponseEvent callback) {
|
||||
responseEvents.put(app + stream, callback);
|
||||
}
|
||||
|
||||
public void removeEvent(String app, String stream) {
|
||||
responseEvents.remove(app + stream);
|
||||
}
|
||||
}
|
||||
0
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java
Executable file → Normal file
0
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java
Executable file → Normal file
@ -84,7 +84,7 @@ public class RedisPushStreamStatusListMsgListener implements MessageListener {
|
||||
streamPushItem.setMediaServerId(mediaServerService.getDefaultMediaServer().getId());
|
||||
streamPushItem.setOriginType(2);
|
||||
streamPushItem.setOriginTypeStr("rtsp_push");
|
||||
streamPushItem.setTotalReaderCount("0");
|
||||
streamPushItem.setTotalReaderCount(0);
|
||||
streamPushItemForSave.add(streamPushItem);
|
||||
allGBId.put(streamPushItem.getGbId(), streamPushItem);
|
||||
} else {
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
package com.genersoft.iot.vmp.service.redisMsg;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.ChannelOnlineEvent;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
|
||||
/**
|
||||
* 接收其他wvp发送流变化通知
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class RedisStreamMsgListener implements MessageListener {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class);
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private ZLMMediaListManager zlmMediaListManager;
|
||||
|
||||
private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] bytes) {
|
||||
boolean isEmpty = taskQueue.isEmpty();
|
||||
taskQueue.offer(message);
|
||||
if (isEmpty) {
|
||||
taskExecutor.execute(() -> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
Message msg = taskQueue.poll();
|
||||
try {
|
||||
JSONObject steamMsgJson = JSON.parseObject(msg.getBody(), JSONObject.class);
|
||||
if (steamMsgJson == null) {
|
||||
logger.warn("[收到redis 流变化]消息解析失败");
|
||||
continue;
|
||||
}
|
||||
String serverId = steamMsgJson.getString("serverId");
|
||||
|
||||
if (userSetting.getServerId().equals(serverId)) {
|
||||
// 自己发送的消息忽略即可
|
||||
continue;
|
||||
}
|
||||
logger.info("[收到redis 流变化]: {}", new String(message.getBody()));
|
||||
String app = steamMsgJson.getString("app");
|
||||
String stream = steamMsgJson.getString("stream");
|
||||
boolean register = steamMsgJson.getBoolean("register");
|
||||
String mediaServerId = steamMsgJson.getString("mediaServerId");
|
||||
OnStreamChangedHookParam onStreamChangedHookParam = new OnStreamChangedHookParam();
|
||||
onStreamChangedHookParam.setSeverId(serverId);
|
||||
onStreamChangedHookParam.setApp(app);
|
||||
onStreamChangedHookParam.setStream(stream);
|
||||
onStreamChangedHookParam.setRegist(register);
|
||||
onStreamChangedHookParam.setMediaServerId(mediaServerId);
|
||||
onStreamChangedHookParam.setCreateStamp(System.currentTimeMillis()/1000);
|
||||
onStreamChangedHookParam.setAliveSecond(0L);
|
||||
onStreamChangedHookParam.setTotalReaderCount(0);
|
||||
onStreamChangedHookParam.setOriginType(0);
|
||||
onStreamChangedHookParam.setOriginTypeStr("0");
|
||||
onStreamChangedHookParam.setOriginTypeStr("unknown");
|
||||
ChannelOnlineEvent channelOnlineEventLister = zlmMediaListManager.getChannelOnlineEventLister(app, stream);
|
||||
if ( channelOnlineEventLister != null) {
|
||||
try {
|
||||
channelOnlineEventLister.run(app, stream, serverId);;
|
||||
} catch (ParseException e) {
|
||||
logger.error("addPush: ", e);
|
||||
}
|
||||
zlmMediaListManager.removedChannelOnlineEventLister(app, stream);
|
||||
}
|
||||
}catch (Exception e) {
|
||||
logger.warn("[REDIS消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
|
||||
logger.error("[REDIS消息-流变化] 异常内容: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
package com.genersoft.iot.vmp.service.redisMsg.control;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.hook.Hook;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookType;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.SipException;
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* 其他wvp发起的rpc调用,这里的方法被 RedisRpcConfig 通过反射寻找对应的方法名称调用
|
||||
*/
|
||||
@Component
|
||||
public class RedisRpcController {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisRpcController.class);
|
||||
|
||||
@Autowired
|
||||
private SSRCFactory ssrcFactory;
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
private SendRtpPortManager sendRtpPortManager;
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private HookSubscribe hookSubscribe;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ISIPCommanderForPlatform commanderFroPlatform;
|
||||
|
||||
|
||||
@Autowired
|
||||
private IVideoManagerStorage storager;
|
||||
|
||||
|
||||
/**
|
||||
* 获取发流的信息
|
||||
*/
|
||||
public RedisRpcResponse getSendRtpItem(RedisRpcRequest request) {
|
||||
String sendRtpItemKey = request.getParam().toString();
|
||||
SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey);
|
||||
if (sendRtpItem == null) {
|
||||
logger.info("[redis-rpc] 获取发流的信息, 未找到redis中的发流信息, key:{}", sendRtpItemKey);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
return response;
|
||||
}
|
||||
logger.info("[redis-rpc] 获取发流的信息: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort());
|
||||
// 查询本级是否有这个流
|
||||
MediaServer mediaServerItem = mediaServerService.getMediaServerByAppAndStream(sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
if (mediaServerItem == null) {
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
}
|
||||
// 自平台内容
|
||||
int localPort = sendRtpPortManager.getNextPort(mediaServerItem);
|
||||
if (localPort == 0) {
|
||||
logger.info("[redis-rpc] getSendRtpItem->服务器端口资源不足" );
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
}
|
||||
// 写入redis, 超时时回复
|
||||
sendRtpItem.setStatus(1);
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
sendRtpItem.setLocalIp(mediaServerItem.getSdpIp());
|
||||
if (sendRtpItem.getSsrc() == null) {
|
||||
// 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
|
||||
sendRtpItem.setSsrc(ssrc);
|
||||
}
|
||||
redisCatchStorage.updateSendRTPSever(sendRtpItem);
|
||||
redisTemplate.opsForValue().set(sendRtpItemKey, sendRtpItem);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
response.setBody(sendRtpItemKey);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听流上线
|
||||
*/
|
||||
public RedisRpcResponse waitePushStreamOnline(RedisRpcRequest request) {
|
||||
SendRtpItem sendRtpItem = JSONObject.parseObject(request.getParam().toString(), SendRtpItem.class);
|
||||
logger.info("[redis-rpc] 监听流上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort());
|
||||
// 查询本级是否有这个流
|
||||
MediaServer mediaServer = mediaServerService.getMediaServerByAppAndStream(sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
if (mediaServer != null) {
|
||||
logger.info("[redis-rpc] 监听流上线时发现流已存在直接返回: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() );
|
||||
// 读取redis中的上级点播信息,生成sendRtpItm发送出去
|
||||
if (sendRtpItem.getSsrc() == null) {
|
||||
// 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(mediaServer.getId()) : ssrcFactory.getPlayBackSsrc(mediaServer.getId());
|
||||
sendRtpItem.setSsrc(ssrc);
|
||||
}
|
||||
sendRtpItem.setMediaServerId(mediaServer.getId());
|
||||
sendRtpItem.setLocalIp(mediaServer.getSdpIp());
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
|
||||
redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setBody(sendRtpItem.getRedisKey());
|
||||
response.setStatusCode(200);
|
||||
}
|
||||
// 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null);
|
||||
hookSubscribe.addSubscribe(hook, (hookData) -> {
|
||||
logger.info("[redis-rpc] 监听流上线,流已上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort());
|
||||
// 读取redis中的上级点播信息,生成sendRtpItm发送出去
|
||||
if (sendRtpItem.getSsrc() == null) {
|
||||
// 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(hookData.getMediaServer().getId()) : ssrcFactory.getPlayBackSsrc(hookData.getMediaServer().getId());
|
||||
sendRtpItem.setSsrc(ssrc);
|
||||
}
|
||||
sendRtpItem.setMediaServerId(hookData.getMediaServer().getId());
|
||||
sendRtpItem.setLocalIp(hookData.getMediaServer().getSdpIp());
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
|
||||
redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setBody(sendRtpItem.getRedisKey());
|
||||
response.setStatusCode(200);
|
||||
// 手动发送结果
|
||||
sendResponse(response);
|
||||
hookSubscribe.removeSubscribe(hook);
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听流上线
|
||||
*/
|
||||
public RedisRpcResponse stopWaitePushStreamOnline(RedisRpcRequest request) {
|
||||
SendRtpItem sendRtpItem = JSONObject.parseObject(request.getParam().toString(), SendRtpItem.class);
|
||||
logger.info("[redis-rpc] 停止监听流上线: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() );
|
||||
// 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null);
|
||||
hookSubscribe.removeSubscribe(hook);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 开始发流
|
||||
*/
|
||||
public RedisRpcResponse startSendRtp(RedisRpcRequest request) {
|
||||
String sendRtpItemKey = request.getParam().toString();
|
||||
SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
if (sendRtpItem == null) {
|
||||
logger.info("[redis-rpc] 开始发流, 未找到redis中的发流信息, key:{}", sendRtpItemKey);
|
||||
WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到redis中的发流信息");
|
||||
response.setBody(wvpResult);
|
||||
return response;
|
||||
}
|
||||
logger.info("[redis-rpc] 开始发流: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort());
|
||||
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
if (mediaServer == null) {
|
||||
logger.info("[redis-rpc] startSendRtp->未找到MediaServer: {}", sendRtpItem.getMediaServerId() );
|
||||
WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到MediaServer");
|
||||
response.setBody(wvpResult);
|
||||
return response;
|
||||
}
|
||||
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
if (mediaInfo != null) {
|
||||
logger.info("[redis-rpc] startSendRtp->流不在线: {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream() );
|
||||
WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "流不在线");
|
||||
response.setBody(wvpResult);
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
mediaServerService.startSendRtp(mediaServer, null, sendRtpItem);
|
||||
}catch (ControllerException exception) {
|
||||
logger.info("[redis-rpc] 发流失败: {}/{}, 目标地址: {}:{}, {}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), exception.getMsg());
|
||||
WVPResult wvpResult = WVPResult.fail(exception.getCode(), exception.getMsg());
|
||||
response.setBody(wvpResult);
|
||||
return response;
|
||||
}
|
||||
logger.info("[redis-rpc] 发流成功: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort());
|
||||
WVPResult wvpResult = WVPResult.success();
|
||||
response.setBody(wvpResult);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止发流
|
||||
*/
|
||||
public RedisRpcResponse stopSendRtp(RedisRpcRequest request) {
|
||||
String sendRtpItemKey = request.getParam().toString();
|
||||
SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
if (sendRtpItem == null) {
|
||||
logger.info("[redis-rpc] 停止推流, 未找到redis中的发流信息, key:{}", sendRtpItemKey);
|
||||
WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到redis中的发流信息");
|
||||
response.setBody(wvpResult);
|
||||
return response;
|
||||
}
|
||||
logger.info("[redis-rpc] 停止推流: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() );
|
||||
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
|
||||
if (mediaServer == null) {
|
||||
logger.info("[redis-rpc] stopSendRtp->未找到MediaServer: {}", sendRtpItem.getMediaServerId() );
|
||||
WVPResult wvpResult = WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到MediaServer");
|
||||
response.setBody(wvpResult);
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc());
|
||||
}catch (ControllerException exception) {
|
||||
logger.info("[redis-rpc] 停止推流失败: {}/{}, 目标地址: {}:{}, code: {}, msg: {}", sendRtpItem.getApp(),
|
||||
sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort(), exception.getCode(), exception.getMsg() );
|
||||
response.setBody(WVPResult.fail(exception.getCode(), exception.getMsg()));
|
||||
return response;
|
||||
}
|
||||
logger.info("[redis-rpc] 停止推流成功: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() );
|
||||
response.setBody(WVPResult.success());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 其他wvp通知推流已经停止了
|
||||
*/
|
||||
public RedisRpcResponse rtpSendStopped(RedisRpcRequest request) {
|
||||
String sendRtpItemKey = request.getParam().toString();
|
||||
SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(sendRtpItemKey);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(200);
|
||||
if (sendRtpItem == null) {
|
||||
logger.info("[redis-rpc] 推流已经停止, 未找到redis中的发流信息, key:{}", sendRtpItemKey);
|
||||
return response;
|
||||
}
|
||||
logger.info("[redis-rpc] 推流已经停止: {}/{}, 目标地址: {}:{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getIp(), sendRtpItem.getPort() );
|
||||
String platformId = sendRtpItem.getPlatformId();
|
||||
ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
|
||||
if (platform == null) {
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
|
||||
redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getCallId(), sendRtpItem.getStream());
|
||||
redisCatchStorage.sendPlatformStopPlayMsg(sendRtpItem, platform);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private void sendResponse(RedisRpcResponse response){
|
||||
logger.info("[redis-rpc] >> {}", response);
|
||||
response.setToId(userSetting.getServerId());
|
||||
RedisRpcMessage message = new RedisRpcMessage();
|
||||
message.setResponse(response);
|
||||
redisTemplate.convertAndSend(RedisRpcConfig.REDIS_REQUEST_CHANNEL_KEY, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
package com.genersoft.iot.vmp.service.redisMsg.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.common.CommonCallback;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
|
||||
import com.genersoft.iot.vmp.media.event.hook.Hook;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
|
||||
import com.genersoft.iot.vmp.media.event.hook.HookType;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class RedisRpcServiceImpl implements IRedisRpcService {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(RedisRpcServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private RedisRpcConfig redisRpcConfig;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private HookSubscribe hookSubscribe;
|
||||
|
||||
@Autowired
|
||||
private SSRCFactory ssrcFactory;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private RedisRpcRequest buildRequest(String uri, Object param) {
|
||||
RedisRpcRequest request = new RedisRpcRequest();
|
||||
request.setFromId(userSetting.getServerId());
|
||||
request.setParam(param);
|
||||
request.setUri(uri);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendRtpItem getSendRtpItem(String sendRtpItemKey) {
|
||||
RedisRpcRequest request = buildRequest("getSendRtpItem", sendRtpItemKey);
|
||||
RedisRpcResponse response = redisRpcConfig.request(request, 10);
|
||||
if (response.getBody() == null) {
|
||||
return null;
|
||||
}
|
||||
return (SendRtpItem)redisTemplate.opsForValue().get(response.getBody().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WVPResult startSendRtp(String sendRtpItemKey, SendRtpItem sendRtpItem) {
|
||||
logger.info("[请求其他WVP] 开始推流,wvp:{}, {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
RedisRpcRequest request = buildRequest("startSendRtp", sendRtpItemKey);
|
||||
request.setToId(sendRtpItem.getServerId());
|
||||
RedisRpcResponse response = redisRpcConfig.request(request, 10);
|
||||
return JSON.parseObject(response.getBody().toString(), WVPResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WVPResult stopSendRtp(String sendRtpItemKey) {
|
||||
SendRtpItem sendRtpItem = (SendRtpItem)redisTemplate.opsForValue().get(sendRtpItemKey);
|
||||
if (sendRtpItem == null) {
|
||||
logger.info("[请求其他WVP] 停止推流, 未找到redis中的发流信息, key:{}", sendRtpItemKey);
|
||||
return WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到发流信息");
|
||||
}
|
||||
logger.info("[请求其他WVP] 停止推流,wvp:{}, {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
RedisRpcRequest request = buildRequest("stopSendRtp", sendRtpItemKey);
|
||||
request.setToId(sendRtpItem.getServerId());
|
||||
RedisRpcResponse response = redisRpcConfig.request(request, 10);
|
||||
return JSON.parseObject(response.getBody().toString(), WVPResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long waitePushStreamOnline(SendRtpItem sendRtpItem, CommonCallback<String> callback) {
|
||||
logger.info("[请求所有WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
// 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null);
|
||||
RedisRpcRequest request = buildRequest("waitePushStreamOnline", sendRtpItem);
|
||||
request.setToId(sendRtpItem.getServerId());
|
||||
hookSubscribe.addSubscribe(hook, (hookData) -> {
|
||||
|
||||
// 读取redis中的上级点播信息,生成sendRtpItm发送出去
|
||||
if (sendRtpItem.getSsrc() == null) {
|
||||
// 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(hookData.getMediaServer().getId()) : ssrcFactory.getPlayBackSsrc(hookData.getMediaServer().getId());
|
||||
sendRtpItem.setSsrc(ssrc);
|
||||
}
|
||||
sendRtpItem.setMediaServerId(hookData.getMediaServer().getId());
|
||||
sendRtpItem.setLocalIp(hookData.getMediaServer().getSdpIp());
|
||||
sendRtpItem.setServerId(userSetting.getServerId());
|
||||
redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem);
|
||||
if (callback != null) {
|
||||
callback.run(sendRtpItem.getRedisKey());
|
||||
}
|
||||
hookSubscribe.removeSubscribe(hook);
|
||||
redisRpcConfig.removeCallback(request.getSn());
|
||||
});
|
||||
|
||||
redisRpcConfig.request(request, response -> {
|
||||
if (response.getBody() == null) {
|
||||
logger.info("[请求所有WVP监听流上线] 流上线,但是未找到发流信息:{}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
return;
|
||||
}
|
||||
logger.info("[请求所有WVP监听流上线] 流上线 {}/{}->{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.toString());
|
||||
|
||||
if (callback != null) {
|
||||
callback.run(response.getBody().toString());
|
||||
}
|
||||
hookSubscribe.removeSubscribe(hook);
|
||||
});
|
||||
return request.getSn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopWaitePushStreamOnline(SendRtpItem sendRtpItem) {
|
||||
logger.info("[停止WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
|
||||
Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null);
|
||||
hookSubscribe.removeSubscribe(hook);
|
||||
RedisRpcRequest request = buildRequest("stopWaitePushStreamOnline", sendRtpItem);
|
||||
request.setToId(sendRtpItem.getServerId());
|
||||
redisRpcConfig.request(request, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rtpSendStopped(String sendRtpItemKey) {
|
||||
SendRtpItem sendRtpItem = (SendRtpItem)redisTemplate.opsForValue().get(sendRtpItemKey);
|
||||
if (sendRtpItem == null) {
|
||||
logger.info("[停止WVP监听流上线] 未找到redis中的发流信息, key:{}", sendRtpItemKey);
|
||||
return;
|
||||
}
|
||||
RedisRpcRequest request = buildRequest("rtpSendStopped", sendRtpItemKey);
|
||||
request.setToId(sendRtpItem.getServerId());
|
||||
redisRpcConfig.request(request, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallback(long key) {
|
||||
redisRpcConfig.removeCallback(key);
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,7 @@ package com.genersoft.iot.vmp.storager;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.common.SystemAllInfo;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
|
||||
@ -45,6 +42,8 @@ public interface IRedisCatchStorage {
|
||||
|
||||
void updateSendRTPSever(SendRtpItem sendRtpItem);
|
||||
|
||||
List<SendRtpItem> querySendRTPServer(String platformGbId, String channelId, String streamId);
|
||||
|
||||
/**
|
||||
* 查询RTP推送信息缓存
|
||||
* @param platformGbId
|
||||
@ -197,6 +196,8 @@ public interface IRedisCatchStorage {
|
||||
|
||||
void addDiskInfo(List<Map<String, Object>> diskInfo);
|
||||
|
||||
void deleteSendRTPServer(SendRtpItem sendRtpItem);
|
||||
|
||||
List<SendRtpItem> queryAllSendRTPServer();
|
||||
|
||||
List<Device> getAllDevices();
|
||||
@ -209,7 +210,7 @@ public interface IRedisCatchStorage {
|
||||
|
||||
void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel);
|
||||
|
||||
void sendPlatformStopPlayMsg(MessageForPushChannel messageForPushChannel);
|
||||
void sendPlatformStopPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform);
|
||||
|
||||
void addPushListItem(String app, String stream, MediaArrivalEvent param);
|
||||
|
||||
@ -219,4 +220,11 @@ public interface IRedisCatchStorage {
|
||||
|
||||
void sendPushStreamClose(MessageForPushChannel messageForPushChannel);
|
||||
|
||||
void addWaiteSendRtpItem(SendRtpItem sendRtpItem, int platformPlayTimeout);
|
||||
|
||||
SendRtpItem getWaiteSendRtpItem(String app, String stream);
|
||||
|
||||
void sendStartSendRtp(SendRtpItem sendRtpItem);
|
||||
|
||||
void sendPushStreamOnline(SendRtpItem sendRtpItem);
|
||||
}
|
||||
|
||||
@ -50,12 +50,15 @@ public interface CloudRecordServiceMapper {
|
||||
" <if test= 'mediaServerItemList != null ' > and media_server_id in " +
|
||||
" <foreach collection='mediaServerItemList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
|
||||
" </if>" +
|
||||
" <if test= 'ids != null ' > and id in " +
|
||||
" <foreach collection='ids' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
|
||||
" </if>" +
|
||||
" order by start_time DESC" +
|
||||
|
||||
" </script>")
|
||||
List<CloudRecordItem> getList(@Param("query") String query, @Param("app") String app, @Param("stream") String stream,
|
||||
@Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
|
||||
@Param("callId")String callId, List<MediaServer> mediaServerItemList);
|
||||
@Param("callId")String callId, List<MediaServer> mediaServerItemList,
|
||||
List<Integer> ids);
|
||||
|
||||
|
||||
@Select(" <script>" +
|
||||
|
||||
@ -52,8 +52,8 @@ public interface DeviceChannelMapper {
|
||||
"<if test='status != null'>, status=#{status}</if>" +
|
||||
"<if test='streamId != null'>, stream_id=#{streamId}</if>" +
|
||||
"<if test='hasAudio != null'>, has_audio=#{hasAudio}</if>" +
|
||||
", custom_longitude=#{longitude}" +
|
||||
", custom_latitude=#{latitude}" +
|
||||
"<if test='customLongitude != null'>, custom_longitude=#{customLongitude}</if>" +
|
||||
"<if test='customLatitude != null'>, custom_latitude=#{customLatitude}</if>" +
|
||||
"<if test='longitudeGcj02 != null'>, longitude_gcj02=#{longitudeGcj02}</if>" +
|
||||
"<if test='latitudeGcj02 != null'>, latitude_gcj02=#{latitudeGcj02}</if>" +
|
||||
"<if test='longitudeWgs84 != null'>, longitude_wgs84=#{longitudeWgs84}</if>" +
|
||||
@ -89,8 +89,10 @@ public interface DeviceChannelMapper {
|
||||
"dc.password, " +
|
||||
"COALESCE(dc.custom_ptz_type, dc.ptz_type) AS ptz_type, " +
|
||||
"dc.status, " +
|
||||
"COALESCE(dc.custom_longitude, dc.longitude) AS longitude, " +
|
||||
"COALESCE(dc.custom_latitude, dc.latitude) AS latitude, " +
|
||||
"dc.longitude, " +
|
||||
"dc.latitude, " +
|
||||
"dc.custom_longitude, " +
|
||||
"dc.custom_latitude, " +
|
||||
"dc.stream_id, " +
|
||||
"dc.device_id, " +
|
||||
"dc.parental, " +
|
||||
@ -345,6 +347,8 @@ public interface DeviceChannelMapper {
|
||||
"<if test='item.hasAudio != null'>, has_audio=#{item.hasAudio}</if>" +
|
||||
"<if test='item.longitude != null'>, longitude=#{item.longitude}</if>" +
|
||||
"<if test='item.latitude != null'>, latitude=#{item.latitude}</if>" +
|
||||
"<if test='item.customLongitude != null'>, custom_longitude=#{item.customLongitude}</if>" +
|
||||
"<if test='item.customLatitude != null'>, custom_latitude=#{item.customLatitude}</if>" +
|
||||
"<if test='item.longitudeGcj02 != null'>, longitude_gcj02=#{item.longitudeGcj02}</if>" +
|
||||
"<if test='item.latitudeGcj02 != null'>, latitude_gcj02=#{item.latitudeGcj02}</if>" +
|
||||
"<if test='item.longitudeWgs84 != null'>, longitude_wgs84=#{item.longitudeWgs84}</if>" +
|
||||
@ -353,7 +357,8 @@ public interface DeviceChannelMapper {
|
||||
"<if test='item.gpsTime != null'>, gps_time=#{item.gpsTime}</if>" +
|
||||
"<if test='item.streamIdentification != null'>, stream_identification=#{item.streamIdentification}</if>" +
|
||||
"<if test='item.id > 0'>WHERE id=#{item.id}</if>" +
|
||||
"<if test='item.id == 0'>WHERE device_id=#{item.deviceId} AND channel_id=#{item.channelId}</if>" +
|
||||
"<if test='item.id == 0 and item.channelId != null '>WHERE device_id=#{item.deviceId} AND channel_id=#{item.channelId}</if>" +
|
||||
"<if test='item.id == 0 and item.channelId == null '>WHERE device_id=#{item.deviceId}</if>" +
|
||||
"</foreach>" +
|
||||
"</script>"})
|
||||
int batchUpdate(List<DeviceChannel> updateChannels);
|
||||
@ -550,4 +555,24 @@ public interface DeviceChannelMapper {
|
||||
" <if test='channelId != null'> and channel_id = #{channelId} </if>" +
|
||||
"</script>")
|
||||
void updateChannelStreamIdentification(DeviceChannel channel);
|
||||
|
||||
|
||||
@Update({"<script>" +
|
||||
"<foreach collection='channelList' item='item' separator=';'>" +
|
||||
" UPDATE" +
|
||||
" wvp_device_channel" +
|
||||
" SET update_time=#{item.updateTime}" +
|
||||
"<if test='item.longitude != null'>, longitude=#{item.longitude}</if>" +
|
||||
"<if test='item.latitude != null'>, latitude=#{item.latitude}</if>" +
|
||||
"<if test='item.longitudeGcj02 != null'>, longitude_gcj02=#{item.longitudeGcj02}</if>" +
|
||||
"<if test='item.latitudeGcj02 != null'>, latitude_gcj02=#{item.latitudeGcj02}</if>" +
|
||||
"<if test='item.longitudeWgs84 != null'>, longitude_wgs84=#{item.longitudeWgs84}</if>" +
|
||||
"<if test='item.latitudeWgs84 != null'>, latitude_wgs84=#{item.latitudeWgs84}</if>" +
|
||||
"<if test='item.gpsTime != null'>, gps_time=#{item.gpsTime}</if>" +
|
||||
"<if test='item.id > 0'>WHERE id=#{item.id}</if>" +
|
||||
"<if test='item.id == 0'>WHERE device_id=#{item.deviceId} AND channel_id=#{item.channelId}</if>" +
|
||||
"</foreach>" +
|
||||
"</script>"})
|
||||
void batchUpdatePosition(List<DeviceChannel> channelList);
|
||||
|
||||
}
|
||||
|
||||
@ -33,4 +33,33 @@ public interface DeviceMobilePositionMapper {
|
||||
@Delete("DELETE FROM wvp_device_mobile_position WHERE device_id = #{deviceId}")
|
||||
int clearMobilePositionsByDeviceId(String deviceId);
|
||||
|
||||
|
||||
@Insert("<script> " +
|
||||
"insert into wvp_device_mobile_position " +
|
||||
"(device_id,channel_id, device_name,time,longitude,latitude,altitude,speed,direction,report_source," +
|
||||
"longitude_gcj02,latitude_gcj02,longitude_wgs84,latitude_wgs84,create_time)"+
|
||||
"values " +
|
||||
"<foreach collection='mobilePositions' index='index' item='item' separator=','> " +
|
||||
"(#{item.deviceId}, #{item.channelId}, #{item.deviceName}, #{item.time}, #{item.longitude}, " +
|
||||
"#{item.latitude}, #{item.altitude}, #{item.speed},#{item.direction}," +
|
||||
"#{item.reportSource}, #{item.longitudeGcj02}, #{item.latitudeGcj02}, #{item.longitudeWgs84}, #{item.latitudeWgs84}, " +
|
||||
"#{item.createTime}) " +
|
||||
"</foreach> " +
|
||||
"</script>")
|
||||
void batchadd2(List<MobilePosition> mobilePositions);
|
||||
|
||||
@Insert("<script> " +
|
||||
"<foreach collection='mobilePositions' index='index' item='item' separator=';'> " +
|
||||
"insert into wvp_device_mobile_position " +
|
||||
"(device_id,channel_id, device_name,time,longitude,latitude,altitude,speed,direction,report_source," +
|
||||
"longitude_gcj02,latitude_gcj02,longitude_wgs84,latitude_wgs84,create_time)"+
|
||||
"values " +
|
||||
"(#{item.deviceId}, #{item.channelId}, #{item.deviceName}, #{item.time}, #{item.longitude}, " +
|
||||
"#{item.latitude}, #{item.altitude}, #{item.speed},#{item.direction}," +
|
||||
"#{item.reportSource}, #{item.longitudeGcj02}, #{item.latitudeGcj02}, #{item.longitudeWgs84}, #{item.latitudeWgs84}, " +
|
||||
"#{item.createTime}) " +
|
||||
"</foreach> " +
|
||||
"</script>")
|
||||
void batchadd(List<MobilePosition> mobilePositions);
|
||||
|
||||
}
|
||||
|
||||
@ -41,6 +41,7 @@ public interface MediaServerMapper {
|
||||
"type,"+
|
||||
"create_time,"+
|
||||
"update_time,"+
|
||||
"transcode_suffix,"+
|
||||
"hook_alive_interval"+
|
||||
") VALUES " +
|
||||
"(" +
|
||||
@ -72,6 +73,7 @@ public interface MediaServerMapper {
|
||||
"#{type}, " +
|
||||
"#{createTime}, " +
|
||||
"#{updateTime}, " +
|
||||
"#{transcodeSuffix}, " +
|
||||
"#{hookAliveInterval})")
|
||||
int add(MediaServer mediaServerItem);
|
||||
|
||||
@ -102,6 +104,7 @@ public interface MediaServerMapper {
|
||||
"<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
|
||||
"<if test=\"recordDay != null\">, record_day=#{recordDay}</if>" +
|
||||
"<if test=\"recordPath != null\">, record_path=#{recordPath}</if>" +
|
||||
"<if test=\"transcodeSuffix != null\">, transcode_suffix=#{transcodeSuffix}</if>" +
|
||||
"<if test=\"type != null\">, type=#{type}</if>" +
|
||||
"WHERE id=#{id}"+
|
||||
" </script>"})
|
||||
@ -133,6 +136,7 @@ public interface MediaServerMapper {
|
||||
"<if test=\"recordDay != null\">, record_day=#{recordDay}</if>" +
|
||||
"<if test=\"recordPath != null\">, record_path=#{recordPath}</if>" +
|
||||
"<if test=\"type != null\">, type=#{type}</if>" +
|
||||
"<if test=\"transcodeSuffix != null\">, transcode_suffix=#{transcodeSuffix}</if>" +
|
||||
"<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
|
||||
"WHERE ip=#{ip} and http_port=#{httpPort}"+
|
||||
" </script>"})
|
||||
|
||||
@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
|
||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
@ -81,13 +82,9 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
|
||||
@Override
|
||||
public void resetAllCSEQ() {
|
||||
String scanKey = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId() + "_*";
|
||||
List<Object> keys = RedisUtil.scan(redisTemplate, scanKey);
|
||||
for (Object o : keys) {
|
||||
String key = (String) o;
|
||||
String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId();
|
||||
redisTemplate.opsForValue().set(key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAllSN() {
|
||||
@ -146,15 +143,26 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
|
||||
@Override
|
||||
public void updateSendRTPSever(SendRtpItem sendRtpItem) {
|
||||
redisTemplate.opsForValue().set(sendRtpItem.getRedisKey(), sendRtpItem);
|
||||
}
|
||||
|
||||
String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX +
|
||||
userSetting.getServerId() + "_"
|
||||
+ sendRtpItem.getMediaServerId() + "_"
|
||||
+ sendRtpItem.getPlatformId() + "_"
|
||||
+ sendRtpItem.getChannelId() + "_"
|
||||
+ sendRtpItem.getStream() + "_"
|
||||
+ sendRtpItem.getCallId();
|
||||
redisTemplate.opsForValue().set(key, sendRtpItem);
|
||||
@Override
|
||||
public List<SendRtpItem> querySendRTPServer(String platformGbId, String channelId, String streamId) {
|
||||
String scanKey = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX
|
||||
+ userSetting.getServerId() + "_*_"
|
||||
+ platformGbId + "_"
|
||||
+ channelId + "_"
|
||||
+ streamId + "_"
|
||||
+ "*";
|
||||
List<SendRtpItem> result = new ArrayList<>();
|
||||
List<Object> scan = RedisUtil.scan(redisTemplate, scanKey);
|
||||
if (!scan.isEmpty()) {
|
||||
for (Object o : scan) {
|
||||
String key = (String) o;
|
||||
result.add(JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpItem.class));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -172,7 +180,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
callId = "*";
|
||||
}
|
||||
String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX
|
||||
+ userSetting.getServerId() + "_*_"
|
||||
+ "*_*_"
|
||||
+ platformGbId + "_"
|
||||
+ channelId + "_"
|
||||
+ streamId + "_"
|
||||
@ -268,11 +276,20 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
List<Object> scan = RedisUtil.scan(redisTemplate, key);
|
||||
if (scan.size() > 0) {
|
||||
for (Object keyStr : scan) {
|
||||
logger.info("[删除 redis的SendRTP]: {}", keyStr.toString());
|
||||
redisTemplate.delete(keyStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除RTP推送信息缓存
|
||||
*/
|
||||
@Override
|
||||
public void deleteSendRTPServer(SendRtpItem sendRtpItem) {
|
||||
deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(),sendRtpItem.getCallId(), sendRtpItem.getStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SendRtpItem> queryAllSendRTPServer() {
|
||||
String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX
|
||||
@ -555,7 +572,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
@Override
|
||||
public void sendMobilePositionMsg(JSONObject jsonObject) {
|
||||
String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_MOBILE_POSITION;
|
||||
logger.info("[redis发送通知] 发送 移动位置 {}: {}", key, jsonObject.toString());
|
||||
logger.debug("[redis发送通知] 发送 移动位置 {}: {}", key, jsonObject.toString());
|
||||
redisTemplate.convertAndSend(key, jsonObject);
|
||||
}
|
||||
|
||||
@ -646,9 +663,15 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlatformStopPlayMsg(MessageForPushChannel msg) {
|
||||
public void sendPlatformStopPlayMsg(SendRtpItem sendRtpItem, ParentPlatform platform) {
|
||||
|
||||
MessageForPushChannel msg = MessageForPushChannel.getInstance(0,
|
||||
sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
|
||||
sendRtpItem.getPlatformId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
|
||||
msg.setPlatFormIndex(platform.getId());
|
||||
|
||||
String key = VideoManagerConstants.VM_MSG_STREAM_STOP_PLAY_NOTIFY;
|
||||
logger.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId());
|
||||
logger.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId());
|
||||
redisTemplate.convertAndSend(key, JSON.toJSON(msg));
|
||||
}
|
||||
|
||||
@ -681,4 +704,30 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
||||
logger.info("[redis发送通知] 发送 停止向上级推流 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId());
|
||||
redisTemplate.convertAndSend(key, JSON.toJSON(msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWaiteSendRtpItem(SendRtpItem sendRtpItem, int platformPlayTimeout) {
|
||||
String key = VideoManagerConstants.WAITE_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream();
|
||||
redisTemplate.opsForValue().set(key, sendRtpItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendRtpItem getWaiteSendRtpItem(String app, String stream) {
|
||||
String key = VideoManagerConstants.WAITE_SEND_PUSH_STREAM + app + "_" + stream;
|
||||
return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpItem.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStartSendRtp(SendRtpItem sendRtpItem) {
|
||||
String key = VideoManagerConstants.START_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream();
|
||||
logger.info("[redis发送通知] 通知其他WVP推流 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getPlatformId());
|
||||
redisTemplate.convertAndSend(key, JSON.toJSON(sendRtpItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPushStreamOnline(SendRtpItem sendRtpItem) {
|
||||
String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED;
|
||||
logger.info("[redis发送通知] 流上线 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getPlatformId());
|
||||
redisTemplate.convertAndSend(key, JSON.toJSON(sendRtpItem));
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ 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.media.zlm.dto.StreamProxyItem;
|
||||
import com.genersoft.iot.vmp.service.IGbStreamService;
|
||||
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
|
||||
@ -139,7 +138,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
gbIdSet.add(deviceChannel.getChannelId());
|
||||
if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
|
||||
deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
|
||||
deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
|
||||
deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).getHasAudio());
|
||||
if (allChannelMap.get(deviceChannel.getChannelId()).isStatus() !=deviceChannel.isStatus()){
|
||||
List<String> strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId());
|
||||
if (!CollectionUtils.isEmpty(strings)){
|
||||
@ -275,7 +274,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
|
||||
deviceChannel.setUpdateTime(DateUtil.getNow());
|
||||
if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
|
||||
deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
|
||||
deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
|
||||
deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).getHasAudio());
|
||||
if (allChannelMap.get(deviceChannel.getChannelId()).isStatus() !=deviceChannel.isStatus()){
|
||||
List<String> strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId());
|
||||
if (!CollectionUtils.isEmpty(strings)){
|
||||
|
||||
@ -106,6 +106,14 @@ public class DateUtil {
|
||||
return formatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间戳 转 yyyy_MM_dd_HH_mm_ss
|
||||
*/
|
||||
public static String timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(long timestamp) {
|
||||
Instant instant = Instant.ofEpochMilli(timestamp);
|
||||
return urlFormatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr)));
|
||||
}
|
||||
|
||||
/**
|
||||
* yyyy_MM_dd_HH_mm_ss 转时间戳(毫秒)
|
||||
*
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package com.genersoft.iot.vmp.utils;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MediaServerUtils {
|
||||
public static Map<String, String> urlParamToMap(String params) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
if (ObjectUtils.isEmpty(params)) {
|
||||
return map;
|
||||
}
|
||||
String[] paramsArray = params.split("&");
|
||||
if (paramsArray.length == 0) {
|
||||
return map;
|
||||
}
|
||||
for (String param : paramsArray) {
|
||||
String[] paramArray = param.split("=");
|
||||
if (paramArray.length == 2) {
|
||||
map.put(paramArray[0], paramArray[1]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,9 @@ import com.genersoft.iot.vmp.service.ICloudRecordService;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.cloudRecord.bean.CloudRecordUrl;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -21,9 +23,15 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Tag(name = "云端录像接口")
|
||||
@ -67,22 +75,22 @@ public class CloudRecordController {
|
||||
if (ObjectUtils.isEmpty(month)) {
|
||||
month = calendar.get(Calendar.MONTH) + 1;
|
||||
}
|
||||
List<MediaServer> mediaServerItems;
|
||||
List<MediaServer> mediaServers;
|
||||
if (!ObjectUtils.isEmpty(mediaServerId)) {
|
||||
mediaServerItems = new ArrayList<>();
|
||||
MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServerItem == null) {
|
||||
mediaServers = new ArrayList<>();
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
|
||||
}
|
||||
mediaServerItems.add(mediaServerItem);
|
||||
mediaServers.add(mediaServer);
|
||||
} else {
|
||||
mediaServerItems = mediaServerService.getAllOnlineList();
|
||||
mediaServers = mediaServerService.getAllOnlineList();
|
||||
}
|
||||
if (mediaServerItems.isEmpty()) {
|
||||
if (mediaServers.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return cloudRecordService.getDateList(app, stream, year, month, mediaServerItems);
|
||||
return cloudRecordService.getDateList(app, stream, year, month, mediaServers);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ -96,6 +104,7 @@ public class CloudRecordController {
|
||||
@Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false)
|
||||
@Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
|
||||
@Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false)
|
||||
@Parameter(name = "callId", description = "每次录像的唯一标识,置空则查询全部流媒体", required = false)
|
||||
public PageInfo<CloudRecordItem> openRtpServer(
|
||||
@RequestParam(required = false) String query,
|
||||
@RequestParam(required = false) String app,
|
||||
@ -104,24 +113,25 @@ public class CloudRecordController {
|
||||
@RequestParam int count,
|
||||
@RequestParam(required = false) String startTime,
|
||||
@RequestParam(required = false) String endTime,
|
||||
@RequestParam(required = false) String mediaServerId
|
||||
@RequestParam(required = false) String mediaServerId,
|
||||
@RequestParam(required = false) String callId
|
||||
|
||||
) {
|
||||
logger.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}",
|
||||
app, stream, mediaServerId, page, count, startTime, endTime);
|
||||
logger.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}",
|
||||
app, stream, mediaServerId, page, count, startTime, endTime, callId);
|
||||
|
||||
List<MediaServer> mediaServerItems;
|
||||
List<MediaServer> mediaServers;
|
||||
if (!ObjectUtils.isEmpty(mediaServerId)) {
|
||||
mediaServerItems = new ArrayList<>();
|
||||
MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServerItem == null) {
|
||||
mediaServers = new ArrayList<>();
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
|
||||
}
|
||||
mediaServerItems.add(mediaServerItem);
|
||||
mediaServers.add(mediaServer);
|
||||
} else {
|
||||
mediaServerItems = mediaServerService.getAllOnlineList();
|
||||
mediaServers = mediaServerService.getAllOnlineList();
|
||||
}
|
||||
if (mediaServerItems.isEmpty()) {
|
||||
if (mediaServers.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "当前无流媒体");
|
||||
}
|
||||
if (query != null && ObjectUtils.isEmpty(query.trim())) {
|
||||
@ -139,7 +149,10 @@ public class CloudRecordController {
|
||||
if (endTime != null && ObjectUtils.isEmpty(endTime.trim())) {
|
||||
endTime = null;
|
||||
}
|
||||
return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServerItems);
|
||||
if (callId != null && ObjectUtils.isEmpty(callId.trim())) {
|
||||
callId = null;
|
||||
}
|
||||
return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ -162,20 +175,20 @@ public class CloudRecordController {
|
||||
@RequestParam(required = false) String callId,
|
||||
@RequestParam(required = false) String remoteHost
|
||||
){
|
||||
MediaServer mediaServerItem;
|
||||
MediaServer mediaServer;
|
||||
if (mediaServerId == null) {
|
||||
mediaServerItem = mediaServerService.getDefaultMediaServer();
|
||||
mediaServer = mediaServerService.getDefaultMediaServer();
|
||||
}else {
|
||||
mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||
mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
}
|
||||
if (mediaServerItem == null) {
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的流媒体");
|
||||
}else {
|
||||
if (remoteHost == null) {
|
||||
remoteHost = request.getScheme() + "://" + mediaServerItem.getIp() + ":" + mediaServerItem.getRecordAssistPort();
|
||||
remoteHost = request.getScheme() + "://" + mediaServer.getIp() + ":" + mediaServer.getRecordAssistPort();
|
||||
}
|
||||
}
|
||||
return cloudRecordService.addTask(app, stream, mediaServerItem, startTime, endTime, callId, remoteHost, mediaServerId != null);
|
||||
return cloudRecordService.addTask(app, stream, mediaServer, startTime, endTime, callId, remoteHost, mediaServerId != null);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ -265,4 +278,212 @@ public class CloudRecordController {
|
||||
){
|
||||
return cloudRecordService.getPlayUrlPath(recordId);
|
||||
}
|
||||
|
||||
/************************* 以下这些接口只适合wvp和zlm部署在同一台服务器的情况,且wvp只有一个zlm节点的情况 ***************************************/
|
||||
|
||||
/**
|
||||
* 下载指定录像文件的压缩包
|
||||
* @param query 检索内容
|
||||
* @param app 应用名
|
||||
* @param stream 流ID
|
||||
* @param startTime 开始时间(yyyy-MM-dd HH:mm:ss)
|
||||
* @param endTime 结束时间(yyyy-MM-dd HH:mm:ss)
|
||||
* @param mediaServerId 流媒体ID,置空则查询全部流媒体
|
||||
* @param callId 每次录像的唯一标识,置空则查询全部流媒体
|
||||
* @param ids 指定的Id
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping("/zip")
|
||||
public void downloadZipFile(
|
||||
HttpServletResponse response,
|
||||
@RequestParam(required = false) String query,
|
||||
@RequestParam(required = false) String app,
|
||||
@RequestParam(required = false) String stream,
|
||||
@RequestParam(required = false) String startTime,
|
||||
@RequestParam(required = false) String endTime,
|
||||
@RequestParam(required = false) String mediaServerId,
|
||||
@RequestParam(required = false) String callId,
|
||||
@RequestParam(required = false) List<Integer> ids
|
||||
|
||||
) {
|
||||
logger.info("[下载指定录像文件的压缩包] 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}",
|
||||
app, stream, mediaServerId, startTime, endTime, callId);
|
||||
|
||||
List<MediaServer> mediaServers;
|
||||
if (!ObjectUtils.isEmpty(mediaServerId)) {
|
||||
mediaServers = new ArrayList<>();
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
|
||||
}
|
||||
mediaServers.add(mediaServer);
|
||||
} else {
|
||||
mediaServers = mediaServerService.getAll();
|
||||
}
|
||||
if (mediaServers.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "当前无流媒体");
|
||||
}
|
||||
if (query != null && ObjectUtils.isEmpty(query.trim())) {
|
||||
query = null;
|
||||
}
|
||||
if (app != null && ObjectUtils.isEmpty(app.trim())) {
|
||||
app = null;
|
||||
}
|
||||
if (stream != null && ObjectUtils.isEmpty(stream.trim())) {
|
||||
stream = null;
|
||||
}
|
||||
if (startTime != null && ObjectUtils.isEmpty(startTime.trim())) {
|
||||
startTime = null;
|
||||
}
|
||||
if (endTime != null && ObjectUtils.isEmpty(endTime.trim())) {
|
||||
endTime = null;
|
||||
}
|
||||
if (callId != null && ObjectUtils.isEmpty(callId.trim())) {
|
||||
callId = null;
|
||||
}
|
||||
if (stream != null && callId != null) {
|
||||
response.addHeader( "Content-Disposition", "attachment;filename=" + stream + "_" + callId + ".zip" );
|
||||
}
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordService.getAllList(query, app, stream, startTime, endTime, mediaServers, callId, ids);
|
||||
if (ObjectUtils.isEmpty(cloudRecordItemList)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
zos.putNextEntry(new ZipEntry(DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()) + ".mp4"));
|
||||
File file = new File(cloudRecordItem.getFilePath());
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
FileInputStream fis = new FileInputStream(cloudRecordItem.getFilePath());
|
||||
byte[] buf = new byte[2*1024];
|
||||
int len;
|
||||
while ((len = fis.read(buf)) != -1){
|
||||
zos.write(buf, 0, len);
|
||||
}
|
||||
zos.closeEntry();
|
||||
fis.close();
|
||||
}
|
||||
zos.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("[下载指定录像文件的压缩包] 失败: 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}",
|
||||
app, stream, mediaServerId, startTime, endTime, callId, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param query 检索内容
|
||||
* @param app 应用名
|
||||
* @param stream 流ID
|
||||
* @param startTime 开始时间(yyyy-MM-dd HH:mm:ss)
|
||||
* @param endTime 结束时间(yyyy-MM-dd HH:mm:ss)
|
||||
* @param mediaServerId 流媒体ID,置空则查询全部流媒体
|
||||
* @param callId 每次录像的唯一标识,置空则查询全部流媒体
|
||||
* @param remoteHost 拼接播放地址时使用的远程地址
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping("/list-url")
|
||||
@Operation(summary = "分页查询云端录像", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "query", description = "检索内容", required = false)
|
||||
@Parameter(name = "app", description = "应用名", required = false)
|
||||
@Parameter(name = "stream", description = "流ID", required = false)
|
||||
@Parameter(name = "page", description = "当前页", required = true)
|
||||
@Parameter(name = "count", description = "每页查询数量", required = true)
|
||||
@Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false)
|
||||
@Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
|
||||
@Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false)
|
||||
@Parameter(name = "callId", description = "每次录像的唯一标识,置空则查询全部流媒体", required = false)
|
||||
public PageInfo<CloudRecordUrl> getListWithUrl(
|
||||
HttpServletRequest request,
|
||||
@RequestParam(required = false) String query,
|
||||
@RequestParam(required = false) String app,
|
||||
@RequestParam(required = false) String stream,
|
||||
@RequestParam int page,
|
||||
@RequestParam int count,
|
||||
@RequestParam(required = false) String startTime,
|
||||
@RequestParam(required = false) String endTime,
|
||||
@RequestParam(required = false) String mediaServerId,
|
||||
@RequestParam(required = false) String callId,
|
||||
@RequestParam(required = false) String remoteHost
|
||||
|
||||
) {
|
||||
logger.info("[云端录像] 查询URL app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}",
|
||||
app, stream, mediaServerId, page, count, startTime, endTime, callId);
|
||||
|
||||
List<MediaServer> mediaServers;
|
||||
if (!ObjectUtils.isEmpty(mediaServerId)) {
|
||||
mediaServers = new ArrayList<>();
|
||||
MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
|
||||
}
|
||||
mediaServers.add(mediaServer);
|
||||
} else {
|
||||
mediaServers = mediaServerService.getAll();
|
||||
}
|
||||
if (mediaServers.isEmpty()) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "当前无流媒体");
|
||||
}
|
||||
if (query != null && ObjectUtils.isEmpty(query.trim())) {
|
||||
query = null;
|
||||
}
|
||||
if (app != null && ObjectUtils.isEmpty(app.trim())) {
|
||||
app = null;
|
||||
}
|
||||
if (stream != null && ObjectUtils.isEmpty(stream.trim())) {
|
||||
stream = null;
|
||||
}
|
||||
if (startTime != null && ObjectUtils.isEmpty(startTime.trim())) {
|
||||
startTime = null;
|
||||
}
|
||||
if (endTime != null && ObjectUtils.isEmpty(endTime.trim())) {
|
||||
endTime = null;
|
||||
}
|
||||
if (callId != null && ObjectUtils.isEmpty(callId.trim())) {
|
||||
callId = null;
|
||||
}
|
||||
MediaServer mediaServer = mediaServerService.getDefaultMediaServer();
|
||||
if (mediaServer == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体节点");
|
||||
}
|
||||
if (remoteHost == null) {
|
||||
remoteHost = request.getScheme() + "://" + request.getLocalAddr() + ":" +
|
||||
(request.getScheme().equals("https")? mediaServer.getHttpSSlPort() : mediaServer.getHttpPort());
|
||||
}
|
||||
PageInfo<CloudRecordItem> cloudRecordItemPageInfo = cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId);
|
||||
PageInfo<CloudRecordUrl> cloudRecordUrlPageInfo = new PageInfo<>();
|
||||
if (!ObjectUtils.isEmpty(cloudRecordItemPageInfo)) {
|
||||
cloudRecordUrlPageInfo.setPageNum(cloudRecordItemPageInfo.getPageNum());
|
||||
cloudRecordUrlPageInfo.setPageSize(cloudRecordItemPageInfo.getPageSize());
|
||||
cloudRecordUrlPageInfo.setSize(cloudRecordItemPageInfo.getSize());
|
||||
cloudRecordUrlPageInfo.setEndRow(cloudRecordItemPageInfo.getEndRow());
|
||||
cloudRecordUrlPageInfo.setStartRow(cloudRecordItemPageInfo.getStartRow());
|
||||
cloudRecordUrlPageInfo.setPages(cloudRecordItemPageInfo.getPages());
|
||||
cloudRecordUrlPageInfo.setPrePage(cloudRecordItemPageInfo.getPrePage());
|
||||
cloudRecordUrlPageInfo.setNextPage(cloudRecordItemPageInfo.getNextPage());
|
||||
cloudRecordUrlPageInfo.setIsFirstPage(cloudRecordItemPageInfo.isIsFirstPage());
|
||||
cloudRecordUrlPageInfo.setIsLastPage(cloudRecordItemPageInfo.isIsLastPage());
|
||||
cloudRecordUrlPageInfo.setHasPreviousPage(cloudRecordItemPageInfo.isHasPreviousPage());
|
||||
cloudRecordUrlPageInfo.setHasNextPage(cloudRecordItemPageInfo.isHasNextPage());
|
||||
cloudRecordUrlPageInfo.setNavigatePages(cloudRecordItemPageInfo.getNavigatePages());
|
||||
cloudRecordUrlPageInfo.setNavigateFirstPage(cloudRecordItemPageInfo.getNavigateFirstPage());
|
||||
cloudRecordUrlPageInfo.setNavigateLastPage(cloudRecordItemPageInfo.getNavigateLastPage());
|
||||
cloudRecordUrlPageInfo.setNavigatepageNums(cloudRecordItemPageInfo.getNavigatepageNums());
|
||||
cloudRecordUrlPageInfo.setTotal(cloudRecordItemPageInfo.getTotal());
|
||||
List<CloudRecordUrl> cloudRecordUrlList = new ArrayList<>(cloudRecordItemPageInfo.getList().size());
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordItemPageInfo.getList();
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
CloudRecordUrl cloudRecordUrl = new CloudRecordUrl();
|
||||
cloudRecordUrl.setId(cloudRecordItem.getId());
|
||||
cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath()
|
||||
+ "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()) );
|
||||
cloudRecordUrl.setPlayUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath());
|
||||
cloudRecordUrlList.add(cloudRecordUrl);
|
||||
}
|
||||
cloudRecordUrlPageInfo.setList(cloudRecordUrlList);
|
||||
}
|
||||
return cloudRecordUrlPageInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package com.genersoft.iot.vmp.vmanager.cloudRecord.bean;
|
||||
|
||||
public class CloudRecordUrl {
|
||||
|
||||
private String playUrl;
|
||||
private String downloadUrl;
|
||||
private int id;
|
||||
|
||||
public String getPlayUrl() {
|
||||
return playUrl;
|
||||
}
|
||||
|
||||
public void setPlayUrl(String playUrl) {
|
||||
this.playUrl = playUrl;
|
||||
}
|
||||
|
||||
public String getDownloadUrl() {
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
public void setDownloadUrl(String downloadUrl) {
|
||||
this.downloadUrl = downloadUrl;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,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.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
@ -129,6 +130,9 @@ public class PlayController {
|
||||
}
|
||||
streamInfo.channgeStreamIp(host);
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(newMediaServerItem.getTranscodeSuffix()) && !"null".equalsIgnoreCase(newMediaServerItem.getTranscodeSuffix())) {
|
||||
streamInfo.setStream(streamInfo.getStream() + "_" + newMediaServerItem.getTranscodeSuffix());
|
||||
}
|
||||
wvpResult.setData(new StreamContent(streamInfo));
|
||||
}else {
|
||||
wvpResult.setCode(code);
|
||||
|
||||
@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.log;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig;
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import com.genersoft.iot.vmp.service.ILogService;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.LogDto;
|
||||
@ -92,4 +93,14 @@ public class LogController {
|
||||
logService.clear();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RedisRpcConfig redisRpcConfig;
|
||||
|
||||
@GetMapping("/test/count")
|
||||
public Object count() {
|
||||
return redisRpcConfig.getCallbackCount();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -274,7 +274,7 @@ public class StreamPushController {
|
||||
stream.setStatus(false);
|
||||
stream.setPushIng(false);
|
||||
stream.setAliveSecond(0L);
|
||||
stream.setTotalReaderCount("0");
|
||||
stream.setTotalReaderCount(0);
|
||||
if (!streamPushService.add(stream)) {
|
||||
throw new ControllerException(ErrorCode.ERROR100);
|
||||
}
|
||||
|
||||
@ -241,6 +241,8 @@ user-settings:
|
||||
register-again-after-time: 60
|
||||
# 国标续订方式,true为续订,每次注册在同一个会话里,false为重新注册,每次使用新的会话
|
||||
register-keep-int-dialog: false
|
||||
# 开启接口文档页面。 默认开启,生产环境建议关闭,遇到swagger相关的漏洞时也可以关闭
|
||||
doc-enable: true
|
||||
# 跨域配置,不配置此项则允许所有跨域请求,配置后则只允许配置的页面的地址请求, 可以配置多个
|
||||
allowed-origins:
|
||||
- http://localhost:8008
|
||||
|
||||
@ -112,7 +112,4 @@ user-settings:
|
||||
auto-apply-play: true
|
||||
# 设备/通道状态变化时发送消息
|
||||
device-status-notify: true
|
||||
# [可选] 日志配置, 一般不需要改
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
|
||||
|
||||
@ -74,6 +74,8 @@
|
||||
<template slot-scope="scope">
|
||||
<el-button size="medium" icon="el-icon-video-play" type="text" @click="play(scope.row)">播放
|
||||
</el-button>
|
||||
<el-button size="medium" icon="el-icon-download" type="text" @click="downloadFile(scope.row)">下载
|
||||
</el-button>
|
||||
<!-- <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c"-->
|
||||
<!-- @click="deleteRecord(scope.row)">删除-->
|
||||
<!-- </el-button>-->
|
||||
@ -234,14 +236,29 @@ export default {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
getFileBasePath(item) {
|
||||
let basePath = ""
|
||||
if (axios.defaults.baseURL.startsWith("http")) {
|
||||
basePath = `${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
|
||||
}else {
|
||||
basePath = `${window.location.origin}${axios.defaults.baseURL}/record_proxy/${item.mediaServerId}`
|
||||
downloadFile(file){
|
||||
console.log(file)
|
||||
this.$axios({
|
||||
method: 'get',
|
||||
url: `/api/cloud/record/play/path`,
|
||||
params: {
|
||||
recordId: file.id,
|
||||
}
|
||||
return basePath;
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
const link = document.createElement('a');
|
||||
link.target = "_blank";
|
||||
if (res.data.code === 0) {
|
||||
if (location.protocol === "https:") {
|
||||
link.href = res.data.data.httpsPath + "&save_name=" + file.fileName;
|
||||
}else {
|
||||
link.href = res.data.data.httpPath + "&save_name=" + file.fileName;
|
||||
}
|
||||
link.click();
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
deleteRecord() {
|
||||
// TODO
|
||||
|
||||
@ -45,8 +45,7 @@
|
||||
<i class="el-icon-video-camera" ></i>
|
||||
{{ getFileShowName(item) }}
|
||||
</el-tag>
|
||||
<a class="el-icon-download" style="color: #409EFF;font-weight: 600;margin-left: 10px;"
|
||||
:href="`${getFileBasePath(item)}/download.html?url=download/${app}/${stream}/${chooseDate}/${item.fileName}`"
|
||||
<a class="el-icon-download" @click="downloadFile(item)" style="color: #409EFF;font-weight: 600;margin-left: 10px;"
|
||||
target="_blank"/>
|
||||
</li>
|
||||
</ul>
|
||||
@ -230,9 +229,6 @@
|
||||
mounted() {
|
||||
this.recordListStyle.height = this.winHeight + "px";
|
||||
this.playerStyle["height"] = this.winHeight + "px";
|
||||
console.log(this.app)
|
||||
console.log(this.stream)
|
||||
console.log(this.mediaServerId)
|
||||
// 查询当年有视频的日期
|
||||
this.getDateInYear(()=>{
|
||||
if (Object.values(this.dateFilesObj).length > 0){
|
||||
@ -314,21 +310,61 @@
|
||||
});
|
||||
},
|
||||
chooseFile(file){
|
||||
console.log(file)
|
||||
if (file == null) {
|
||||
this.videoUrl = "";
|
||||
this.choosedFile = "";
|
||||
}else {
|
||||
this.choosedFile = file.fileName;
|
||||
this.videoUrl = `${this.getFileBasePath(file)}/download/${this.app}/${this.stream}/${this.chooseDate}/${file.fileName}`
|
||||
console.log(this.videoUrl)
|
||||
this.$axios({
|
||||
method: 'get',
|
||||
url: `/api/cloud/record/play/path`,
|
||||
params: {
|
||||
recordId: file.id,
|
||||
}
|
||||
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
if (res.data.code === 0) {
|
||||
if (location.protocol === "https:") {
|
||||
this.videoUrl = res.data.data.httpsPath;
|
||||
}else {
|
||||
this.videoUrl = res.data.data.httpPath;
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
downloadFile(file){
|
||||
console.log(file)
|
||||
this.$axios({
|
||||
method: 'get',
|
||||
url: `/api/cloud/record/play/path`,
|
||||
params: {
|
||||
recordId: file.id,
|
||||
}
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
const link = document.createElement('a');
|
||||
link.target = "_blank";
|
||||
if (res.data.code === 0) {
|
||||
if (location.protocol === "https:") {
|
||||
link.href = res.data.data.httpsPath + "&save_name=" + file.fileName;
|
||||
}else {
|
||||
link.href = res.data.data.httpPath + "&save_name=" + file.fileName;
|
||||
}
|
||||
link.click();
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
backToList() {
|
||||
this.$router.back()
|
||||
},
|
||||
getFileShowName(item) {
|
||||
return moment.unix(item.startTime).format('HH:mm:ss') + "-" + moment.unix(item.endTime).format('HH:mm:ss')
|
||||
return moment(item.startTime).format('HH:mm:ss') + "-" + moment(item.endTime).format('HH:mm:ss')
|
||||
},
|
||||
chooseMediaChange() {
|
||||
|
||||
|
||||
@ -326,7 +326,9 @@ export default {
|
||||
e.ptzType = e.ptzType + "";
|
||||
that.$set(e, "edit", false);
|
||||
that.$set(e, "location", "");
|
||||
if (e.longitude && e.latitude) {
|
||||
if (e.customLongitude && e.customLatitude) {
|
||||
that.$set(e, "location", e.customLongitude + "," + e.customLatitude);
|
||||
}else if (e.longitude && e.latitude) {
|
||||
that.$set(e, "location", e.longitude + "," + e.latitude);
|
||||
}
|
||||
});
|
||||
@ -481,7 +483,9 @@ export default {
|
||||
e.ptzType = e.ptzType + "";
|
||||
this.$set(e, "edit", false);
|
||||
this.$set(e, "location", "");
|
||||
if (e.longitude && e.latitude) {
|
||||
if (e.customLongitude && e.customLatitude) {
|
||||
this.$set(e, "location", e.customLongitude + "," + e.customLatitude);
|
||||
}else if (e.longitude && e.latitude) {
|
||||
this.$set(e, "location", e.longitude + "," + e.latitude);
|
||||
}
|
||||
});
|
||||
@ -603,8 +607,8 @@ export default {
|
||||
this.$message.warning("位置信息格式有误,例:117.234,36.378");
|
||||
return;
|
||||
} else {
|
||||
row.longitude = parseFloat(segements[0]);
|
||||
row.latitude = parseFloat(segements[1]);
|
||||
row.customLongitude = parseFloat(segements[0]);
|
||||
row.custom_latitude = parseFloat(segements[1]);
|
||||
if (!(row.longitude && row.latitude)) {
|
||||
this.$message.warning("位置信息格式有误,例:117.234,36.378");
|
||||
return;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div ref="container" @dblclick="fullscreenSwich"
|
||||
style="width:100%; height: 100%; background-color: #000000;margin:0 auto;position: relative;">
|
||||
<div style="width:100%; padding-top: 56.25%; position: relative;"></div>
|
||||
<div class="buttons-box" id="buttonsBox">
|
||||
<div class="buttons-box-left">
|
||||
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i>
|
||||
@ -66,6 +67,9 @@ export default {
|
||||
// });
|
||||
// ro.observe(this.$refs.container);
|
||||
// },
|
||||
mounted(){
|
||||
this.updatePlayerDomSize();
|
||||
},
|
||||
watch: {
|
||||
videoUrl: {
|
||||
handler(val, _) {
|
||||
@ -91,6 +95,7 @@ export default {
|
||||
if (width > 0 && height > 0) {
|
||||
dom.style.width = width + 'px';
|
||||
dom.style.height = height + "px";
|
||||
dom.style.paddingTop = 0;
|
||||
console.log(width)
|
||||
console.log(height)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
v-if="Object.keys(this.player).length > 1">
|
||||
<el-tab-pane label="Jessibuca" name="jessibuca">
|
||||
<jessibucaPlayer v-if="activePlayer === 'jessibuca'" ref="jessibuca" :visible.sync="showVideoDialog"
|
||||
:videoUrl="videoUrl" :error="videoError" :message="videoError" style="height: 515px"
|
||||
:videoUrl="videoUrl" :error="videoError" :message="videoError"
|
||||
:hasAudio="hasAudio" fluent autoplay live></jessibucaPlayer>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="WebRTC" name="webRTC">
|
||||
@ -19,7 +19,7 @@
|
||||
</el-tabs>
|
||||
<jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca"
|
||||
:visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError"
|
||||
height="100px" :hasAudio="hasAudio" fluent autoplay live></jessibucaPlayer>
|
||||
:hasAudio="hasAudio" fluent autoplay live></jessibucaPlayer>
|
||||
<rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca"
|
||||
:visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError"
|
||||
height="100px" :hasAudio="hasAudio" fluent autoplay live></rtc-player>
|
||||
|
||||
@ -32,7 +32,14 @@
|
||||
<el-input v-model="platform.deviceGBId" clearable @input="deviceGBIdChange"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="本地IP" prop="deviceIp">
|
||||
<el-input v-model="platform.deviceIp" :disabled="true"></el-input>
|
||||
<el-select v-model="platform.deviceIp" placeholder="请选择与上级相通的网卡" style="width: 100%">
|
||||
<el-option
|
||||
v-for="ip in deviceIps"
|
||||
:key="ip"
|
||||
:label="ip"
|
||||
:value="ip">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="本地端口" prop="devicePort">
|
||||
<el-input v-model="platform.devicePort" :disabled="true" type="number"></el-input>
|
||||
@ -51,7 +58,7 @@
|
||||
<el-input v-model="platform.username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="SIP认证密码" prop="password">
|
||||
<el-input v-model="platform.password" ></el-input>
|
||||
<el-input v-model="platform.password"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="注册周期(秒)" prop="expires">
|
||||
<el-input v-model="platform.expires"></el-input>
|
||||
@ -93,16 +100,17 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="其他选项">
|
||||
<el-checkbox label="启用" v-model="platform.enable" @change="checkExpires"></el-checkbox>
|
||||
<!-- <el-checkbox label="云台控制" v-model="platform.ptz"></el-checkbox>-->
|
||||
<!-- <el-checkbox label="云台控制" v-model="platform.ptz"></el-checkbox>-->
|
||||
<el-checkbox label="拉起推流" v-model="platform.startOfflinePush"></el-checkbox>
|
||||
<el-checkbox label="RTCP保活" v-model="platform.rtcp" @change="rtcpCheckBoxChange"></el-checkbox>
|
||||
<el-checkbox label="消息通道" v-model="platform.asMessageChannel" ></el-checkbox>
|
||||
<el-checkbox label="推送通道" v-model="platform.autoPushChannel" ></el-checkbox>
|
||||
<el-checkbox label="消息通道" v-model="platform.asMessageChannel"></el-checkbox>
|
||||
<el-checkbox label="推送通道" v-model="platform.autoPushChannel"></el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">{{
|
||||
onSubmit_text
|
||||
}}</el-button>
|
||||
}}
|
||||
</el-button>
|
||||
<el-button @click="close">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -165,51 +173,56 @@ export default {
|
||||
administrativeDivision: "",
|
||||
sendStreamIp: null,
|
||||
},
|
||||
deviceIps: [], // 存储用户设备IP数组
|
||||
rules: {
|
||||
name: [{ required: true, message: "请输入平台名称", trigger: "blur" }],
|
||||
name: [{required: true, message: "请输入平台名称", trigger: "blur"}],
|
||||
serverGBId: [
|
||||
{ required: true, message: "请输入SIP服务国标编码", trigger: "blur" },
|
||||
{required: true, message: "请输入SIP服务国标编码", trigger: "blur"},
|
||||
],
|
||||
serverGBDomain: [
|
||||
{ required: true, message: "请输入SIP服务国标域", trigger: "blur" },
|
||||
{required: true, message: "请输入SIP服务国标域", trigger: "blur"},
|
||||
],
|
||||
serverIP: [{ required: true, message: "请输入SIP服务IP", trigger: "blur" }],
|
||||
serverPort: [{ required: true, message: "请输入SIP服务端口", trigger: "blur" }],
|
||||
deviceGBId: [{ validator: deviceGBIdRules, trigger: "blur" }],
|
||||
username: [{ required: false, message: "请输入SIP认证用户名", trigger: "blur" }],
|
||||
password: [{ required: false, message: "请输入SIP认证密码", trigger: "blur" }],
|
||||
expires: [{ required: true, message: "请输入注册周期", trigger: "blur" }],
|
||||
keepTimeout: [{ required: true, message: "请输入心跳周期", trigger: "blur" }],
|
||||
transport: [{ required: true, message: "请选择信令传输", trigger: "blur" }],
|
||||
characterSet: [{ required: true, message: "请选择编码字符集", trigger: "blur" }],
|
||||
serverIP: [{required: true, message: "请输入SIP服务IP", trigger: "blur"}],
|
||||
serverPort: [{required: true, message: "请输入SIP服务端口", trigger: "blur"}],
|
||||
deviceGBId: [{validator: deviceGBIdRules, trigger: "blur"}],
|
||||
username: [{required: false, message: "请输入SIP认证用户名", trigger: "blur"}],
|
||||
password: [{required: false, message: "请输入SIP认证密码", trigger: "blur"}],
|
||||
expires: [{required: true, message: "请输入注册周期", trigger: "blur"}],
|
||||
keepTimeout: [{required: true, message: "请输入心跳周期", trigger: "blur"}],
|
||||
transport: [{required: true, message: "请选择信令传输", trigger: "blur"}],
|
||||
characterSet: [{required: true, message: "请选择编码字符集", trigger: "blur"}],
|
||||
deviceIp: [{required: true, message: "请选择本地IP", trigger: "blur"}],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openDialog: function (platform, callback) {
|
||||
var that = this;
|
||||
if (platform == null) {
|
||||
this.onSubmit_text = "立即创建";
|
||||
this.saveUrl = "/api/platform/add";
|
||||
this.$axios({
|
||||
method: 'get',
|
||||
url:`/api/platform/server_config`
|
||||
url: `/api/platform/server_config`
|
||||
}).then(function (res) {
|
||||
console.log(res);
|
||||
if (platform == null) {
|
||||
if (res.data.code === 0) {
|
||||
that.platform.deviceGBId = res.data.data.username;
|
||||
that.platform.deviceIp = res.data.data.deviceIp;
|
||||
that.deviceIps = res.data.data.deviceIp.split(',');
|
||||
that.platform.deviceIp = that.deviceIps[0];
|
||||
that.platform.devicePort = res.data.data.devicePort;
|
||||
that.platform.username = res.data.data.username;
|
||||
that.platform.password = res.data.data.password;
|
||||
that.platform.sendStreamIp = res.data.data.sendStreamIp;
|
||||
that.platform.administrativeDivision = res.data.data.username.substr(0, 6);
|
||||
}
|
||||
|
||||
} else {
|
||||
that.deviceIps = res.data.data.deviceIp.split(',');
|
||||
}
|
||||
}).catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}else {
|
||||
if (platform == null) {
|
||||
this.onSubmit_text = "立即创建";
|
||||
this.saveUrl = "/api/platform/add";
|
||||
} else {
|
||||
this.platform.id = platform.id;
|
||||
this.platform.enable = platform.enable;
|
||||
this.platform.ptz = platform.ptz;
|
||||
@ -248,7 +261,7 @@ export default {
|
||||
},
|
||||
deviceGBIdChange: function () {
|
||||
|
||||
this.platform.username = this.platform.deviceGBId ;
|
||||
this.platform.username = this.platform.deviceGBId;
|
||||
if (this.platform.administrativeDivision == null) {
|
||||
this.platform.administrativeDivision = this.platform.deviceGBId.substr(0, 6);
|
||||
}
|
||||
@ -256,12 +269,12 @@ export default {
|
||||
onSubmit: function () {
|
||||
this.saveForm()
|
||||
},
|
||||
saveForm: function (){
|
||||
saveForm: function () {
|
||||
this.$axios({
|
||||
method: 'post',
|
||||
url: this.saveUrl,
|
||||
data: this.platform
|
||||
}).then((res) =>{
|
||||
}).then((res) => {
|
||||
if (res.data.code === 0) {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
@ -272,14 +285,14 @@ export default {
|
||||
if (this.listChangeCallback != null) {
|
||||
this.listChangeCallback();
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: res.data.msg,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}).catch((error)=> {
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
@ -318,7 +331,8 @@ export default {
|
||||
var that = this;
|
||||
await that.$axios({
|
||||
method: 'get',
|
||||
url:`/api/platform/exit/${deviceGbId}`})
|
||||
url: `/api/platform/exit/${deviceGbId}`
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.code === 0) {
|
||||
result = res.data.data;
|
||||
@ -329,12 +343,12 @@ export default {
|
||||
});
|
||||
return result;
|
||||
},
|
||||
checkExpires: function() {
|
||||
checkExpires: function () {
|
||||
if (this.platform.enable && this.platform.expires === "0") {
|
||||
this.platform.expires = "3600";
|
||||
}
|
||||
},
|
||||
rtcpCheckBoxChange: function (result){
|
||||
rtcpCheckBoxChange: function (result) {
|
||||
if (result) {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
@ -355,10 +369,12 @@ input::-webkit-inner-spin-button {
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 火狐 */
|
||||
input{
|
||||
-moz-appearance:textfield;
|
||||
input {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.control-wrapper-not-used {
|
||||
position: relative;
|
||||
width: 6.25rem;
|
||||
|
||||
@ -97,9 +97,9 @@ enable_ts=1
|
||||
fmp4_demand=0
|
||||
hls_demand=0
|
||||
hls_save_path=./www
|
||||
modify_stamp=0
|
||||
modify_stamp=2
|
||||
mp4_as_player=0
|
||||
mp4_max_second=3600
|
||||
mp4_max_second=300
|
||||
mp4_save_path=./www
|
||||
rtmp_demand=0
|
||||
rtsp_demand=0
|
||||
@ -163,4 +163,16 @@ pktBufSize=8192
|
||||
port=9000
|
||||
timeoutSec=5
|
||||
|
||||
|
||||
[transcode]
|
||||
acodec=pcma
|
||||
decoder_h264=h264_qsv,h264_videotoolbox,h264_bm,libopenh264
|
||||
decoder_h265=hevc_qsv,hevc_videotoolbox,hevc_bm
|
||||
enable_ffmpeg_log=0
|
||||
encoder_h264=h264_qsv,h264_videotoolbox,h264_bm,libx264,libopenh264
|
||||
encoder_h265=hevc_qsv,hevc_videotoolbox,hevc_bm,libx265
|
||||
filter=
|
||||
suffix=transport
|
||||
vcodec=H264
|
||||
|
||||
; } ---
|
||||
|
||||
@ -31,7 +31,6 @@ create table wvp_device (
|
||||
password character varying(255),
|
||||
as_message_channel bool default false,
|
||||
keepalive_interval_time integer,
|
||||
switch_primary_sub_stream bool default false,
|
||||
broadcast_push_after_ack bool default false,
|
||||
constraint uk_device_device unique (device_id)
|
||||
);
|
||||
|
||||
@ -31,7 +31,6 @@ create table wvp_device (
|
||||
password character varying(255),
|
||||
as_message_channel bool default false,
|
||||
keepalive_interval_time integer,
|
||||
switch_primary_sub_stream bool default false,
|
||||
broadcast_push_after_ack bool default false,
|
||||
constraint uk_device_device unique (device_id)
|
||||
);
|
||||
|
||||
@ -4,5 +4,15 @@ alter table wvp_device_channel
|
||||
alter table wvp_device
|
||||
drop switch_primary_sub_stream;
|
||||
|
||||
# 第一个补丁包
|
||||
alter table wvp_platform
|
||||
add send_stream_ip character varying(50);
|
||||
|
||||
alter table wvp_device
|
||||
change on_line on_line bool default false;
|
||||
|
||||
alter table wvp_device
|
||||
change id id serial primary key;
|
||||
|
||||
alter table wvp_device
|
||||
change ssrc_check ssrc_check bool default false;
|
||||
@ -4,5 +4,15 @@ alter table wvp_device_channel
|
||||
alter table wvp_device
|
||||
drop switch_primary_sub_stream;
|
||||
|
||||
# 第一个补丁包
|
||||
alter table wvp_platform
|
||||
add send_stream_ip character varying(50);
|
||||
|
||||
alter table wvp_device
|
||||
change on_line on_line bool default false;
|
||||
|
||||
alter table wvp_device
|
||||
change id id serial primary key;
|
||||
|
||||
alter table wvp_device
|
||||
change ssrc_check ssrc_check bool default false;
|
||||
@ -31,7 +31,6 @@ create table wvp_device (
|
||||
password character varying(255),
|
||||
as_message_channel bool default false,
|
||||
keepalive_interval_time integer,
|
||||
switch_primary_sub_stream bool default false,
|
||||
broadcast_push_after_ack bool default false,
|
||||
constraint uk_device_device unique (device_id)
|
||||
);
|
||||
@ -172,6 +171,7 @@ create table wvp_media_server (
|
||||
hook_alive_interval integer,
|
||||
record_path character varying(255),
|
||||
record_day integer default 7,
|
||||
transcode_suffix character varying(255),
|
||||
constraint uk_media_server_unique_ip_http_port unique (ip, http_port)
|
||||
);
|
||||
|
||||
@ -320,6 +320,7 @@ create table wvp_resources_tree (
|
||||
parentId integer,
|
||||
path character varying(255)
|
||||
);
|
||||
|
||||
create table wvp_user_api_key (
|
||||
id serial primary key ,
|
||||
user_id bigint,
|
||||
@ -332,6 +333,7 @@ create table wvp_user_api_key (
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
|
||||
/*初始数据*/
|
||||
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
|
||||
INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');
|
||||
@ -31,7 +31,6 @@ create table wvp_device (
|
||||
password character varying(255),
|
||||
as_message_channel bool default false,
|
||||
keepalive_interval_time integer,
|
||||
switch_primary_sub_stream bool default false,
|
||||
broadcast_push_after_ack bool default false,
|
||||
constraint uk_device_device unique (device_id)
|
||||
);
|
||||
@ -172,6 +171,7 @@ create table wvp_media_server (
|
||||
hook_alive_interval integer,
|
||||
record_path character varying(255),
|
||||
record_day integer default 7,
|
||||
transcode_suffix character varying(255),
|
||||
constraint uk_media_server_unique_ip_http_port unique (ip, http_port)
|
||||
);
|
||||
|
||||
@ -320,6 +320,7 @@ create table wvp_resources_tree (
|
||||
parentId integer,
|
||||
path character varying(255)
|
||||
);
|
||||
|
||||
create table wvp_user_api_key (
|
||||
id serial primary key ,
|
||||
user_id bigint,
|
||||
@ -332,6 +333,7 @@ create table wvp_user_api_key (
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
|
||||
/*初始数据*/
|
||||
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
|
||||
INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');
|
||||
@ -1,3 +1,6 @@
|
||||
alter table wvp_media_server
|
||||
add transcode_suffix character varying(255);
|
||||
|
||||
alter table wvp_media_server
|
||||
add type character varying(50) default 'zlm';
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
alter table wvp_media_server
|
||||
add transcode_suffix character varying(255);
|
||||
|
||||
alter table wvp_media_server
|
||||
add type character varying(50) default 'zlm';
|
||||
|
||||
342
数据库/2.7.2/初始化-mysql-2.7.2.sql
Normal file
342
数据库/2.7.2/初始化-mysql-2.7.2.sql
Normal file
@ -0,0 +1,342 @@
|
||||
/*建表*/
|
||||
create table wvp_device (
|
||||
id serial primary key ,
|
||||
device_id character varying(50) not null ,
|
||||
name character varying(255),
|
||||
manufacturer character varying(255),
|
||||
model character varying(255),
|
||||
firmware character varying(255),
|
||||
transport character varying(50),
|
||||
stream_mode character varying(50),
|
||||
on_line bool default false,
|
||||
register_time character varying(50),
|
||||
keepalive_time character varying(50),
|
||||
ip character varying(50),
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
port integer,
|
||||
expires integer,
|
||||
subscribe_cycle_for_catalog integer DEFAULT 0,
|
||||
subscribe_cycle_for_mobile_position integer DEFAULT 0,
|
||||
mobile_position_submission_interval integer DEFAULT 5,
|
||||
subscribe_cycle_for_alarm integer DEFAULT 0,
|
||||
host_address character varying(50),
|
||||
charset character varying(50),
|
||||
ssrc_check bool default false,
|
||||
geo_coord_sys character varying(50),
|
||||
media_server_id character varying(50),
|
||||
custom_name character varying(255),
|
||||
sdp_ip character varying(50),
|
||||
local_ip character varying(50),
|
||||
password character varying(255),
|
||||
as_message_channel bool default false,
|
||||
keepalive_interval_time integer,
|
||||
broadcast_push_after_ack bool default false,
|
||||
constraint uk_device_device unique (device_id)
|
||||
);
|
||||
|
||||
create table wvp_device_alarm (
|
||||
id serial primary key ,
|
||||
device_id character varying(50) not null,
|
||||
channel_id character varying(50) not null,
|
||||
alarm_priority character varying(50),
|
||||
alarm_method character varying(50),
|
||||
alarm_time character varying(50),
|
||||
alarm_description character varying(255),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
alarm_type character varying(50),
|
||||
create_time character varying(50) not null
|
||||
);
|
||||
|
||||
create table wvp_device_channel (
|
||||
id serial primary key ,
|
||||
channel_id character varying(50) not null,
|
||||
name character varying(255),
|
||||
custom_name character varying(255),
|
||||
manufacture character varying(50),
|
||||
model character varying(50),
|
||||
owner character varying(50),
|
||||
civil_code character varying(50),
|
||||
block character varying(50),
|
||||
address character varying(50),
|
||||
parent_id character varying(50),
|
||||
safety_way integer,
|
||||
register_way integer,
|
||||
cert_num character varying(50),
|
||||
certifiable integer,
|
||||
err_code integer,
|
||||
end_time character varying(50),
|
||||
secrecy character varying(50),
|
||||
ip_address character varying(50),
|
||||
port integer,
|
||||
password character varying(255),
|
||||
ptz_type integer,
|
||||
custom_ptz_type integer,
|
||||
status bool default false,
|
||||
longitude double precision,
|
||||
custom_longitude double precision,
|
||||
latitude double precision,
|
||||
custom_latitude double precision,
|
||||
stream_id character varying(255),
|
||||
device_id character varying(50) not null,
|
||||
parental character varying(50),
|
||||
has_audio bool default false,
|
||||
create_time character varying(50) not null,
|
||||
update_time character varying(50) not null,
|
||||
sub_count integer,
|
||||
longitude_gcj02 double precision,
|
||||
latitude_gcj02 double precision,
|
||||
longitude_wgs84 double precision,
|
||||
latitude_wgs84 double precision,
|
||||
business_group_id character varying(50),
|
||||
gps_time character varying(50),
|
||||
stream_identification character varying(50),
|
||||
constraint uk_wvp_device_channel_unique_device_channel unique (device_id, channel_id)
|
||||
);
|
||||
|
||||
create table wvp_device_mobile_position (
|
||||
id serial primary key,
|
||||
device_id character varying(50) not null,
|
||||
channel_id character varying(50) not null,
|
||||
device_name character varying(255),
|
||||
time character varying(50),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
altitude double precision,
|
||||
speed double precision,
|
||||
direction double precision,
|
||||
report_source character varying(50),
|
||||
longitude_gcj02 double precision,
|
||||
latitude_gcj02 double precision,
|
||||
longitude_wgs84 double precision,
|
||||
latitude_wgs84 double precision,
|
||||
create_time character varying(50)
|
||||
);
|
||||
|
||||
create table wvp_gb_stream (
|
||||
gb_stream_id serial primary key,
|
||||
app character varying(255) not null,
|
||||
stream character varying(255) not null,
|
||||
gb_id character varying(50) not null,
|
||||
name character varying(255),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
stream_type character varying(50),
|
||||
media_server_id character varying(50),
|
||||
create_time character varying(50),
|
||||
constraint uk_gb_stream_unique_gb_id unique (gb_id),
|
||||
constraint uk_gb_stream_unique_app_stream unique (app, stream)
|
||||
);
|
||||
|
||||
create table wvp_log (
|
||||
id serial primary key ,
|
||||
name character varying(50),
|
||||
type character varying(50),
|
||||
uri character varying(200),
|
||||
address character varying(50),
|
||||
result character varying(50),
|
||||
timing bigint,
|
||||
username character varying(50),
|
||||
create_time character varying(50)
|
||||
);
|
||||
|
||||
create table wvp_media_server (
|
||||
id character varying(255) primary key ,
|
||||
ip character varying(50),
|
||||
hook_ip character varying(50),
|
||||
sdp_ip character varying(50),
|
||||
stream_ip character varying(50),
|
||||
http_port integer,
|
||||
http_ssl_port integer,
|
||||
rtmp_port integer,
|
||||
rtmp_ssl_port integer,
|
||||
rtp_proxy_port integer,
|
||||
rtsp_port integer,
|
||||
rtsp_ssl_port integer,
|
||||
flv_port integer,
|
||||
flv_ssl_port integer,
|
||||
ws_flv_port integer,
|
||||
ws_flv_ssl_port integer,
|
||||
auto_config bool default false,
|
||||
secret character varying(50),
|
||||
type character varying(50) default 'zlm',
|
||||
rtp_enable bool default false,
|
||||
rtp_port_range character varying(50),
|
||||
send_rtp_port_range character varying(50),
|
||||
record_assist_port integer,
|
||||
default_server bool default false,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
hook_alive_interval integer,
|
||||
record_path character varying(255),
|
||||
record_day integer default 7,
|
||||
transcode_suffix character varying(255),
|
||||
constraint uk_media_server_unique_ip_http_port unique (ip, http_port)
|
||||
);
|
||||
|
||||
create table wvp_platform (
|
||||
id serial primary key ,
|
||||
enable bool default false,
|
||||
name character varying(255),
|
||||
server_gb_id character varying(50),
|
||||
server_gb_domain character varying(50),
|
||||
server_ip character varying(50),
|
||||
server_port integer,
|
||||
device_gb_id character varying(50),
|
||||
device_ip character varying(50),
|
||||
device_port character varying(50),
|
||||
username character varying(255),
|
||||
password character varying(50),
|
||||
expires character varying(50),
|
||||
keep_timeout character varying(50),
|
||||
transport character varying(50),
|
||||
character_set character varying(50),
|
||||
catalog_id character varying(50),
|
||||
ptz bool default false,
|
||||
rtcp bool default false,
|
||||
status bool default false,
|
||||
start_offline_push bool default false,
|
||||
administrative_division character varying(50),
|
||||
catalog_group integer,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
as_message_channel bool default false,
|
||||
auto_push_channel bool default false,
|
||||
send_stream_ip character varying(50),
|
||||
constraint uk_platform_unique_server_gb_id unique (server_gb_id)
|
||||
);
|
||||
|
||||
create table wvp_platform_catalog (
|
||||
id character varying(50),
|
||||
platform_id character varying(50),
|
||||
name character varying(255),
|
||||
parent_id character varying(50),
|
||||
civil_code character varying(50),
|
||||
business_group_id character varying(50),
|
||||
constraint uk_platform_catalog_id_platform_id unique (id, platform_id)
|
||||
);
|
||||
|
||||
create table wvp_platform_gb_channel (
|
||||
id serial primary key ,
|
||||
platform_id character varying(50),
|
||||
catalog_id character varying(50),
|
||||
device_channel_id integer,
|
||||
constraint uk_platform_gb_channel_platform_id_catalog_id_device_channel_id unique (platform_id, catalog_id, device_channel_id)
|
||||
);
|
||||
|
||||
create table wvp_platform_gb_stream (
|
||||
id serial primary key,
|
||||
platform_id character varying(50),
|
||||
catalog_id character varying(50),
|
||||
gb_stream_id integer,
|
||||
constraint uk_platform_gb_stream_platform_id_catalog_id_gb_stream_id unique (platform_id, catalog_id, gb_stream_id)
|
||||
);
|
||||
|
||||
create table wvp_stream_proxy (
|
||||
id serial primary key,
|
||||
type character varying(50),
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
url character varying(255),
|
||||
src_url character varying(255),
|
||||
dst_url character varying(255),
|
||||
timeout_ms integer,
|
||||
ffmpeg_cmd_key character varying(255),
|
||||
rtp_type character varying(50),
|
||||
media_server_id character varying(50),
|
||||
enable_audio bool default false,
|
||||
enable_mp4 bool default false,
|
||||
enable bool default false,
|
||||
status boolean,
|
||||
enable_remove_none_reader bool default false,
|
||||
create_time character varying(50),
|
||||
name character varying(255),
|
||||
update_time character varying(50),
|
||||
stream_key character varying(255),
|
||||
enable_disable_none_reader bool default false,
|
||||
constraint uk_stream_proxy_app_stream unique (app, stream)
|
||||
);
|
||||
|
||||
create table wvp_stream_push (
|
||||
id serial primary key,
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
total_reader_count character varying(50),
|
||||
origin_type integer,
|
||||
origin_type_str character varying(50),
|
||||
create_time character varying(50),
|
||||
alive_second integer,
|
||||
media_server_id character varying(50),
|
||||
server_id character varying(50),
|
||||
push_time character varying(50),
|
||||
status bool default false,
|
||||
update_time character varying(50),
|
||||
push_ing bool default false,
|
||||
self bool default false,
|
||||
constraint uk_stream_push_app_stream unique (app, stream)
|
||||
);
|
||||
create table wvp_cloud_record (
|
||||
id serial primary key,
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
call_id character varying(255),
|
||||
start_time bigint,
|
||||
end_time bigint,
|
||||
media_server_id character varying(50),
|
||||
file_name character varying(255),
|
||||
folder character varying(255),
|
||||
file_path character varying(255),
|
||||
collect bool default false,
|
||||
file_size bigint,
|
||||
time_len bigint,
|
||||
constraint uk_stream_push_app_stream_path unique (app, stream, file_path)
|
||||
);
|
||||
|
||||
create table wvp_user (
|
||||
id serial primary key,
|
||||
username character varying(255),
|
||||
password character varying(255),
|
||||
role_id integer,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
push_key character varying(50),
|
||||
constraint uk_user_username unique (username)
|
||||
);
|
||||
|
||||
create table wvp_user_role (
|
||||
id serial primary key,
|
||||
name character varying(50),
|
||||
authority character varying(50),
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
create table wvp_resources_tree (
|
||||
id serial primary key ,
|
||||
is_catalog bool default true,
|
||||
device_channel_id integer ,
|
||||
gb_stream_id integer,
|
||||
name character varying(255),
|
||||
parentId integer,
|
||||
path character varying(255)
|
||||
);
|
||||
|
||||
create table wvp_user_api_key (
|
||||
id serial primary key ,
|
||||
user_id bigint,
|
||||
app character varying(255) ,
|
||||
api_key text,
|
||||
expired_at bigint,
|
||||
remark character varying(255),
|
||||
enable bool default true,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
|
||||
/*初始数据*/
|
||||
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
|
||||
INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');
|
||||
|
||||
|
||||
|
||||
342
数据库/2.7.2/初始化-postgresql-kingbase-2.7.2.sql
Normal file
342
数据库/2.7.2/初始化-postgresql-kingbase-2.7.2.sql
Normal file
@ -0,0 +1,342 @@
|
||||
/*建表*/
|
||||
create table wvp_device (
|
||||
id serial primary key ,
|
||||
device_id character varying(50) not null ,
|
||||
name character varying(255),
|
||||
manufacturer character varying(255),
|
||||
model character varying(255),
|
||||
firmware character varying(255),
|
||||
transport character varying(50),
|
||||
stream_mode character varying(50),
|
||||
on_line bool default false,
|
||||
register_time character varying(50),
|
||||
keepalive_time character varying(50),
|
||||
ip character varying(50),
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
port integer,
|
||||
expires integer,
|
||||
subscribe_cycle_for_catalog integer DEFAULT 0,
|
||||
subscribe_cycle_for_mobile_position integer DEFAULT 0,
|
||||
mobile_position_submission_interval integer DEFAULT 5,
|
||||
subscribe_cycle_for_alarm integer DEFAULT 0,
|
||||
host_address character varying(50),
|
||||
charset character varying(50),
|
||||
ssrc_check bool default false,
|
||||
geo_coord_sys character varying(50),
|
||||
media_server_id character varying(50),
|
||||
custom_name character varying(255),
|
||||
sdp_ip character varying(50),
|
||||
local_ip character varying(50),
|
||||
password character varying(255),
|
||||
as_message_channel bool default false,
|
||||
keepalive_interval_time integer,
|
||||
broadcast_push_after_ack bool default false,
|
||||
constraint uk_device_device unique (device_id)
|
||||
);
|
||||
|
||||
create table wvp_device_alarm (
|
||||
id serial primary key ,
|
||||
device_id character varying(50) not null,
|
||||
channel_id character varying(50) not null,
|
||||
alarm_priority character varying(50),
|
||||
alarm_method character varying(50),
|
||||
alarm_time character varying(50),
|
||||
alarm_description character varying(255),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
alarm_type character varying(50),
|
||||
create_time character varying(50) not null
|
||||
);
|
||||
|
||||
create table wvp_device_channel (
|
||||
id serial primary key ,
|
||||
channel_id character varying(50) not null,
|
||||
name character varying(255),
|
||||
custom_name character varying(255),
|
||||
manufacture character varying(50),
|
||||
model character varying(50),
|
||||
owner character varying(50),
|
||||
civil_code character varying(50),
|
||||
block character varying(50),
|
||||
address character varying(50),
|
||||
parent_id character varying(50),
|
||||
safety_way integer,
|
||||
register_way integer,
|
||||
cert_num character varying(50),
|
||||
certifiable integer,
|
||||
err_code integer,
|
||||
end_time character varying(50),
|
||||
secrecy character varying(50),
|
||||
ip_address character varying(50),
|
||||
port integer,
|
||||
password character varying(255),
|
||||
ptz_type integer,
|
||||
custom_ptz_type integer,
|
||||
status bool default false,
|
||||
longitude double precision,
|
||||
custom_longitude double precision,
|
||||
latitude double precision,
|
||||
custom_latitude double precision,
|
||||
stream_id character varying(255),
|
||||
device_id character varying(50) not null,
|
||||
parental character varying(50),
|
||||
has_audio bool default false,
|
||||
create_time character varying(50) not null,
|
||||
update_time character varying(50) not null,
|
||||
sub_count integer,
|
||||
longitude_gcj02 double precision,
|
||||
latitude_gcj02 double precision,
|
||||
longitude_wgs84 double precision,
|
||||
latitude_wgs84 double precision,
|
||||
business_group_id character varying(50),
|
||||
gps_time character varying(50),
|
||||
stream_identification character varying(50),
|
||||
constraint uk_wvp_device_channel_unique_device_channel unique (device_id, channel_id)
|
||||
);
|
||||
|
||||
create table wvp_device_mobile_position (
|
||||
id serial primary key,
|
||||
device_id character varying(50) not null,
|
||||
channel_id character varying(50) not null,
|
||||
device_name character varying(255),
|
||||
time character varying(50),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
altitude double precision,
|
||||
speed double precision,
|
||||
direction double precision,
|
||||
report_source character varying(50),
|
||||
longitude_gcj02 double precision,
|
||||
latitude_gcj02 double precision,
|
||||
longitude_wgs84 double precision,
|
||||
latitude_wgs84 double precision,
|
||||
create_time character varying(50)
|
||||
);
|
||||
|
||||
create table wvp_gb_stream (
|
||||
gb_stream_id serial primary key,
|
||||
app character varying(255) not null,
|
||||
stream character varying(255) not null,
|
||||
gb_id character varying(50) not null,
|
||||
name character varying(255),
|
||||
longitude double precision,
|
||||
latitude double precision,
|
||||
stream_type character varying(50),
|
||||
media_server_id character varying(50),
|
||||
create_time character varying(50),
|
||||
constraint uk_gb_stream_unique_gb_id unique (gb_id),
|
||||
constraint uk_gb_stream_unique_app_stream unique (app, stream)
|
||||
);
|
||||
|
||||
create table wvp_log (
|
||||
id serial primary key ,
|
||||
name character varying(50),
|
||||
type character varying(50),
|
||||
uri character varying(200),
|
||||
address character varying(50),
|
||||
result character varying(50),
|
||||
timing bigint,
|
||||
username character varying(50),
|
||||
create_time character varying(50)
|
||||
);
|
||||
|
||||
create table wvp_media_server (
|
||||
id character varying(255) primary key ,
|
||||
ip character varying(50),
|
||||
hook_ip character varying(50),
|
||||
sdp_ip character varying(50),
|
||||
stream_ip character varying(50),
|
||||
http_port integer,
|
||||
http_ssl_port integer,
|
||||
rtmp_port integer,
|
||||
rtmp_ssl_port integer,
|
||||
rtp_proxy_port integer,
|
||||
rtsp_port integer,
|
||||
rtsp_ssl_port integer,
|
||||
flv_port integer,
|
||||
flv_ssl_port integer,
|
||||
ws_flv_port integer,
|
||||
ws_flv_ssl_port integer,
|
||||
auto_config bool default false,
|
||||
secret character varying(50),
|
||||
type character varying(50) default 'zlm',
|
||||
rtp_enable bool default false,
|
||||
rtp_port_range character varying(50),
|
||||
send_rtp_port_range character varying(50),
|
||||
record_assist_port integer,
|
||||
default_server bool default false,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
hook_alive_interval integer,
|
||||
record_path character varying(255),
|
||||
record_day integer default 7,
|
||||
transcode_suffix character varying(255),
|
||||
constraint uk_media_server_unique_ip_http_port unique (ip, http_port)
|
||||
);
|
||||
|
||||
create table wvp_platform (
|
||||
id serial primary key ,
|
||||
enable bool default false,
|
||||
name character varying(255),
|
||||
server_gb_id character varying(50),
|
||||
server_gb_domain character varying(50),
|
||||
server_ip character varying(50),
|
||||
server_port integer,
|
||||
device_gb_id character varying(50),
|
||||
device_ip character varying(50),
|
||||
device_port character varying(50),
|
||||
username character varying(255),
|
||||
password character varying(50),
|
||||
expires character varying(50),
|
||||
keep_timeout character varying(50),
|
||||
transport character varying(50),
|
||||
character_set character varying(50),
|
||||
catalog_id character varying(50),
|
||||
ptz bool default false,
|
||||
rtcp bool default false,
|
||||
status bool default false,
|
||||
start_offline_push bool default false,
|
||||
administrative_division character varying(50),
|
||||
catalog_group integer,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
as_message_channel bool default false,
|
||||
auto_push_channel bool default false,
|
||||
send_stream_ip character varying(50),
|
||||
constraint uk_platform_unique_server_gb_id unique (server_gb_id)
|
||||
);
|
||||
|
||||
create table wvp_platform_catalog (
|
||||
id character varying(50),
|
||||
platform_id character varying(50),
|
||||
name character varying(255),
|
||||
parent_id character varying(50),
|
||||
civil_code character varying(50),
|
||||
business_group_id character varying(50),
|
||||
constraint uk_platform_catalog_id_platform_id unique (id, platform_id)
|
||||
);
|
||||
|
||||
create table wvp_platform_gb_channel (
|
||||
id serial primary key ,
|
||||
platform_id character varying(50),
|
||||
catalog_id character varying(50),
|
||||
device_channel_id integer,
|
||||
constraint uk_platform_gb_channel_platform_id_catalog_id_device_channel_id unique (platform_id, catalog_id, device_channel_id)
|
||||
);
|
||||
|
||||
create table wvp_platform_gb_stream (
|
||||
id serial primary key,
|
||||
platform_id character varying(50),
|
||||
catalog_id character varying(50),
|
||||
gb_stream_id integer,
|
||||
constraint uk_platform_gb_stream_platform_id_catalog_id_gb_stream_id unique (platform_id, catalog_id, gb_stream_id)
|
||||
);
|
||||
|
||||
create table wvp_stream_proxy (
|
||||
id serial primary key,
|
||||
type character varying(50),
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
url character varying(255),
|
||||
src_url character varying(255),
|
||||
dst_url character varying(255),
|
||||
timeout_ms integer,
|
||||
ffmpeg_cmd_key character varying(255),
|
||||
rtp_type character varying(50),
|
||||
media_server_id character varying(50),
|
||||
enable_audio bool default false,
|
||||
enable_mp4 bool default false,
|
||||
enable bool default false,
|
||||
status boolean,
|
||||
enable_remove_none_reader bool default false,
|
||||
create_time character varying(50),
|
||||
name character varying(255),
|
||||
update_time character varying(50),
|
||||
stream_key character varying(255),
|
||||
enable_disable_none_reader bool default false,
|
||||
constraint uk_stream_proxy_app_stream unique (app, stream)
|
||||
);
|
||||
|
||||
create table wvp_stream_push (
|
||||
id serial primary key,
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
total_reader_count character varying(50),
|
||||
origin_type integer,
|
||||
origin_type_str character varying(50),
|
||||
create_time character varying(50),
|
||||
alive_second integer,
|
||||
media_server_id character varying(50),
|
||||
server_id character varying(50),
|
||||
push_time character varying(50),
|
||||
status bool default false,
|
||||
update_time character varying(50),
|
||||
push_ing bool default false,
|
||||
self bool default false,
|
||||
constraint uk_stream_push_app_stream unique (app, stream)
|
||||
);
|
||||
create table wvp_cloud_record (
|
||||
id serial primary key,
|
||||
app character varying(255),
|
||||
stream character varying(255),
|
||||
call_id character varying(255),
|
||||
start_time int8,
|
||||
end_time int8,
|
||||
media_server_id character varying(50),
|
||||
file_name character varying(255),
|
||||
folder character varying(255),
|
||||
file_path character varying(255),
|
||||
collect bool default false,
|
||||
file_size int8,
|
||||
time_len int8,
|
||||
constraint uk_stream_push_app_stream_path unique (app, stream, file_path)
|
||||
);
|
||||
|
||||
create table wvp_user (
|
||||
id serial primary key,
|
||||
username character varying(255),
|
||||
password character varying(255),
|
||||
role_id integer,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50),
|
||||
push_key character varying(50),
|
||||
constraint uk_user_username unique (username)
|
||||
);
|
||||
|
||||
create table wvp_user_role (
|
||||
id serial primary key,
|
||||
name character varying(50),
|
||||
authority character varying(50),
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
create table wvp_resources_tree (
|
||||
id serial primary key ,
|
||||
is_catalog bool default true,
|
||||
device_channel_id integer ,
|
||||
gb_stream_id integer,
|
||||
name character varying(255),
|
||||
parentId integer,
|
||||
path character varying(255)
|
||||
);
|
||||
|
||||
create table wvp_user_api_key (
|
||||
id serial primary key ,
|
||||
user_id bigint,
|
||||
app character varying(255) ,
|
||||
api_key text,
|
||||
expired_at bigint,
|
||||
remark character varying(255),
|
||||
enable bool default true,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
|
||||
|
||||
/*初始数据*/
|
||||
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
|
||||
INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');
|
||||
|
||||
|
||||
|
||||
26
数据库/2.7.2/更新-mysql-2.7.2.sql
Normal file
26
数据库/2.7.2/更新-mysql-2.7.2.sql
Normal file
@ -0,0 +1,26 @@
|
||||
alter table wvp_media_server
|
||||
add transcode_suffix character varying(255);
|
||||
|
||||
alter table wvp_media_server
|
||||
add type character varying(50) default 'zlm';
|
||||
|
||||
alter table wvp_media_server
|
||||
add flv_port integer;
|
||||
alter table wvp_media_server
|
||||
add flv_ssl_port integer;
|
||||
alter table wvp_media_server
|
||||
add ws_flv_port integer;
|
||||
alter table wvp_media_server
|
||||
add ws_flv_ssl_port integer;
|
||||
|
||||
create table wvp_user_api_key (
|
||||
id serial primary key ,
|
||||
user_id bigint,
|
||||
app character varying(255) ,
|
||||
api_key text,
|
||||
expired_at bigint,
|
||||
remark character varying(255),
|
||||
enable bool default true,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
26
数据库/2.7.2/更新-postgresql-kingbase-2.7.2.sql
Normal file
26
数据库/2.7.2/更新-postgresql-kingbase-2.7.2.sql
Normal file
@ -0,0 +1,26 @@
|
||||
alter table wvp_media_server
|
||||
add transcode_suffix character varying(255);
|
||||
|
||||
alter table wvp_media_server
|
||||
add type character varying(50) default 'zlm';
|
||||
|
||||
alter table wvp_media_server
|
||||
add flv_port integer;
|
||||
alter table wvp_media_server
|
||||
add flv_ssl_port integer;
|
||||
alter table wvp_media_server
|
||||
add ws_flv_port integer;
|
||||
alter table wvp_media_server
|
||||
add ws_flv_ssl_port integer;
|
||||
|
||||
create table wvp_user_api_key (
|
||||
id serial primary key ,
|
||||
user_id bigint,
|
||||
app character varying(255) ,
|
||||
api_key text,
|
||||
expired_at bigint,
|
||||
remark character varying(255),
|
||||
enable bool default true,
|
||||
create_time character varying(50),
|
||||
update_time character varying(50)
|
||||
);
|
||||
Loading…
Reference in New Issue
Block a user