Commit eb4716ba82f13078dd88e967e7906080c0ac0205
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 | +} | ... | ... |