Merge branch '648540858:wvp-28181-2.0' into wvp-28181-2.0
| | |
| | | - [X] 添加RTMP视频 |
| | | - [X] 云端录像(需要部署单独服务配合使用) |
| | | - [X] 多流媒体节点,自动选择负载最低的节点使用。 |
| | | - [X] 支持使用mysql作为数据库,默认sqlite3,开箱即用。 |
| | | - [X] WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。 |
| | | |
| | | [//]: # (# docker快速体验) |
| | |
| | | <version>8.0.22</version> |
| | | </dependency> |
| | | |
| | | <!-- 添加sqlite-jdbc数据库驱动 --> |
| | | <dependency> |
| | | <groupId>org.xerial</groupId> |
| | | <artifactId>sqlite-jdbc</artifactId> |
| | | <version>3.32.3.2</version> |
| | | </dependency> |
| | | |
| | | <!--Mybatis分页插件 --> |
| | | <dependency> |
| | | <groupId>com.github.pagehelper</groupId> |
| | |
| | | public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_";
|
| | |
|
| | | public static final String EVENT_ONLINE_REGISTER = "1";
|
| | | |
| | | public static final String EVENT_ONLINE_KEEPLIVE = "2";
|
| | |
|
| | | public static final String EVENT_ONLINE_MESSAGE = "3";
|
| | |
|
| | |
| | | import com.genersoft.iot.vmp.conf.security.SecurityUtils; |
| | | import com.genersoft.iot.vmp.service.ILogService; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.LogDto; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.text.SimpleDateFormat; |
| | | |
| | | /** |
| | | * @author lin |
| | |
| | | |
| | | private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class); |
| | | |
| | | private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @Autowired |
| | | private UserSetting userSetting; |
| | |
| | | logDto.setTiming(System.currentTimeMillis() - start); |
| | | logDto.setType(servletRequest.getMethod()); |
| | | logDto.setUri(servletRequest.getRequestURI()); |
| | | logDto.setCreateTime(format.format(System.currentTimeMillis())); |
| | | logDto.setCreateTime(DateUtil.getNow()); |
| | | logService.add(logDto); |
| | | // logger.warn("[Api Access] [{}] [{}] [{}] [{}] [{}] {}ms", |
| | | // uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()), |
| | |
| | | package com.genersoft.iot.vmp.conf; |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.Date; |
| | | import java.time.Instant; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.ScheduledFuture; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * 动态定时任务 |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class DynamicTask { |
| | | |
| | | private Logger logger = LoggerFactory.getLogger(DynamicTask.class); |
| | | private final Logger logger = LoggerFactory.getLogger(DynamicTask.class); |
| | | |
| | | @Autowired |
| | | private ThreadPoolTaskScheduler threadPoolTaskScheduler; |
| | | |
| | | private Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>(); |
| | | private Map<String, Runnable> runnableMap = new ConcurrentHashMap<>(); |
| | | private final Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>(); |
| | | private final Map<String, Runnable> runnableMap = new ConcurrentHashMap<>(); |
| | | |
| | | @Bean |
| | | public ThreadPoolTaskScheduler threadPoolTaskScheduler() { |
| | |
| | | * 循环执行的任务 |
| | | * @param key 任务ID |
| | | * @param task 任务 |
| | | * @param cycleForCatalog 间隔 |
| | | * @param cycleForCatalog 间隔 毫秒 |
| | | * @return |
| | | */ |
| | | public void startCron(String key, Runnable task, int cycleForCatalog) { |
| | | ScheduledFuture future = futureMap.get(key); |
| | | ScheduledFuture<?> future = futureMap.get(key); |
| | | if (future != null) { |
| | | if (future.isCancelled()) { |
| | | logger.info("任务【{}】已存在但是关闭状态!!!", key); |
| | | logger.debug("任务【{}】已存在但是关闭状态!!!", key); |
| | | } else { |
| | | logger.info("任务【{}】已存在且已启动!!!", key); |
| | | logger.debug("任务【{}】已存在且已启动!!!", key); |
| | | return; |
| | | } |
| | | } |
| | | // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔 |
| | | future = threadPoolTaskScheduler.scheduleAtFixedRate(task, cycleForCatalog * 1000L); |
| | | future = threadPoolTaskScheduler.scheduleAtFixedRate(task, cycleForCatalog); |
| | | if (future != null){ |
| | | futureMap.put(key, future); |
| | | runnableMap.put(key, task); |
| | | logger.info("任务【{}】启动成功!!!", key); |
| | | logger.debug("任务【{}】启动成功!!!", key); |
| | | }else { |
| | | logger.info("任务【{}】启动失败!!!", key); |
| | | logger.debug("任务【{}】启动失败!!!", key); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | public void startDelay(String key, Runnable task, int delay) { |
| | | stop(key); |
| | | Date starTime = new Date(System.currentTimeMillis() + delay); |
| | | |
| | | // 获取执行的时刻 |
| | | Instant startInstant = Instant.now().plusMillis(TimeUnit.MILLISECONDS.toMillis(delay)); |
| | | |
| | | ScheduledFuture future = futureMap.get(key); |
| | | if (future != null) { |
| | | if (future.isCancelled()) { |
| | | logger.info("任务【{}】已存在但是关闭状态!!!", key); |
| | | logger.debug("任务【{}】已存在但是关闭状态!!!", key); |
| | | } else { |
| | | logger.info("任务【{}】已存在且已启动!!!", key); |
| | | logger.debug("任务【{}】已存在且已启动!!!", key); |
| | | return; |
| | | } |
| | | } |
| | | // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔 |
| | | future = threadPoolTaskScheduler.schedule(task, starTime); |
| | | future = threadPoolTaskScheduler.schedule(task, startInstant); |
| | | if (future != null){ |
| | | futureMap.put(key, future); |
| | | runnableMap.put(key, task); |
| | | logger.info("任务【{}】启动成功!!!", key); |
| | | logger.debug("任务【{}】启动成功!!!", key); |
| | | }else { |
| | | logger.info("任务【{}】启动失败!!!", key); |
| | | logger.debug("任务【{}】启动失败!!!", key); |
| | | } |
| | | } |
| | | |
| | |
| | | package com.genersoft.iot.vmp.conf; |
| | | |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | @Configuration("mediaConfig") |
| | | public class MediaConfig{ |
| | |
| | | mediaServerItem.setRecordAssistPort(recordAssistPort); |
| | | mediaServerItem.setHookAliveInterval(120); |
| | | |
| | | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | mediaServerItem.setCreateTime(format.format(System.currentTimeMillis())); |
| | | mediaServerItem.setUpdateTime(format.format(System.currentTimeMillis())); |
| | | mediaServerItem.setCreateTime(DateUtil.getNow()); |
| | | mediaServerItem.setUpdateTime(DateUtil.getNow()); |
| | | |
| | | return mediaServerItem; |
| | | } |
| | |
| | | * 允许线程空闲时间(单位:默认为秒) |
| | | */ |
| | | private static final int keepAliveTime = 30; |
| | | |
| | | /** |
| | | * 缓冲队列大小 |
| | | */ |
| | | private static final int queueCapacity = 500; |
| | | private static final int queueCapacity = 10000; |
| | | /** |
| | | * 线程池名前缀 |
| | | */ |
| | |
| | | Properties properties = new Properties();
|
| | | properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
|
| | | properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getMonitorIp());
|
| | | /**
|
| | | * 完整配置参考 gov.nist.javax.sip.SipStackImpl,需要下载源码
|
| | | * gov/nist/javax/sip/SipStackImpl.class
|
| | | */
|
| | | properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "true");
|
| | | properties.setProperty("gov.nist.javax.sip.DELIVER_UNSOLICITED_NOTIFY", "true"); // 接收所有notify请求,即使没有订阅
|
| | | properties.setProperty("gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG", "true"); // 为_NULL _对话框传递_终止的_事件
|
| | | properties.setProperty("gov.nist.javax.sip.RELEASE_REFERENCES_STRATEGY", "Normal"); // 会话清理策略
|
| | | properties.setProperty("gov.nist.javax.sip.RELIABLE_CONNECTION_KEEP_ALIVE_TIMEOUT", "10");
|
| | | /**
|
| | | * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE =
|
| | | * 0; public static final int TRACE_MESSAGES = 16; public static final int
|
| | |
| | | tcpSipProvider.setDialogErrorsAutomaticallyHandled();
|
| | | tcpSipProvider.addSipListener(sipProcessorObserver);
|
| | | // tcpSipProvider.setAutomaticDialogSupportEnabled(false);
|
| | | logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}");
|
| | | logger.info("[Sip Server] TCP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | } catch (TransportNotSupportedException e) {
|
| | | e.printStackTrace();
|
| | | } catch (InvalidArgumentException e) {
|
| | | logger.error("无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | logger.error("[Sip Server] 无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | , sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | } catch (TooManyListenersException e) {
|
| | | e.printStackTrace();
|
| | |
| | | } catch (TransportNotSupportedException e) {
|
| | | e.printStackTrace();
|
| | | } catch (InvalidArgumentException e) {
|
| | | logger.error("无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | logger.error("[Sip Server] 无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
|
| | | , sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | } catch (TooManyListenersException e) {
|
| | | e.printStackTrace();
|
| | | } catch (ObjectInUseException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | logger.info("Sip Server UDP 启动成功 port [" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "]");
|
| | | logger.info("[Sip Server] UDP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
|
| | | return udpSipProvider;
|
| | | }
|
| | |
|
| | |
| | | |
| | | import java.security.MessageDigest; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.text.DecimalFormat; |
| | | import java.util.Date; |
| | | import java.time.Instant; |
| | | import java.util.Random; |
| | | |
| | | import javax.sip.address.URI; |
| | |
| | | * @return a generated nonce. |
| | | */ |
| | | private String generateNonce() { |
| | | // Get the time of day and run MD5 over it. |
| | | Date date = new Date(); |
| | | long time = date.getTime(); |
| | | long time = Instant.now().toEpochMilli(); |
| | | Random rand = new Random(); |
| | | long pad = rand.nextLong(); |
| | | // String nonceString = (new Long(time)).toString() |
| | | // + (new Long(pad)).toString(); |
| | | String nonceString = Long.valueOf(time).toString() |
| | | + Long.valueOf(pad).toString(); |
| | | byte mdbytes[] = messageDigest.digest(nonceString.getBytes()); |
| | | // Convert the mdbytes array into a hex string. |
| | | return toHexString(mdbytes); |
| | | } |
| | | |
| | |
| | | package com.genersoft.iot.vmp.gb28181.bean; |
| | | |
| | | import java.util.Date; |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | |
| | | public class CatalogData { |
| | | private int sn; // 命令序列号 |
| | | private int total; |
| | | private List<DeviceChannel> channelList; |
| | | private Date lastTime; |
| | | private Instant lastTime; |
| | | private Device device; |
| | | private String errorMsg; |
| | | |
| | |
| | | this.channelList = channelList; |
| | | } |
| | | |
| | | public Date getLastTime() { |
| | | public Instant getLastTime() { |
| | | return lastTime; |
| | | } |
| | | |
| | | public void setLastTime(Date lastTime) { |
| | | public void setLastTime(Instant lastTime) { |
| | | this.lastTime = lastTime; |
| | | } |
| | | |
| | |
| | | private String mediaServerId; |
| | | |
| | | /** |
| | | * 首次注册 |
| | | */ |
| | | private boolean firsRegister; |
| | | |
| | | /** |
| | | * 字符集, 支持 UTF-8 与 GB2312 |
| | | */ |
| | | private String charset ; |
| | |
| | | |
| | | public void setMediaServerId(String mediaServerId) { |
| | | this.mediaServerId = mediaServerId; |
| | | } |
| | | |
| | | public boolean isFirsRegister() { |
| | | return firsRegister; |
| | | } |
| | | |
| | | public void setFirsRegister(boolean firsRegister) { |
| | | this.firsRegister = firsRegister; |
| | | } |
| | | |
| | | public String getCharset() { |
| | |
| | | package com.genersoft.iot.vmp.gb28181.bean;
|
| | |
|
| | |
|
| | | //import gov.nist.javax.sip.header.SIPDate;
|
| | |
|
| | | import java.time.Instant;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | |
| | | private String name;
|
| | |
|
| | | private int sumNum;
|
| | |
|
| | | private Instant lastTime;
|
| | |
|
| | | private List<RecordItem> recordList;
|
| | |
|
| | |
| | | public void setSn(String sn) {
|
| | | this.sn = sn;
|
| | | }
|
| | |
|
| | | public Instant getLastTime() {
|
| | | return lastTime;
|
| | | }
|
| | |
|
| | | public void setLastTime(Instant lastTime) {
|
| | | this.lastTime = lastTime;
|
| | | }
|
| | | }
|
| | |
| | | package com.genersoft.iot.vmp.gb28181.bean;
|
| | |
|
| | |
|
| | | import com.genersoft.iot.vmp.utils.DateUtil;
|
| | | import org.jetbrains.annotations.NotNull;
|
| | |
|
| | | import java.text.ParseException;
|
| | | import java.text.SimpleDateFormat;
|
| | | import java.util.Date;
|
| | | import java.time.Instant;
|
| | | import java.time.temporal.TemporalAccessor;
|
| | |
|
| | | /**
|
| | | * @description:设备录像bean
|
| | |
| | |
|
| | | @Override
|
| | | public int compareTo(@NotNull RecordItem recordItem) {
|
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
| | | try {
|
| | | Date startTime_now = sdf.parse(startTime);
|
| | | Date startTime_param = sdf.parse(recordItem.getStartTime());
|
| | | if (startTime_param.compareTo(startTime_now) > 0) {
|
| | | TemporalAccessor startTimeNow = DateUtil.formatter.parse(startTime);
|
| | | TemporalAccessor startTimeParam = DateUtil.formatter.parse(recordItem.getStartTime());
|
| | | Instant startTimeParamInstant = Instant.from(startTimeParam);
|
| | | Instant startTimeNowInstant = Instant.from(startTimeNow);
|
| | | if (startTimeNowInstant.equals(startTimeParamInstant)) {
|
| | | return 0;
|
| | | }else if (Instant.from(startTimeParam).isAfter(Instant.from(startTimeNow)) ) {
|
| | | return -1;
|
| | | }else {
|
| | | return 1;
|
| | | }
|
| | | } catch (ParseException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return 0;
|
| | |
|
| | | }
|
| | | }
|
| | |
| | | private String CallId; |
| | | |
| | | /** |
| | | * 发送时,rtp的pt(uint8_t),不传时默认为96 |
| | | */ |
| | | private int pt = 96; |
| | | |
| | | /** |
| | | * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es; |
| | | */ |
| | | private boolean usePs = true; |
| | | |
| | | /** |
| | | * 当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0 |
| | | */ |
| | | private boolean onlyAudio = false; |
| | | |
| | | /** |
| | | * 播放类型 |
| | | */ |
| | | private InviteStreamType playType; |
| | |
| | | this.dialog = dialog; |
| | | } |
| | | |
| | | public int getPt() { |
| | | return pt; |
| | | } |
| | | |
| | | public void setPt(int pt) { |
| | | this.pt = pt; |
| | | } |
| | | |
| | | public boolean isUsePs() { |
| | | return usePs; |
| | | } |
| | | |
| | | public void setUsePs(boolean usePs) { |
| | | this.usePs = usePs; |
| | | } |
| | | |
| | | public boolean isOnlyAudio() { |
| | | return onlyAudio; |
| | | } |
| | | |
| | | public void setOnlyAudio(boolean onlyAudio) { |
| | | this.onlyAudio = onlyAudio; |
| | | } |
| | | } |
| | |
| | | // 添加任务处理GPS定时推送 |
| | | dynamicTask.startCron(key, new MobilePositionSubscribeHandlerTask(redisCatchStorage, sipCommanderForPlatform, |
| | | storager, platformId, subscribeInfo.getSn(), key, this, dynamicTask), |
| | | subscribeInfo.getGpsInterval()); |
| | | subscribeInfo.getGpsInterval() * 1000); |
| | | String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; |
| | | dynamicTask.stop(taskOverdueKey); |
| | | // 添加任务处理订阅过期 |
| | | dynamicTask.startDelay(taskOverdueKey, () -> { |
| | | System.out.println("订阅过期"); |
| | | removeMobilePositionSubscribe(subscribeInfo.getId()); |
| | | }, |
| | | subscribeInfo.getExpires() * 1000); |
| | |
| | | package com.genersoft.iot.vmp.gb28181.event;
|
| | |
|
| | | import com.genersoft.iot.vmp.gb28181.bean.*;
|
| | | import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent;
|
| | | import com.genersoft.iot.vmp.gb28181.event.device.RequestTimeoutEvent;
|
| | | import com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire.PlatformKeepaliveExpireEvent;
|
| | | import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformCycleRegisterEvent;
|
| | | import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformNotRegisterEvent;
|
| | |
| | | import com.genersoft.iot.vmp.media.zlm.event.ZLMOnlineEvent;
|
| | | import org.springframework.beans.factory.annotation.Autowired;
|
| | | import org.springframework.context.ApplicationEventPublisher;
|
| | | import org.springframework.scheduling.annotation.Async;
|
| | | import org.springframework.stereotype.Component;
|
| | |
|
| | | import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent;
|
| | | import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
|
| | |
|
| | | import javax.sip.TimeoutEvent;
|
| | | import java.util.ArrayList;
|
| | | import java.util.HashSet;
|
| | | import java.util.List;
|
| | |
| | |
|
| | | @Autowired
|
| | | private ApplicationEventPublisher applicationEventPublisher;
|
| | | |
| | | public void onlineEventPublish(Device device, String from, int expires) {
|
| | | OnlineEvent onEvent = new OnlineEvent(this);
|
| | | onEvent.setDevice(device);
|
| | | onEvent.setFrom(from);
|
| | | onEvent.setExpires(expires);
|
| | | applicationEventPublisher.publishEvent(onEvent);
|
| | | }
|
| | |
|
| | | public void onlineEventPublish(Device device, String from) {
|
| | | OnlineEvent onEvent = new OnlineEvent(this);
|
| | | onEvent.setDevice(device);
|
| | | onEvent.setFrom(from);
|
| | | applicationEventPublisher.publishEvent(onEvent);
|
| | | }
|
| | | |
| | | public void outlineEventPublish(String deviceId, String from){
|
| | | OfflineEvent outEvent = new OfflineEvent(this);
|
| | | outEvent.setDeviceId(deviceId);
|
| | | outEvent.setFrom(from);
|
| | | applicationEventPublisher.publishEvent(outEvent);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 平台心跳到期事件
|
| | |
| | | }
|
| | |
|
| | |
|
| | | public void requestTimeOut(TimeoutEvent timeoutEvent) {
|
| | | RequestTimeoutEvent requestTimeoutEvent = new RequestTimeoutEvent(this);
|
| | | requestTimeoutEvent.setTimeoutEvent(timeoutEvent);
|
| | | applicationEventPublisher.publishEvent(requestTimeoutEvent);
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | *
|
| | | * @param platformId
|
| | |
| | | import javax.sip.*; |
| | | import javax.sip.header.CallIdHeader; |
| | | import javax.sip.message.Response; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.time.Instant; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class SipSubscribe { |
| | | |
| | |
| | | |
| | | private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>(); |
| | | |
| | | private Map<String, Date> okTimeSubscribes = new ConcurrentHashMap<>(); |
| | | private Map<String, Date> errorTimeSubscribes = new ConcurrentHashMap<>(); |
| | | private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>(); |
| | | private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>(); |
| | | |
| | | // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 |
| | | // @Scheduled(fixedRate= 100 * 60 * 60 ) |
| | | @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次 |
| | | public void execute(){ |
| | | logger.info("[定时任务] 清理过期的SIP订阅信息"); |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.setTime(new Date()); |
| | | calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) - 5); |
| | | |
| | | Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); |
| | | |
| | | for (String key : okTimeSubscribes.keySet()) { |
| | | if (okTimeSubscribes.get(key).before(calendar.getTime())){ |
| | | // logger.info("[定时任务] 清理过期的订阅信息: {}", key); |
| | | if (okTimeSubscribes.get(key).isBefore(instant)){ |
| | | okSubscribes.remove(key); |
| | | okTimeSubscribes.remove(key); |
| | | } |
| | | } |
| | | for (String key : errorTimeSubscribes.keySet()) { |
| | | if (errorTimeSubscribes.get(key).before(calendar.getTime())){ |
| | | // logger.info("[定时任务] 清理过期的订阅信息: {}", key); |
| | | if (errorTimeSubscribes.get(key).isBefore(instant)){ |
| | | errorSubscribes.remove(key); |
| | | errorTimeSubscribes.remove(key); |
| | | } |
| | |
| | | |
| | | public void addErrorSubscribe(String key, SipSubscribe.Event event) { |
| | | errorSubscribes.put(key, event); |
| | | errorTimeSubscribes.put(key, new Date()); |
| | | errorTimeSubscribes.put(key, Instant.now()); |
| | | } |
| | | |
| | | public void addOkSubscribe(String key, SipSubscribe.Event event) { |
| | | okSubscribes.put(key, event); |
| | | okTimeSubscribes.put(key, new Date()); |
| | | okTimeSubscribes.put(key, Instant.now()); |
| | | } |
| | | |
| | | public SipSubscribe.Event getErrorSubscribe(String key) { |
New file |
| | |
| | | package com.genersoft.iot.vmp.gb28181.event.device; |
| | | |
| | | import org.springframework.context.ApplicationEvent; |
| | | |
| | | import javax.sip.TimeoutEvent; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | public class RequestTimeoutEvent extends ApplicationEvent { |
| | | public RequestTimeoutEvent(Object source) { |
| | | super(source); |
| | | } |
| | | |
| | | |
| | | private TimeoutEvent timeoutEvent; |
| | | |
| | | public TimeoutEvent getTimeoutEvent() { |
| | | return timeoutEvent; |
| | | } |
| | | |
| | | public void setTimeoutEvent(TimeoutEvent timeoutEvent) { |
| | | this.timeoutEvent = timeoutEvent; |
| | | } |
| | | } |
New file |
| | |
| | | package com.genersoft.iot.vmp.gb28181.event.device; |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.ApplicationListener; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.sip.ClientTransaction; |
| | | import javax.sip.address.SipURI; |
| | | import javax.sip.header.CallIdHeader; |
| | | import javax.sip.header.ToHeader; |
| | | import javax.sip.message.Request; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class RequestTimeoutEventImpl implements ApplicationListener<RequestTimeoutEvent> { |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public void onApplicationEvent(RequestTimeoutEvent event) { |
| | | ClientTransaction clientTransaction = event.getTimeoutEvent().getClientTransaction(); |
| | | if (clientTransaction != null) { |
| | | Request request = clientTransaction.getRequest(); |
| | | if (request != null) { |
| | | String host = ((SipURI) request.getRequestURI()).getHost(); |
| | | int port = ((SipURI) request.getRequestURI()).getPort(); |
| | | Device device = deviceService.getDeviceByHostAndPort(host, port); |
| | | if (device == null) { |
| | | return; |
| | | } |
| | | deviceService.offline(device.getDeviceId()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| | | |
| | | /** |
| | | * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件 |
| | | * @author: swwheihei |
| | | * @date: 2020年5月6日 上午11:35:46 |
| | | * 设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件 |
| | | * @author swwheihei |
| | | */ |
| | | @Component |
| | | public class KeepaliveTimeoutListenerForPlatform extends RedisKeyExpirationEventMessageListener { |
| | |
| | | // 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线 |
| | | String PLATFORM_KEEPLIVEKEY_PREFIX = VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetting.getServerId() + "_"; |
| | | String PLATFORM_REGISTER_PREFIX = VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetting.getServerId() + "_"; |
| | | String KEEPLIVEKEY_PREFIX = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetting.getServerId() + "_"; |
| | | String REGISTER_INFO_PREFIX = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_"; |
| | | if (expiredKey.startsWith(PLATFORM_KEEPLIVEKEY_PREFIX)) { |
| | | String platformGBId = expiredKey.substring(PLATFORM_KEEPLIVEKEY_PREFIX.length(),expiredKey.length()); |
| | | ParentPlatform platform = storager.queryParentPlatByServerGBId(platformGBId); |
| | | String platformGbId = expiredKey.substring(PLATFORM_KEEPLIVEKEY_PREFIX.length()); |
| | | ParentPlatform platform = storager.queryParentPlatByServerGBId(platformGbId); |
| | | if (platform != null) { |
| | | publisher.platformKeepaliveExpireEventPublish(platformGBId); |
| | | publisher.platformKeepaliveExpireEventPublish(platformGbId); |
| | | } |
| | | }else if (expiredKey.startsWith(PLATFORM_REGISTER_PREFIX)) { |
| | | String platformGBId = expiredKey.substring(PLATFORM_REGISTER_PREFIX.length(),expiredKey.length()); |
| | | ParentPlatform platform = storager.queryParentPlatByServerGBId(platformGBId); |
| | | String platformGbId = expiredKey.substring(PLATFORM_REGISTER_PREFIX.length(),expiredKey.length()); |
| | | ParentPlatform platform = storager.queryParentPlatByServerGBId(platformGbId); |
| | | if (platform != null) { |
| | | publisher.platformRegisterCycleEventPublish(platformGBId); |
| | | } |
| | | }else if (expiredKey.startsWith(KEEPLIVEKEY_PREFIX)){ |
| | | String deviceId = expiredKey.substring(KEEPLIVEKEY_PREFIX.length(),expiredKey.length()); |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | if (device != null) { |
| | | publisher.outlineEventPublish(deviceId, KEEPLIVEKEY_PREFIX); |
| | | publisher.platformRegisterCycleEventPublish(platformGbId); |
| | | } |
| | | }else if (expiredKey.startsWith(REGISTER_INFO_PREFIX)) { |
| | | String callId = expiredKey.substring(REGISTER_INFO_PREFIX.length()); |
| | |
| | | sipSubscribe.getErrorSubscribe(callId).response(eventResult); |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| | | import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.Instant; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | @Component |
| | | public class CatalogDataCatch { |
| | | |
| | | public static Map<String, CatalogData> data = new ConcurrentHashMap<>(); |
| | | |
| | | @Autowired |
| | | private DeferredResultHolder deferredResultHolder; |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorage storager; |
| | |
| | | CatalogData catalogData = data.get(device.getDeviceId()); |
| | | if (catalogData == null || catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { |
| | | catalogData = new CatalogData(); |
| | | catalogData.setChannelList(new ArrayList<>()); |
| | | catalogData.setChannelList(Collections.synchronizedList(new ArrayList<>())); |
| | | catalogData.setDevice(device); |
| | | catalogData.setSn(sn); |
| | | catalogData.setStatus(CatalogData.CatalogDataStatus.ready); |
| | | catalogData.setLastTime(new Date(System.currentTimeMillis())); |
| | | catalogData.setLastTime(Instant.now()); |
| | | data.put(device.getDeviceId(), catalogData); |
| | | } |
| | | } |
| | |
| | | catalogData.setSn(sn); |
| | | catalogData.setTotal(total); |
| | | catalogData.setDevice(device); |
| | | catalogData.setChannelList(new ArrayList<>()); |
| | | catalogData.setChannelList(Collections.synchronizedList(new ArrayList<>())); |
| | | catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); |
| | | catalogData.setLastTime(new Date(System.currentTimeMillis())); |
| | | catalogData.setLastTime(Instant.now()); |
| | | data.put(deviceId, catalogData); |
| | | }else { |
| | | // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 |
| | |
| | | catalogData.setDevice(device); |
| | | catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); |
| | | catalogData.getChannelList().addAll(deviceChannelList); |
| | | catalogData.setLastTime(new Date(System.currentTimeMillis())); |
| | | catalogData.setLastTime(Instant.now()); |
| | | } |
| | | } |
| | | |
| | |
| | | @Scheduled(fixedRate = 5 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 |
| | | private void timerTask(){ |
| | | Set<String> keys = data.keySet(); |
| | | Calendar calendarBefore5S = Calendar.getInstance(); |
| | | calendarBefore5S.setTime(new Date()); |
| | | calendarBefore5S.set(Calendar.SECOND, calendarBefore5S.get(Calendar.SECOND) - 5); |
| | | |
| | | Calendar calendarBefore30S = Calendar.getInstance(); |
| | | calendarBefore30S.setTime(new Date()); |
| | | calendarBefore30S.set(Calendar.SECOND, calendarBefore30S.get(Calendar.SECOND) - 30); |
| | | Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(5)); |
| | | Instant instantBefore30S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(30)); |
| | | |
| | | for (String deviceId : keys) { |
| | | CatalogData catalogData = data.get(deviceId); |
| | | if ( catalogData.getLastTime().before(calendarBefore5S.getTime())) { // 超过五秒收不到消息任务超时, 只更新这一部分数据 |
| | | if ( catalogData.getLastTime().isBefore(instantBefore5S)) { // 超过五秒收不到消息任务超时, 只更新这一部分数据 |
| | | if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) { |
| | | storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); |
| | | if (catalogData.getTotal() != catalogData.getChannelList().size()) { |
| | |
| | | } |
| | | catalogData.setStatus(CatalogData.CatalogDataStatus.end); |
| | | } |
| | | if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) && catalogData.getLastTime().before(calendarBefore30S.getTime())) { // 超过三十秒,如果标记为end则删除 |
| | | if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) && catalogData.getLastTime().isBefore(instantBefore30S)) { // 超过三十秒,如果标记为end则删除 |
| | | data.remove(deviceId); |
| | | } |
| | | } |
New file |
| | |
| | | package com.genersoft.iot.vmp.gb28181.session; |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.bean.*; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.Instant; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class RecordDataCatch { |
| | | |
| | | public static Map<String, RecordInfo> data = new ConcurrentHashMap<>(); |
| | | |
| | | @Autowired |
| | | private DeferredResultHolder deferredResultHolder; |
| | | |
| | | |
| | | public int put(String deviceId, String sn, int sumNum, List<RecordItem> recordItems) { |
| | | String key = deviceId + sn; |
| | | RecordInfo recordInfo = data.get(key); |
| | | if (recordInfo == null) { |
| | | recordInfo = new RecordInfo(); |
| | | recordInfo.setDeviceId(deviceId); |
| | | recordInfo.setSn(sn.trim()); |
| | | recordInfo.setSumNum(sumNum); |
| | | recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>())); |
| | | recordInfo.setLastTime(Instant.now()); |
| | | recordInfo.getRecordList().addAll(recordItems); |
| | | data.put(key, recordInfo); |
| | | }else { |
| | | // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 |
| | | if (!Objects.equals(sn.trim(), recordInfo.getSn())) { |
| | | return 0; |
| | | } |
| | | recordInfo.getRecordList().addAll(recordItems); |
| | | recordInfo.setLastTime(Instant.now()); |
| | | } |
| | | return recordInfo.getRecordList().size(); |
| | | } |
| | | |
| | | @Scheduled(fixedRate = 5 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 |
| | | private void timerTask(){ |
| | | Set<String> keys = data.keySet(); |
| | | // 获取五秒前的时刻 |
| | | Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(5)); |
| | | for (String key : keys) { |
| | | RecordInfo recordInfo = data.get(key); |
| | | // 超过五秒收不到消息任务超时, 只更新这一部分数据 |
| | | if ( recordInfo.getLastTime().isBefore(instantBefore5S)) { |
| | | // 处理录像数据, 返回给前端 |
| | | String msgKey = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn(); |
| | | |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(0); |
| | | wvpResult.setMsg("success"); |
| | | // 对数据进行排序 |
| | | Collections.sort(recordInfo.getRecordList()); |
| | | wvpResult.setData(recordInfo); |
| | | |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(msgKey); |
| | | msg.setData(wvpResult); |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | data.remove(key); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public boolean isComplete(String deviceId, String sn) { |
| | | RecordInfo recordInfo = data.get(deviceId + sn); |
| | | return recordInfo != null && recordInfo.getRecordList().size() == recordInfo.getSumNum(); |
| | | } |
| | | |
| | | public RecordInfo getRecordInfo(String deviceId, String sn) { |
| | | return data.get(deviceId + sn); |
| | | } |
| | | |
| | | public void remove(String deviceId, String sn) { |
| | | data.remove(deviceId + sn); |
| | | } |
| | | } |
| | |
| | | }
|
| | |
|
| | |
|
| | | public ClientTransaction getTransactionByStream(String deviceId, String channelId, String stream){
|
| | | SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
|
| | | public ClientTransaction getTransaction(String deviceId, String channelId, String stream, String callId){
|
| | | SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, stream);
|
| | | if (ssrcTransaction == null) {
|
| | | return null;
|
| | | }
|
New file |
| | |
| | | package com.genersoft.iot.vmp.gb28181.task; |
| | | |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.CommandLineRunner; |
| | | import org.springframework.core.annotation.Order; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | |
| | | /** |
| | | * 系统启动时控制设备 |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | @Order(value=4) |
| | | public class SipDeviceRunner implements CommandLineRunner { |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorage storager; |
| | | |
| | | @Autowired |
| | | private IRedisCatchStorage redisCatchStorage; |
| | | |
| | | @Autowired |
| | | private UserSetting userSetting; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public void run(String... args) throws Exception { |
| | | List<Device> deviceList = deviceService.getAllOnlineDevice(); |
| | | |
| | | for (Device device : deviceList) { |
| | | if (deviceService.expire(device)){ |
| | | deviceService.offline(device.getDeviceId()); |
| | | }else { |
| | | deviceService.online(device); |
| | | } |
| | | } |
| | | // 重置cseq计数 |
| | | redisCatchStorage.resetAllCSEQ(); |
| | | } |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.gb28181.transmit; |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| | | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.RegisterRequestProcessor; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd.KeepaliveNotifyMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.response.ISIPResponseProcessor; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor; |
| | | import org.slf4j.Logger; |
| | |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.sip.*; |
| | | import javax.sip.header.CSeqHeader; |
| | | import javax.sip.header.CallIdHeader; |
| | | import javax.sip.address.SipURI; |
| | | import javax.sip.address.URI; |
| | | import javax.sip.header.*; |
| | | import javax.sip.message.Request; |
| | | import javax.sip.message.Response; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | |
| | | @Autowired |
| | | private SipSubscribe sipSubscribe; |
| | | |
| | | @Autowired |
| | | private EventPublisher eventPublisher; |
| | | |
| | | // @Autowired |
| | | // @Qualifier(value = "taskExecutor") |
| | | // private ThreadPoolTaskExecutor poolTaskExecutor; |
| | | |
| | | |
| | | |
| | | /** |
| | | * 添加 request订阅 |
| | |
| | | */ |
| | | @Override |
| | | public void processTimeout(TimeoutEvent timeoutEvent) { |
| | | if(timeoutProcessor != null) { |
| | | timeoutProcessor.process(timeoutEvent); |
| | | logger.info("[消息发送超时]"); |
| | | ClientTransaction clientTransaction = timeoutEvent.getClientTransaction(); |
| | | eventPublisher.requestTimeOut(timeoutEvent); |
| | | if (clientTransaction != null) { |
| | | Request request = clientTransaction.getRequest(); |
| | | if (request != null) { |
| | | CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); |
| | | if (callIdHeader != null) { |
| | | SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); |
| | | SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent); |
| | | subscribe.response(eventResult); |
| | | sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Timeout timeout = timeoutEvent.getTimeout(); |
| | | // ServerTransaction serverTransaction = timeoutEvent.getServerTransaction(); |
| | | // if (serverTransaction != null) { |
| | | // Request request = serverTransaction.getRequest(); |
| | | // URI requestURI = request.getRequestURI(); |
| | | // Header header = request.getHeader(FromHeader.NAME); |
| | | // } |
| | | // if(timeoutProcessor != null) { |
| | | // timeoutProcessor.process(timeoutEvent); |
| | | // } |
| | | } |
| | | |
| | | @Override |
| | | public void processIOException(IOExceptionEvent exceptionEvent) { |
| | |
| | | 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.SIPRequestHeaderProvider;
|
| | | import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
|
| | | import com.genersoft.iot.vmp.utils.DateUtil;
|
| | | import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
|
| | | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
| | |
| | | import org.slf4j.LoggerFactory;
|
| | | import org.springframework.beans.factory.annotation.Autowired;
|
| | | import org.springframework.beans.factory.annotation.Qualifier;
|
| | | import org.springframework.boot.SpringBootVersion;
|
| | | import org.springframework.context.annotation.DependsOn;
|
| | | import org.springframework.stereotype.Component;
|
| | | import org.springframework.util.StringUtils;
|
| | |
| | | //
|
| | | StringBuffer content = new StringBuffer(200);
|
| | | content.append("v=0\r\n");
|
| | | content.append("o="+ sipConfig.getId()+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
| | | content.append("o="+ channelId+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
| | | content.append("s=Play\r\n");
|
| | | content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
|
| | | content.append("t=0 0\r\n");
|
| | |
| | | content.append("a=rtpmap:126 H264/90000\r\n");
|
| | | content.append("a=rtpmap:125 H264S/90000\r\n");
|
| | | content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
|
| | | content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
|
| | | content.append("a=fmtp:99 profile-level-id=3\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | |
| | | }
|
| | | }else {
|
| | | if("TCP-PASSIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if ("TCP-ACTIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if("UDP".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
|
| | | }
|
| | | content.append("a=recvonly\r\n");
|
| | | content.append("a=rtpmap:96 PS/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
|
| | | content.append("a=setup:passive\r\n");
|
| | | content.append("a=connection:new\r\n");
|
| | |
| | |
|
| | | StringBuffer content = new StringBuffer(200);
|
| | | content.append("v=0\r\n");
|
| | | content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("s=Playback\r\n");
|
| | | content.append("u="+channelId+":0\r\n");
|
| | | content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
|
| | |
| | | content.append("a=rtpmap:126 H264/90000\r\n");
|
| | | content.append("a=rtpmap:125 H264S/90000\r\n");
|
| | | content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
|
| | | content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
|
| | | content.append("a=fmtp:99 profile-level-id=3\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | |
| | | }
|
| | | }else {
|
| | | if("TCP-PASSIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if ("TCP-ACTIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if("UDP".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
|
| | | }
|
| | | content.append("a=recvonly\r\n");
|
| | | content.append("a=rtpmap:96 PS/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | | content.append("a=setup:passive\r\n");
|
| | | content.append("a=connection:new\r\n");
|
| | |
| | |
|
| | | StringBuffer content = new StringBuffer(200);
|
| | | content.append("v=0\r\n");
|
| | | content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
|
| | | content.append("s=Download\r\n");
|
| | | content.append("u="+channelId+":0\r\n");
|
| | | content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
|
| | |
| | | }
|
| | | }else {
|
| | | if("TCP-PASSIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if ("TCP-ACTIVE".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
|
| | | }else if("UDP".equals(streamMode)) {
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
|
| | | content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
|
| | | }
|
| | | content.append("a=recvonly\r\n");
|
| | | content.append("a=rtpmap:96 PS/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:97 MPEG4/90000\r\n");
|
| | | content.append("a=rtpmap:98 H264/90000\r\n");
|
| | | content.append("a=rtpmap:99 H265/90000\r\n");
|
| | | if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
|
| | | content.append("a=setup:passive\r\n");
|
| | | content.append("a=connection:new\r\n");
|
| | |
| | | (MediaServerItem mediaServerItemInUse, JSONObject json)->{
|
| | | hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
|
| | | subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
|
| | | subscribeKey.put("regist", false);
|
| | | subscribeKey.put("schema", "rtmp");
|
| | | // 添加流注销的订阅,注销了后向设备发送bye
|
| | | subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
|
| | | (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd)->{
|
| | | ClientTransaction transaction = streamSession.getTransaction(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
|
| | | if (transaction != null) {
|
| | | logger.info("[录像]下载结束, 发送BYE");
|
| | | streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
|
| | | }
|
| | | });
|
| | | });
|
| | |
|
| | | Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
|
| | |
| | | @Override
|
| | | public void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent) {
|
| | | try {
|
| | | SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, null, stream);
|
| | | ClientTransaction transaction = streamSession.getTransactionByStream(deviceId, channelId, stream);
|
| | | SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, callId, stream);
|
| | | ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId, stream, callId);
|
| | |
|
| | | if (transaction == null) {
|
| | | logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
|
| | |
| | | sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
|
| | | errorEvent.response(eventResult);
|
| | | sipSubscribe.removeErrorSubscribe(eventResult.callId);
|
| | | sipSubscribe.removeOkSubscribe(eventResult.callId);
|
| | | }));
|
| | | }
|
| | | // 添加订阅
|
| | |
| | | sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult ->{
|
| | | okEvent.response(eventResult);
|
| | | sipSubscribe.removeOkSubscribe(eventResult.callId);
|
| | | sipSubscribe.removeErrorSubscribe(eventResult.callId);
|
| | | });
|
| | | }
|
| | |
|
| | |
| | | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider; |
| | | import com.genersoft.iot.vmp.gb28181.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.genersoft.iot.vmp.common.StreamInfo; |
| | | import com.genersoft.iot.vmp.conf.DynamicTask; |
| | | 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.SIPProcessorObserver; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| | |
| | | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import org.ehcache.shadow.org.terracotta.offheapstore.storage.IntegerStorageEngine; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.InitializingBean; |
| | |
| | | private IRedisCatchStorage redisCatchStorage; |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorage storager; |
| | | |
| | | @Autowired |
| | | private ZLMRTPServerFactory zlmrtpServerFactory; |
| | | |
| | | @Autowired |
| | |
| | | |
| | | @Autowired |
| | | private DynamicTask dynamicTask; |
| | | |
| | | @Autowired |
| | | private ISIPCommander cmder; |
| | | |
| | | @Autowired |
| | | private ISIPCommanderForPlatform commanderForPlatform; |
| | | |
| | | |
| | | /** |
| | |
| | | if (dialog.getState()== DialogState.CONFIRMED) { |
| | | String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); |
| | | logger.info("ACK请求: platformGbId->{}", platformGbId); |
| | | ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformGbId); |
| | | // 取消设置的超时任务 |
| | | dynamicTask.stop(callIdHeader.getCallId()); |
| | | String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); |
| | |
| | | param.put("dst_port", sendRtpItem.getPort()); |
| | | param.put("is_udp", is_Udp); |
| | | param.put("src_port", sendRtpItem.getLocalPort()); |
| | | zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); |
| | | |
| | | param.put("pt", sendRtpItem.getPt()); |
| | | param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); |
| | | param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); |
| | | JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); |
| | | if (jsonObject == null) { |
| | | logger.error("RTP推流失败: 请检查ZLM服务"); |
| | | } else if (jsonObject.getInteger("code") == 0) { |
| | | logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); |
| | | } else { |
| | | logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"),JSONObject.toJSON(param)); |
| | | if (sendRtpItem.isOnlyAudio()) { |
| | | // TODO 可能是语音对讲 |
| | | }else { |
| | | // 向上级平台 |
| | | commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); |
| | | } |
| | | } |
| | | |
| | | |
| | | // if (streamInfo == null) { // 流还没上来,对方就回复ack |
| | |
| | | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| | | 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.SerializeUtils; |
| | | import gov.nist.javax.sdp.TimeDescriptionImpl; |
| | | import gov.nist.javax.sdp.fields.TimeField; |
| | |
| | | import javax.sip.message.Request; |
| | | import javax.sip.message.Response; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.time.Instant; |
| | | import java.util.Vector; |
| | | |
| | | /** |
| | |
| | | |
| | | Long startTime = null; |
| | | Long stopTime = null; |
| | | Date start = null; |
| | | Date end = null; |
| | | Instant start = null; |
| | | Instant end = null; |
| | | if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { |
| | | TimeDescriptionImpl timeDescription = (TimeDescriptionImpl)(sdp.getTimeDescriptions(false).get(0)); |
| | | TimeField startTimeFiled = (TimeField)timeDescription.getTime(); |
| | | startTime = startTimeFiled.getStartTime(); |
| | | stopTime = startTimeFiled.getStopTime(); |
| | | |
| | | start = new Date(startTime*1000); |
| | | end = new Date(stopTime*1000); |
| | | start = Instant.ofEpochMilli(startTime*1000); |
| | | end = Instant.ofEpochMilli(stopTime*1000); |
| | | } |
| | | // 获取支持的格式 |
| | | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| | |
| | | sendRtpItem.setApp("rtp"); |
| | | if ("Playback".equals(sessionName)) { |
| | | sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true, true); |
| | | sendRtpItem.setStreamId(ssrcInfo.getStream()); |
| | | // 写入redis, 超时时回复 |
| | | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| | | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, format.format(start), |
| | | format.format(end), null, result -> { |
| | | playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), |
| | | DateUtil.formatter.format(end), null, result -> { |
| | | if (result.getCode() != 0){ |
| | | logger.warn("录像回放失败"); |
| | | if (result.getEvent() != null) { |
| | |
| | | if (mediaServerItem.isRtpEnable()) { |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | } |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, true, false); |
| | | sendRtpItem.setStreamId(ssrcInfo.getStream()); |
| | | // 写入redis, 超时时回复 |
| | | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| | |
| | | FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); |
| | | String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); |
| | | |
| | | Element rootElement = getRootElement(evt); |
| | | Device device = redisCatchStorage.getDevice(deviceId); |
| | | if (device == null) { |
| | | if (device == null || device.getOnline() == 0) { |
| | | logger.warn("[收到 目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); |
| | | return; |
| | | } |
| | | if (device != null ) { |
| | | rootElement = getRootElement(evt, device.getCharset()); |
| | | } |
| | | Element rootElement = getRootElement(evt, device.getCharset()); |
| | | Element deviceListElement = rootElement.element("DeviceList"); |
| | | if (deviceListElement == null) { |
| | | return; |
| | |
| | | channel.setDeviceId(device.getDeviceId()); |
| | | logger.info("[收到 目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); |
| | | switch (eventElement.getText().toUpperCase()) { |
| | | case CatalogEvent.ON: // 上线 |
| | | case CatalogEvent.ON: |
| | | // 上线 |
| | | logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId()); |
| | | storager.deviceChannelOnline(deviceId, channel.getChannelId()); |
| | | // 回复200 OK |
| | | responseAck(evt, Response.OK); |
| | | break; |
| | | case CatalogEvent.OFF : // 离线 |
| | | case CatalogEvent.OFF : |
| | | // 离线 |
| | | logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId()); |
| | | storager.deviceChannelOffline(deviceId, channel.getChannelId()); |
| | | // 回复200 OK |
| | | responseAck(evt, Response.OK); |
| | | break; |
| | | case CatalogEvent.VLOST: // 视频丢失 |
| | | case CatalogEvent.VLOST: |
| | | // 视频丢失 |
| | | logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId()); |
| | | storager.deviceChannelOffline(deviceId, channel.getChannelId()); |
| | | // 回复200 OK |
| | | responseAck(evt, Response.OK); |
| | | break; |
| | | case CatalogEvent.DEFECT: // 故障 |
| | | case CatalogEvent.DEFECT: |
| | | // 故障 |
| | | // 回复200 OK |
| | | responseAck(evt, Response.OK); |
| | | break; |
| | | case CatalogEvent.ADD: // 增加 |
| | | case CatalogEvent.ADD: |
| | | // 增加 |
| | | logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId()); |
| | | storager.updateChannel(deviceId, channel); |
| | | responseAck(evt, Response.OK); |
| | | break; |
| | | case CatalogEvent.DEL: // 删除 |
| | | case CatalogEvent.DEL: |
| | | // 删除 |
| | | logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId()); |
| | | storager.delChannel(deviceId, channel.getChannelId()); |
| | | responseAck(evt, Response.OK); |
| | | break; |
| | | case CatalogEvent.UPDATE: // 更新 |
| | | case CatalogEvent.UPDATE: |
| | | // 更新 |
| | | logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId()); |
| | | storager.updateChannel(deviceId, channel); |
| | | responseAck(evt, Response.OK); |
| | |
| | | // 转发变化信息 |
| | | eventPublisher.catalogEventPublish(null, channel, eventElement.getText().toUpperCase()); |
| | | |
| | | } |
| | | |
| | | if (!redisCatchStorage.deviceIsOnline(deviceId)) { |
| | | publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE); |
| | | } |
| | | } |
| | | } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| | | 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.service.IDeviceService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import gov.nist.javax.sip.RequestEventExt; |
| | | import gov.nist.javax.sip.address.AddressImpl; |
| | | import gov.nist.javax.sip.address.SipUri; |
| | |
| | | @Autowired |
| | | private SIPProcessorObserver sipProcessorObserver; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public void afterPropertiesSet() throws Exception { |
| | | // 添加消息处理的订阅 |
| | |
| | | try { |
| | | RequestEventExt evtExt = (RequestEventExt) evt; |
| | | String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort(); |
| | | logger.info("[{}] 收到注册请求,开始处理", requestAddress); |
| | | logger.info("[注册请求] 开始处理: {}", requestAddress); |
| | | Request request = evt.getRequest(); |
| | | ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME); |
| | | Response response = null; |
| | | boolean passwordCorrect = false; |
| | | // 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功 |
| | | int registerFlag = 0; |
| | | boolean registerFlag = false; |
| | | FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); |
| | | AddressImpl address = (AddressImpl) fromHeader.getAddress(); |
| | | SipUri uri = (SipUri) address.getURI(); |
| | |
| | | |
| | | AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); |
| | | if (authHead == null) { |
| | | logger.info("[{}] 未携带授权头 回复401", requestAddress); |
| | | logger.info("[注册请求] 未携带授权头 回复401: {}", requestAddress); |
| | | response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); |
| | | new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); |
| | | sendResponse(evt, response); |
| | |
| | | // 注册失败 |
| | | response = getMessageFactory().createResponse(Response.FORBIDDEN, request); |
| | | response.setReasonPhrase("wrong password"); |
| | | logger.info("[{}] 密码/SIP服务器ID错误, 回复403", requestAddress); |
| | | logger.info("[注册请求] 密码/SIP服务器ID错误, 回复403: {}", requestAddress); |
| | | sendResponse(evt, response); |
| | | return; |
| | | } |
| | | |
| | | Device deviceInRedis = redisCatchStorage.getDevice(deviceId); |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | if (deviceInRedis != null && device == null) { |
| | | // redis 存在脏数据 |
| | | redisCatchStorage.clearCatchByDeviceId(deviceId); |
| | | } |
| | | Device device = deviceService.queryDevice(deviceId); |
| | | |
| | | // 携带授权头并且密码正确 |
| | | response = getMessageFactory().createResponse(Response.OK, request); |
| | | // 添加date头 |
| | |
| | | device.setStreamMode("UDP"); |
| | | device.setCharset("GB2312"); |
| | | device.setDeviceId(deviceId); |
| | | device.setFirsRegister(true); |
| | | } else { |
| | | device.setFirsRegister(device.getOnline() == 0); |
| | | } |
| | | device.setIp(received); |
| | | device.setPort(rPort); |
| | | device.setHostAddress(received.concat(":").concat(String.valueOf(rPort))); |
| | | if (expiresHeader.getExpires() == 0) { |
| | | // 注销成功 |
| | | registerFlag = 2; |
| | | registerFlag = false; |
| | | } else { |
| | | // 注册成功 |
| | | device.setExpires(expiresHeader.getExpires()); |
| | | registerFlag = 1; |
| | | registerFlag = true; |
| | | // 判断TCP还是UDP |
| | | ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); |
| | | String transport = reqViaHeader.getTransport(); |
| | |
| | | sendResponse(evt, response); |
| | | // 注册成功 |
| | | // 保存到redis |
| | | if (registerFlag == 1) { |
| | | logger.info("[{}] 注册成功! deviceId:" + deviceId, requestAddress); |
| | | publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_REGISTER, expiresHeader.getExpires()); |
| | | } else if (registerFlag == 2) { |
| | | logger.info("[{}] 注销成功! deviceId:" + deviceId, requestAddress); |
| | | publisher.outlineEventPublish(deviceId, VideoManagerConstants.EVENT_OUTLINE_UNREGISTER); |
| | | if (registerFlag) { |
| | | logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); |
| | | device.setRegisterTime(DateUtil.getNow()); |
| | | deviceService.online(device); |
| | | } else { |
| | | logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); |
| | | deviceService.offline(deviceId); |
| | | } |
| | | } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) { |
| | | e.printStackTrace(); |
| | |
| | | private void sendResponse(RequestEvent evt, Response response) throws InvalidArgumentException, SipException { |
| | | ServerTransaction serverTransaction = getServerTransaction(evt); |
| | | if (serverTransaction == null) { |
| | | logger.warn("回复失败:{}", response); |
| | | logger.warn("[回复失败]:{}", response); |
| | | return; |
| | | } |
| | | serverTransaction.sendResponse(response); |
| | |
| | | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; |
| | | |
| | | import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import org.dom4j.Element; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | |
| | | public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { |
| | | |
| | | private Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class); |
| | | private final String cmdType = "Keepalive"; |
| | | private final static String cmdType = "Keepalive"; |
| | | |
| | | @Autowired |
| | | private NotifyMessageHandler notifyMessageHandler; |
| | | |
| | | @Autowired |
| | | private EventPublisher publisher; |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorage videoManagerStorager; |
| | | |
| | | @Autowired |
| | | private IRedisCatchStorage redisCatchStorage; |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public void afterPropertiesSet() throws Exception { |
| | |
| | | |
| | | @Override |
| | | public void handForDevice(RequestEvent evt, Device device, Element element) { |
| | | // 检查设备是否存在并在线, 不在线则设置为在线 |
| | | if (device == null) { |
| | | // 未注册的设备不做处理 |
| | | return; |
| | | } |
| | | try { |
| | | if (device != null ) { |
| | | if (device.getOnline() == 1) { |
| | | // 回复200 OK |
| | | responseAck(evt, Response.OK); |
| | | }else { |
| | | // 对于已经离线的设备判断他的注册是否已经过期 |
| | | if (!deviceService.expire(device)){ |
| | | device.setKeepaliveTime(DateUtil.getNow()); |
| | | // 判断RPort是否改变,改变则说明路由nat信息变化,修改设备信息 |
| | | // 获取到通信地址等信息 |
| | | ViaHeader viaHeader = (ViaHeader) evt.getRequest().getHeader(ViaHeader.NAME); |
| | |
| | | if (device.getPort() != rPort) { |
| | | device.setPort(rPort); |
| | | device.setHostAddress(received.concat(":").concat(String.valueOf(rPort))); |
| | | videoManagerStorager.updateDevice(device); |
| | | redisCatchStorage.updateDevice(device); |
| | | } |
| | | if (!redisCatchStorage.deviceIsOnline(device.getDeviceId())) { |
| | | publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); |
| | | device.setKeepaliveTime(DateUtil.getNow()); |
| | | deviceService.online(device); |
| | | // 回复200 OK |
| | | responseAck(evt, Response.OK); |
| | | } |
| | | } |
| | | } catch (SipException e) { |
| | |
| | | CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); |
| | | String NotifyType =getText(rootElement, "NotifyType"); |
| | | if (NotifyType.equals("121")){ |
| | | logger.info("媒体播放完毕,通知关流"); |
| | | logger.info("[录像流]推送完毕,收到关流通知"); |
| | | String channelId =getText(rootElement, "DeviceID"); |
| | | // redisCatchStorage.stopPlayback(device.getDeviceId(), channelId, null, callIdHeader.getCallId()); |
| | | // redisCatchStorage.stopDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId()); |
| | | // 查询是设备 |
| | | StreamInfo streamInfo = redisCatchStorage.queryDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId()); |
| | | // 设置进度100% |
| | | streamInfo.setProgress(1); |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; |
| | | import org.dom4j.Element; |
| | |
| | | import javax.sip.SipException; |
| | | import javax.sip.message.Response; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | |
| | | |
| | | private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class); |
| | | private final String cmdType = "Catalog"; |
| | | |
| | | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @Autowired |
| | | private ResponseMessageHandler responseMessageHandler; |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import org.dom4j.DocumentException; |
| | |
| | | |
| | | import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { |
| | | |
| | |
| | | @Autowired |
| | | private EventPublisher publisher; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Override |
| | | public void afterPropertiesSet() throws Exception { |
| | | responseMessageHandler.addHandler(cmdType, this); |
| | |
| | | @Override |
| | | public void handForDevice(RequestEvent evt, Device device, Element rootElement) { |
| | | logger.debug("接收到DeviceInfo应答消息"); |
| | | // 检查设备是否存在, 不存在则不回复 |
| | | if (device == null || device.getOnline() == 0) { |
| | | logger.warn("[接收到DeviceInfo应答消息,但是设备已经离线]:" + (device != null ? device.getDeviceId():"" )); |
| | | return; |
| | | } |
| | | try { |
| | | rootElement = getRootElement(evt, device.getCharset()); |
| | | Element deviceIdElement = rootElement.element("DeviceID"); |
| | |
| | | if (StringUtils.isEmpty(device.getStreamMode())) { |
| | | device.setStreamMode("UDP"); |
| | | } |
| | | storager.updateDevice(device); |
| | | deviceService.updateDevice(device); |
| | | // storager.updateDevice(device); |
| | | |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(key); |
| | |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | // 回复200 OK |
| | | responseAck(evt, Response.OK); |
| | | if (redisCatchStorage.deviceIsOnline(device.getDeviceId())) { |
| | | publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE); |
| | | } |
| | | } catch (DocumentException e) { |
| | | e.printStackTrace(); |
| | | } catch (InvalidArgumentException e) { |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import org.dom4j.Element; |
| | | import org.slf4j.Logger; |
| | |
| | | import javax.sip.SipException; |
| | | import javax.sip.message.Response; |
| | | import java.text.ParseException; |
| | | import java.util.Objects; |
| | | |
| | | @Component |
| | | public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { |
| | |
| | | @Autowired |
| | | private ResponseMessageHandler responseMessageHandler; |
| | | |
| | | |
| | | @Autowired |
| | | private DeferredResultHolder deferredResultHolder; |
| | | |
| | | @Autowired |
| | | private EventPublisher publisher; |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private IRedisCatchStorage redisCatchStorage; |
| | |
| | | public void handForDevice(RequestEvent evt, Device device, Element element) { |
| | | logger.info("接收到DeviceStatus应答消息"); |
| | | // 检查设备是否存在, 不存在则不回复 |
| | | if (device == null) { |
| | | return; |
| | | } |
| | | // 回复200 OK |
| | | try { |
| | | responseAck(evt, Response.OK); |
| | |
| | | e.printStackTrace(); |
| | | } |
| | | Element deviceIdElement = element.element("DeviceID"); |
| | | Element onlineElement = element.element("Online"); |
| | | String channelId = deviceIdElement.getText(); |
| | | JSONObject json = new JSONObject(); |
| | | XmlUtil.node2Json(element, json); |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug(json.toJSONString()); |
| | | } |
| | | String text = onlineElement.getText(); |
| | | if (Objects.equals(text.trim().toUpperCase(), "ONLINE")) { |
| | | deviceService.online(device); |
| | | }else { |
| | | deviceService.offline(device.getDeviceId()); |
| | | } |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId() + channelId); |
| | | msg.setData(json); |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | |
| | | if (redisCatchStorage.deviceIsOnline(device.getDeviceId())) { |
| | | publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; |
| | | |
| | | import com.genersoft.iot.vmp.conf.SipConfig; |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.domain.req.PresetQuerySipReq; |
| | | import com.genersoft.iot.vmp.gb28181.bean.*; |
| | | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| | | import com.genersoft.iot.vmp.gb28181.session.CatalogDataCatch; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; |
| | | import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; |
| | | import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import org.dom4j.DocumentException; |
| | | import org.dom4j.Element; |
| | | import org.slf4j.Logger; |
| | |
| | | import org.springframework.beans.factory.InitializingBean; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.sip.InvalidArgumentException; |
| | | import javax.sip.RequestEvent; |
| | | import javax.sip.SipException; |
| | | import javax.sip.message.Response; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | |
| | | import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; |
| | | |
| | |
| | | |
| | | private Logger logger = LoggerFactory.getLogger(PresetQueryResponseMessageHandler.class); |
| | | private final String cmdType = "PresetQuery"; |
| | | |
| | | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @Autowired |
| | | private ResponseMessageHandler responseMessageHandler; |
| | |
| | | import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; |
| | | import com.genersoft.iot.vmp.gb28181.bean.RecordItem; |
| | | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread; |
| | | import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; |
| | | import com.genersoft.iot.vmp.gb28181.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import org.dom4j.DocumentException; |
| | | import org.dom4j.Element; |
| | | import org.slf4j.Logger; |
| | |
| | | import org.springframework.beans.factory.InitializingBean; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.sip.InvalidArgumentException; |
| | | import javax.sip.RequestEvent; |
| | | import javax.sip.SipException; |
| | | import javax.sip.message.Response; |
| | | import java.text.ParseException; |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | import java.util.*; |
| | | |
| | | import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; |
| | | |
| | | /** |
| | | * @author lin |
| | | */ |
| | | @Component |
| | | public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { |
| | | |
| | |
| | | private ResponseMessageHandler responseMessageHandler; |
| | | |
| | | @Autowired |
| | | private RedisUtil redis; |
| | | private RecordDataCatch recordDataCatch; |
| | | |
| | | @Autowired |
| | | private DeferredResultHolder deferredResultHolder; |
| | | |
| | | |
| | | |
| | | @Autowired |
| | | private EventPublisher eventPublisher; |
| | |
| | | responseAck(evt, Response.OK); |
| | | |
| | | rootElement = getRootElement(evt, device.getCharset()); |
| | | String uuid = UUID.randomUUID().toString().replace("-", ""); |
| | | RecordInfo recordInfo = new RecordInfo(); |
| | | String sn = getText(rootElement, "SN"); |
| | | String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + sn; |
| | | recordInfo.setDeviceId(device.getDeviceId()); |
| | | recordInfo.setSn(sn); |
| | | recordInfo.setName(getText(rootElement, "Name")); |
| | | if (getText(rootElement, "SumNum") == null || getText(rootElement, "SumNum") == "") { |
| | | recordInfo.setSumNum(0); |
| | | } else { |
| | | recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum"))); |
| | | |
| | | String sumNumStr = getText(rootElement, "SumNum"); |
| | | int sumNum = 0; |
| | | if (!StringUtils.isEmpty(sumNumStr)) { |
| | | sumNum = Integer.parseInt(sumNumStr); |
| | | } |
| | | Element recordListElement = rootElement.element("RecordList"); |
| | | if (recordListElement == null || recordInfo.getSumNum() == 0) { |
| | | if (recordListElement == null || sumNum == 0) { |
| | | logger.info("无录像数据"); |
| | | eventPublisher.recordEndEventPush(recordInfo); |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(key); |
| | | msg.setData(recordInfo); |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | recordDataCatch.put(device.getDeviceId(), sn, sumNum, new ArrayList<>()); |
| | | releaseRequest(device.getDeviceId(), sn); |
| | | } else { |
| | | Iterator<Element> recordListIterator = recordListElement.elementIterator(); |
| | | List<RecordItem> recordList = new ArrayList<RecordItem>(); |
| | | if (recordListIterator != null) { |
| | | RecordItem record = new RecordItem(); |
| | | logger.info("处理录像列表数据..."); |
| | | List<RecordItem> recordList = new ArrayList<>(); |
| | | // 遍历DeviceList |
| | | while (recordListIterator.hasNext()) { |
| | | Element itemRecord = recordListIterator.next(); |
| | |
| | | logger.info("记录为空,下一个..."); |
| | | continue; |
| | | } |
| | | record = new RecordItem(); |
| | | RecordItem record = new RecordItem(); |
| | | record.setDeviceId(getText(itemRecord, "DeviceID")); |
| | | record.setName(getText(itemRecord, "Name")); |
| | | record.setFilePath(getText(itemRecord, "FilePath")); |
| | | record.setFileSize(getText(itemRecord, "FileSize")); |
| | | record.setAddress(getText(itemRecord, "Address")); |
| | | record.setStartTime( |
| | | DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "StartTime"))); |
| | | record.setEndTime( |
| | | DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "EndTime"))); |
| | | |
| | | String startTimeStr = getText(itemRecord, "StartTime"); |
| | | record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr)); |
| | | |
| | | String endTimeStr = getText(itemRecord, "EndTime"); |
| | | record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr)); |
| | | |
| | | record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 |
| | | : Integer.parseInt(getText(itemRecord, "Secrecy"))); |
| | | record.setType(getText(itemRecord, "Type")); |
| | | record.setRecorderId(getText(itemRecord, "RecorderID")); |
| | | recordList.add(record); |
| | | } |
| | | recordInfo.setRecordList(recordList); |
| | | int count = recordDataCatch.put(device.getDeviceId(), sn, sumNum, recordList); |
| | | logger.info("[国标录像], {}->{}: {}/{}", device.getDeviceId(), sn, count, sumNum); |
| | | } |
| | | eventPublisher.recordEndEventPush(recordInfo); |
| | | // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题 |
| | | String cacheKey = CACHE_RECORDINFO_KEY + device.getDeviceId() + sn; |
| | | redis.set(cacheKey + "_" + uuid, recordList, 90); |
| | | if (!threadNameList.contains(cacheKey)) { |
| | | threadNameList.add(cacheKey); |
| | | CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo); |
| | | chk.setName(cacheKey); |
| | | chk.setDeferredResultHolder(deferredResultHolder); |
| | | chk.setRedis(redis); |
| | | chk.setLogger(logger); |
| | | chk.start(); |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug("Start Thread " + cacheKey + "."); |
| | | } |
| | | } else { |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug("Thread " + cacheKey + " already started."); |
| | | } |
| | | |
| | | if (recordDataCatch.isComplete(device.getDeviceId(), sn)){ |
| | | releaseRequest(device.getDeviceId(), sn); |
| | | } |
| | | } |
| | | } catch (SipException e) { |
| | |
| | | public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { |
| | | |
| | | } |
| | | |
| | | public void releaseRequest(String deviceId, String sn){ |
| | | String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(0); |
| | | wvpResult.setMsg("success"); |
| | | // 对数据进行排序 |
| | | Collections.sort(recordDataCatch.getRecordInfo(deviceId, sn).getRecordList()); |
| | | wvpResult.setData(recordDataCatch.getRecordInfo(deviceId, sn)); |
| | | |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setKey(key); |
| | | msg.setData(wvpResult); |
| | | deferredResultHolder.invokeAllResult(msg); |
| | | recordDataCatch.remove(deviceId, sn); |
| | | } |
| | | } |
| | |
| | | * 调用zlm RESTFUL API —— startSendRtp |
| | | */ |
| | | public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) { |
| | | Boolean result = false; |
| | | JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServerItem, param); |
| | | if (jsonObject == null) { |
| | | logger.error("RTP推流失败: 请检查ZLM服务"); |
| | | } else if (jsonObject.getInteger("code") == 0) { |
| | | result= true; |
| | | logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); |
| | | } else { |
| | | logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"),JSONObject.toJSON(param)); |
| | | } |
| | | return jsonObject; |
| | | return zlmresTfulUtils.startSendRtp(mediaServerItem, param); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Autowired |
| | | private DynamicTask dynamicTask; |
| | | |
| | | @Qualifier("taskExecutor") |
| | | @Autowired |
| | | private ThreadPoolTaskExecutor taskExecutor; |
| | | |
| | | @Override |
| | | public void run(String... strings) throws Exception { |
| | |
| | | }); |
| | | |
| | | // 获取zlm信息 |
| | | logger.info("[zlm接入]等待默认zlm中..."); |
| | | logger.info("[zlm] 等待默认zlm中..."); |
| | | |
| | | // 获取所有的zlm, 并开启主动连接 |
| | | List<MediaServerItem> all = mediaServerService.getAllFromDatabase(); |
| | |
| | | startGetMedia = new HashMap<>(); |
| | | } |
| | | startGetMedia.put(mediaServerItem.getId(), true); |
| | | taskExecutor.execute(()->{ |
| | | connectZlmServer(mediaServerItem); |
| | | }); |
| | | } |
| | | String taskKey = "zlm-connect-timeout"; |
| | | dynamicTask.startDelay(taskKey, ()->{ |
| | |
| | | startGetMedia = null; |
| | | } |
| | | // TODO 清理数据库中与redis不匹配的zlm |
| | | }, 6 * 1000 ); |
| | | }, 60 * 1000 ); |
| | | } |
| | | |
| | | @Async |
| | | public void connectZlmServer(MediaServerItem mediaServerItem){ |
| | | ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem, 1); |
| | | String connectZlmServerTaskKey = "connect-zlm-" + mediaServerItem.getId(); |
| | | ZLMServerConfig zlmServerConfigFirst = getMediaServerConfig(mediaServerItem); |
| | | if (zlmServerConfigFirst != null) { |
| | | zlmServerConfigFirst.setIp(mediaServerItem.getIp()); |
| | | zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort()); |
| | | startGetMedia.remove(mediaServerItem.getId()); |
| | | mediaServerService.zlmServerOnline(zlmServerConfigFirst); |
| | | }else { |
| | | logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | publisher.zlmOfflineEventPublish(mediaServerItem.getId()); |
| | | } |
| | | |
| | | dynamicTask.startCron(connectZlmServerTaskKey, ()->{ |
| | | ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem); |
| | | if (zlmServerConfig != null) { |
| | | dynamicTask.stop(connectZlmServerTaskKey); |
| | | zlmServerConfig.setIp(mediaServerItem.getIp()); |
| | | zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort()); |
| | | startGetMedia.remove(mediaServerItem.getId()); |
| | | mediaServerService.zlmServerOnline(zlmServerConfig); |
| | | } |
| | | }, 2000); |
| | | } |
| | | |
| | | public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem, int index) { |
| | | public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) { |
| | | if (startGetMedia == null) { return null;} |
| | | if (!mediaServerItem.isDefaultServer() && mediaServerService.getOne(mediaServerItem.getId()) == null) { |
| | | return null; |
| | |
| | | zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); |
| | | } |
| | | } else { |
| | | logger.error("[ {} ]-[ {}:{} ]第{}次主动连接失败, 2s后重试", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort(), index); |
| | | if (index == 1 && !StringUtils.isEmpty(mediaServerItem.getId())) { |
| | | logger.info("[ {} ]-[ {}:{} ]第{}次主动连接失败, 开始清理相关资源", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort(), index); |
| | | publisher.zlmOfflineEventPublish(mediaServerItem.getId()); |
| | | } |
| | | try { |
| | | Thread.sleep(2000); |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | zlmServerConfig = getMediaServerConfig(mediaServerItem, index += 1); |
| | | logger.error("[ {} ]-[ {}:{} ]主动连接失败, 2s后重试", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | } |
| | | return zlmServerConfig; |
| | | |
| | | } |
| | | |
| | | /** |
| | | * zlm 连接成功或者zlm重启后 |
| | | */ |
| | | // private void zLmRunning(ZLMServerConfig zlmServerConfig){ |
| | | // logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功..."); |
| | | // // 关闭循环获取zlm配置 |
| | | // startGetMedia = false; |
| | | // MediaServerItem mediaServerItem = new MediaServerItem(zlmServerConfig, sipIp); |
| | | // storager.updateMediaServer(mediaServerItem); |
| | | // |
| | | // if (mediaServerItem.isAutoConfig()) setZLMConfig(mediaServerItem); |
| | | // zlmServerManger.updateServerCatchFromHook(zlmServerConfig); |
| | | // |
| | | // // 清空所有session |
| | | //// zlmMediaListManager.clearAllSessions(); |
| | | // |
| | | // // 更新流列表 |
| | | // zlmMediaListManager.updateMediaList(mediaServerItem); |
| | | // // 恢复流代理, 只查找这个这个流媒体 |
| | | // List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer( |
| | | // mediaServerItem.getId(), true); |
| | | // for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { |
| | | // logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); |
| | | // JSONObject jsonObject = streamProxyService.addStreamProxyToZlm(streamProxyDto); |
| | | // if (jsonObject == null) { |
| | | // // 设置为未启用 |
| | | // logger.info("恢复流代理失败,请检查流地址后重新启用" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); |
| | | // streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream()); |
| | | // } |
| | | // } |
| | | // } |
| | | } |
| | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.ApplicationListener; |
| | | import org.springframework.context.event.EventListener; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | |
| | | /** |
| | | * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源: |
| | |
| | | @Autowired |
| | | private IPlayService playService; |
| | | |
| | | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @Async |
| | | @EventListener |
| | | public void onApplicationEvent(ZLMOnlineEvent event) { |
| | | |
| | | logger.info("ZLM上线事件触发,ID:" + event.getMediaServerId()); |
| | | logger.info("[ZLM] 上线 ID:" + event.getMediaServerId()); |
| | | streamPushService.zlmServerOnline(event.getMediaServerId()); |
| | | streamProxyService.zlmServerOnline(event.getMediaServerId()); |
| | | |
| | |
| | | @EventListener |
| | | public void onApplicationEvent(ZLMOfflineEvent event) { |
| | | |
| | | logger.info("ZLM离线事件触发,ID:" + event.getMediaServerId()); |
| | | logger.info("[ZLM] 离线,ID:" + event.getMediaServerId()); |
| | | // 处理ZLM离线 |
| | | mediaServerService.zlmServerOffline(event.getMediaServerId()); |
| | | streamProxyService.zlmServerOffline(event.getMediaServerId()); |
| | |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 设备相关业务处理 |
| | | * @author lin |
| | | */ |
| | | public interface IDeviceService { |
| | | |
| | | /** |
| | | * 设备上线 |
| | | * @param device 设备信息 |
| | | */ |
| | | void online(Device device); |
| | | |
| | | /** |
| | | * 设备下线 |
| | | * @param deviceId 设备编号 |
| | | */ |
| | | void offline(String deviceId); |
| | | |
| | | /** |
| | | * 添加目录订阅 |
| | | * @param device 设备信息 |
| | | * @return |
| | | * @return 布尔 |
| | | */ |
| | | boolean addCatalogSubscribe(Device device); |
| | | |
| | | /** |
| | | * 移除目录订阅 |
| | | * @param device 设备信息 |
| | | * @return |
| | | * @return 布尔 |
| | | */ |
| | | boolean removeCatalogSubscribe(Device device); |
| | | |
| | | /** |
| | | * 添加移动位置订阅 |
| | | * @param device 设备信息 |
| | | * @return |
| | | * @return 布尔 |
| | | */ |
| | | boolean addMobilePositionSubscribe(Device device); |
| | | |
| | | /** |
| | | * 移除移动位置订阅 |
| | | * @param device 设备信息 |
| | | * @return |
| | | * @return 布尔 |
| | | */ |
| | | boolean removeMobilePositionSubscribe(Device device); |
| | | |
| | | /** |
| | | * 移除移动位置订阅 |
| | | * @param deviceId 设备ID |
| | | * @return |
| | | * @return 同步状态 |
| | | */ |
| | | SyncStatus getChannelSyncStatus(String deviceId); |
| | | |
| | | /** |
| | | * 查看是否仍在同步 |
| | | * @param deviceId 设备ID |
| | | * @return |
| | | * @return 布尔 |
| | | */ |
| | | Boolean isSyncRunning(String deviceId); |
| | | |
| | | /** |
| | | * 通道同步 |
| | | * @param device |
| | | * @param device 设备信息 |
| | | */ |
| | | void sync(Device device); |
| | | |
| | | /** |
| | | * 查询设备信息 |
| | | * @param deviceId 设备编号 |
| | | * @return 设备信息 |
| | | */ |
| | | Device queryDevice(String deviceId); |
| | | |
| | | /** |
| | | * 获取所有在线设备 |
| | | * @return 设备列表 |
| | | */ |
| | | List<Device> getAllOnlineDevice(); |
| | | |
| | | /** |
| | | * 判断是否注册已经失效 |
| | | * @param device 设备信息 |
| | | * @return 布尔 |
| | | */ |
| | | boolean expire(Device device); |
| | | |
| | | /** |
| | | * 检查设备状态 |
| | | * @param device 设备信息 |
| | | */ |
| | | void checkDeviceStatus(Device device); |
| | | |
| | | /** |
| | | * 根据IP和端口获取设备信息 |
| | | * @param host IP |
| | | * @param port 端口 |
| | | * @return 设备信息 |
| | | */ |
| | | Device getDeviceByHostAndPort(String host, int port); |
| | | |
| | | /** |
| | | * 更新设备 |
| | | * @param device 设备信息 |
| | | */ |
| | | void updateDevice(Device device); |
| | | } |
| | |
| | | |
| | | void updateVmServer(List<MediaServerItem> mediaServerItemList); |
| | | |
| | | SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck); |
| | | SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback); |
| | | |
| | | SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback); |
| | | |
| | |
| | | |
| | | import com.genersoft.iot.vmp.conf.DynamicTask; |
| | | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| | | import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; |
| | | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; |
| | | import com.genersoft.iot.vmp.service.IDeviceService; |
| | | import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; |
| | | import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; |
| | | import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.dao.DeviceMapper; |
| | | 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.beans.factory.annotation.Qualifier; |
| | | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.sip.DialogState; |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * 设备业务(目录订阅) |
| | |
| | | public class DeviceServiceImpl implements IDeviceService { |
| | | |
| | | private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); |
| | | |
| | | private final String registerExpireTaskKeyPrefix = "device-register-expire-"; |
| | | |
| | | @Autowired |
| | | private DynamicTask dynamicTask; |
| | |
| | | @Autowired |
| | | private IRedisCatchStorage redisCatchStorage; |
| | | |
| | | @Autowired |
| | | private DeviceMapper deviceMapper; |
| | | |
| | | @Autowired |
| | | private ISIPCommander commander; |
| | | |
| | | @Autowired |
| | | private VideoStreamSessionManager streamSession; |
| | | |
| | | @Autowired |
| | | private IMediaServerService mediaServerService; |
| | | |
| | | @Override |
| | | public void online(Device device) { |
| | | logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); |
| | | Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId()); |
| | | Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); |
| | | |
| | | String now = DateUtil.getNow(); |
| | | if (deviceInRedis != null && deviceInDb == null) { |
| | | // redis 存在脏数据 |
| | | redisCatchStorage.clearCatchByDeviceId(device.getDeviceId()); |
| | | |
| | | } |
| | | device.setUpdateTime(now); |
| | | device.setOnline(1); |
| | | |
| | | // 第一次上线 |
| | | if (device.getCreateTime() == null) { |
| | | device.setCreateTime(now); |
| | | logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId()); |
| | | commander.deviceInfoQuery(device); |
| | | sync(device); |
| | | deviceMapper.add(device); |
| | | }else { |
| | | deviceMapper.update(device); |
| | | } |
| | | redisCatchStorage.updateDevice(device); |
| | | // 上线添加订阅 |
| | | if (device.getSubscribeCycleForCatalog() > 0) { |
| | | // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 |
| | | addCatalogSubscribe(device); |
| | | } |
| | | if (device.getSubscribeCycleForMobilePosition() > 0) { |
| | | addMobilePositionSubscribe(device); |
| | | } |
| | | // 刷新过期任务 |
| | | String registerExpireTaskKey = registerExpireTaskKeyPrefix + device.getDeviceId(); |
| | | dynamicTask.stop(registerExpireTaskKey); |
| | | dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getExpires() * 1000); |
| | | } |
| | | |
| | | @Override |
| | | public void offline(String deviceId) { |
| | | Device device = deviceMapper.getDeviceByDeviceId(deviceId); |
| | | if (device == null) { |
| | | return; |
| | | } |
| | | String registerExpireTaskKey = registerExpireTaskKeyPrefix + deviceId; |
| | | dynamicTask.stop(registerExpireTaskKey); |
| | | device.setOnline(0); |
| | | redisCatchStorage.updateDevice(device); |
| | | deviceMapper.update(device); |
| | | // 离线释放所有ssrc |
| | | List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null); |
| | | if (ssrcTransactions != null && ssrcTransactions.size() > 0) { |
| | | for (SsrcTransaction ssrcTransaction : ssrcTransactions) { |
| | | mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); |
| | | mediaServerService.closeRTPServer(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); |
| | | streamSession.remove(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); |
| | | } |
| | | } |
| | | // 移除订阅 |
| | | removeCatalogSubscribe(device); |
| | | removeMobilePositionSubscribe(device); |
| | | } |
| | | |
| | | @Override |
| | | public boolean addCatalogSubscribe(Device device) { |
| | | if (device == null || device.getSubscribeCycleForCatalog() < 0) { |
| | |
| | | // 提前开始刷新订阅 |
| | | int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForCatalog(),30); |
| | | // 设置最小值为30 |
| | | dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, subscribeCycleForCatalog -1); |
| | | dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, (subscribeCycleForCatalog -1) * 1000); |
| | | return true; |
| | | } |
| | | |
| | |
| | | // 设置最小值为30 |
| | | int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30); |
| | | // 提前开始刷新订阅 |
| | | dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, subscribeCycleForCatalog -1 ); |
| | | dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog -1 ) * 1000); |
| | | return true; |
| | | } |
| | | |
| | |
| | | catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public Device queryDevice(String deviceId) { |
| | | return deviceMapper.getDeviceByDeviceId(deviceId); |
| | | } |
| | | |
| | | @Override |
| | | public List<Device> getAllOnlineDevice() { |
| | | return deviceMapper.getOnlineDevices(); |
| | | } |
| | | |
| | | @Override |
| | | public boolean expire(Device device) { |
| | | Instant registerTimeDate = Instant.from(DateUtil.formatter.parse(device.getRegisterTime())); |
| | | Instant expireInstant = registerTimeDate.plusMillis(TimeUnit.SECONDS.toMillis(device.getExpires())); |
| | | return expireInstant.isBefore(Instant.now()); |
| | | } |
| | | |
| | | @Override |
| | | public void checkDeviceStatus(Device device) { |
| | | if (device == null || device.getOnline() == 0) { |
| | | return; |
| | | } |
| | | sipCommander.deviceStatusQuery(device, null); |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public Device getDeviceByHostAndPort(String host, int port) { |
| | | return deviceMapper.getDeviceByHostAndPort(host, port); |
| | | } |
| | | |
| | | @Override |
| | | public void updateDevice(Device device) { |
| | | |
| | | Device deviceInStore = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); |
| | | if (deviceInStore == null) { |
| | | logger.warn("更新设备时未找到设备信息"); |
| | | return; |
| | | } |
| | | if (!StringUtils.isEmpty(device.getName())) { |
| | | deviceInStore.setName(device.getName()); |
| | | } |
| | | if (!StringUtils.isEmpty(device.getCharset())) { |
| | | deviceInStore.setCharset(device.getCharset()); |
| | | } |
| | | if (!StringUtils.isEmpty(device.getMediaServerId())) { |
| | | deviceInStore.setMediaServerId(device.getMediaServerId()); |
| | | } |
| | | |
| | | // 目录订阅相关的信息 |
| | | if (device.getSubscribeCycleForCatalog() > 0) { |
| | | if (deviceInStore.getSubscribeCycleForCatalog() == 0 || deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { |
| | | deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); |
| | | // 开启订阅 |
| | | addCatalogSubscribe(deviceInStore); |
| | | } |
| | | }else if (device.getSubscribeCycleForCatalog() == 0) { |
| | | if (deviceInStore.getSubscribeCycleForCatalog() != 0) { |
| | | deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); |
| | | // 取消订阅 |
| | | removeCatalogSubscribe(deviceInStore); |
| | | } |
| | | } |
| | | |
| | | // 移动位置订阅相关的信息 |
| | | if (device.getSubscribeCycleForMobilePosition() > 0) { |
| | | if (deviceInStore.getSubscribeCycleForMobilePosition() == 0 || deviceInStore.getSubscribeCycleForMobilePosition() != device.getSubscribeCycleForMobilePosition()) { |
| | | deviceInStore.setMobilePositionSubmissionInterval(device.getMobilePositionSubmissionInterval()); |
| | | deviceInStore.setSubscribeCycleForMobilePosition(device.getSubscribeCycleForMobilePosition()); |
| | | // 开启订阅 |
| | | addMobilePositionSubscribe(deviceInStore); |
| | | } |
| | | }else if (device.getSubscribeCycleForMobilePosition() == 0) { |
| | | if (deviceInStore.getSubscribeCycleForMobilePosition() != 0) { |
| | | // 取消订阅 |
| | | removeMobilePositionSubscribe(deviceInStore); |
| | | } |
| | | } |
| | | |
| | | String now = DateUtil.getNow(); |
| | | device.setUpdateTime(now); |
| | | device.setCharset(device.getCharset().toUpperCase()); |
| | | device.setUpdateTime(DateUtil.getNow()); |
| | | if (deviceMapper.update(device) > 0) { |
| | | redisCatchStorage.updateDevice(device); |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.redis.JedisUtil; |
| | | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | |
| | | @Autowired |
| | | JedisUtil jedisUtil; |
| | | |
| | | private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | /** |
| | | * 初始化 |
| | | */ |
| | | @Override |
| | | public void updateVmServer(List<MediaServerItem> mediaServerItemList) { |
| | | logger.info("[缓存初始化] Media Server "); |
| | | logger.info("[zlm] 缓存初始化 "); |
| | | for (MediaServerItem mediaServerItem : mediaServerItemList) { |
| | | if (StringUtils.isEmpty(mediaServerItem.getId())) { |
| | | continue; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck) { |
| | | return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,false); |
| | | public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) { |
| | | return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,isPlayback); |
| | | } |
| | | |
| | | @Override |
| | |
| | | result.sort((serverItem1, serverItem2)->{ |
| | | int sortResult = 0; |
| | | try { |
| | | sortResult = format.parse(serverItem1.getCreateTime()).compareTo(format.parse(serverItem2.getCreateTime())); |
| | | sortResult = DateUtil.format.parse(serverItem1.getCreateTime()).compareTo(DateUtil.format.parse(serverItem2.getCreateTime())); |
| | | } catch (ParseException e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | @Override |
| | | public WVPResult<String> add(MediaServerItem mediaServerItem) { |
| | | WVPResult<String> result = new WVPResult<>(); |
| | | mediaServerItem.setCreateTime(this.format.format(System.currentTimeMillis())); |
| | | mediaServerItem.setUpdateTime(this.format.format(System.currentTimeMillis())); |
| | | mediaServerItem.setCreateTime(DateUtil.getNow()); |
| | | mediaServerItem.setUpdateTime(DateUtil.getNow()); |
| | | mediaServerItem.setHookAliveInterval(120); |
| | | JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); |
| | | if (responseJSON != null) { |
| | |
| | | */ |
| | | @Override |
| | | public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]正在连接", |
| | | logger.info("[ZLM] 正在连接 : {} -> {}:{}", |
| | | zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); |
| | | |
| | | MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); |
| | |
| | | setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); |
| | | } |
| | | publisher.zlmOnlineEventPublish(serverItem.getId()); |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]连接成功", |
| | | logger.info("[ZLM] 连接成功 {} - {}:{} ", |
| | | zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); |
| | | } |
| | | |
| | |
| | | */ |
| | | @Override |
| | | public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]正在设置zlm", |
| | | logger.info("[ZLM] 正在设置 :{} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | String protocol = sslEnabled ? "https" : "http"; |
| | | String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); |
| | |
| | | |
| | | if (responseJSON != null && responseJSON.getInteger("code") == 0) { |
| | | if (restart) { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm成功, 开始重启以保证配置生效", |
| | | logger.info("[ZLM] 设置成功,开始重启以保证配置生效 {} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | zlmresTfulUtils.restartServer(mediaServerItem); |
| | | }else { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm成功", |
| | | logger.info("[ZLM] 设置成功 {} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | } |
| | | |
| | | |
| | | }else { |
| | | logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm失败", |
| | | logger.info("[ZLM] 设置zlm失败 {} -> {}:{}", |
| | | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| | | } |
| | | |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| | | import com.genersoft.iot.vmp.gb28181.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| | | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| | |
| | | if (mediaServerItem.isRtpEnable()) { |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | } |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck()); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); |
| | | play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response)->{ |
| | | if (hookEvent != null) { |
| | | hookEvent.response(mediaServerItem, response); |
| | |
| | | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| | | } |
| | | if (ssrcInfo == null) { |
| | | ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck()); |
| | | ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); |
| | | } |
| | | |
| | | // 超时处理 |
| | |
| | | return null; |
| | | } |
| | | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true, true); |
| | | |
| | | return playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, inviteStreamCallback, callback); |
| | | } |
| | |
| | | return null; |
| | | } |
| | | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true); |
| | | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true, true); |
| | | |
| | | return download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed,infoCallBack, hookCallBack); |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.gb28181.bean.*; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; |
| | | import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | 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.data.redis.connection.Message; |
| | | import org.springframework.data.redis.connection.MessageListener; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | |
| | | @Component |
| | | public class RedisAlarmMsgListener implements MessageListener { |
| | |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorage storage; |
| | | |
| | | private final SimpleDateFormat formatForGB = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
| | | |
| | | @Override |
| | | public void onMessage(Message message, byte[] bytes) { |
| | |
| | | deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription()); |
| | | deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn()); |
| | | deviceAlarm.setAlarmPriority("1"); |
| | | deviceAlarm.setAlarmTime(formatForGB.format(System.currentTimeMillis())); |
| | | deviceAlarm.setAlarmTime(DateUtil.getNow()); |
| | | deviceAlarm.setAlarmType("1"); |
| | | deviceAlarm.setLongitude(0); |
| | | deviceAlarm.setLatitude(0); |
| | |
| | | void clearCatchByDeviceId(String deviceId); |
| | | |
| | | /** |
| | | * 获取mediaServer节点 |
| | | * @param mediaServerId |
| | | * @return |
| | | */ |
| | | // MediaServerItem getMediaInfo(String mediaServerId); |
| | | |
| | | /** |
| | | * 设置所有设备离线 |
| | | */ |
| | | void outlineForAll(); |
| | | |
| | | /** |
| | | * 获取所有在线的 |
| | | */ |
| | | List<String> getOnlineForAll(); |
| | | |
| | | /** |
| | | * 在redis添加wvp的信息 |
| | | */ |
| | | void updateWVPInfo(JSONObject jsonObject, int time); |
| | |
| | | * @return true:存在 false:不存在 |
| | | */ |
| | | public boolean exists(String deviceId); |
| | | |
| | | /** |
| | | * 视频设备创建 |
| | | * |
| | | * @param device 设备对象 |
| | | * @return true:创建成功 false:创建失败 |
| | | */ |
| | | public boolean create(Device device); |
| | | |
| | | /** |
| | | * 视频设备更新 |
| | | * |
| | | * @param device 设备对象 |
| | | * @return true:创建成功 false:创建失败 |
| | | */ |
| | | public boolean updateDevice(Device device); |
| | | |
| | | /** |
| | | * 添加设备通道 |
| | |
| | | |
| | | @Update(value = {"UPDATE device_channel SET latitude=${latitude}, longitude=${longitude} WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) |
| | | void updatePotion(String deviceId, String channelId, double longitude, double latitude); |
| | | |
| | | @Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0") |
| | | List<DeviceChannel> getAllChannelInPlay(); |
| | | |
| | | } |
| | |
| | | |
| | | @Update("UPDATE device SET online=0") |
| | | int outlineForAll(); |
| | | |
| | | @Select("SELECT * FROM device WHERE online = 1") |
| | | List<Device> getOnlineDevices(); |
| | | @Select("SELECT * FROM device WHERE ip = #{host} AND port=${port}") |
| | | Device getDeviceByHostAndPort(String host, int port); |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.service.bean.ThirdPartyGB; |
| | | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| | | import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | |
| | | @SuppressWarnings("rawtypes") |
| | |
| | | |
| | | @Autowired |
| | | private UserSetting userSetting; |
| | | |
| | | private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @Override |
| | | public Long getCSEQ(String method) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void outlineForAll() { |
| | | List<Object> onlineDevices = redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetting.getServerId() + "_" + "*" ); |
| | | for (int i = 0; i < onlineDevices.size(); i++) { |
| | | String key = (String) onlineDevices.get(i); |
| | | redis.del(key); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<String> getOnlineForAll() { |
| | | List<String> result = new ArrayList<>(); |
| | | List<Object> onlineDevices = redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetting.getServerId() + "_" + "*" ); |
| | | for (int i = 0; i < onlineDevices.size(); i++) { |
| | | String key = (String) onlineDevices.get(i); |
| | | result.add((String) redis.get(key)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public void updateWVPInfo(JSONObject jsonObject, int time) { |
| | | String key = VideoManagerConstants.WVP_SERVER_PREFIX + userSetting.getServerId(); |
| | | redis.set(key, jsonObject, time); |
| | |
| | | public void addCpuInfo(double cpuInfo) { |
| | | String key = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetting.getServerId(); |
| | | SystemInfoDto<Double> systemInfoDto = new SystemInfoDto<>(); |
| | | systemInfoDto.setTime(format.format(System.currentTimeMillis())); |
| | | systemInfoDto.setTime(DateUtil.getNow()); |
| | | systemInfoDto.setData(cpuInfo); |
| | | redis.lSet(key, systemInfoDto); |
| | | // 每秒一个,最多只存30个 |
| | |
| | | public void addMemInfo(double memInfo) { |
| | | String key = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetting.getServerId(); |
| | | SystemInfoDto<Double> systemInfoDto = new SystemInfoDto<>(); |
| | | systemInfoDto.setTime(format.format(System.currentTimeMillis())); |
| | | systemInfoDto.setTime(DateUtil.getNow()); |
| | | systemInfoDto.setData(memInfo); |
| | | redis.lSet(key, systemInfoDto); |
| | | // 每秒一个,最多只存30个 |
| | |
| | | public void addNetInfo(Map<String, String> networkInterfaces) { |
| | | String key = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetting.getServerId(); |
| | | SystemInfoDto<Map<String, String>> systemInfoDto = new SystemInfoDto<>(); |
| | | systemInfoDto.setTime(format.format(System.currentTimeMillis())); |
| | | systemInfoDto.setTime(DateUtil.getNow()); |
| | | systemInfoDto.setData(networkInterfaces); |
| | | redis.lSet(key, systemInfoDto); |
| | | // 每秒一个,最多只存30个 |
| | |
| | | |
| | | @Override |
| | | public boolean deviceIsOnline(String deviceId) { |
| | | String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetting.getServerId() + "_" + deviceId; |
| | | return redis.hasKey(key); |
| | | return getDevice(deviceId).getOnline() == 1; |
| | | } |
| | | } |
| | |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.storager.dao.*; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; |
| | | import com.github.pagehelper.PageHelper; |
| | | import com.github.pagehelper.PageInfo; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * 视频设备数据存储-jdbc实现 |
| | |
| | | @Autowired |
| | | private ParentPlatformMapper parentPlatformMapper; |
| | | |
| | | private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | |
| | | /** |
| | | * 根据设备ID判断设备是否存在 |
| | | * |
| | |
| | | return deviceMapper.getDeviceByDeviceId(deviceId) != null; |
| | | } |
| | | |
| | | /** |
| | | * 视频设备创建 |
| | | * |
| | | * @param device 设备对象 |
| | | * @return true:创建成功 false:创建失败 |
| | | */ |
| | | @Override |
| | | public synchronized boolean create(Device device) { |
| | | redisCatchStorage.updateDevice(device); |
| | | return deviceMapper.add(device) > 0; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 视频设备更新 |
| | | * |
| | | * @param device 设备对象 |
| | | * @return true:更新成功 false:更新失败 |
| | | */ |
| | | @Override |
| | | public synchronized boolean updateDevice(Device device) { |
| | | String now = this.format.format(System.currentTimeMillis()); |
| | | device.setUpdateTime(now); |
| | | Device deviceByDeviceId = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); |
| | | device.setCharset(device.getCharset().toUpperCase()); |
| | | if (deviceByDeviceId == null) { |
| | | device.setCreateTime(now); |
| | | redisCatchStorage.updateDevice(device); |
| | | return deviceMapper.add(device) > 0; |
| | | }else { |
| | | redisCatchStorage.updateDevice(device); |
| | | |
| | | return deviceMapper.update(device) > 0; |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public synchronized void updateChannel(String deviceId, DeviceChannel channel) { |
| | | String channelId = channel.getChannelId(); |
| | |
| | | if (streamInfo != null) { |
| | | channel.setStreamId(streamInfo.getStream()); |
| | | } |
| | | String now = this.format.format(System.currentTimeMillis()); |
| | | String now = DateUtil.getNow(); |
| | | channel.setUpdateTime(now); |
| | | DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); |
| | | if (deviceChannel == null) { |
| | |
| | | if (streamInfo != null) { |
| | | channel.setStreamId(streamInfo.getStream()); |
| | | } |
| | | String now = this.format.format(System.currentTimeMillis()); |
| | | String now = DateUtil.getNow(); |
| | | channel.setUpdateTime(now); |
| | | channel.setCreateTime(now); |
| | | addChannels.add(channel); |
| | |
| | | if (streamInfo != null) { |
| | | channel.setStreamId(streamInfo.getStream()); |
| | | } |
| | | String now = this.format.format(System.currentTimeMillis()); |
| | | String now = DateUtil.getNow(); |
| | | channel.setUpdateTime(now); |
| | | if (channelsInStore.get(channel.getChannelId()) != null) { |
| | | updateChannels.add(channel); |
| | |
| | | if (deviceChannelList == null) { |
| | | return false; |
| | | } |
| | | List<DeviceChannel> allChannelInPlay = deviceChannelMapper.getAllChannelInPlay(); |
| | | Map<String,DeviceChannel> allChannelMapInPlay = new ConcurrentHashMap<>(); |
| | | if (allChannelInPlay.size() > 0) { |
| | | for (DeviceChannel deviceChannel : allChannelInPlay) { |
| | | allChannelMapInPlay.put(deviceChannel.getChannelId(), deviceChannel); |
| | | } |
| | | } |
| | | TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); |
| | | // 数据去重 |
| | | List<DeviceChannel> channels = new ArrayList<>(); |
| | | StringBuilder stringBuilder = new StringBuilder(); |
| | | Map<String, Integer> subContMap = new HashMap<>(); |
| | | if (deviceChannelList != null && deviceChannelList.size() > 1) { |
| | | if (deviceChannelList.size() > 1) { |
| | | // 数据去重 |
| | | Set<String> gbIdSet = new HashSet<>(); |
| | | for (DeviceChannel deviceChannel : deviceChannelList) { |
| | | if (!gbIdSet.contains(deviceChannel.getChannelId())) { |
| | | gbIdSet.add(deviceChannel.getChannelId()); |
| | | if (allChannelMapInPlay.containsKey(deviceChannel.getChannelId())) { |
| | | deviceChannel.setStreamId(allChannelMapInPlay.get(deviceChannel.getChannelId()).getStreamId()); |
| | | } |
| | | channels.add(deviceChannel); |
| | | if (!StringUtils.isEmpty(deviceChannel.getParentId())) { |
| | | if (subContMap.get(deviceChannel.getParentId()) == null) { |
| | |
| | | boolean result = false; |
| | | streamProxyItem.setStreamType("proxy"); |
| | | streamProxyItem.setStatus(true); |
| | | String now = this.format.format(System.currentTimeMillis()); |
| | | String now = DateUtil.getNow(); |
| | | streamProxyItem.setCreateTime(now); |
| | | streamProxyItem.setCreateStamp(System.currentTimeMillis()); |
| | | try { |
New file |
| | |
| | | package com.genersoft.iot.vmp.utils; |
| | | |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.Instant; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.format.DateTimeParseException; |
| | | import java.time.temporal.TemporalAccessor; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | /** |
| | | * 全局时间工具类 |
| | | * @author lin |
| | | */ |
| | | public class DateUtil { |
| | | |
| | | private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss"; |
| | | public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; |
| | | |
| | | public static final SimpleDateFormat formatISO8601 = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()); |
| | | public static final SimpleDateFormat format = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); |
| | | |
| | | public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()).withZone(ZoneId.systemDefault()); |
| | | public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()).withZone(ZoneId.systemDefault()); |
| | | |
| | | public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { |
| | | return formatterISO8601.format(formatter.parse(formatTime)); |
| | | } |
| | | |
| | | public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { |
| | | return formatter.format(formatterISO8601.parse(formatTime)); |
| | | |
| | | } |
| | | |
| | | public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { |
| | | TemporalAccessor temporalAccessor = formatter.parse(formatTime); |
| | | Instant instant = Instant.from(temporalAccessor); |
| | | return instant.getEpochSecond(); |
| | | } |
| | | |
| | | public static String getNow() { |
| | | LocalDateTime nowDateTime = LocalDateTime.now(); |
| | | return formatter.format(nowDateTime); |
| | | } |
| | | |
| | | public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) { |
| | | try { |
| | | LocalDate.parse(timeStr, dateTimeFormatter); |
| | | return true; |
| | | }catch (DateTimeParseException exception) { |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | device.setSubscribeCycleForMobilePosition(Integer.parseInt(expires)); |
| | | device.setMobilePositionSubmissionInterval(Integer.parseInt(interval)); |
| | | storager.updateDevice(device); |
| | | deviceService.updateDevice(device); |
| | | String result = msg; |
| | | if (deviceService.removeMobilePositionSubscribe(device)) { |
| | | result += ",成功"; |
| | |
| | | import com.genersoft.iot.vmp.service.IDeviceAlarmService; |
| | | import com.genersoft.iot.vmp.service.IGbStreamService; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import com.github.pagehelper.PageInfo; |
| | | import io.swagger.annotations.Api; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Arrays; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Api(tags = "报警信息管理") |
| | |
| | | |
| | | @Autowired |
| | | private IVideoManagerStorage storage; |
| | | |
| | | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | private SimpleDateFormat formatForGB = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
| | | |
| | | /** |
| | | * 分页查询报警 |
| | |
| | | |
| | | try { |
| | | if (startTime != null) { |
| | | format.parse(startTime); |
| | | DateUtil.format.parse(startTime); |
| | | } |
| | | if (endTime != null) { |
| | | format.parse(endTime); |
| | | DateUtil.format.parse(endTime); |
| | | } |
| | | } catch (ParseException e) { |
| | | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); |
| | |
| | | } |
| | | try { |
| | | if (time != null) { |
| | | format.parse(time); |
| | | DateUtil.format.parse(time); |
| | | } |
| | | } catch (ParseException e) { |
| | | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); |
| | |
| | | deviceAlarm.setAlarmDescription("test"); |
| | | deviceAlarm.setAlarmMethod("1"); |
| | | deviceAlarm.setAlarmPriority("1"); |
| | | deviceAlarm.setAlarmTime(formatForGB.format(System.currentTimeMillis())); |
| | | deviceAlarm.setAlarmTime(DateUtil.formatISO8601.format(System.currentTimeMillis())); |
| | | deviceAlarm.setAlarmType("1"); |
| | | deviceAlarm.setLongitude(115.33333); |
| | | deviceAlarm.setLatitude(39.33333); |
| | |
| | | public ResponseEntity<PageInfo> updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | device.setStreamMode(streamMode); |
| | | storager.updateDevice(device); |
| | | // storager.updateDevice(device); |
| | | deviceService.updateDevice(device); |
| | | return new ResponseEntity<>(null,HttpStatus.OK); |
| | | } |
| | | |
| | |
| | | public ResponseEntity<WVPResult<String>> updateDevice(Device device){ |
| | | |
| | | if (device != null && device.getDeviceId() != null) { |
| | | Device deviceInStore = storager.queryVideoDevice(device.getDeviceId()); |
| | | if (!StringUtils.isEmpty(device.getName())) { |
| | | deviceInStore.setName(device.getName()); |
| | | } |
| | | if (!StringUtils.isEmpty(device.getCharset())) { |
| | | deviceInStore.setCharset(device.getCharset()); |
| | | } |
| | | if (!StringUtils.isEmpty(device.getMediaServerId())) { |
| | | deviceInStore.setMediaServerId(device.getMediaServerId()); |
| | | } |
| | | |
| | | // 目录订阅相关的信息 |
| | | if (device.getSubscribeCycleForCatalog() > 0) { |
| | | if (deviceInStore.getSubscribeCycleForCatalog() == 0 || deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { |
| | | deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); |
| | | // 开启订阅 |
| | | deviceService.addCatalogSubscribe(deviceInStore); |
| | | } |
| | | }else if (device.getSubscribeCycleForCatalog() == 0) { |
| | | if (deviceInStore.getSubscribeCycleForCatalog() != 0) { |
| | | deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); |
| | | // 取消订阅 |
| | | deviceService.removeCatalogSubscribe(deviceInStore); |
| | | } |
| | | } |
| | | |
| | | // 移动位置订阅相关的信息 |
| | | if (device.getSubscribeCycleForMobilePosition() > 0) { |
| | | if (deviceInStore.getSubscribeCycleForMobilePosition() == 0 || deviceInStore.getSubscribeCycleForMobilePosition() != device.getSubscribeCycleForMobilePosition()) { |
| | | deviceInStore.setMobilePositionSubmissionInterval(device.getMobilePositionSubmissionInterval()); |
| | | deviceInStore.setSubscribeCycleForMobilePosition(device.getSubscribeCycleForMobilePosition()); |
| | | // 开启订阅 |
| | | deviceService.addMobilePositionSubscribe(deviceInStore); |
| | | } |
| | | }else if (device.getSubscribeCycleForMobilePosition() == 0) { |
| | | if (deviceInStore.getSubscribeCycleForMobilePosition() != 0) { |
| | | // 取消订阅 |
| | | deviceService.removeMobilePositionSubscribe(deviceInStore); |
| | | } |
| | | } |
| | | |
| | | // TODO 报警订阅相关的信息 |
| | | |
| | | storager.updateDevice(device); |
| | | cmder.deviceInfoQuery(device); |
| | | deviceService.updateDevice(device); |
| | | // cmder.deviceInfoQuery(device); |
| | | } |
| | | WVPResult<String> result = new WVPResult<>(); |
| | | result.setCode(0); |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.service.IMediaServerService; |
| | | import com.genersoft.iot.vmp.service.IPlayService; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiImplicitParam; |
| | | import io.swagger.annotations.ApiImplicitParams; |
| | |
| | | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| | | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.UUID; |
| | | |
| | | @Api(tags = "国标录像") |
| | |
| | | @ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class), |
| | | }) |
| | | @GetMapping("/query/{deviceId}/{channelId}") |
| | | public DeferredResult<ResponseEntity<RecordInfo>> recordinfo(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){ |
| | | public DeferredResult<ResponseEntity<WVPResult<RecordInfo>>> recordinfo(@PathVariable String deviceId, @PathVariable String channelId, String startTime, String endTime){ |
| | | |
| | | if (logger.isDebugEnabled()) { |
| | | logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s",deviceId, startTime, endTime)); |
| | | } |
| | | DeferredResult<ResponseEntity<WVPResult<RecordInfo>>> result = new DeferredResult<>(); |
| | | if (!DateUtil.verification(startTime, DateUtil.formatter)){ |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("startTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); |
| | | |
| | | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); |
| | | result.setResult(resultResponseEntity); |
| | | return result; |
| | | } |
| | | if (!DateUtil.verification(endTime, DateUtil.formatter)){ |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("endTime error, format is " + DateUtil.yyyy_MM_dd_HH_mm_ss); |
| | | ResponseEntity<WVPResult<RecordInfo>> resultResponseEntity = new ResponseEntity<>(wvpResult, HttpStatus.OK); |
| | | result.setResult(resultResponseEntity); |
| | | return result; |
| | | } |
| | | |
| | | Device device = storager.queryVideoDevice(deviceId); |
| | | // 指定超时时间 1分钟30秒 |
| | | DeferredResult<ResponseEntity<RecordInfo>> result = new DeferredResult<>(90*1000L); |
| | | String uuid = UUID.randomUUID().toString(); |
| | | int sn = (int)((Math.random()*9+1)*100000); |
| | | String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; |
| | |
| | | msg.setId(uuid); |
| | | msg.setKey(key); |
| | | cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, null, null, null, (eventResult -> { |
| | | msg.setData("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg ); |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg); |
| | | msg.setData(wvpResult); |
| | | resultHolder.invokeResult(msg); |
| | | })); |
| | | |
| | |
| | | resultHolder.put(key, uuid, result); |
| | | result.onTimeout(()->{ |
| | | msg.setData("timeout"); |
| | | WVPResult<RecordInfo> wvpResult = new WVPResult<>(); |
| | | wvpResult.setCode(-1); |
| | | wvpResult.setMsg("timeout"); |
| | | msg.setData(wvpResult); |
| | | resultHolder.invokeResult(msg); |
| | | }); |
| | | return result; |
| | |
| | | import com.genersoft.iot.vmp.conf.UserSetting; |
| | | import com.genersoft.iot.vmp.service.ILogService; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.LogDto; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import com.github.pagehelper.PageInfo; |
| | | import io.swagger.annotations.Api; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | |
| | | @Api(tags = "日志管理") |
| | | @CrossOrigin |
| | |
| | | |
| | | @Autowired |
| | | private UserSetting userSetting; |
| | | |
| | | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | /** |
| | | * 分页查询日志 |
| | |
| | | |
| | | try { |
| | | if (startTime != null) { |
| | | format.parse(startTime); |
| | | DateUtil.format.parse(startTime); |
| | | } |
| | | if (endTime != null) { |
| | | format.parse(endTime); |
| | | DateUtil.format.parse(endTime); |
| | | } |
| | | } catch (ParseException e) { |
| | | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); |
| | |
| | | |
| | | import com.genersoft.iot.vmp.conf.security.SecurityUtils; |
| | | import com.genersoft.iot.vmp.service.IRoleService; |
| | | import com.genersoft.iot.vmp.service.IUserService; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.Role; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.User; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiImplicitParam; |
| | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.security.authentication.AuthenticationManager; |
| | | import org.springframework.util.DigestUtils; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.List; |
| | | |
| | | @Api(tags = "角色管理") |
| | |
| | | |
| | | @Autowired |
| | | private IRoleService roleService; |
| | | |
| | | private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @ApiOperation("添加角色") |
| | | @ApiImplicitParams({ |
| | |
| | | Role role = new Role(); |
| | | role.setName(name); |
| | | role.setAuthority(authority); |
| | | role.setCreateTime(format.format(System.currentTimeMillis())); |
| | | role.setUpdateTime(format.format(System.currentTimeMillis())); |
| | | role.setCreateTime(DateUtil.getNow()); |
| | | role.setUpdateTime(DateUtil.getNow()); |
| | | |
| | | int addResult = roleService.add(role); |
| | | |
| | |
| | | import com.genersoft.iot.vmp.service.IUserService; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.Role; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.User; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiImplicitParam; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.security.sasl.AuthenticationException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.List; |
| | | |
| | | @Api(tags = "用户管理") |
| | |
| | | |
| | | @Autowired |
| | | private IRoleService roleService; |
| | | |
| | | private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @ApiOperation("登录") |
| | | @ApiImplicitParams({ |
| | |
| | | return new ResponseEntity<>(result, HttpStatus.OK); |
| | | } |
| | | user.setRole(role); |
| | | user.setCreateTime(format.format(System.currentTimeMillis())); |
| | | user.setUpdateTime(format.format(System.currentTimeMillis())); |
| | | user.setCreateTime(DateUtil.getNow()); |
| | | user.setUpdateTime(DateUtil.getNow()); |
| | | int addResult = userService.addUser(user); |
| | | |
| | | result.setCode(addResult > 0 ? 0 : -1); |
| | |
| | | poolMaxIdle: 500 |
| | | # [可选] 最大的等待时间(秒) |
| | | poolMaxWait: 5 |
| | | # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 |
| | | # [必选] jdbc数据库配置 |
| | | datasource: |
| | | type: com.alibaba.druid.pool.DruidDataSource |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | |
| | | password: face2020 |
| | | # [可选] 超时时间 |
| | | timeout: 10000 |
| | | # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 |
| | | # mysql数据源 |
| | | datasource: |
| | | type: com.alibaba.druid.pool.DruidDataSource |
| | |
| | | password: ${REDIS_PWD:root} |
| | | # [可选] 超时时间 |
| | | timeout: 10000 |
| | | # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 |
| | | # [必选] jdbc数据库配置 |
| | | datasource: |
| | | # 使用mysql 打开23-28行注释, 删除29-36行 |
| | | name: wvp |
| | |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; |
| | | import com.genersoft.iot.vmp.service.IDeviceAlarmService; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import org.junit.runner.RunWith; |
| | | import org.springframework.boot.test.context.SpringBootTest; |
| | | import org.springframework.test.context.junit4.SpringRunner; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | |
| | |
| | | |
| | | @Resource |
| | | private IDeviceAlarmService deviceAlarmService; |
| | | |
| | | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @org.junit.jupiter.api.Test |
| | | void getAllAlarm() { |
| | |
| | | */ |
| | | deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + ""); |
| | | Date date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00"); |
| | | deviceAlarm.setAlarmTime(format.format(date)); |
| | | deviceAlarm.setAlarmTime(DateUtil.format.format(date)); |
| | | /** |
| | | * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- |
| | | */ |
| | |
| | | private Date randomDate(String beginDate, String endDate) { |
| | | try { |
| | | |
| | | Date start = format.parse(beginDate);//构造开始日期 |
| | | Date end = format.parse(endDate);//构造结束日期 |
| | | Date start = DateUtil.format.parse(beginDate);//构造开始日期 |
| | | Date end = DateUtil.format.parse(endDate);//构造结束日期 |
| | | //getTime()表示返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。 |
| | | if (start.getTime() >= end.getTime()) { |
| | | return null; |
| | |
| | | import com.genersoft.iot.vmp.service.IUserService; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.Role; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.User; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import org.junit.runner.RunWith; |
| | | import org.springframework.boot.test.context.SpringBootTest; |
| | | import org.springframework.test.context.junit4.SpringRunner; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.List; |
| | | |
| | | |
| | |
| | | @Resource |
| | | private IRoleService roleService; |
| | | |
| | | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | @org.junit.jupiter.api.Test |
| | | void getAllUser() { |
| | | List<Role> all = roleService.getAll(); |
| | |
| | | Role role = new Role(); |
| | | role.setName("test+" + i); |
| | | role.setAuthority("adadadda"); |
| | | role.setCreateTime(format.format(System.currentTimeMillis())); |
| | | role.setUpdateTime(format.format(System.currentTimeMillis())); |
| | | role.setCreateTime(DateUtil.getNow()); |
| | | role.setUpdateTime(DateUtil.getNow()); |
| | | roleService.add(role); |
| | | } |
| | | } |
| | |
| | | package com.genersoft.iot.vmp.service.impl; |
| | | |
| | | import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; |
| | | import com.genersoft.iot.vmp.service.IDeviceAlarmService; |
| | | import com.genersoft.iot.vmp.service.IUserService; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.Role; |
| | | import com.genersoft.iot.vmp.storager.dao.dto.User; |
| | | import com.genersoft.iot.vmp.utils.DateUtil; |
| | | import org.junit.runner.RunWith; |
| | | import org.springframework.boot.test.context.SpringBootTest; |
| | | import org.springframework.test.context.junit4.SpringRunner; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | |
| | |
| | | @Resource |
| | | private IUserService userService; |
| | | |
| | | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | @org.junit.jupiter.api.Test |
| | | void getAllUser() { |
| | |
| | | Role role = new Role(); |
| | | role.setId(1); |
| | | user.setRole(role); |
| | | user.setCreateTime(format.format(System.currentTimeMillis())); |
| | | user.setUpdateTime(format.format(System.currentTimeMillis())); |
| | | user.setCreateTime(DateUtil.getNow()); |
| | | user.setUpdateTime(DateUtil.getNow()); |
| | | userService.addUser(user); |
| | | } |
| | | } |
| | |
| | | Role role = new Role(); |
| | | role.setId(2); |
| | | user.setRole(role); |
| | | user.setUpdateTime(format.format(System.currentTimeMillis())); |
| | | user.setUpdateTime(DateUtil.getNow()); |
| | | userService.updateUsers(user); |
| | | } |
| | | |
| | |
| | | that.$message.error(res.data.msg); |
| | | } |
| | | }).catch(function (e) { |
| | | that.isLoging = false; |
| | | that.$message.error("请求超时"); |
| | | }); |
| | | }, |
| | | queryRecords: function (itemData) { |
| | | var format = moment().format("YYYY-M-D"); |
| | | var format = moment().format("yyyy-MM-DD"); |
| | | let deviceId = this.deviceId; |
| | | let channelId = itemData.channelId; |
| | | this.$refs.devicePlayer.openDialog("record", deviceId, channelId, {date: format}) |
| | |
| | | let that = this; |
| | | this.$axios({ |
| | | method: 'get', |
| | | url: '/zlm/' + that.mediaServerChoose + '/index/api/kick_session&id=' + id |
| | | url: '/zlm/' + that.mediaServerChoose + '/index/api/kick_session?id=' + id |
| | | }).then(function (res) { |
| | | that.getAllSession(); |
| | | that.$message({ |
| | |
| | | <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;"> |
| | | <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span> |
| | | <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" > |
| | | <template slot="append"> |
| | | <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i> |
| | | </template> |
| | | <el-button slot="append" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button> |
| | | <el-dropdown slot="prepend" v-if="streamInfo" trigger="click" @command="copyUrl"> |
| | | <el-button > |
| | | 更多地址<i class="el-icon-arrow-down el-icon--right"></i> |
| | | </el-button> |
| | | <el-dropdown-menu slot="dropdown" > |
| | | <el-dropdown-item :command="streamInfo.flv"> |
| | | <el-tag >FLV:</el-tag> |
| | | <span>{{ streamInfo.flv }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.https_flv"> |
| | | <el-tag >FLV(https):</el-tag> |
| | | <span>{{ streamInfo.https_flv }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.ws_flv"> |
| | | <el-tag >FLV(ws):</el-tag> |
| | | <span >{{ streamInfo.ws_flv }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.wss_flv"> |
| | | <el-tag >FLV(wss):</el-tag> |
| | | <span>{{ streamInfo.wss_flv }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.fmp4"> |
| | | <el-tag >FMP4:</el-tag> |
| | | <span>{{ streamInfo.fmp4 }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.https_fmp4"> |
| | | <el-tag >FMP4(https):</el-tag> |
| | | <span>{{ streamInfo.https_fmp4 }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.ws_fmp4"> |
| | | <el-tag >FMP4(ws):</el-tag> |
| | | <span>{{ streamInfo.ws_fmp4 }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.wss_fmp4"> |
| | | <el-tag >FMP4(wss):</el-tag> |
| | | <span>{{ streamInfo.wss_fmp4 }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.hls"> |
| | | <el-tag>HLS:</el-tag> |
| | | <span>{{ streamInfo.hls }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.https_hls"> |
| | | <el-tag >HLS(https):</el-tag> |
| | | <span>{{ streamInfo.https_hls }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.ws_hls"> |
| | | <el-tag >HLS(ws):</el-tag> |
| | | <span>{{ streamInfo.ws_hls }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.wss_hls"> |
| | | <el-tag >HLS(wss):</el-tag> |
| | | <span>{{ streamInfo.wss_hls }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.ts"> |
| | | <el-tag>TS:</el-tag> |
| | | <span>{{ streamInfo.ts }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.https_ts"> |
| | | <el-tag>TS(https):</el-tag> |
| | | <span>{{ streamInfo.https_ts }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.ws_ts"> |
| | | <el-tag>TS(ws):</el-tag> |
| | | <span>{{ streamInfo.ws_ts }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.wss_ts"> |
| | | <el-tag>TS(wss):</el-tag> |
| | | <span>{{ streamInfo.wss_ts }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.rtc"> |
| | | <el-tag >RTC:</el-tag> |
| | | <span>{{ streamInfo.rtc }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.rtmp"> |
| | | <el-tag >RTMP:</el-tag> |
| | | <span>{{ streamInfo.rtmp }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.rtmps"> |
| | | <el-tag >RTMPS:</el-tag> |
| | | <span>{{ streamInfo.rtmps }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.rtsp"> |
| | | <el-tag >RTSP:</el-tag> |
| | | <span>{{ streamInfo.rtsp }}</span> |
| | | </el-dropdown-item> |
| | | <el-dropdown-item :command="streamInfo.rtsps"> |
| | | <el-tag >RTSPS:</el-tag> |
| | | <span>{{ streamInfo.rtsps }}</span> |
| | | </el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </el-dropdown> |
| | | </el-input> |
| | | |
| | | </div> |
| | | </el-tab-pane> |
| | | <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}--> |
| | |
| | | |
| | | <div class="control-panel"> |
| | | <el-button-group> |
| | | <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium" type="info">预置位编号</el-tag> |
| | | <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium">预置位编号</el-tag> |
| | | <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini" v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1" :max="255"></el-input-number> |
| | | <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置</el-button> |
| | | <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary" icon="el-icon-place" @click="presetPosition(130, presetPos)">调用</el-button> |
| | | <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除</el-button> |
| | | <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">巡航速度</el-tag> |
| | | <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">巡航速度</el-tag> |
| | | <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> |
| | | <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置</el-button> |
| | | <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium" type="info">停留时间</el-tag> |
| | | <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">停留时间</el-tag> |
| | | <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> |
| | | <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置</el-button> |
| | | <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium" type="info">巡航组编号</el-tag> |
| | | <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium">巡航组编号</el-tag> |
| | | <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number> |
| | | <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点</el-button> |
| | | <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点</el-button> |
| | | <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组</el-button> |
| | | <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航</el-button> |
| | | <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium" type="info">扫描速度</el-tag> |
| | | <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium">扫描速度</el-tag> |
| | | <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number> |
| | | <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置</el-button> |
| | | <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium" type="info">扫描组编号</el-tag> |
| | | <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium">扫描组编号</el-tag> |
| | | <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number> |
| | | <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界</el-button> |
| | | <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界</el-button> |
| | |
| | | </div> |
| | | |
| | | </el-tab-pane> |
| | | |
| | | </el-tabs> |
| | | </div> |
| | | </el-dialog> |
| | |
| | | seekTime: 0, |
| | | recordStartTime: 0, |
| | | showTimeText: "00:00:00", |
| | | streamInfo: null, |
| | | }; |
| | | }, |
| | | methods: { |
| | |
| | | console.log(val) |
| | | }, |
| | | play: function (streamInfo, hasAudio) { |
| | | this.streamInfo = streamInfo; |
| | | this.hasAudio = hasAudio; |
| | | this.isLoging = false; |
| | | // this.videoUrl = streamInfo.rtc; |
| | |
| | | method: 'get', |
| | | url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime |
| | | }).then(function (res) { |
| | | console.log(res) |
| | | if(res.data.code === 0) { |
| | | // 处理时间信息 |
| | | that.videoHistory.searchHistoryResult = res.data.recordList; |
| | | that.videoHistory.searchHistoryResult = res.data.data.recordList; |
| | | that.recordsLoading = false; |
| | | }else { |
| | | this.$message({ |
| | | showClose: true, |
| | | message: res.data.msg, |
| | | type: "error", |
| | | }); |
| | | } |
| | | |
| | | }).catch(function (e) { |
| | | console.log(e.message); |
| | | // that.videoHistory.searchHistoryResult = falsificationData.recordData; |
| | |
| | | console.log(resultArray) |
| | | return resultArray; |
| | | }, |
| | | copyUrl: function (dropdownItem){ |
| | | console.log(dropdownItem) |
| | | this.$copyText(dropdownItem).then((e)=> { |
| | | this.$message.success("成功拷贝到粘贴板"); |
| | | }, (e)=> { |
| | | |
| | | }) |
| | | }, |
| | | gbPlay(){ |
| | | console.log('前端控制:播放'); |
| | | this.$axios({ |
| | |
| | | this.$axios({ |
| | | method: 'get', |
| | | url: `/api/playback/seek/${this.streamId }/` + Math.floor(this.seekTime * val / 100000) |
| | | }).then(function (res) {}); |
| | | } |
| | | }).then( (res)=> { |
| | | setTimeout(()=>{ |
| | | this.$refs.videoPlayer.play(this.videoUrl) |
| | | }, 600) |
| | | }); |
| | | }, |
| | | |
| | | |
| | | } |
| | | }; |
| | |
| | | isEnd: true, |
| | | } |
| | | }).then((res) => { |
| | | console.log(res) |
| | | if (res.data.code == 0) { |
| | | this.percentage = parseFloat(res.data.data.percentage)*100 |
| | | if (res.data.data[0].percentage === '1') { |