Commit eb4716ba82f13078dd88e967e7906080c0ac0205

Authored by 648540858
1 parent f1217682

添加目录订阅消息与接口

Showing 19 changed files with 3103 additions and 0 deletions
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +import org.springframework.beans.factory.annotation.Autowired;
  4 +import org.springframework.context.annotation.Bean;
  5 +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
  6 +import org.springframework.scheduling.support.CronTrigger;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import java.util.Map;
  10 +import java.util.concurrent.ConcurrentHashMap;
  11 +import java.util.concurrent.ScheduledFuture;
  12 +
  13 +/**
  14 + * 动态定时任务
  15 + */
  16 +@Component
  17 +public class DynamicTask {
  18 +
  19 + @Autowired
  20 + private ThreadPoolTaskScheduler threadPoolTaskScheduler;
  21 +
  22 + private Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
  23 +
  24 + @Bean
  25 + public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
  26 + return new ThreadPoolTaskScheduler();
  27 + }
  28 +
  29 + public String startCron(String key, Runnable task, String corn) {
  30 + stopCron(key);
  31 + ScheduledFuture future = threadPoolTaskScheduler.schedule(task, new CronTrigger(corn));
  32 + futureMap.put(key, future);
  33 + return "startCron";
  34 + }
  35 +
  36 + public void stopCron(String key) {
  37 + if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
  38 + futureMap.get(key).cancel(true);
  39 + }
  40 + }
  41 +
  42 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/ISIPRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request;
  2 +
  3 +import javax.sip.RequestEvent;
  4 +
  5 +/**
  6 + * @description: 对SIP事件进行处理,包括request, response, timeout, ioException, transactionTerminated,dialogTerminated
  7 + * @author: panlinlin
  8 + * @date: 2021年11月5日 15:47
  9 + */
  10 +public interface ISIPRequestProcessor {
  11 +
  12 + void process(RequestEvent event);
  13 +
  14 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request;
  2 +
  3 +import gov.nist.javax.sip.SipProviderImpl;
  4 +import gov.nist.javax.sip.SipStackImpl;
  5 +import gov.nist.javax.sip.message.SIPRequest;
  6 +import gov.nist.javax.sip.stack.SIPServerTransaction;
  7 +import org.dom4j.Document;
  8 +import org.dom4j.DocumentException;
  9 +import org.dom4j.Element;
  10 +import org.dom4j.io.SAXReader;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import org.springframework.beans.factory.InitializingBean;
  14 +import org.springframework.beans.factory.annotation.Autowired;
  15 +import org.springframework.beans.factory.annotation.Qualifier;
  16 +
  17 +import javax.sip.*;
  18 +import javax.sip.address.Address;
  19 +import javax.sip.address.AddressFactory;
  20 +import javax.sip.address.SipURI;
  21 +import javax.sip.header.ContentTypeHeader;
  22 +import javax.sip.header.HeaderFactory;
  23 +import javax.sip.header.ViaHeader;
  24 +import javax.sip.message.MessageFactory;
  25 +import javax.sip.message.Request;
  26 +import javax.sip.message.Response;
  27 +import java.io.ByteArrayInputStream;
  28 +import java.text.ParseException;
  29 +
  30 +/**
  31 + * @description:处理接收IPCamera发来的SIP协议请求消息
  32 + * @author: songww
  33 + * @date: 2020年5月3日 下午4:42:22
  34 + */
  35 +public abstract class SIPRequestProcessorAbstract implements InitializingBean, ISIPRequestProcessor {
  36 +
  37 + private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorAbstract.class);
  38 +
  39 + @Autowired
  40 + @Qualifier(value="tcpSipProvider")
  41 + private SipProviderImpl tcpSipProvider;
  42 +
  43 + @Autowired
  44 + @Qualifier(value="udpSipProvider")
  45 + private SipProviderImpl udpSipProvider;
  46 +
  47 + /**
  48 + * 根据 RequestEvent 获取 ServerTransaction
  49 + * @param evt
  50 + * @return
  51 + */
  52 + public ServerTransaction getServerTransaction(RequestEvent evt) {
  53 + Request request = evt.getRequest();
  54 + ServerTransaction serverTransaction = evt.getServerTransaction();
  55 + // 判断TCP还是UDP
  56 + boolean isTcp = false;
  57 + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
  58 + String transport = reqViaHeader.getTransport();
  59 + if (transport.equals("TCP")) {
  60 + isTcp = true;
  61 + }
  62 +
  63 + if (serverTransaction == null) {
  64 + try {
  65 + if (isTcp) {
  66 + SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
  67 + serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
  68 + if (serverTransaction == null) {
  69 + serverTransaction = tcpSipProvider.getNewServerTransaction(request);
  70 + }
  71 + } else {
  72 + SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
  73 + serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
  74 + if (serverTransaction == null) {
  75 + serverTransaction = udpSipProvider.getNewServerTransaction(request);
  76 + }
  77 + }
  78 + } catch (TransactionAlreadyExistsException e) {
  79 + logger.error(e.getMessage());
  80 + } catch (TransactionUnavailableException e) {
  81 + logger.error(e.getMessage());
  82 + }
  83 + }
  84 + return serverTransaction;
  85 + }
  86 +
  87 + public AddressFactory getAddressFactory() {
  88 + try {
  89 + return SipFactory.getInstance().createAddressFactory();
  90 + } catch (PeerUnavailableException e) {
  91 + e.printStackTrace();
  92 + }
  93 + return null;
  94 + }
  95 +
  96 + public HeaderFactory getHeaderFactory() {
  97 + try {
  98 + return SipFactory.getInstance().createHeaderFactory();
  99 + } catch (PeerUnavailableException e) {
  100 + e.printStackTrace();
  101 + }
  102 + return null;
  103 + }
  104 +
  105 + public MessageFactory getMessageFactory() {
  106 + try {
  107 + return SipFactory.getInstance().createMessageFactory();
  108 + } catch (PeerUnavailableException e) {
  109 + e.printStackTrace();
  110 + }
  111 + return null;
  112 + }
  113 +
  114 + /***
  115 + * 回复状态码
  116 + * 100 trying
  117 + * 200 OK
  118 + * 400
  119 + * 404
  120 + * @param evt
  121 + * @throws SipException
  122 + * @throws InvalidArgumentException
  123 + * @throws ParseException
  124 + */
  125 + public void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException {
  126 + Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
  127 + ServerTransaction serverTransaction = getServerTransaction(evt);
  128 + serverTransaction.sendResponse(response);
  129 + if (statusCode >= 200) {
  130 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  131 + }
  132 + }
  133 +
  134 + public void responseAck(RequestEvent evt, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
  135 + Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
  136 + response.setReasonPhrase(msg);
  137 + ServerTransaction serverTransaction = getServerTransaction(evt);
  138 + serverTransaction.sendResponse(response);
  139 + if (statusCode >= 200) {
  140 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  141 + }
  142 + }
  143 +
  144 + /**
  145 + * 回复带sdp的200
  146 + * @param evt
  147 + * @param sdp
  148 + * @throws SipException
  149 + * @throws InvalidArgumentException
  150 + * @throws ParseException
  151 + */
  152 + public void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException {
  153 + Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
  154 + SipFactory sipFactory = SipFactory.getInstance();
  155 + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  156 + response.setContent(sdp, contentTypeHeader);
  157 +
  158 + SipURI sipURI = (SipURI)evt.getRequest().getRequestURI();
  159 +
  160 + Address concatAddress = sipFactory.createAddressFactory().createAddress(
  161 + sipFactory.createAddressFactory().createSipURI(sipURI.getUser(), sipURI.getHost()+":"+sipURI.getPort()
  162 + ));
  163 + response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
  164 + getServerTransaction(evt).sendResponse(response);
  165 + }
  166 +
  167 + public Element getRootElement(RequestEvent evt) throws DocumentException {
  168 + return getRootElement(evt, "gb2312");
  169 + }
  170 + public Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
  171 + if (charset == null) charset = "gb2312";
  172 + Request request = evt.getRequest();
  173 + SAXReader reader = new SAXReader();
  174 + reader.setEncoding(charset);
  175 + Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
  176 + return xml.getRootElement();
  177 + }
  178 +
  179 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  7 +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  8 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  9 +import com.genersoft.iot.vmp.service.IMediaServerService;
  10 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.stereotype.Component;
  15 +
  16 +import javax.sip.Dialog;
  17 +import javax.sip.DialogState;
  18 +import javax.sip.RequestEvent;
  19 +import javax.sip.address.SipURI;
  20 +import javax.sip.header.FromHeader;
  21 +import javax.sip.header.HeaderAddress;
  22 +import javax.sip.header.ToHeader;
  23 +import java.util.HashMap;
  24 +import java.util.Map;
  25 +
  26 +/**
  27 + * @description:ACK请求处理器
  28 + * @author: swwheihei
  29 + * @date: 2020年5月3日 下午5:31:45
  30 + */
  31 +@Component
  32 +public class AckRequestProcessor extends SIPRequestProcessorAbstract {
  33 +
  34 + private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
  35 + private String method = "ACK";
  36 +
  37 + @Autowired
  38 + private SIPProcessorObserver sipProcessorObserver;
  39 +
  40 + @Override
  41 + public void afterPropertiesSet() throws Exception {
  42 + // 添加消息处理的订阅
  43 + sipProcessorObserver.addRequestProcessor(method, this);
  44 + }
  45 +
  46 + @Autowired
  47 + private IRedisCatchStorage redisCatchStorage;
  48 +
  49 + @Autowired
  50 + private ZLMRTPServerFactory zlmrtpServerFactory;
  51 +
  52 + @Autowired
  53 + private IMediaServerService mediaServerService;
  54 +
  55 +
  56 + /**
  57 + * 处理 ACK请求
  58 + *
  59 + * @param evt
  60 + */
  61 + @Override
  62 + public void process(RequestEvent evt) {
  63 + Dialog dialog = evt.getDialog();
  64 + if (dialog == null) return;
  65 + if (dialog.getState()== DialogState.CONFIRMED) {
  66 + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
  67 + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
  68 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId);
  69 + String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
  70 + String deviceId = sendRtpItem.getDeviceId();
  71 + StreamInfo streamInfo = null;
  72 + if (deviceId == null) {
  73 + streamInfo = new StreamInfo();
  74 + streamInfo.setApp(sendRtpItem.getApp());
  75 + streamInfo.setStreamId(sendRtpItem.getStreamId());
  76 + }else {
  77 + streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
  78 + sendRtpItem.setStreamId(streamInfo.getStreamId());
  79 + streamInfo.setApp("rtp");
  80 + }
  81 +
  82 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  83 + logger.info(platformGbId);
  84 + logger.info(channelId);
  85 + Map<String, Object> param = new HashMap<>();
  86 + param.put("vhost","__defaultVhost__");
  87 + param.put("app",streamInfo.getApp());
  88 + param.put("stream",streamInfo.getStreamId());
  89 + param.put("ssrc", sendRtpItem.getSsrc());
  90 + param.put("dst_url",sendRtpItem.getIp());
  91 + param.put("dst_port", sendRtpItem.getPort());
  92 + param.put("is_udp", is_Udp);
  93 + //param.put ("src_port", sendRtpItem.getLocalPort());
  94 + // 设备推流查询,成功后才能转推
  95 + boolean rtpPushed = false;
  96 + long startTime = System.currentTimeMillis();
  97 + while (!rtpPushed) {
  98 + try {
  99 + if (System.currentTimeMillis() - startTime < 30 * 1000) {
  100 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  101 + if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
  102 + rtpPushed = true;
  103 + logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",
  104 + streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());
  105 + zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
  106 + } else {
  107 + logger.info("等待设备推流[{}/{}].......",
  108 + streamInfo.getApp() ,streamInfo.getStreamId());
  109 + Thread.sleep(1000);
  110 + continue;
  111 + }
  112 + } else {
  113 + rtpPushed = true;
  114 + logger.info("设备推流[{}/{}]超时,终止向上级推流",
  115 + streamInfo.getApp() ,streamInfo.getStreamId());
  116 + }
  117 + } catch (InterruptedException e) {
  118 + e.printStackTrace();
  119 + }
  120 + }
  121 + }
  122 + }
  123 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  9 +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  10 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  11 +import com.genersoft.iot.vmp.service.IMediaServerService;
  12 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  13 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  14 +import org.slf4j.Logger;
  15 +import org.slf4j.LoggerFactory;
  16 +import org.springframework.beans.factory.annotation.Autowired;
  17 +import org.springframework.stereotype.Component;
  18 +
  19 +import javax.sip.*;
  20 +import javax.sip.address.SipURI;
  21 +import javax.sip.header.FromHeader;
  22 +import javax.sip.header.HeaderAddress;
  23 +import javax.sip.header.ToHeader;
  24 +import javax.sip.message.Response;
  25 +import java.text.ParseException;
  26 +import java.util.HashMap;
  27 +import java.util.Map;
  28 +
  29 +/**
  30 + * @description: BYE请求处理器
  31 + * @author: lawrencehj
  32 + * @date: 2021年3月9日
  33 + */
  34 +@Component
  35 +public class ByeRequestProcessor extends SIPRequestProcessorAbstract {
  36 +
  37 + private Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);
  38 +
  39 + @Autowired
  40 + private ISIPCommander cmder;
  41 +
  42 + @Autowired
  43 + private IRedisCatchStorage redisCatchStorage;
  44 +
  45 + @Autowired
  46 + private IVideoManagerStorager storager;
  47 +
  48 + @Autowired
  49 + private ZLMRTPServerFactory zlmrtpServerFactory;
  50 +
  51 + @Autowired
  52 + private IMediaServerService mediaServerService;
  53 +
  54 + private String method = "BYE";
  55 +
  56 + @Autowired
  57 + private SIPProcessorObserver sipProcessorObserver;
  58 +
  59 + @Override
  60 + public void afterPropertiesSet() throws Exception {
  61 + // 添加消息处理的订阅
  62 + sipProcessorObserver.addRequestProcessor(method, this);
  63 + }
  64 +
  65 + /**
  66 + * 处理BYE请求
  67 + * @param evt
  68 + */
  69 + @Override
  70 + public void process(RequestEvent evt) {
  71 + try {
  72 + responseAck(evt, Response.OK);
  73 + Dialog dialog = evt.getDialog();
  74 + if (dialog == null) return;
  75 + if (dialog.getState().equals(DialogState.TERMINATED)) {
  76 + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
  77 + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
  78 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId);
  79 + logger.info("收到bye, [{}/{}]", platformGbId, channelId);
  80 + if (sendRtpItem != null){
  81 + String streamId = sendRtpItem.getStreamId();
  82 + Map<String, Object> param = new HashMap<>();
  83 + param.put("vhost","__defaultVhost__");
  84 + param.put("app",sendRtpItem.getApp());
  85 + param.put("stream",streamId);
  86 + param.put("ssrc",sendRtpItem.getSsrc());
  87 + logger.info("停止向上级推流:" + streamId);
  88 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  89 + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
  90 + redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
  91 + if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
  92 + logger.info(streamId + "无其它观看者,通知设备停止推流");
  93 + cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);
  94 + }
  95 + }
  96 + // 可能是设备主动停止
  97 + Device device = storager.queryVideoDeviceByChannelId(platformGbId);
  98 + if (device != null) {
  99 + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
  100 + if (streamInfo != null) {
  101 + redisCatchStorage.stopPlay(streamInfo);
  102 + }
  103 + storager.stopPlay(device.getDeviceId(), channelId);
  104 + mediaServerService.closeRTPServer(device, channelId);
  105 + }
  106 + }
  107 + } catch (SipException e) {
  108 + e.printStackTrace();
  109 + } catch (InvalidArgumentException e) {
  110 + e.printStackTrace();
  111 + } catch (ParseException e) {
  112 + e.printStackTrace();
  113 + }
  114 + }
  115 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  4 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  5 +import org.springframework.beans.factory.annotation.Autowired;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +import javax.sip.RequestEvent;
  9 +
  10 +/**
  11 + * @description:CANCEL请求处理器
  12 + * @author: swwheihei
  13 + * @date: 2020年5月3日 下午5:32:23
  14 + */
  15 +@Component
  16 +public class CancelRequestProcessor extends SIPRequestProcessorAbstract {
  17 +
  18 + private String method = "CANCEL";
  19 +
  20 + @Autowired
  21 + private SIPProcessorObserver sipProcessorObserver;
  22 +
  23 + @Override
  24 + public void afterPropertiesSet() throws Exception {
  25 + // 添加消息处理的订阅
  26 + sipProcessorObserver.addRequestProcessor(method, this);
  27 + }
  28 +
  29 + /**
  30 + * 处理CANCEL请求
  31 + *
  32 + * @param evt 事件
  33 + */
  34 + @Override
  35 + public void process(RequestEvent evt) {
  36 + // TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令
  37 +
  38 + }
  39 +
  40 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.*;
  4 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  8 +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  9 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  10 +import com.genersoft.iot.vmp.service.IMediaServerService;
  11 +import com.genersoft.iot.vmp.service.IPlayService;
  12 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  13 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  14 +import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
  15 +import gov.nist.javax.sip.address.AddressImpl;
  16 +import gov.nist.javax.sip.address.SipUri;
  17 +import org.slf4j.Logger;
  18 +import org.slf4j.LoggerFactory;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.stereotype.Component;
  21 +
  22 +import javax.sdp.*;
  23 +import javax.sip.InvalidArgumentException;
  24 +import javax.sip.RequestEvent;
  25 +import javax.sip.ServerTransaction;
  26 +import javax.sip.SipException;
  27 +import javax.sip.address.SipURI;
  28 +import javax.sip.header.FromHeader;
  29 +import javax.sip.message.Request;
  30 +import javax.sip.message.Response;
  31 +import java.text.ParseException;
  32 +import java.util.Vector;
  33 +
  34 +/**
  35 + * @description:处理INVITE请求
  36 + * @author: panll
  37 + * @date: 2021年1月14日
  38 + */
  39 +@SuppressWarnings("rawtypes")
  40 +@Component
  41 +public class InviteRequestProcessor extends SIPRequestProcessorAbstract {
  42 +
  43 + private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class);
  44 +
  45 + private String method = "INVITE";
  46 +
  47 + @Autowired
  48 + private SIPCommanderFroPlatform cmderFroPlatform;
  49 +
  50 + @Autowired
  51 + private IVideoManagerStorager storager;
  52 +
  53 + @Autowired
  54 + private IRedisCatchStorage redisCatchStorage;
  55 +
  56 + @Autowired
  57 + private SIPCommander cmder;
  58 +
  59 + @Autowired
  60 + private IPlayService playService;
  61 +
  62 + @Autowired
  63 + private ZLMRTPServerFactory zlmrtpServerFactory;
  64 +
  65 + @Autowired
  66 + private IMediaServerService mediaServerService;
  67 +
  68 + @Autowired
  69 + private SIPProcessorObserver sipProcessorObserver;
  70 +
  71 + @Override
  72 + public void afterPropertiesSet() throws Exception {
  73 + // 添加消息处理的订阅
  74 + sipProcessorObserver.addRequestProcessor(method, this);
  75 + }
  76 +
  77 + /**
  78 + * 处理invite请求
  79 + *
  80 + * @param evt
  81 + * 请求消息
  82 + */
  83 + @Override
  84 + public void process(RequestEvent evt) {
  85 + // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
  86 + try {
  87 + Request request = evt.getRequest();
  88 + SipURI sipURI = (SipURI) request.getRequestURI();
  89 + String channelId = sipURI.getUser();
  90 + String requesterId = null;
  91 +
  92 + FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
  93 + AddressImpl address = (AddressImpl) fromHeader.getAddress();
  94 + SipUri uri = (SipUri) address.getURI();
  95 + requesterId = uri.getUser();
  96 +
  97 + if (requesterId == null || channelId == null) {
  98 + logger.info("无法从FromHeader的Address中获取到平台id,返回400");
  99 + responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误
  100 + return;
  101 + }
  102 +
  103 + // 查询请求方是否上级平台
  104 + ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
  105 + if (platform != null) {
  106 + // 查询平台下是否有该通道
  107 + DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
  108 + GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
  109 + MediaServerItem mediaServerItem = null;
  110 + // 不是通道可能是直播流
  111 + if (channel != null && gbStream == null ) {
  112 + if (channel.getStatus() == 0) {
  113 + logger.info("通道离线,返回400");
  114 + responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
  115 + return;
  116 + }
  117 + responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
  118 + }else if(channel == null && gbStream != null){
  119 + String mediaServerId = gbStream.getMediaServerId();
  120 + mediaServerItem = mediaServerService.getOne(mediaServerId);
  121 + if (mediaServerItem == null) {
  122 + logger.info("[ app={}, stream={} ]找不到zlm {},返回410",gbStream.getApp(), gbStream.getStream(), mediaServerId);
  123 + responseAck(evt, Response.GONE, "media server not found");
  124 + return;
  125 + }
  126 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
  127 + if (!streamReady ) {
  128 + logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream());
  129 + responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
  130 + return;
  131 + }
  132 + responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
  133 + }else {
  134 + logger.info("通道不存在,返回404");
  135 + responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在
  136 + return;
  137 + }
  138 + // 解析sdp消息, 使用jainsip 自带的sdp解析方式
  139 + String contentString = new String(request.getRawContent());
  140 +
  141 + // jainSip不支持y=字段, 移除移除以解析。
  142 + int ssrcIndex = contentString.indexOf("y=");
  143 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
  144 + String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  145 + String substring = contentString.substring(0, contentString.indexOf("y="));
  146 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  147 +
  148 + // 获取支持的格式
  149 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  150 + // 查看是否支持PS 负载96
  151 + //String ip = null;
  152 + int port = -1;
  153 + //boolean recvonly = false;
  154 + boolean mediaTransmissionTCP = false;
  155 + Boolean tcpActive = null;
  156 + for (Object description : mediaDescriptions) {
  157 + MediaDescription mediaDescription = (MediaDescription) description;
  158 + Media media = mediaDescription.getMedia();
  159 +
  160 + Vector mediaFormats = media.getMediaFormats(false);
  161 + if (mediaFormats.contains("96")) {
  162 + port = media.getMediaPort();
  163 + //String mediaType = media.getMediaType();
  164 + String protocol = media.getProtocol();
  165 +
  166 + // 区分TCP发流还是udp, 当前默认udp
  167 + if ("TCP/RTP/AVP".equals(protocol)) {
  168 + String setup = mediaDescription.getAttribute("setup");
  169 + if (setup != null) {
  170 + mediaTransmissionTCP = true;
  171 + if ("active".equals(setup)) {
  172 + tcpActive = true;
  173 + } else if ("passive".equals(setup)) {
  174 + tcpActive = false;
  175 + }
  176 + }
  177 + }
  178 + break;
  179 + }
  180 + }
  181 + if (port == -1) {
  182 + logger.info("不支持的媒体格式,返回415");
  183 + // 回复不支持的格式
  184 + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
  185 + return;
  186 + }
  187 + String username = sdp.getOrigin().getUsername();
  188 + String addressStr = sdp.getOrigin().getAddress();
  189 + //String sessionName = sdp.getSessionName().getValue();
  190 + logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc);
  191 + Device device = null;
  192 + // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
  193 + if (channel != null) {
  194 + device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
  195 + if (device == null) {
  196 + logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
  197 + responseAck(evt, Response.SERVER_INTERNAL_ERROR);
  198 + return;
  199 + }
  200 + mediaServerItem = playService.getNewMediaServerItem(device);
  201 + if (mediaServerItem == null) {
  202 + logger.warn("未找到可用的zlm");
  203 + responseAck(evt, Response.BUSY_HERE);
  204 + return;
  205 + }
  206 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  207 + device.getDeviceId(), channelId,
  208 + mediaTransmissionTCP);
  209 + if (tcpActive != null) {
  210 + sendRtpItem.setTcpActive(tcpActive);
  211 + }
  212 + if (sendRtpItem == null) {
  213 + logger.warn("服务器端口资源不足");
  214 + responseAck(evt, Response.BUSY_HERE);
  215 + return;
  216 + }
  217 +
  218 + // 写入redis, 超时时回复
  219 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  220 + // 通知下级推流,
  221 + PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{
  222 + // 收到推流, 回复200OK, 等待ack
  223 + // if (sendRtpItem == null) return;
  224 + sendRtpItem.setStatus(1);
  225 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  226 + // TODO 添加对tcp的支持
  227 +
  228 + StringBuffer content = new StringBuffer(200);
  229 + content.append("v=0\r\n");
  230 + content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
  231 + content.append("s=Play\r\n");
  232 + content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
  233 + content.append("t=0 0\r\n");
  234 + content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
  235 + content.append("a=sendonly\r\n");
  236 + content.append("a=rtpmap:96 PS/90000\r\n");
  237 + content.append("y="+ ssrc + "\r\n");
  238 + content.append("f=\r\n");
  239 +
  240 + try {
  241 + responseAck(evt, content.toString());
  242 + } catch (SipException e) {
  243 + e.printStackTrace();
  244 + } catch (InvalidArgumentException e) {
  245 + e.printStackTrace();
  246 + } catch (ParseException e) {
  247 + e.printStackTrace();
  248 + }
  249 + } ,((event) -> {
  250 + // 未知错误。直接转发设备点播的错误
  251 + Response response = null;
  252 + try {
  253 + response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
  254 + ServerTransaction serverTransaction = getServerTransaction(evt);
  255 + serverTransaction.sendResponse(response);
  256 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  257 + } catch (ParseException | SipException | InvalidArgumentException e) {
  258 + e.printStackTrace();
  259 + }
  260 + }));
  261 + if (logger.isDebugEnabled()) {
  262 + logger.debug(playResult.getResult().toString());
  263 + }
  264 +
  265 + }else if (gbStream != null) {
  266 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  267 + gbStream.getApp(), gbStream.getStream(), channelId,
  268 + mediaTransmissionTCP);
  269 +
  270 + if (tcpActive != null) {
  271 + sendRtpItem.setTcpActive(tcpActive);
  272 + }
  273 + if (sendRtpItem == null) {
  274 + logger.warn("服务器端口资源不足");
  275 + responseAck(evt, Response.BUSY_HERE);
  276 + return;
  277 + }
  278 +
  279 + // 写入redis, 超时时回复
  280 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  281 +
  282 + sendRtpItem.setStatus(1);
  283 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  284 + // TODO 添加对tcp的支持
  285 + StringBuffer content = new StringBuffer(200);
  286 + content.append("v=0\r\n");
  287 + content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  288 + content.append("s=Play\r\n");
  289 + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  290 + content.append("t=0 0\r\n");
  291 + content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
  292 + content.append("a=sendonly\r\n");
  293 + content.append("a=rtpmap:96 PS/90000\r\n");
  294 + content.append("y="+ ssrc + "\r\n");
  295 + content.append("f=\r\n");
  296 +
  297 + try {
  298 + responseAck(evt, content.toString());
  299 + } catch (SipException e) {
  300 + e.printStackTrace();
  301 + } catch (InvalidArgumentException e) {
  302 + e.printStackTrace();
  303 + } catch (ParseException e) {
  304 + e.printStackTrace();
  305 + }
  306 + }
  307 +
  308 + } else {
  309 + // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
  310 + Device device = storager.queryVideoDevice(requesterId);
  311 + if (device != null) {
  312 + logger.info("收到设备" + requesterId + "的语音广播Invite请求");
  313 + responseAck(evt, Response.TRYING);
  314 +
  315 + String contentString = new String(request.getRawContent());
  316 + // jainSip不支持y=字段, 移除移除以解析。
  317 + String substring = contentString;
  318 + String ssrc = "0000000404";
  319 + int ssrcIndex = contentString.indexOf("y=");
  320 + if (ssrcIndex > 0) {
  321 + substring = contentString.substring(0, ssrcIndex);
  322 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  323 + }
  324 + ssrcIndex = substring.indexOf("f=");
  325 + if (ssrcIndex > 0) {
  326 + substring = contentString.substring(0, ssrcIndex);
  327 + }
  328 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  329 +
  330 + // 获取支持的格式
  331 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  332 + // 查看是否支持PS 负载96
  333 + int port = -1;
  334 + //boolean recvonly = false;
  335 + boolean mediaTransmissionTCP = false;
  336 + Boolean tcpActive = null;
  337 + for (int i = 0; i < mediaDescriptions.size(); i++) {
  338 + MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
  339 + Media media = mediaDescription.getMedia();
  340 +
  341 + Vector mediaFormats = media.getMediaFormats(false);
  342 + if (mediaFormats.contains("8")) {
  343 + port = media.getMediaPort();
  344 + String protocol = media.getProtocol();
  345 + // 区分TCP发流还是udp, 当前默认udp
  346 + if ("TCP/RTP/AVP".equals(protocol)) {
  347 + String setup = mediaDescription.getAttribute("setup");
  348 + if (setup != null) {
  349 + mediaTransmissionTCP = true;
  350 + if ("active".equals(setup)) {
  351 + tcpActive = true;
  352 + } else if ("passive".equals(setup)) {
  353 + tcpActive = false;
  354 + }
  355 + }
  356 + }
  357 + break;
  358 + }
  359 + }
  360 + if (port == -1) {
  361 + logger.info("不支持的媒体格式,返回415");
  362 + // 回复不支持的格式
  363 + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
  364 + return;
  365 + }
  366 + String username = sdp.getOrigin().getUsername();
  367 + String addressStr = sdp.getOrigin().getAddress();
  368 + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);
  369 +
  370 + } else {
  371 + logger.warn("来自无效设备/平台的请求");
  372 + responseAck(evt, Response.BAD_REQUEST);
  373 + }
  374 + }
  375 +
  376 + } catch (SipException | InvalidArgumentException | ParseException e) {
  377 + e.printStackTrace();
  378 + logger.warn("sdp解析错误");
  379 + e.printStackTrace();
  380 + } catch (SdpParseException e) {
  381 + e.printStackTrace();
  382 + } catch (SdpException e) {
  383 + e.printStackTrace();
  384 + }
  385 + }
  386 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/MessageRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.VManageBootstrap;
  5 +import com.genersoft.iot.vmp.common.StreamInfo;
  6 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
  7 +import com.genersoft.iot.vmp.conf.SipConfig;
  8 +import com.genersoft.iot.vmp.conf.UserSetup;
  9 +import com.genersoft.iot.vmp.gb28181.bean.*;
  10 +import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
  11 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  12 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  13 +import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread;
  14 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  15 +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  16 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  17 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
  18 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  19 +import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
  20 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  21 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  22 +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
  23 +import com.genersoft.iot.vmp.service.IDeviceAlarmService;
  24 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  25 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  26 +import com.genersoft.iot.vmp.utils.GpsUtil;
  27 +import com.genersoft.iot.vmp.utils.SpringBeanFactory;
  28 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  29 +import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
  30 +import gov.nist.javax.sip.SipStackImpl;
  31 +import gov.nist.javax.sip.address.SipUri;
  32 +import org.dom4j.DocumentException;
  33 +import org.dom4j.Element;
  34 +import org.slf4j.Logger;
  35 +import org.slf4j.LoggerFactory;
  36 +import org.springframework.beans.factory.annotation.Autowired;
  37 +import org.springframework.stereotype.Component;
  38 +import org.springframework.util.StringUtils;
  39 +
  40 +import javax.sip.*;
  41 +import javax.sip.address.SipURI;
  42 +import javax.sip.header.FromHeader;
  43 +import javax.sip.header.HeaderAddress;
  44 +import javax.sip.header.ToHeader;
  45 +import javax.sip.message.Response;
  46 +import java.text.ParseException;
  47 +import java.util.ArrayList;
  48 +import java.util.Iterator;
  49 +import java.util.List;
  50 +import java.util.UUID;
  51 +
  52 +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
  53 +
  54 +/**
  55 + * @description:MESSAGE请求处理器
  56 + * @author: swwheihei
  57 + * @date: 2020年5月3日 下午5:32:41
  58 + */
  59 +@SuppressWarnings(value={"unchecked", "rawtypes"})
  60 +@Component
  61 +public class MessageRequestProcessor extends SIPRequestProcessorAbstract {
  62 +
  63 + public static volatile List<String> threadNameList = new ArrayList();
  64 + private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
  65 +
  66 + private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";
  67 + private static final String MESSAGE_KEEP_ALIVE = "Keepalive";
  68 + private static final String MESSAGE_CONFIG_DOWNLOAD = "ConfigDownload";
  69 + private static final String MESSAGE_CATALOG = "Catalog";
  70 + private static final String MESSAGE_DEVICE_INFO = "DeviceInfo";
  71 + private static final String MESSAGE_ALARM = "Alarm";
  72 + private static final String MESSAGE_RECORD_INFO = "RecordInfo";
  73 + private static final String MESSAGE_MEDIA_STATUS = "MediaStatus";
  74 + private static final String MESSAGE_BROADCAST = "Broadcast";
  75 + private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
  76 + private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl";
  77 + private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig";
  78 + private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
  79 + private static final String MESSAGE_PRESET_QUERY = "PresetQuery";
  80 + private String method = "MESSAGE";
  81 +
  82 + @Autowired
  83 + private UserSetup userSetup;
  84 +
  85 + @Autowired
  86 + private SIPCommander cmder;
  87 +
  88 + @Autowired
  89 + private SipConfig config;
  90 +
  91 + @Autowired
  92 + private SIPCommanderFroPlatform cmderFroPlatform;
  93 +
  94 + @Autowired
  95 + private IVideoManagerStorager storager;
  96 +
  97 + @Autowired
  98 + private IRedisCatchStorage redisCatchStorage;
  99 +
  100 + @Autowired
  101 + private EventPublisher publisher;
  102 +
  103 + @Autowired
  104 + private RedisUtil redis;
  105 +
  106 + @Autowired
  107 + private DeferredResultHolder deferredResultHolder;
  108 +
  109 + @Autowired
  110 + private DeviceOffLineDetector offLineDetector;
  111 +
  112 + @Autowired
  113 + private IDeviceAlarmService deviceAlarmService;
  114 +
  115 + @Autowired
  116 + private SIPProcessorObserver sipProcessorObserver;
  117 +
  118 + @Override
  119 + public void afterPropertiesSet() throws Exception {
  120 + // 添加消息处理的订阅
  121 + sipProcessorObserver.addRequestProcessor(method, this);
  122 + }
  123 +
  124 + /**
  125 + * 处理MESSAGE请求
  126 + *
  127 + * @param evt
  128 + */
  129 + @Override
  130 + public void process(RequestEvent evt) {
  131 +
  132 + try {
  133 + Element rootElement = getRootElement(evt);
  134 + String cmd = getText(rootElement, "CmdType");
  135 +
  136 + if (MESSAGE_KEEP_ALIVE.equals(cmd)) {
  137 + logger.debug("接收到KeepAlive消息");
  138 + processMessageKeepAlive(evt);
  139 + } else if (MESSAGE_CONFIG_DOWNLOAD.equals(cmd)) {
  140 + logger.debug("接收到ConfigDownload消息");
  141 + processMessageConfigDownload(evt);
  142 + } else if (MESSAGE_CATALOG.equals(cmd)) {
  143 + logger.debug("接收到Catalog消息");
  144 + processMessageCatalogList(evt);
  145 + } else if (MESSAGE_DEVICE_INFO.equals(cmd)) {
  146 + // DeviceInfo消息处理
  147 + processMessageDeviceInfo(evt);
  148 + } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) {
  149 + // DeviceStatus消息处理
  150 + processMessageDeviceStatus(evt);
  151 + } else if (MESSAGE_DEVICE_CONTROL.equals(cmd)) {
  152 + logger.debug("接收到DeviceControl消息");
  153 + processMessageDeviceControl(evt);
  154 + } else if (MESSAGE_DEVICE_CONFIG.equals(cmd)) {
  155 + logger.info("接收到DeviceConfig消息");
  156 + processMessageDeviceConfig(evt);
  157 + } else if (MESSAGE_ALARM.equals(cmd)) {
  158 + logger.debug("接收到Alarm消息");
  159 + processMessageAlarm(evt);
  160 + } else if (MESSAGE_RECORD_INFO.equals(cmd)) {
  161 + logger.debug("接收到RecordInfo消息");
  162 + processMessageRecordInfo(evt);
  163 + }else if (MESSAGE_MEDIA_STATUS.equals(cmd)) {
  164 + logger.debug("接收到MediaStatus消息");
  165 + processMessageMediaStatus(evt);
  166 + } else if (MESSAGE_MOBILE_POSITION.equals(cmd)) {
  167 + logger.debug("接收到MobilePosition消息");
  168 + processMessageMobilePosition(evt);
  169 + } else if (MESSAGE_PRESET_QUERY.equals(cmd)) {
  170 + logger.debug("接收到PresetQuery消息");
  171 + processMessagePresetQuery(evt);
  172 + } else if (MESSAGE_BROADCAST.equals(cmd)) {
  173 + // Broadcast消息处理
  174 + processMessageBroadcast(evt);
  175 + } else {
  176 + logger.debug("接收到消息:" + cmd);
  177 + responseAck(evt, Response.OK);
  178 + }
  179 + } catch (DocumentException | SipException |InvalidArgumentException | ParseException e) {
  180 + e.printStackTrace();
  181 + }
  182 + }
  183 +
  184 + /**
  185 + * 处理MobilePosition移动位置消息
  186 + *
  187 + * @param evt
  188 + */
  189 + private void processMessageMobilePosition(RequestEvent evt) {
  190 + try {
  191 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  192 + Device device = storager.queryVideoDevice(deviceId);
  193 + if (device == null) {
  194 + logger.warn("处理MobilePosition移动位置消息时未找到设备信息");
  195 + responseAck(evt, Response.NOT_FOUND);
  196 + return;
  197 + }
  198 + Element rootElement = getRootElement(evt, device.getCharset());
  199 +
  200 + MobilePosition mobilePosition = new MobilePosition();
  201 + if (!StringUtils.isEmpty(device.getName())) {
  202 + mobilePosition.setDeviceName(device.getName());
  203 + }
  204 + mobilePosition.setDeviceId(deviceId);
  205 + mobilePosition.setChannelId(getText(rootElement, "DeviceID"));
  206 + mobilePosition.setTime(getText(rootElement, "Time"));
  207 + mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude")));
  208 + mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude")));
  209 + if (NumericUtil.isDouble(getText(rootElement, "Speed"))) {
  210 + mobilePosition.setSpeed(Double.parseDouble(getText(rootElement, "Speed")));
  211 + } else {
  212 + mobilePosition.setSpeed(0.0);
  213 + }
  214 + if (NumericUtil.isDouble(getText(rootElement, "Direction"))) {
  215 + mobilePosition.setDirection(Double.parseDouble(getText(rootElement, "Direction")));
  216 + } else {
  217 + mobilePosition.setDirection(0.0);
  218 + }
  219 + if (NumericUtil.isDouble(getText(rootElement, "Altitude"))) {
  220 + mobilePosition.setAltitude(Double.parseDouble(getText(rootElement, "Altitude")));
  221 + } else {
  222 + mobilePosition.setAltitude(0.0);
  223 + }
  224 + mobilePosition.setReportSource("Mobile Position");
  225 + BaiduPoint bp = new BaiduPoint();
  226 + bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
  227 + logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
  228 + mobilePosition.setGeodeticSystem("BD-09");
  229 + mobilePosition.setCnLng(bp.getBdLng());
  230 + mobilePosition.setCnLat(bp.getBdLat());
  231 + if (!userSetup.getSavePositionHistory()) {
  232 + storager.clearMobilePositionsByDeviceId(deviceId);
  233 + }
  234 + storager.insertMobilePosition(mobilePosition);
  235 + //回复 200 OK
  236 + responseAck(evt, Response.OK);
  237 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  238 + e.printStackTrace();
  239 + }
  240 + }
  241 +
  242 + /**
  243 + * 处理DeviceStatus设备状态Message
  244 + *
  245 + * @param evt
  246 + */
  247 + private void processMessageDeviceStatus(RequestEvent evt) {
  248 + try {
  249 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  250 + Device device = storager.queryVideoDevice(deviceId);
  251 + if (device == null) {
  252 + logger.warn("处理DeviceStatus设备状态Message时未找到设备信息");
  253 + responseAck(evt, Response.NOT_FOUND);
  254 + return;
  255 + }
  256 + Element rootElement = getRootElement(evt);
  257 + String name = rootElement.getName();
  258 + Element deviceIdElement = rootElement.element("DeviceID");
  259 + String channelId = deviceIdElement.getText();
  260 + if (name.equalsIgnoreCase("Query")) { // 区分是Response——查询响应,还是Query——查询请求
  261 + logger.info("接收到DeviceStatus查询消息");
  262 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
  263 + String platformId = ((SipUri) fromHeader.getAddress().getURI()).getUser();
  264 + if (platformId == null) {
  265 + responseAck(evt, Response.NOT_FOUND);
  266 + return;
  267 + } else {
  268 + // 回复200 OK
  269 + responseAck(evt, Response.OK);
  270 + String sn = rootElement.element("SN").getText();
  271 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
  272 + cmderFroPlatform.deviceStatusResponse(parentPlatform, sn, fromHeader.getTag());
  273 + }
  274 + } else {
  275 + logger.info("接收到DeviceStatus应答消息");
  276 + // 检查设备是否存在, 不存在则不回复
  277 + if (storager.exists(deviceId)) {
  278 + // 回复200 OK
  279 + responseAck(evt, Response.OK);
  280 + JSONObject json = new JSONObject();
  281 + XmlUtil.node2Json(rootElement, json);
  282 + if (logger.isDebugEnabled()) {
  283 + logger.debug(json.toJSONString());
  284 + }
  285 + RequestMessage msg = new RequestMessage();
  286 + msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId + channelId);
  287 + msg.setData(json);
  288 + deferredResultHolder.invokeAllResult(msg);
  289 +
  290 + if (offLineDetector.isOnline(deviceId)) {
  291 + publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
  292 + } else {
  293 + }
  294 + }
  295 + }
  296 +
  297 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  298 + e.printStackTrace();
  299 + }
  300 + }
  301 +
  302 + /**
  303 + * 处理DeviceControl设备状态Message
  304 + *
  305 + * @param evt
  306 + */
  307 + private void processMessageDeviceControl(RequestEvent evt) {
  308 + try {
  309 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  310 + Device device = storager.queryVideoDevice(deviceId);
  311 + if (device == null) {
  312 + logger.warn("处理DeviceControl设备状态Message未找到设备信息");
  313 + responseAck(evt, Response.NOT_FOUND);
  314 + return;
  315 + }
  316 + Element rootElement = getRootElement(evt);
  317 + String channelId = getText(rootElement, "DeviceID");
  318 + //String result = getText(rootElement, "Result");
  319 + // 回复200 OK
  320 + responseAck(evt, Response.OK);
  321 + if (rootElement.getName().equals("Response")) {//} !StringUtils.isEmpty(result)) {
  322 + // 此处是对本平台发出DeviceControl指令的应答
  323 + JSONObject json = new JSONObject();
  324 + XmlUtil.node2Json(rootElement, json);
  325 + if (logger.isDebugEnabled()) {
  326 + logger.debug(json.toJSONString());
  327 + }
  328 + RequestMessage msg = new RequestMessage();
  329 + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId;
  330 + msg.setKey(key);
  331 + msg.setData(json);
  332 + deferredResultHolder.invokeAllResult(msg);
  333 + } else {
  334 + // 此处是上级发出的DeviceControl指令
  335 + String platformId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
  336 + String targetGBId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
  337 + // 远程启动功能
  338 + if (!StringUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
  339 + if (deviceId.equals(targetGBId)) {
  340 + // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
  341 + logger.info("执行远程启动本平台命令");
  342 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
  343 + cmderFroPlatform.unregister(parentPlatform, null, null);
  344 +
  345 + Thread restartThread = new Thread(new Runnable() {
  346 + @Override
  347 + public void run() {
  348 + try {
  349 + Thread.sleep(3000);
  350 + SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
  351 + SipStackImpl stack = (SipStackImpl)up.getSipStack();
  352 + stack.stop();
  353 + Iterator listener = stack.getListeningPoints();
  354 + while (listener.hasNext()) {
  355 + stack.deleteListeningPoint((ListeningPoint) listener.next());
  356 + }
  357 + Iterator providers = stack.getSipProviders();
  358 + while (providers.hasNext()) {
  359 + stack.deleteSipProvider((SipProvider) providers.next());
  360 + }
  361 + VManageBootstrap.restart();
  362 + } catch (InterruptedException ignored) {
  363 + } catch (ObjectInUseException e) {
  364 + e.printStackTrace();
  365 + }
  366 + }
  367 + });
  368 +
  369 + restartThread.setDaemon(false);
  370 + restartThread.start();
  371 + } else {
  372 + // 远程启动指定设备
  373 + }
  374 + }
  375 + // 云台/前端控制命令
  376 + if (!StringUtils.isEmpty(getText(rootElement,"PTZCmd")) && !deviceId.equals(targetGBId)) {
  377 + String cmdString = getText(rootElement,"PTZCmd");
  378 + Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, deviceId);
  379 + cmder.fronEndCmd(deviceForPlatform, deviceId, cmdString);
  380 + }
  381 + }
  382 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  383 + e.printStackTrace();
  384 + }
  385 + }
  386 +
  387 + /**
  388 + * 处理DeviceConfig设备状态Message
  389 + *
  390 + * @param evt
  391 + */
  392 + private void processMessageDeviceConfig(RequestEvent evt) {
  393 + try {
  394 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  395 + // 查询设备是否存在
  396 + Device device = storager.queryVideoDevice(deviceId);
  397 + if (device == null) {
  398 + logger.warn("处理DeviceConfig设备状态Message消息时未找到设备信息");
  399 + responseAck(evt, Response.NOT_FOUND);
  400 + return;
  401 + }
  402 + Element rootElement = getRootElement(evt);
  403 + String channelId = getText(rootElement, "DeviceID");
  404 + // 回复200 OK
  405 + responseAck(evt, Response.OK);
  406 + if (rootElement.getName().equals("Response")) {
  407 + // 此处是对本平台发出DeviceControl指令的应答
  408 + JSONObject json = new JSONObject();
  409 + XmlUtil.node2Json(rootElement, json);
  410 + if (logger.isDebugEnabled()) {
  411 + logger.debug(json.toJSONString());
  412 + }
  413 + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + deviceId + channelId;
  414 + RequestMessage msg = new RequestMessage();
  415 + msg.setKey(key);
  416 + msg.setData(json);
  417 + deferredResultHolder.invokeAllResult(msg);
  418 + } else {
  419 + // 此处是上级发出的DeviceConfig指令
  420 + }
  421 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  422 + e.printStackTrace();
  423 + }
  424 + }
  425 +
  426 + /**
  427 + * 处理ConfigDownload设备状态Message
  428 + *
  429 + * @param evt
  430 + */
  431 + private void processMessageConfigDownload(RequestEvent evt) {
  432 + try {
  433 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  434 + // 查询设备是否存在
  435 + Device device = storager.queryVideoDevice(deviceId);
  436 + if (device == null) {
  437 + logger.warn("处理ConfigDownload设备状态Message时未找到设备信息");
  438 + responseAck(evt, Response.NOT_FOUND);
  439 + return;
  440 + }
  441 + Element rootElement = getRootElement(evt);
  442 + String channelId = getText(rootElement, "DeviceID");
  443 + String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + deviceId + channelId;
  444 + // 回复200 OK
  445 + responseAck(evt, Response.OK);
  446 + if (rootElement.getName().equals("Response")) {
  447 + // 此处是对本平台发出DeviceControl指令的应答
  448 + JSONObject json = new JSONObject();
  449 + XmlUtil.node2Json(rootElement, json);
  450 + if (logger.isDebugEnabled()) {
  451 + logger.debug(json.toJSONString());
  452 + }
  453 + RequestMessage msg = new RequestMessage();
  454 + msg.setKey(key);
  455 + msg.setData(json);
  456 + deferredResultHolder.invokeAllResult(msg);
  457 + } else {
  458 + // 此处是上级发出的DeviceConfig指令
  459 + }
  460 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  461 + e.printStackTrace();
  462 + }
  463 + }
  464 +
  465 + /**
  466 + * 处理PresetQuery预置位列表Message
  467 + *
  468 + * @param evt
  469 + */
  470 + private void processMessagePresetQuery(RequestEvent evt) {
  471 + try {
  472 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  473 + // 查询设备是否存在
  474 + Device device = storager.queryVideoDevice(deviceId);
  475 + if (device == null) {
  476 + logger.warn("处理PresetQuery预置位列表Message时未找到设备信息");
  477 + responseAck(evt, Response.NOT_FOUND);
  478 + return;
  479 + }
  480 + Element rootElement = getRootElement(evt);
  481 + String channelId = getText(rootElement, "DeviceID");
  482 + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + deviceId + channelId;
  483 + // 回复200 OK
  484 + responseAck(evt, Response.OK);
  485 + if (rootElement.getName().equals("Response")) {// !StringUtils.isEmpty(result)) {
  486 + // 此处是对本平台发出DeviceControl指令的应答
  487 + JSONObject json = new JSONObject();
  488 + XmlUtil.node2Json(rootElement, json);
  489 + if (logger.isDebugEnabled()) {
  490 + logger.debug(json.toJSONString());
  491 + }
  492 + RequestMessage msg = new RequestMessage();
  493 + msg.setKey(key);
  494 + msg.setData(json);
  495 + deferredResultHolder.invokeAllResult(msg);
  496 + } else {
  497 + // 此处是上级发出的DeviceControl指令
  498 + }
  499 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  500 + e.printStackTrace();
  501 + }
  502 + }
  503 +
  504 + /**
  505 + * 处理DeviceInfo设备信息Message
  506 + *
  507 + * @param evt
  508 + */
  509 + private void processMessageDeviceInfo(RequestEvent evt) {
  510 + try {
  511 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  512 + // 查询设备是否存在
  513 + Device device = storager.queryVideoDevice(deviceId);
  514 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(deviceId);
  515 +
  516 + Element rootElement = getRootElement(evt);
  517 + String requestName = rootElement.getName();
  518 + Element deviceIdElement = rootElement.element("DeviceID");
  519 + String channelId = deviceIdElement.getTextTrim();
  520 + String key = DeferredResultHolder.CALLBACK_CMD_DEVICEINFO + deviceId + channelId;
  521 + if (device != null ) {
  522 + rootElement = getRootElement(evt, device.getCharset());
  523 + }
  524 + if (requestName.equals("Query")) {
  525 + logger.info("接收到DeviceInfo查询消息");
  526 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
  527 + if (parentPlatform == null) {
  528 + responseAck(evt, Response.NOT_FOUND);
  529 + return;
  530 + } else {
  531 + // 回复200 OK
  532 + responseAck(evt, Response.OK);
  533 + String sn = rootElement.element("SN").getText();
  534 + cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
  535 + }
  536 + } else {
  537 + logger.debug("接收到DeviceInfo应答消息");
  538 + if (device == null) {
  539 + logger.warn("处理DeviceInfo设备信息Message时未找到设备信息");
  540 + responseAck(evt, Response.NOT_FOUND);
  541 + return;
  542 + }
  543 +
  544 + device.setName(getText(rootElement, "DeviceName"));
  545 +
  546 + device.setManufacturer(getText(rootElement, "Manufacturer"));
  547 + device.setModel(getText(rootElement, "Model"));
  548 + device.setFirmware(getText(rootElement, "Firmware"));
  549 + if (StringUtils.isEmpty(device.getStreamMode())) {
  550 + device.setStreamMode("UDP");
  551 + }
  552 + storager.updateDevice(device);
  553 +
  554 + RequestMessage msg = new RequestMessage();
  555 + msg.setKey(key);
  556 + msg.setData(device);
  557 + deferredResultHolder.invokeAllResult(msg);
  558 + // 回复200 OK
  559 + responseAck(evt, Response.OK);
  560 + if (offLineDetector.isOnline(deviceId)) {
  561 + publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
  562 + }
  563 + }
  564 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  565 + e.printStackTrace();
  566 + }
  567 + }
  568 +
  569 + /***
  570 + * 收到catalog设备目录列表请求 处理
  571 + *
  572 + * @param evt
  573 + */
  574 + private void processMessageCatalogList(RequestEvent evt) {
  575 + try {
  576 +
  577 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  578 + // 查询设备是否存在
  579 + Device device = storager.queryVideoDevice(deviceId);
  580 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(deviceId);
  581 +
  582 +
  583 + Element rootElement = getRootElement(evt);
  584 + String name = rootElement.getName();
  585 + Element deviceIdElement = rootElement.element("DeviceID");
  586 + String channelId = deviceIdElement.getText();
  587 + Element deviceListElement = rootElement.element("DeviceList");
  588 + String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId;
  589 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
  590 + if (name.equalsIgnoreCase("Query")) { // 区分是Response——查询响应,还是Query——查询请求
  591 + // TODO 后续将代码拆分
  592 + if (parentPlatform == null) {
  593 + responseAck(evt, Response.NOT_FOUND);
  594 + return;
  595 + } else {
  596 + // 回复200 OK
  597 + responseAck(evt, Response.OK);
  598 +
  599 + Element snElement = rootElement.element("SN");
  600 + String sn = snElement.getText();
  601 + // 准备回复通道信息
  602 + List<ChannelReduce> channelReduces = storager.queryChannelListInParentPlatform(parentPlatform.getServerGBId());
  603 + // 查询关联的直播通道
  604 + List<GbStream> gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId());
  605 + int size = channelReduces.size() + gbStreams.size();
  606 + // 回复级联的通道
  607 + if (channelReduces.size() > 0) {
  608 + for (ChannelReduce channelReduce : channelReduces) {
  609 + DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
  610 + cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
  611 + }
  612 + }
  613 + // 回复直播的通道
  614 + if (gbStreams.size() > 0) {
  615 + for (GbStream gbStream : gbStreams) {
  616 + DeviceChannel deviceChannel = new DeviceChannel();
  617 + deviceChannel.setChannelId(gbStream.getGbId());
  618 + deviceChannel.setName(gbStream.getName());
  619 + deviceChannel.setLongitude(gbStream.getLongitude());
  620 + deviceChannel.setLatitude(gbStream.getLatitude());
  621 + deviceChannel.setDeviceId(parentPlatform.getDeviceGBId());
  622 + deviceChannel.setManufacture("wvp-pro");
  623 + deviceChannel.setStatus(gbStream.isStatus()?1:0);
  624 +// deviceChannel.setParentId(parentPlatform.getDeviceGBId());
  625 + deviceChannel.setRegisterWay(1);
  626 + deviceChannel.setCivilCode(config.getDomain());
  627 + deviceChannel.setModel("live");
  628 + deviceChannel.setOwner("wvp-pro");
  629 +// deviceChannel.setAddress("test");
  630 + deviceChannel.setParental(0);
  631 + deviceChannel.setSecrecy("0");
  632 + deviceChannel.setSecrecy("0");
  633 +
  634 + cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
  635 + }
  636 + }
  637 + if (size == 0) {
  638 + // 回复无通道
  639 + cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), size);
  640 + }
  641 + }
  642 +
  643 +
  644 + } else {
  645 + if (device == null) {
  646 + logger.warn("收到catalog设备目录列表请求时未找到设备信息");
  647 + responseAck(evt, Response.NOT_FOUND);
  648 + return;
  649 + }
  650 + deviceListElement = getRootElement(evt, device.getCharset()).element("DeviceList");
  651 + Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
  652 + if (deviceListIterator != null) {
  653 +
  654 + // 遍历DeviceList
  655 + while (deviceListIterator.hasNext()) {
  656 + Element itemDevice = deviceListIterator.next();
  657 + Element channelDeviceElement = itemDevice.element("DeviceID");
  658 + if (channelDeviceElement == null) {
  659 + continue;
  660 + }
  661 + String channelDeviceId = channelDeviceElement.getText();
  662 + Element channdelNameElement = itemDevice.element("Name");
  663 + String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
  664 + Element statusElement = itemDevice.element("Status");
  665 + String status = statusElement != null ? statusElement.getText().toString() : "ON";
  666 + DeviceChannel deviceChannel = new DeviceChannel();
  667 + deviceChannel.setName(channelName);
  668 + deviceChannel.setChannelId(channelDeviceId);
  669 + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
  670 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
  671 + deviceChannel.setStatus(1);
  672 + }
  673 + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
  674 + deviceChannel.setStatus(0);
  675 + }
  676 +
  677 + deviceChannel.setManufacture(getText(itemDevice, "Manufacturer"));
  678 + deviceChannel.setModel(getText(itemDevice, "Model"));
  679 + deviceChannel.setOwner(getText(itemDevice, "Owner"));
  680 + deviceChannel.setCivilCode(getText(itemDevice, "CivilCode"));
  681 + deviceChannel.setBlock(getText(itemDevice, "Block"));
  682 + deviceChannel.setAddress(getText(itemDevice, "Address"));
  683 + if (getText(itemDevice, "Parental") == null || getText(itemDevice, "Parental") == "") {
  684 + deviceChannel.setParental(0);
  685 + } else {
  686 + deviceChannel.setParental(Integer.parseInt(getText(itemDevice, "Parental")));
  687 + }
  688 + deviceChannel.setParentId(getText(itemDevice, "ParentID"));
  689 + if (getText(itemDevice, "SafetyWay") == null || getText(itemDevice, "SafetyWay") == "") {
  690 + deviceChannel.setSafetyWay(0);
  691 + } else {
  692 + deviceChannel.setSafetyWay(Integer.parseInt(getText(itemDevice, "SafetyWay")));
  693 + }
  694 + if (getText(itemDevice, "RegisterWay") == null || getText(itemDevice, "RegisterWay") == "") {
  695 + deviceChannel.setRegisterWay(1);
  696 + } else {
  697 + deviceChannel.setRegisterWay(Integer.parseInt(getText(itemDevice, "RegisterWay")));
  698 + }
  699 + deviceChannel.setCertNum(getText(itemDevice, "CertNum"));
  700 + if (getText(itemDevice, "Certifiable") == null || getText(itemDevice, "Certifiable") == "") {
  701 + deviceChannel.setCertifiable(0);
  702 + } else {
  703 + deviceChannel.setCertifiable(Integer.parseInt(getText(itemDevice, "Certifiable")));
  704 + }
  705 + if (getText(itemDevice, "ErrCode") == null || getText(itemDevice, "ErrCode") == "") {
  706 + deviceChannel.setErrCode(0);
  707 + } else {
  708 + deviceChannel.setErrCode(Integer.parseInt(getText(itemDevice, "ErrCode")));
  709 + }
  710 + deviceChannel.setEndTime(getText(itemDevice, "EndTime"));
  711 + deviceChannel.setSecrecy(getText(itemDevice, "Secrecy"));
  712 + deviceChannel.setIpAddress(getText(itemDevice, "IPAddress"));
  713 + if (getText(itemDevice, "Port") == null || getText(itemDevice, "Port") == "") {
  714 + deviceChannel.setPort(0);
  715 + } else {
  716 + deviceChannel.setPort(Integer.parseInt(getText(itemDevice, "Port")));
  717 + }
  718 + deviceChannel.setPassword(getText(itemDevice, "Password"));
  719 + if (NumericUtil.isDouble(getText(itemDevice, "Longitude"))) {
  720 + deviceChannel.setLongitude(Double.parseDouble(getText(itemDevice, "Longitude")));
  721 + } else {
  722 + deviceChannel.setLongitude(0.00);
  723 + }
  724 + if (NumericUtil.isDouble(getText(itemDevice, "Latitude"))) {
  725 + deviceChannel.setLatitude(Double.parseDouble(getText(itemDevice, "Latitude")));
  726 + } else {
  727 + deviceChannel.setLatitude(0.00);
  728 + }
  729 + if (getText(itemDevice, "PTZType") == null || getText(itemDevice, "PTZType") == "") {
  730 + deviceChannel.setPTZType(0);
  731 + } else {
  732 + deviceChannel.setPTZType(Integer.parseInt(getText(itemDevice, "PTZType")));
  733 + }
  734 + deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
  735 + storager.updateChannel(device.getDeviceId(), deviceChannel);
  736 + }
  737 +
  738 + RequestMessage msg = new RequestMessage();
  739 + msg.setKey(key);
  740 + msg.setData(device);
  741 + deferredResultHolder.invokeAllResult(msg);
  742 + // 回复200 OK
  743 + responseAck(evt, Response.OK);
  744 + if (offLineDetector.isOnline(deviceId)) {
  745 + publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
  746 + }
  747 + }
  748 + }
  749 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  750 + e.printStackTrace();
  751 + }
  752 + }
  753 +
  754 + /***
  755 + * 收到alarm设备报警信息 处理
  756 + *
  757 + * @param evt
  758 + */
  759 + private void processMessageAlarm(RequestEvent evt) {
  760 + try {
  761 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  762 + Device device = storager.queryVideoDevice(deviceId);
  763 + if (device == null) {
  764 + logger.warn("处理alarm设备报警信息未找到设备信息");
  765 + responseAck(evt, Response.NOT_FOUND);
  766 + return;
  767 + }
  768 + Element rootElement = getRootElement(evt, device.getCharset());
  769 + Element deviceIdElement = rootElement.element("DeviceID");
  770 + String channelId = deviceIdElement.getText().toString();
  771 + String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId + channelId;
  772 + // 回复200 OK
  773 + responseAck(evt, Response.OK);
  774 +
  775 + if (device.getCharset() != null) {
  776 + rootElement = getRootElement(evt, device.getCharset());
  777 + }
  778 +
  779 + if (rootElement.getName().equals("Notify")) { // 处理报警通知
  780 + DeviceAlarm deviceAlarm = new DeviceAlarm();
  781 + deviceAlarm.setDeviceId(deviceId);
  782 + deviceAlarm.setChannelId(channelId);
  783 + deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority"));
  784 + deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod"));
  785 + deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime"));
  786 + if (getText(rootElement, "AlarmDescription") == null) {
  787 + deviceAlarm.setAlarmDescription("");
  788 + } else {
  789 + deviceAlarm.setAlarmDescription(getText(rootElement, "AlarmDescription"));
  790 + }
  791 + if (NumericUtil.isDouble(getText(rootElement, "Longitude"))) {
  792 + deviceAlarm.setLongitude(Double.parseDouble(getText(rootElement, "Longitude")));
  793 + } else {
  794 + deviceAlarm.setLongitude(0.00);
  795 + }
  796 + if (NumericUtil.isDouble(getText(rootElement, "Latitude"))) {
  797 + deviceAlarm.setLatitude(Double.parseDouble(getText(rootElement, "Latitude")));
  798 + } else {
  799 + deviceAlarm.setLatitude(0.00);
  800 + }
  801 +
  802 + if (!StringUtils.isEmpty(deviceAlarm.getAlarmMethod())) {
  803 + if ( deviceAlarm.getAlarmMethod().equals("4")) {
  804 + MobilePosition mobilePosition = new MobilePosition();
  805 + mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
  806 + mobilePosition.setTime(deviceAlarm.getAlarmTime());
  807 + mobilePosition.setLongitude(deviceAlarm.getLongitude());
  808 + mobilePosition.setLatitude(deviceAlarm.getLatitude());
  809 + mobilePosition.setReportSource("GPS Alarm");
  810 + BaiduPoint bp = new BaiduPoint();
  811 + bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
  812 + logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
  813 + mobilePosition.setGeodeticSystem("BD-09");
  814 + mobilePosition.setCnLng(bp.getBdLng());
  815 + mobilePosition.setCnLat(bp.getBdLat());
  816 + if (!userSetup.getSavePositionHistory()) {
  817 + storager.clearMobilePositionsByDeviceId(deviceId);
  818 + }
  819 + storager.insertMobilePosition(mobilePosition);
  820 + }
  821 + }
  822 + logger.debug("存储报警信息、报警分类");
  823 + // 存储报警信息、报警分类
  824 + deviceAlarmService.add(deviceAlarm);
  825 +
  826 + if (offLineDetector.isOnline(deviceId)) {
  827 + publisher.deviceAlarmEventPublish(deviceAlarm);
  828 + }
  829 + } else if (rootElement.getName().equals("Response")) { // 处理报警查询响应
  830 + JSONObject json = new JSONObject();
  831 + XmlUtil.node2Json(rootElement, json);
  832 + if (logger.isDebugEnabled()) {
  833 + logger.debug(json.toJSONString());
  834 + }
  835 + RequestMessage msg = new RequestMessage();
  836 + msg.setKey(key);
  837 + msg.setData(json);
  838 + deferredResultHolder.invokeAllResult(msg);
  839 + }
  840 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  841 + e.printStackTrace();
  842 + }
  843 + }
  844 +
  845 + /***
  846 + * 收到keepalive请求 处理
  847 + *
  848 + * @param evt
  849 + */
  850 + private void processMessageKeepAlive(RequestEvent evt) {
  851 + try {
  852 +
  853 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  854 + // 查询设备是否存在
  855 + Device device = storager.queryVideoDevice(deviceId);
  856 +
  857 + Element rootElement = getRootElement(evt);
  858 + String channelId = getText(rootElement, "DeviceID");
  859 +
  860 + // 检查设备是否存在并在线, 不在线则设置为在线
  861 + if (device != null ) {
  862 + // 回复200 OK
  863 + responseAck(evt, Response.OK);
  864 + publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
  865 + }else{
  866 + logger.warn("收到[ "+deviceId+" ]心跳信息, 但是设备不存在, 回复404");
  867 + Response response = getMessageFactory().createResponse(Response.NOT_FOUND, evt.getRequest());
  868 + ServerTransaction serverTransaction = getServerTransaction(evt);
  869 + serverTransaction.sendResponse(response);
  870 + if (serverTransaction.getDialog() != null) {
  871 + serverTransaction.getDialog().delete();
  872 + }
  873 + }
  874 +
  875 +// if (device != null && device.getOnline() == 1) {
  876 +//
  877 +// if (offLineDetector.isOnline(deviceId)) {
  878 +// publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
  879 +// } else {
  880 +// }
  881 +// }else {
  882 +//// logger.warn("收到[ "+deviceId+" ]心跳信息, 但是设备" + (device == null? "不存在":"离线") + ", 回复401");
  883 +//// Response response = getMessageFactory().createResponse(Response.UNAUTHORIZED, evt.getRequest());
  884 +//// getServerTransaction(evt).sendResponse(response);
  885 +// publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
  886 +//
  887 +// }
  888 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  889 + e.printStackTrace();
  890 + }
  891 + }
  892 +
  893 + /***
  894 + * 处理RecordInfo设备录像列表Message请求 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
  895 + *
  896 + * @param evt
  897 + */
  898 + private void processMessageRecordInfo(RequestEvent evt) {
  899 + try {
  900 +
  901 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  902 + // 查询设备是否存在
  903 + Device device = storager.queryVideoDevice(deviceId);
  904 + if (device == null) {
  905 + logger.warn("处理DeviceInfo设备信息Message时未找到设备信息");
  906 + responseAck(evt, Response.NOT_FOUND);
  907 + return;
  908 + }
  909 +
  910 + // 回复200 OK
  911 + responseAck(evt, Response.OK);
  912 + String uuid = UUID.randomUUID().toString().replace("-", "");
  913 + RecordInfo recordInfo = new RecordInfo();
  914 + Element rootElement = getRootElement(evt);
  915 + Element deviceIdElement = rootElement.element("DeviceID");
  916 + String channelId = deviceIdElement.getText().toString();
  917 + String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + channelId;
  918 + if (device != null ) {
  919 + rootElement = getRootElement(evt, device.getCharset());
  920 + }
  921 + recordInfo.setDeviceId(deviceId);
  922 + recordInfo.setChannelId(channelId);
  923 + recordInfo.setName(getText(rootElement, "Name"));
  924 + if (getText(rootElement, "SumNum")== null || getText(rootElement, "SumNum") =="") {
  925 + recordInfo.setSumNum(0);
  926 + } else {
  927 + recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum")));
  928 + }
  929 + String sn = getText(rootElement, "SN");
  930 + Element recordListElement = rootElement.element("RecordList");
  931 + if (recordListElement == null || recordInfo.getSumNum() == 0) {
  932 + logger.info("无录像数据");
  933 + RequestMessage msg = new RequestMessage();
  934 + msg.setKey(key);
  935 + msg.setData(recordInfo);
  936 + deferredResultHolder.invokeAllResult(msg);
  937 + } else {
  938 + Iterator<Element> recordListIterator = recordListElement.elementIterator();
  939 + List<RecordItem> recordList = new ArrayList<RecordItem>();
  940 + if (recordListIterator != null) {
  941 + RecordItem record = new RecordItem();
  942 + logger.info("处理录像列表数据...");
  943 + // 遍历DeviceList
  944 + while (recordListIterator.hasNext()) {
  945 + Element itemRecord = recordListIterator.next();
  946 + Element recordElement = itemRecord.element("DeviceID");
  947 + if (recordElement == null) {
  948 + logger.info("记录为空,下一个...");
  949 + continue;
  950 + }
  951 + record = new RecordItem();
  952 + record.setDeviceId(getText(itemRecord, "DeviceID"));
  953 + record.setName(getText(itemRecord, "Name"));
  954 + record.setFilePath(getText(itemRecord, "FilePath"));
  955 + record.setAddress(getText(itemRecord, "Address"));
  956 + record.setStartTime(
  957 + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "StartTime")));
  958 + record.setEndTime(
  959 + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "EndTime")));
  960 + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
  961 + : Integer.parseInt(getText(itemRecord, "Secrecy")));
  962 + record.setType(getText(itemRecord, "Type"));
  963 + record.setRecorderId(getText(itemRecord, "RecorderID"));
  964 + recordList.add(record);
  965 + }
  966 + recordInfo.setRecordList(recordList);
  967 + }
  968 +
  969 + // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题
  970 + String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
  971 + redis.set(cacheKey + "_" + uuid, recordList, 90);
  972 + if (!threadNameList.contains(cacheKey)) {
  973 + threadNameList.add(cacheKey);
  974 + CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo);
  975 + chk.setName(cacheKey);
  976 + chk.setDeferredResultHolder(deferredResultHolder);
  977 + chk.setRedis(redis);
  978 + chk.setLogger(logger);
  979 + chk.start();
  980 + if (logger.isDebugEnabled()) {
  981 + logger.debug("Start Thread " + cacheKey + ".");
  982 + }
  983 + } else {
  984 + if (logger.isDebugEnabled()) {
  985 + logger.debug("Thread " + cacheKey + " already started.");
  986 + }
  987 + }
  988 +
  989 + // 存在录像且如果当前录像明细个数小于总条数,说明拆包返回,需要组装,暂不返回
  990 + // if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
  991 + // // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分
  992 + // String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
  993 +
  994 + // redis.set(cacheKey + "_" + uuid, recordList, 90);
  995 + // List<Object> cacheKeys = redis.scan(cacheKey + "_*");
  996 + // List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
  997 + // for (int i = 0; i < cacheKeys.size(); i++) {
  998 + // totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
  999 + // }
  1000 + // if (totalRecordList.size() < recordInfo.getSumNum()) {
  1001 + // logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");
  1002 + // return;
  1003 + // }
  1004 + // logger.info("录像数据已全部获取,共" + recordInfo.getSumNum() + "项");
  1005 + // recordInfo.setRecordList(totalRecordList);
  1006 + // for (int i = 0; i < cacheKeys.size(); i++) {
  1007 + // redis.del(cacheKeys.get(i).toString());
  1008 + // }
  1009 + // }
  1010 + // // 自然顺序排序, 元素进行升序排列
  1011 + // recordInfo.getRecordList().sort(Comparator.naturalOrder());
  1012 + }
  1013 + // 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作
  1014 + // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作
  1015 + // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据
  1016 +
  1017 + // RequestMessage msg = new RequestMessage();
  1018 + // msg.setDeviceId(deviceId);
  1019 + // msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
  1020 + // msg.setData(recordInfo);
  1021 + // deferredResultHolder.invokeResult(msg);
  1022 + // logger.info("处理完成,返回结果");
  1023 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  1024 + e.printStackTrace();
  1025 + }
  1026 + }
  1027 +
  1028 + /**
  1029 + * 收到MediaStatus消息处理
  1030 + *
  1031 + * @param evt
  1032 + */
  1033 + private void processMessageMediaStatus(RequestEvent evt){
  1034 + try {
  1035 +
  1036 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  1037 + // 查询设备是否存在
  1038 + Device device = storager.queryVideoDevice(deviceId);
  1039 + if (device == null) {
  1040 + logger.warn("处理DeviceInfo设备信息Message时未找到设备信息");
  1041 + responseAck(evt, Response.NOT_FOUND);
  1042 + return;
  1043 + }
  1044 +
  1045 + // 回复200 OK
  1046 + responseAck(evt, Response.OK);
  1047 + Element rootElement = getRootElement(evt);
  1048 + String channelId = getText(rootElement, "DeviceID");
  1049 + String NotifyType =getText(rootElement, "NotifyType");
  1050 + if (NotifyType.equals("121")){
  1051 + logger.info("媒体播放完毕,通知关流");
  1052 + StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, "*");
  1053 + if (streamInfo != null) {
  1054 + redisCatchStorage.stopPlayback(streamInfo);
  1055 + cmder.streamByeCmd(streamInfo.getDeviceID(), streamInfo.getChannelId());
  1056 + }
  1057 + }
  1058 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  1059 + e.printStackTrace();
  1060 + }
  1061 + }
  1062 +
  1063 + /**
  1064 + * 处理AudioBroadcast语音广播Message
  1065 + *
  1066 + * @param evt
  1067 + */
  1068 + private void processMessageBroadcast(RequestEvent evt) {
  1069 + try {
  1070 +
  1071 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  1072 + // 查询设备是否存在
  1073 + Device device = storager.queryVideoDevice(deviceId);
  1074 + if (device == null) {
  1075 + logger.warn("处理DeviceInfo设备信息Message时未找到设备信息");
  1076 + responseAck(evt, Response.NOT_FOUND);
  1077 + return;
  1078 + }
  1079 +
  1080 + Element rootElement = getRootElement(evt);
  1081 + String channelId = getText(rootElement, "DeviceID");
  1082 + String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId + channelId;
  1083 + // 回复200 OK
  1084 + responseAck(evt, Response.OK);
  1085 + if (rootElement.getName().equals("Response")) {
  1086 + // 此处是对本平台发出Broadcast指令的应答
  1087 + JSONObject json = new JSONObject();
  1088 + XmlUtil.node2Json(rootElement, json);
  1089 + if (logger.isDebugEnabled()) {
  1090 + logger.debug(json.toJSONString());
  1091 + }
  1092 + RequestMessage msg = new RequestMessage();
  1093 + msg.setKey(key);
  1094 + msg.setData(json);
  1095 + deferredResultHolder.invokeAllResult(msg);
  1096 + } else {
  1097 + // 此处是上级发出的Broadcast指令
  1098 + }
  1099 + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
  1100 + e.printStackTrace();
  1101 + }
  1102 + }
  1103 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
  4 +import com.genersoft.iot.vmp.conf.UserSetup;
  5 +import com.genersoft.iot.vmp.gb28181.bean.*;
  6 +import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
  7 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  9 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  12 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  13 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  14 +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
  15 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  16 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  17 +import com.genersoft.iot.vmp.utils.GpsUtil;
  18 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  19 +import org.dom4j.DocumentException;
  20 +import org.dom4j.Element;
  21 +import org.slf4j.Logger;
  22 +import org.slf4j.LoggerFactory;
  23 +import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.springframework.stereotype.Component;
  25 +import org.springframework.util.StringUtils;
  26 +
  27 +import javax.sip.InvalidArgumentException;
  28 +import javax.sip.RequestEvent;
  29 +import javax.sip.SipException;
  30 +import javax.sip.header.FromHeader;
  31 +import javax.sip.message.Response;
  32 +import java.text.ParseException;
  33 +import java.util.Iterator;
  34 +
  35 +/**
  36 + * @description: Notify请求处理器
  37 + * @author: lawrencehj
  38 + * @date: 2021年1月27日
  39 + */
  40 +@Component
  41 +public class NotifyRequestProcessor extends SIPRequestProcessorAbstract {
  42 +
  43 +
  44 + private final static Logger logger = LoggerFactory.getLogger(NotifyRequestProcessor.class);
  45 +
  46 + @Autowired
  47 + private UserSetup userSetup;
  48 +
  49 + @Autowired
  50 + private IVideoManagerStorager storager;
  51 +
  52 + @Autowired
  53 + private IRedisCatchStorage redisCatchStorage;
  54 +
  55 + @Autowired
  56 + private EventPublisher publisher;
  57 +
  58 + @Autowired
  59 + private DeviceOffLineDetector offLineDetector;
  60 +
  61 + private static final String NOTIFY_CATALOG = "Catalog";
  62 + private static final String NOTIFY_ALARM = "Alarm";
  63 + private static final String NOTIFY_MOBILE_POSITION = "MobilePosition";
  64 + private String method = "NOTIFY";
  65 +
  66 + @Autowired
  67 + private SIPProcessorObserver sipProcessorObserver;
  68 +
  69 + @Override
  70 + public void afterPropertiesSet() throws Exception {
  71 + // 添加消息处理的订阅
  72 + sipProcessorObserver.addRequestProcessor(method, this);
  73 + }
  74 +
  75 + @Override
  76 + public void process(RequestEvent evt) {
  77 + try {
  78 + Element rootElement = getRootElement(evt);
  79 + String cmd = XmlUtil.getText(rootElement, "CmdType");
  80 +
  81 + if (NOTIFY_CATALOG.equals(cmd)) {
  82 + logger.info("接收到Catalog通知");
  83 + processNotifyCatalogList(evt);
  84 + } else if (NOTIFY_ALARM.equals(cmd)) {
  85 + logger.info("接收到Alarm通知");
  86 + processNotifyAlarm(evt);
  87 + } else if (NOTIFY_MOBILE_POSITION.equals(cmd)) {
  88 + logger.info("接收到MobilePosition通知");
  89 + processNotifyMobilePosition(evt);
  90 + } else {
  91 + logger.info("接收到消息:" + cmd);
  92 + responseAck(evt, Response.OK);
  93 + }
  94 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  95 + e.printStackTrace();
  96 + }
  97 + }
  98 +
  99 + /**
  100 + * 处理MobilePosition移动位置Notify
  101 + *
  102 + * @param evt
  103 + */
  104 + private void processNotifyMobilePosition(RequestEvent evt) {
  105 + try {
  106 + // 回复 200 OK
  107 + Element rootElement = getRootElement(evt);
  108 + MobilePosition mobilePosition = new MobilePosition();
  109 + Element deviceIdElement = rootElement.element("DeviceID");
  110 + String deviceId = deviceIdElement.getTextTrim().toString();
  111 + Device device = storager.queryVideoDevice(deviceId);
  112 + if (device != null) {
  113 + if (!StringUtils.isEmpty(device.getName())) {
  114 + mobilePosition.setDeviceName(device.getName());
  115 + }
  116 + }
  117 + mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID"));
  118 + mobilePosition.setTime(XmlUtil.getText(rootElement, "Time"));
  119 + mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
  120 + mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
  121 + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) {
  122 + mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed")));
  123 + } else {
  124 + mobilePosition.setSpeed(0.0);
  125 + }
  126 + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) {
  127 + mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction")));
  128 + } else {
  129 + mobilePosition.setDirection(0.0);
  130 + }
  131 + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) {
  132 + mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude")));
  133 + } else {
  134 + mobilePosition.setAltitude(0.0);
  135 + }
  136 + mobilePosition.setReportSource("Mobile Position");
  137 + BaiduPoint bp = new BaiduPoint();
  138 + bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
  139 + logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
  140 + mobilePosition.setGeodeticSystem("BD-09");
  141 + mobilePosition.setCnLng(bp.getBdLng());
  142 + mobilePosition.setCnLat(bp.getBdLat());
  143 + if (!userSetup.getSavePositionHistory()) {
  144 + storager.clearMobilePositionsByDeviceId(deviceId);
  145 + }
  146 + storager.insertMobilePosition(mobilePosition);
  147 + responseAck(evt, Response.OK);
  148 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  149 + e.printStackTrace();
  150 + }
  151 + }
  152 +
  153 + /***
  154 + * 处理alarm设备报警Notify
  155 + *
  156 + * @param evt
  157 + */
  158 + private void processNotifyAlarm(RequestEvent evt) {
  159 + try {
  160 + Element rootElement = getRootElement(evt);
  161 + Element deviceIdElement = rootElement.element("DeviceID");
  162 + String deviceId = deviceIdElement.getText().toString();
  163 +
  164 + Device device = storager.queryVideoDevice(deviceId);
  165 + if (device == null) {
  166 + return;
  167 + }
  168 + rootElement = getRootElement(evt, device.getCharset());
  169 + DeviceAlarm deviceAlarm = new DeviceAlarm();
  170 + deviceAlarm.setDeviceId(deviceId);
  171 + deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
  172 + deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
  173 + deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
  174 + if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
  175 + deviceAlarm.setAlarmDescription("");
  176 + } else {
  177 + deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription"));
  178 + }
  179 + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) {
  180 + deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
  181 + } else {
  182 + deviceAlarm.setLongitude(0.00);
  183 + }
  184 + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) {
  185 + deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
  186 + } else {
  187 + deviceAlarm.setLatitude(0.00);
  188 + }
  189 +
  190 + if (deviceAlarm.getAlarmMethod().equals("4")) {
  191 + MobilePosition mobilePosition = new MobilePosition();
  192 + mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
  193 + mobilePosition.setTime(deviceAlarm.getAlarmTime());
  194 + mobilePosition.setLongitude(deviceAlarm.getLongitude());
  195 + mobilePosition.setLatitude(deviceAlarm.getLatitude());
  196 + mobilePosition.setReportSource("GPS Alarm");
  197 + BaiduPoint bp = new BaiduPoint();
  198 + bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
  199 + logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
  200 + mobilePosition.setGeodeticSystem("BD-09");
  201 + mobilePosition.setCnLng(bp.getBdLng());
  202 + mobilePosition.setCnLat(bp.getBdLat());
  203 + if (!userSetup.getSavePositionHistory()) {
  204 + storager.clearMobilePositionsByDeviceId(deviceId);
  205 + }
  206 + storager.insertMobilePosition(mobilePosition);
  207 + }
  208 + // TODO: 需要实现存储报警信息、报警分类
  209 +
  210 + // 回复200 OK
  211 + responseAck(evt, Response.OK);
  212 + if (offLineDetector.isOnline(deviceId)) {
  213 + publisher.deviceAlarmEventPublish(deviceAlarm);
  214 + }
  215 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  216 + e.printStackTrace();
  217 + }
  218 + }
  219 +
  220 + /***
  221 + * 处理catalog设备目录列表Notify
  222 + *
  223 + * @param evt
  224 + */
  225 + private void processNotifyCatalogList(RequestEvent evt) {
  226 + try {
  227 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
  228 + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
  229 +
  230 + Element rootElement = getRootElement(evt);
  231 + Element deviceIdElement = rootElement.element("DeviceID");
  232 + String channelId = deviceIdElement.getText();
  233 + Device device = storager.queryVideoDevice(deviceId);
  234 + if (device == null) {
  235 + return;
  236 + }
  237 + if (device != null ) {
  238 + rootElement = getRootElement(evt, device.getCharset());
  239 + }
  240 + Element deviceListElement = rootElement.element("DeviceList");
  241 + if (deviceListElement == null) {
  242 + return;
  243 + }
  244 + Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
  245 + if (deviceListIterator != null) {
  246 +
  247 + // 遍历DeviceList
  248 + while (deviceListIterator.hasNext()) {
  249 + Element itemDevice = deviceListIterator.next();
  250 + Element channelDeviceElement = itemDevice.element("DeviceID");
  251 + if (channelDeviceElement == null) {
  252 + continue;
  253 + }
  254 + String channelDeviceId = channelDeviceElement.getTextTrim();
  255 + Element channdelNameElement = itemDevice.element("Name");
  256 + String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
  257 + Element statusElement = itemDevice.element("Status");
  258 + String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
  259 + DeviceChannel deviceChannel = new DeviceChannel();
  260 + deviceChannel.setName(channelName);
  261 + deviceChannel.setChannelId(channelDeviceId);
  262 + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
  263 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
  264 + deviceChannel.setStatus(1);
  265 + }
  266 + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
  267 + deviceChannel.setStatus(0);
  268 + }
  269 +
  270 + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
  271 + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
  272 + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
  273 + deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
  274 + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
  275 + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
  276 + if (XmlUtil.getText(itemDevice, "Parental") == null
  277 + || XmlUtil.getText(itemDevice, "Parental") == "") {
  278 + deviceChannel.setParental(0);
  279 + } else {
  280 + deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
  281 + }
  282 + deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
  283 + if (XmlUtil.getText(itemDevice, "SafetyWay") == null
  284 + || XmlUtil.getText(itemDevice, "SafetyWay") == "") {
  285 + deviceChannel.setSafetyWay(0);
  286 + } else {
  287 + deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
  288 + }
  289 + if (XmlUtil.getText(itemDevice, "RegisterWay") == null
  290 + || XmlUtil.getText(itemDevice, "RegisterWay") == "") {
  291 + deviceChannel.setRegisterWay(1);
  292 + } else {
  293 + deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
  294 + }
  295 + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
  296 + if (XmlUtil.getText(itemDevice, "Certifiable") == null
  297 + || XmlUtil.getText(itemDevice, "Certifiable") == "") {
  298 + deviceChannel.setCertifiable(0);
  299 + } else {
  300 + deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
  301 + }
  302 + if (XmlUtil.getText(itemDevice, "ErrCode") == null
  303 + || XmlUtil.getText(itemDevice, "ErrCode") == "") {
  304 + deviceChannel.setErrCode(0);
  305 + } else {
  306 + deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
  307 + }
  308 + deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
  309 + deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
  310 + deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
  311 + if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") {
  312 + deviceChannel.setPort(0);
  313 + } else {
  314 + deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
  315 + }
  316 + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
  317 + if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {
  318 + deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
  319 + } else {
  320 + deviceChannel.setLongitude(0.00);
  321 + }
  322 + if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) {
  323 + deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
  324 + } else {
  325 + deviceChannel.setLatitude(0.00);
  326 + }
  327 + if (XmlUtil.getText(itemDevice, "PTZType") == null
  328 + || XmlUtil.getText(itemDevice, "PTZType") == "") {
  329 + deviceChannel.setPTZType(0);
  330 + } else {
  331 + deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
  332 + }
  333 + deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
  334 + storager.updateChannel(device.getDeviceId(), deviceChannel);
  335 + }
  336 +
  337 + // RequestMessage msg = new RequestMessage();
  338 + // msg.setDeviceId(deviceId);
  339 + // msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
  340 + // msg.setData(device);
  341 + // deferredResultHolder.invokeResult(msg);
  342 + // 回复200 OK
  343 + responseAck(evt, Response.OK);
  344 + if (offLineDetector.isOnline(deviceId)) {
  345 + publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
  346 + }
  347 + }
  348 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  349 + e.printStackTrace();
  350 + }
  351 + }
  352 +
  353 +
  354 +
  355 +
  356 + public void setCmder(SIPCommander cmder) {
  357 + }
  358 +
  359 + public void setStorager(IVideoManagerStorager storager) {
  360 + this.storager = storager;
  361 + }
  362 +
  363 + public void setPublisher(EventPublisher publisher) {
  364 + this.publisher = publisher;
  365 + }
  366 +
  367 + public void setRedis(RedisUtil redis) {
  368 + }
  369 +
  370 + public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
  371 + }
  372 +
  373 + public void setOffLineDetector(DeviceOffLineDetector offLineDetector) {
  374 + this.offLineDetector = offLineDetector;
  375 + }
  376 +
  377 + public IRedisCatchStorage getRedisCatchStorage() {
  378 + return redisCatchStorage;
  379 + }
  380 +
  381 + public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
  382 + this.redisCatchStorage = redisCatchStorage;
  383 + }
  384 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
  4 +import com.genersoft.iot.vmp.conf.SipConfig;
  5 +import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
  6 +import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;
  7 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  8 +import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
  9 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  12 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  13 +import gov.nist.javax.sip.RequestEventExt;
  14 +import gov.nist.javax.sip.address.AddressImpl;
  15 +import gov.nist.javax.sip.address.SipUri;
  16 +import gov.nist.javax.sip.header.Expires;
  17 +import gov.nist.javax.sip.header.SIPDateHeader;
  18 +import org.slf4j.Logger;
  19 +import org.slf4j.LoggerFactory;
  20 +import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.springframework.stereotype.Component;
  22 +import org.springframework.util.StringUtils;
  23 +
  24 +import javax.sip.InvalidArgumentException;
  25 +import javax.sip.RequestEvent;
  26 +import javax.sip.ServerTransaction;
  27 +import javax.sip.SipException;
  28 +import javax.sip.header.*;
  29 +import javax.sip.message.Request;
  30 +import javax.sip.message.Response;
  31 +import java.security.NoSuchAlgorithmException;
  32 +import java.text.ParseException;
  33 +import java.util.Calendar;
  34 +import java.util.Locale;
  35 +
  36 +/**
  37 + * @description:收到注册请求 处理
  38 + * @author: swwheihei
  39 + * @date: 2020年5月3日 下午4:47:25
  40 + */
  41 +@Component
  42 +public class RegisterRequestProcessor extends SIPRequestProcessorAbstract {
  43 +
  44 + private Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
  45 +
  46 + public String method = "REGISTER";
  47 +
  48 + @Autowired
  49 + private SipConfig sipConfig;
  50 +
  51 + @Autowired
  52 + private RegisterLogicHandler handler;
  53 +
  54 + @Autowired
  55 + private IVideoManagerStorager storager;
  56 +
  57 + @Autowired
  58 + private EventPublisher publisher;
  59 +
  60 + @Autowired
  61 + private SIPProcessorObserver sipProcessorObserver;
  62 +
  63 + @Override
  64 + public void afterPropertiesSet() throws Exception {
  65 + // 添加消息处理的订阅
  66 + sipProcessorObserver.addRequestProcessor(method, this);
  67 + }
  68 +
  69 + /**
  70 + * 收到注册请求 处理
  71 + * @param evt
  72 + */
  73 + @Override
  74 + public void process(RequestEvent evt) {
  75 + try {
  76 + RequestEventExt evtExt = (RequestEventExt)evt;
  77 + String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
  78 + logger.info("[{}] 收到注册请求,开始处理", requestAddress);
  79 + Request request = evt.getRequest();
  80 +
  81 + Response response = null;
  82 + boolean passwordCorrect = false;
  83 + // 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功
  84 + int registerFlag = 0;
  85 + FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
  86 + AddressImpl address = (AddressImpl) fromHeader.getAddress();
  87 + SipUri uri = (SipUri) address.getURI();
  88 + String deviceId = uri.getUser();
  89 + Device device = storager.queryVideoDevice(deviceId);
  90 + AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
  91 + // 校验密码是否正确
  92 + if (authorhead != null) {
  93 + passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request,
  94 + sipConfig.getPassword());
  95 + }
  96 + if (StringUtils.isEmpty(sipConfig.getPassword())){
  97 + passwordCorrect = true;
  98 + }
  99 +
  100 + // 未携带授权头或者密码错误 回复401
  101 + if (authorhead == null ) {
  102 +
  103 + logger.info("[{}] 未携带授权头 回复401", requestAddress);
  104 + response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
  105 + new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
  106 + }else {
  107 + if (!passwordCorrect){
  108 + // 注册失败
  109 + response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
  110 + response.setReasonPhrase("wrong password");
  111 + logger.info("[{}] 密码/SIP服务器ID错误, 回复403", requestAddress);
  112 + }else {
  113 + // 携带授权头并且密码正确
  114 + response = getMessageFactory().createResponse(Response.OK, request);
  115 + // 添加date头
  116 + SIPDateHeader dateHeader = new SIPDateHeader();
  117 + // 使用自己修改的
  118 + WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
  119 + dateHeader.setDate(wvpSipDate);
  120 + response.addHeader(dateHeader);
  121 +
  122 + ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
  123 + if (expiresHeader == null) {
  124 + response = getMessageFactory().createResponse(Response.BAD_REQUEST, request);
  125 + ServerTransaction serverTransaction = getServerTransaction(evt);
  126 + serverTransaction.sendResponse(response);
  127 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  128 + return;
  129 + }
  130 + // 添加Contact头
  131 + response.addHeader(request.getHeader(ContactHeader.NAME));
  132 + // 添加Expires头
  133 + response.addHeader(request.getExpires());
  134 +
  135 + // 获取到通信地址等信息
  136 + ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
  137 + String received = viaHeader.getReceived();
  138 + int rPort = viaHeader.getRPort();
  139 + // 解析本地地址替代
  140 + if (StringUtils.isEmpty(received) || rPort == -1) {
  141 + received = viaHeader.getHost();
  142 + rPort = viaHeader.getPort();
  143 + }
  144 + //
  145 +
  146 + if (device == null) {
  147 + device = new Device();
  148 + device.setStreamMode("UDP");
  149 + device.setCharset("gb2312");
  150 + device.setDeviceId(deviceId);
  151 + device.setFirsRegister(true);
  152 + }
  153 + device.setIp(received);
  154 + device.setPort(rPort);
  155 + device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
  156 + // 注销成功
  157 + if (expiresHeader.getExpires() == 0) {
  158 + registerFlag = 2;
  159 + }
  160 + // 注册成功
  161 + else {
  162 + device.setExpires(expiresHeader.getExpires());
  163 + registerFlag = 1;
  164 + // 判断TCP还是UDP
  165 + boolean isTcp = false;
  166 + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
  167 + String transport = reqViaHeader.getTransport();
  168 + if (transport.equals("TCP")) {
  169 + isTcp = true;
  170 + }
  171 + device.setTransport(isTcp ? "TCP" : "UDP");
  172 + }
  173 + }
  174 + }
  175 +
  176 + ServerTransaction serverTransaction = getServerTransaction(evt);
  177 + serverTransaction.sendResponse(response);
  178 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  179 + // 注册成功
  180 + // 保存到redis
  181 + // 下发catelog查询目录
  182 + if (registerFlag == 1 ) {
  183 + logger.info("[{}] 注册成功! deviceId:" + device.getDeviceId(), requestAddress);
  184 + publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_REGISTER);
  185 + // 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
  186 + handler.onRegister(device);
  187 + } else if (registerFlag == 2) {
  188 + logger.info("[{}] 注销成功! deviceId:" + device.getDeviceId(), requestAddress);
  189 + publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER);
  190 + }
  191 + } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
  192 + e.printStackTrace();
  193 + }
  194 +
  195 + }
  196 +
  197 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  4 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorAbstract;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.sip.InvalidArgumentException;
  11 +import javax.sip.RequestEvent;
  12 +import javax.sip.ServerTransaction;
  13 +import javax.sip.SipException;
  14 +import javax.sip.header.ExpiresHeader;
  15 +import javax.sip.message.Request;
  16 +import javax.sip.message.Response;
  17 +import java.text.ParseException;
  18 +
  19 +/**
  20 + * @description:SUBSCRIBE请求处理器
  21 + * @author: swwheihei
  22 + * @date: 2020年5月3日 下午5:31:20
  23 + */
  24 +@Component
  25 +public class SubscribeRequestProcessor extends SIPRequestProcessorAbstract {
  26 +
  27 + private Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class);
  28 + private String method = "SUBSCRIBE";
  29 +
  30 + @Autowired
  31 + private SIPProcessorObserver sipProcessorObserver;
  32 +
  33 + @Override
  34 + public void afterPropertiesSet() throws Exception {
  35 + // 添加消息处理的订阅
  36 + sipProcessorObserver.addRequestProcessor(method, this);
  37 + }
  38 +
  39 + /**
  40 + * 处理SUBSCRIBE请求
  41 + *
  42 + * @param evt
  43 + */
  44 + @Override
  45 + public void process(RequestEvent evt) {
  46 + Request request = evt.getRequest();
  47 +
  48 + try {
  49 + Response response = null;
  50 + response = getMessageFactory().createResponse(200, request);
  51 + if (response != null) {
  52 + ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30);
  53 + response.setExpires(expireHeader);
  54 + }
  55 + logger.info("response : " + response.toString());
  56 + ServerTransaction transaction = getServerTransaction(evt);
  57 + if (transaction != null) {
  58 + transaction.sendResponse(response);
  59 + transaction.getDialog().delete();
  60 + transaction.terminate();
  61 + } else {
  62 + logger.info("processRequest serverTransactionId is null.");
  63 + }
  64 +
  65 + } catch (ParseException e) {
  66 + e.printStackTrace();
  67 + } catch (SipException e) {
  68 + e.printStackTrace();
  69 + } catch (InvalidArgumentException e) {
  70 + e.printStackTrace();
  71 + }
  72 +
  73 + }
  74 +
  75 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/ISIPResponseProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.response;
  2 +
  3 +import javax.sip.ResponseEvent;
  4 +
  5 +/**
  6 + * @description:处理接收IPCamera发来的SIP协议响应消息
  7 + * @author: swwheihei
  8 + * @date: 2020年5月3日 下午4:42:22
  9 + */
  10 +public interface ISIPResponseProcessor {
  11 +
  12 + void process(ResponseEvent evt);
  13 +
  14 +
  15 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/ByeResponseProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
  2 +
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import com.genersoft.iot.vmp.gb28181.SipLayer;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.sip.ResponseEvent;
  11 +
  12 +/**
  13 + * @description: BYE请求响应器
  14 + * @author: swwheihei
  15 + * @date: 2020年5月3日 下午5:32:05
  16 + */
  17 +@Component
  18 +public class ByeResponseProcessor extends SIPResponseProcessorAbstract {
  19 +
  20 + private String method = "BYE";
  21 +
  22 + @Autowired
  23 + private SipLayer sipLayer;
  24 +
  25 + @Autowired
  26 + private SipConfig config;
  27 +
  28 +
  29 + @Autowired
  30 + private SIPProcessorObserver sipProcessorObserver;
  31 +
  32 + @Override
  33 + public void afterPropertiesSet() throws Exception {
  34 + // 添加消息处理的订阅
  35 + sipProcessorObserver.addResponseProcessor(method, this);
  36 + }
  37 + /**
  38 + * 处理BYE响应
  39 + *
  40 + * @param evt
  41 + */
  42 + @Override
  43 + public void process(ResponseEvent evt) {
  44 + // TODO Auto-generated method stub
  45 + System.out.println("收到bye");
  46 + }
  47 +
  48 +
  49 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/CancelResponseProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
  2 +
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import com.genersoft.iot.vmp.gb28181.SipLayer;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.sip.ResponseEvent;
  11 +
  12 +/**
  13 + * @description: CANCEL响应处理器
  14 + * @author: panlinlin
  15 + * @date: 2021年11月5日 16:35
  16 + */
  17 +@Component
  18 +public class CancelResponseProcessor extends SIPResponseProcessorAbstract {
  19 +
  20 + private String method = "CANCEL";
  21 +
  22 + @Autowired
  23 + private SipLayer sipLayer;
  24 +
  25 + @Autowired
  26 + private SipConfig config;
  27 +
  28 + @Autowired
  29 + private SIPProcessorObserver sipProcessorObserver;
  30 +
  31 + @Override
  32 + public void afterPropertiesSet() throws Exception {
  33 + // 添加消息处理的订阅
  34 + sipProcessorObserver.addResponseProcessor(method, this);
  35 + }
  36 + /**
  37 + * 处理CANCEL响应
  38 + *
  39 + * @param evt
  40 + */
  41 + @Override
  42 + public void process(ResponseEvent evt) {
  43 + // TODO Auto-generated method stub
  44 +
  45 + }
  46 +
  47 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
  2 +
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import com.genersoft.iot.vmp.gb28181.SipLayer;
  5 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
  8 +import gov.nist.javax.sip.ResponseEventExt;
  9 +import gov.nist.javax.sip.stack.SIPDialog;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.beans.factory.annotation.Autowired;
  13 +import org.springframework.stereotype.Component;
  14 +
  15 +import javax.sip.InvalidArgumentException;
  16 +import javax.sip.ResponseEvent;
  17 +import javax.sip.SipException;
  18 +import javax.sip.address.SipURI;
  19 +import javax.sip.header.CSeqHeader;
  20 +import javax.sip.message.Request;
  21 +import javax.sip.message.Response;
  22 +import java.text.ParseException;
  23 +
  24 +
  25 +/**
  26 + * @description: 处理INVITE响应
  27 + * @author: panlinlin
  28 + * @date: 2021年11月5日 16:40
  29 + */
  30 +@Component
  31 +public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
  32 +
  33 + private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
  34 + private String method = "INVITE";
  35 +
  36 + @Autowired
  37 + private SipLayer sipLayer;
  38 +
  39 + @Autowired
  40 + private SipConfig config;
  41 +
  42 +
  43 + @Autowired
  44 + private SIPProcessorObserver sipProcessorObserver;
  45 +
  46 + @Override
  47 + public void afterPropertiesSet() throws Exception {
  48 + // 添加消息处理的订阅
  49 + sipProcessorObserver.addResponseProcessor(method, this);
  50 + }
  51 +
  52 + @Autowired
  53 + private VideoStreamSessionManager streamSession;
  54 +
  55 + /**
  56 + * 处理invite响应
  57 + *
  58 + * @param evt 响应消息
  59 + * @throws ParseException
  60 + */
  61 + @Override
  62 + public void process(ResponseEvent evt ){
  63 + try {
  64 + Response response = evt.getResponse();
  65 + int statusCode = response.getStatusCode();
  66 + // trying不会回复
  67 + if (statusCode == Response.TRYING) {
  68 + }
  69 + // 成功响应
  70 + // 下发ack
  71 + if (statusCode == Response.OK) {
  72 + ResponseEventExt event = (ResponseEventExt)evt;
  73 + SIPDialog dialog = (SIPDialog)evt.getDialog();
  74 + CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
  75 + Request reqAck = dialog.createAck(cseq.getSeqNumber());
  76 + SipURI requestURI = (SipURI) reqAck.getRequestURI();
  77 + try {
  78 + requestURI.setHost(event.getRemoteIpAddress());
  79 + } catch (ParseException e) {
  80 + e.printStackTrace();
  81 + }
  82 + requestURI.setPort(event.getRemotePort());
  83 + reqAck.setRequestURI(requestURI);
  84 + logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack");
  85 + SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI();
  86 + String deviceId = requestURI.getUser();
  87 + String channelId = sipURI.getUser();
  88 +
  89 + dialog.sendAck(reqAck);
  90 +
  91 + }
  92 + } catch (InvalidArgumentException | SipException e) {
  93 + e.printStackTrace();
  94 + }
  95 + }
  96 +
  97 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  4 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
  8 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  9 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.beans.factory.annotation.Autowired;
  13 +import org.springframework.stereotype.Component;
  14 +
  15 +import javax.sip.ResponseEvent;
  16 +import javax.sip.header.CallIdHeader;
  17 +import javax.sip.header.WWWAuthenticateHeader;
  18 +import javax.sip.message.Response;
  19 +
  20 +/**
  21 + * @description:Register响应处理器
  22 + * @author: swwheihei
  23 + * @date: 2020年5月3日 下午5:32:23
  24 + */
  25 +@Component
  26 +public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
  27 +
  28 + private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
  29 + private String method = "REGISTER";
  30 +
  31 + @Autowired
  32 + private ISIPCommanderForPlatform sipCommanderForPlatform;
  33 +
  34 + @Autowired
  35 + private IVideoManagerStorager storager;
  36 +
  37 + @Autowired
  38 + private IRedisCatchStorage redisCatchStorage;
  39 +
  40 + @Autowired
  41 + private SIPProcessorObserver sipProcessorObserver;
  42 +
  43 + @Override
  44 + public void afterPropertiesSet() throws Exception {
  45 + // 添加消息处理的订阅
  46 + sipProcessorObserver.addResponseProcessor(method, this);
  47 + }
  48 +
  49 + /**
  50 + * 处理Register响应
  51 + *
  52 + * @param evt 事件
  53 + */
  54 + @Override
  55 + public void process(ResponseEvent evt) {
  56 + Response response = evt.getResponse();
  57 + CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
  58 + String callId = callIdHeader.getCallId();
  59 +
  60 + String platformGBId = redisCatchStorage.queryPlatformRegisterInfo(callId);
  61 + if (platformGBId == null) {
  62 + logger.info(String.format("未找到callId: %s 的注册/注销平台id", callId ));
  63 + return;
  64 + }
  65 +
  66 + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformGBId);
  67 + if (parentPlatformCatch == null) {
  68 + logger.warn(String.format("收到 %s 的注册/注销%S请求, 但是平台缓存信息未查询到!!!", platformGBId, response.getStatusCode()));
  69 + return;
  70 + }
  71 + String action = parentPlatformCatch.getParentPlatform().getExpires().equals("0") ? "注销" : "注册";
  72 + logger.info(String.format("收到 %s %s的%S响应", platformGBId, action, response.getStatusCode() ));
  73 + ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform();
  74 + if (parentPlatform == null) {
  75 + logger.warn(String.format("收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformGBId, action, response.getStatusCode()));
  76 + return;
  77 + }
  78 +
  79 + if (response.getStatusCode() == 401) {
  80 + WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
  81 + sipCommanderForPlatform.register(parentPlatform, callId, www, null, null);
  82 + }else if (response.getStatusCode() == 200){
  83 + // 注册/注销成功
  84 + logger.info(String.format("%s %s成功", platformGBId, action));
  85 + redisCatchStorage.delPlatformRegisterInfo(callId);
  86 + parentPlatform.setStatus("注册".equals(action));
  87 + // 取回Expires设置,避免注销过程中被置为0
  88 + ParentPlatform parentPlatformTmp = storager.queryParentPlatByServerGBId(platformGBId);
  89 + String expires = parentPlatformTmp.getExpires();
  90 + parentPlatform.setExpires(expires);
  91 + parentPlatform.setId(parentPlatformTmp.getId());
  92 + storager.updateParentPlatformStatus(platformGBId, "注册".equals(action));
  93 +
  94 + redisCatchStorage.updatePlatformRegister(parentPlatform);
  95 +
  96 + redisCatchStorage.updatePlatformKeepalive(parentPlatform);
  97 +
  98 + parentPlatformCatch.setParentPlatform(parentPlatform);
  99 +
  100 + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
  101 + }
  102 + }
  103 +
  104 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  4 +
  5 +/**
  6 + * 设备相关业务处理
  7 + */
  8 +public interface IDeviceService {
  9 +
  10 + /**
  11 + * 添加目录订阅
  12 + * @param device 设备信息
  13 + * @return
  14 + */
  15 + boolean addCatalogSubscribe(Device device);
  16 +
  17 + /**
  18 + * 移除目录订阅
  19 + * @param device 设备信息
  20 + * @return
  21 + */
  22 + boolean removeCatalogSubscribe(Device device);
  23 +
  24 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/bean/CatalogSubscribeTask.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  4 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  5 +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
  6 +import org.dom4j.DocumentException;
  7 +import org.dom4j.Element;
  8 +import org.slf4j.Logger;
  9 +import org.slf4j.LoggerFactory;
  10 +
  11 +import javax.sip.ResponseEvent;
  12 +
  13 +public class CatalogSubscribeTask implements Runnable{
  14 + private final Logger logger = LoggerFactory.getLogger(CatalogSubscribeTask.class);
  15 + private Device device;
  16 + private ISIPCommander sipCommander;
  17 +
  18 + public CatalogSubscribeTask(Device device, ISIPCommander sipCommander) {
  19 + this.device = device;
  20 + this.sipCommander = sipCommander;
  21 + }
  22 +
  23 + @Override
  24 + public void run() {
  25 + sipCommander.catalogSubscribe(device, eventResult -> {
  26 + ResponseEvent event = (ResponseEvent) eventResult.event;
  27 + Element rootElement = null;
  28 + try {
  29 + rootElement = XmlUtil.getRootElement(event.getResponse().getRawContent(), "gb2312");
  30 + } catch (DocumentException e) {
  31 + e.printStackTrace();
  32 + }
  33 + Element resultElement = rootElement.element("Result");
  34 + String result = resultElement.getText();
  35 + if (result.toUpperCase().equals("OK")){
  36 + // 成功
  37 + logger.info("目录订阅成功: {}", device.getDeviceId());
  38 + }else {
  39 + // 失败
  40 + logger.info("目录订阅失败: {}-{}", device.getDeviceId(), result);
  41 + }
  42 +
  43 + },eventResult -> {
  44 + // 失败
  45 + logger.warn("目录订阅失败: {}-信令发送失败", device.getDeviceId());
  46 + });
  47 + }
  48 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.genersoft.iot.vmp.conf.DynamicTask;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  6 +import com.genersoft.iot.vmp.service.IDeviceService;
  7 +import com.genersoft.iot.vmp.service.bean.CatalogSubscribeTask;
  8 +import org.slf4j.Logger;
  9 +import org.slf4j.LoggerFactory;
  10 +import org.springframework.beans.factory.annotation.Autowired;
  11 +import org.springframework.stereotype.Service;
  12 +
  13 +@Service
  14 +public class DeviceServiceImpl implements IDeviceService {
  15 +
  16 + private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class);
  17 +
  18 + @Autowired
  19 + private DynamicTask dynamicTask;
  20 +;
  21 +
  22 + @Autowired
  23 + private ISIPCommander sipCommander;
  24 +
  25 + @Override
  26 + public boolean addCatalogSubscribe(Device device) {
  27 + if (device == null || device.getSubscribeCycleForCatalog() < 0) {
  28 + return false;
  29 + }
  30 + // 添加目录订阅
  31 + CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander);
  32 + catalogSubscribeTask.run();
  33 + // 提前开始刷新订阅
  34 + String cron = getCron(device.getSubscribeCycleForCatalog() - 60);
  35 + dynamicTask.startCron(device.getDeviceId(), catalogSubscribeTask, cron);
  36 + return true;
  37 + }
  38 +
  39 + @Override
  40 + public boolean removeCatalogSubscribe(Device device) {
  41 + if (device == null || device.getSubscribeCycleForCatalog() < 0) {
  42 + return false;
  43 + }
  44 + dynamicTask.stopCron(device.getDeviceId());
  45 + return true;
  46 + }
  47 +
  48 + public String getCron(int time) {
  49 + if (time <= 59) {
  50 + return "0/" + time +" * * * * ?";
  51 + }else if (time <= 60* 59) {
  52 + int minute = time/(60);
  53 + return "0 0/" + minute +" * * * ?";
  54 + }else if (time <= 60* 60* 59) {
  55 + int hour = time/(60*60);
  56 + return "0 0 0/" + hour +" * * ?";
  57 + }else {
  58 + return "0 0/10 * * * ?";
  59 + }
  60 + }
  61 +}
... ...