From bf7ab3fe2ba246208d729901c33a9402209ee26a Mon Sep 17 00:00:00 2001 From: songww Date: Fri, 8 May 2020 21:57:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E6=8F=90=E4=BE=9BNVR=E5=BD=95=E5=83=8F?= =?UTF-8?q?=E6=A3=80=E7=B4=A2=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../genersoft/iot/vmp/conf/RedisConfig.java | 32 +--- .../com/genersoft/iot/vmp/conf/SipConfig.java | 1 - .../genersoft/iot/vmp/gb28181/SipLayer.java | 173 ++++++++++-------- .../gb28181/auth/RegisterLogicHandler.java | 26 +++ .../iot/vmp/gb28181/bean/RecordInfo.java | 51 ++++++ .../iot/vmp/gb28181/bean/RecordItem.java | 99 ++++++++++ .../callback/DeferredResultHolder.java | 42 +++++ .../transmit/callback/RequestMessage.java | 51 ++++++ .../gb28181/transmit/cmd/ISIPCommander.java | 56 +++--- .../transmit/cmd/impl/SIPCommander.java | 126 +++++++------ .../request/impl/MessageRequestProcessor.java | 154 +++++++++++----- .../impl/RegisterRequestProcessor.java | 6 +- .../iot/vmp/gb28181/utils/DateUtil.java | 40 ++++ .../vmp/media/zlm/ZLMHttpHookListener.java | 152 +++++++++++++++ .../vmp/vmanager/device/DeviceController.java | 34 +++- .../iot/vmp/vmanager/play/PlayController.java | 8 +- .../iot/vmp/vmanager/ptz/PtzController.java | 8 +- .../vmp/vmanager/record/RecordController.java | 47 +++++ 18 files changed, 870 insertions(+), 236 deletions(-) create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java create mode 100644 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java create mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java diff --git a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java index 914d0c31..3448a2f7 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java @@ -12,20 +12,19 @@ import com.alibaba.fastjson.parser.ParserConfig; import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; /** - * @Description:Redis中间件配置类 + * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 * @author: songww * @date: 2019年5月30日 上午10:58:25 * */ @Configuration -// @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Bean("redisTemplate") public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); - ParserConfig.getGlobalInstance().setAutoTypeSupport(true); + // 使用fastjson进行序列化处理,提高解析效率 FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(serializer); @@ -33,8 +32,9 @@ public class RedisConfig extends CachingConfigurerSupport { // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); - template.setConnectionFactory(redisConnectionFactory); + // 使用fastjson时需设置此项,否则会报异常not support type + ParserConfig.getGlobalInstance().setAutoTypeSupport(true); return template; } @@ -53,27 +53,5 @@ public class RedisConfig extends CachingConfigurerSupport { container.setConnectionFactory(connectionFactory); return container; } -// @Bean -// RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, -// MessageListenerAdapter listenerAdapter) { -// -// RedisMessageListenerContainer container = new RedisMessageListenerContainer(); -// container.setConnectionFactory(connectionFactory); -// // 订阅了一个叫通道 -// container.addMessageListener(listenerAdapter, new PatternTopic(VideoManagerConstants.KEEPLIVEKEY_PREFIX+"*")); -// // 这个container 可以添加多个 messageListener -// return container; -// } - -// /** -// * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法 -// * @param receiver -// * @return -// */ -// @Bean -// MessageListenerAdapter listenerAdapter(MessageReceiver receiver) { -// //这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage” -// //也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看 -// return new MessageListenerAdapter(receiver, "receiveMessage"); -// } + } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java index e6d23a4a..6d530336 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java @@ -16,7 +16,6 @@ public class SipConfig { String sipPassword; @Value("${media.ip}") String mediaIp; - @Value("${media.port}") Integer mediaPort; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java index 4d08fcc6..273f4ed4 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181; import java.util.Properties; +import javax.annotation.PostConstruct; import javax.sip.DialogTerminatedEvent; import javax.sip.IOExceptionEvent; import javax.sip.ListeningPoint; @@ -26,7 +27,6 @@ import javax.sip.message.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import com.genersoft.iot.vmp.conf.SipConfig; @@ -37,70 +37,78 @@ import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; import gov.nist.javax.sip.SipStackImpl; @Component -public class SipLayer implements SipListener{ - +public class SipLayer implements SipListener, Runnable { + private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); - + @Autowired private SipConfig config; - + private SipProvider tcpSipProvider; - + private SipProvider udpSipProvider; - + @Autowired private SIPProcessorFactory processorFactory; - + private SipStack sipStack; - + private AddressFactory addressFactory; private HeaderFactory headerFactory; private MessageFactory messageFactory; - @Bean - private boolean initSipServer() throws Exception { + @PostConstruct + private void initSipServer() { + Thread thread=new Thread(this); + thread.setDaemon(true); + thread.setName("sip server thread start"); + thread.start(); + } + + @Override + public void run() { SipFactory sipFactory = SipFactory.getInstance(); sipFactory.setPathName("gov.nist"); - headerFactory = sipFactory.createHeaderFactory(); - addressFactory = sipFactory.createAddressFactory(); - messageFactory = sipFactory.createMessageFactory(); - - Properties properties = new Properties(); - properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); - properties.setProperty("javax.sip.IP_ADDRESS", config.getSipIp()); - /** - * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE = - * 0; public static final int TRACE_MESSAGES = 16; public static final int - * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32; - */ - properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "16"); - properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log"); - properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log"); - sipStack = (SipStackImpl) sipFactory.createSipStack(properties); - try { + headerFactory = sipFactory.createHeaderFactory(); + + addressFactory = sipFactory.createAddressFactory(); + messageFactory = sipFactory.createMessageFactory(); + + Properties properties = new Properties(); + properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); + properties.setProperty("javax.sip.IP_ADDRESS", config.getSipIp()); + properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false"); + /** + * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE = + * 0; public static final int TRACE_MESSAGES = 16; public static final int + * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32; + */ + properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0"); + properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log"); + properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log"); + sipStack = (SipStackImpl) sipFactory.createSipStack(properties); + startTcpListener(); startUdpListener(); } catch (Exception e) { - logger.error("Sip Server 启动失败! port {"+config.getSipPort()+"}"); + logger.error("Sip Server 启动失败! port {" + config.getSipPort() + "}"); e.printStackTrace(); - throw e; } - logger.info("Sip Server 启动成功 port {"+config.getSipPort()+"}"); - return true; + logger.info("Sip Server 启动成功 port {" + config.getSipPort() + "}"); } - + private void startTcpListener() throws Exception { ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(config.getSipIp(), config.getSipPort(), "TCP"); tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); tcpSipProvider.addSipListener(this); - } - - private void startUdpListener() throws Exception { + } + + private void startUdpListener() throws Exception { ListeningPoint udpListeningPoint = sipStack.createListeningPoint(config.getSipIp(), config.getSipPort(), "UDP"); udpSipProvider = sipStack.createSipProvider(udpListeningPoint); udpSipProvider.addSipListener(this); - } + } /** * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a @@ -118,60 +126,80 @@ public class SipLayer implements SipListener{ int status = response.getStatusCode(); if ((status >= 200) && (status < 300)) { // Success! ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); - processor.process(evt,this,config); + processor.process(evt, this, config); } else { - logger.warn("接收到失败的response响应!status:"+status+",message:"+response.getContent().toString()); + logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getContent().toString()); } - //trying不会回复 - if(status == Response.TRYING){ + // trying不会回复 + if (status == Response.TRYING) { } } - /** - *

Title: processTimeout

- *

Description:

- * @param timeoutEvent - */ + /** + *

+ * Title: processTimeout + *

+ *

+ * Description: + *

+ * + * @param timeoutEvent + */ @Override public void processTimeout(TimeoutEvent timeoutEvent) { // TODO Auto-generated method stub - + } - /** - *

Title: processIOException

- *

Description:

- * @param exceptionEvent - */ + /** + *

+ * Title: processIOException + *

+ *

+ * Description: + *

+ * + * @param exceptionEvent + */ @Override public void processIOException(IOExceptionEvent exceptionEvent) { // TODO Auto-generated method stub - + } - /** - *

Title: processTransactionTerminated

- *

Description:

- * @param transactionTerminatedEvent - */ + /** + *

+ * Title: processTransactionTerminated + *

+ *

+ * Description: + *

+ * + * @param transactionTerminatedEvent + */ @Override public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) { // TODO Auto-generated method stub - + } - /** - *

Title: processDialogTerminated

- *

Description:

- * @param dialogTerminatedEvent - */ + /** + *

+ * Title: processDialogTerminated + *

+ *

+ * Description: + *

+ * + * @param dialogTerminatedEvent + */ @Override public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { // TODO Auto-generated method stub - + } - + private ServerTransaction getServerTransaction(RequestEvent evt) { Request request = evt.getRequest(); ServerTransaction serverTransaction = evt.getServerTransaction(); @@ -185,11 +213,11 @@ public class SipLayer implements SipListener{ if (serverTransaction == null) { try { - if (isTcp) { - serverTransaction = tcpSipProvider.getNewServerTransaction(request); - } else { - serverTransaction = udpSipProvider.getNewServerTransaction(request); - } + if (isTcp) { + serverTransaction = tcpSipProvider.getNewServerTransaction(request); + } else { + serverTransaction = udpSipProvider.getNewServerTransaction(request); + } } catch (TransactionAlreadyExistsException e) { e.printStackTrace(); } catch (TransactionUnavailableException e) { @@ -199,7 +227,6 @@ public class SipLayer implements SipListener{ return serverTransaction; } - public AddressFactory getAddressFactory() { return addressFactory; } @@ -219,5 +246,5 @@ public class SipLayer implements SipListener{ public SipProvider getUdpSipProvider() { return udpSipProvider; } - + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java new file mode 100644 index 00000000..5d5bf21f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java @@ -0,0 +1,26 @@ +package com.genersoft.iot.vmp.gb28181.auth; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; + +/** + * @Description:注册逻辑处理,当设备注册后触发逻辑。 + * @author: songww + * @date: 2020年5月8日 下午9:41:46 + */ +@Component +public class RegisterLogicHandler { + + @Autowired + private SIPCommander cmder; + + public void onRegister(Device device) { + // TODO 后续处理,只有第一次注册时调用查询设备信息,如需更新调用更新API接口 + cmder.deviceInfoQuery(device); + + cmder.catalogQuery(device); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java new file mode 100644 index 00000000..2d2ca1e0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import java.util.List; + +/** + * @Description:设备录像信息bean + * @author: songww + * @date: 2020年5月8日 下午2:05:56 + */ +public class RecordInfo { + + private String deviceId; + + private String name; + + private int sumNum; + + private List recordList; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSumNum() { + return sumNum; + } + + public void setSumNum(int sumNum) { + this.sumNum = sumNum; + } + + public List getRecordList() { + return recordList; + } + + public void setRecordList(List recordList) { + this.recordList = recordList; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java new file mode 100644 index 00000000..b0e713bb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java @@ -0,0 +1,99 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * @Description:设备录像bean + * @author: songww + * @date: 2020年5月8日 下午2:06:54 + */ +public class RecordItem { + + private String deviceId; + + private String name; + + private String filePath; + + private String address; + + private String startTime; + + private String endTime; + + private int secrecy; + + private String type; + + private String recordId; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public int getSecrecy() { + return secrecy; + } + + public void setSecrecy(int secrecy) { + this.secrecy = secrecy; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getRecordId() { + return recordId; + } + + public void setRecordId(String recordId) { + this.recordId = recordId; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java new file mode 100644 index 00000000..f9c8d253 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java @@ -0,0 +1,42 @@ +package com.genersoft.iot.vmp.gb28181.transmit.callback; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.async.DeferredResult; + +/** + * @Description:TODO(这里用一句话描述这个类的作用) + * @author: songww + * @date: 2020年5月8日 下午7:59:05 + */ +@Component +public class DeferredResultHolder { + + public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO"; + + public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG"; + + public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; + + private Map map = new HashMap(); + + public void put(String key, DeferredResult result) { + map.put(key, result); + } + + public DeferredResult get(String key) { + return map.get(key); + } + + public void invokeResult(RequestMessage msg) { + DeferredResult result = map.get(msg.getId()); + if (result == null) { + return; + } + result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK)); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java new file mode 100644 index 00000000..ac95f752 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.gb28181.transmit.callback; + +/** + * @Description:TODO(这里用一句话描述这个类的作用) + * @author: songww + * @date: 2020年5月8日 下午1:09:18 + */ +public class RequestMessage { + + private String id; + + private String deviceId; + + private String type; + + private Object data; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + this.id = type + deviceId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + this.id = type + deviceId; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index 2fe88ae3..f226bf51 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -12,48 +12,48 @@ public interface ISIPCommander { /** * 云台方向放控制,使用配置文件中的默认镜头移动速度 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param moveSpeed 镜头移动速度 */ - public boolean ptzdirectCmd(String deviceId,String channelId,int leftRight, int upDown); + public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); /** * 云台方向放控制 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param moveSpeed 镜头移动速度 */ - public boolean ptzdirectCmd(String deviceId,String channelId,int leftRight, int upDown, int moveSpeed); + public boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); /** * 云台缩放控制,使用配置文件中的默认镜头缩放速度 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 */ - public boolean ptzZoomCmd(String deviceId,String channelId,int inOut); + public boolean ptzZoomCmd(Device device,String channelId,int inOut); /** * 云台缩放控制 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 * @param zoomSpeed 镜头缩放速度 */ - public boolean ptzZoomCmd(String deviceId,String channelId,int inOut, int moveSpeed); + public boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); /** * 云台控制,支持方向与缩放控制 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 @@ -61,67 +61,67 @@ public interface ISIPCommander { * @param moveSpeed 镜头移动速度 * @param zoomSpeed 镜头缩放速度 */ - public boolean ptzCmd(String deviceId,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); + public boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); /** * 请求预览视频流 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ - public String playStreamCmd(String deviceId,String channelId); + public String playStreamCmd(Device device,String channelId); /** * 语音广播 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ - public String audioBroadcastCmd(String deviceId,String channelId); + public boolean audioBroadcastCmd(Device device,String channelId); /** * 音视频录像控制 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ - public String recordCmd(String deviceId,String channelId); + public boolean recordCmd(Device device,String channelId); /** * 报警布防/撤防命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ - public String guardCmd(String deviceId); + public boolean guardCmd(Device device); /** * 报警复位命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ - public String alarmCmd(String deviceId); + public boolean alarmCmd(Device device); /** * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ - public String iFameCmd(String deviceId,String channelId); + public boolean iFameCmd(Device device,String channelId); /** * 看守位控制命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ - public String homePositionCmd(String deviceId); + public boolean homePositionCmd(Device device); /** * 设备配置命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ - public String deviceConfigCmd(String deviceId); + public boolean deviceConfigCmd(Device device); /** @@ -150,8 +150,10 @@ public interface ISIPCommander { * 查询录像信息 * * @param device 视频设备 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ - public boolean recordInfoQuery(Device device); + public boolean recordInfoQuery(Device device, String startTime, String endTime); /** * 查询报警信息 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 09f09346..630d44ce 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -8,6 +8,7 @@ import javax.sip.SipException; import javax.sip.message.Request; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import com.genersoft.iot.vmp.conf.SipConfig; @@ -15,7 +16,7 @@ import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.gb28181.utils.DateUtil; /** * @Description:设备能力接口,用于定义设备的控制、查询能力 @@ -34,66 +35,63 @@ public class SIPCommander implements ISIPCommander { @Autowired private SipLayer sipLayer; - @Autowired - private IVideoManagerStorager storager; - /** * 云台方向放控制,使用配置文件中的默认镜头移动速度 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param moveSpeed 镜头移动速度 */ @Override - public boolean ptzdirectCmd(String deviceId, String channelId, int leftRight, int upDown) { - return ptzCmd(deviceId, channelId, leftRight, upDown, 0, config.getSpeed(), 0); + public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) { + return ptzCmd(device, channelId, leftRight, upDown, 0, config.getSpeed(), 0); } /** * 云台方向放控制 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param moveSpeed 镜头移动速度 */ @Override - public boolean ptzdirectCmd(String deviceId, String channelId, int leftRight, int upDown, int moveSpeed) { - return ptzCmd(deviceId, channelId, leftRight, upDown, 0, moveSpeed, 0); + public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) { + return ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0); } /** * 云台缩放控制,使用配置文件中的默认镜头缩放速度 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 */ @Override - public boolean ptzZoomCmd(String deviceId, String channelId, int inOut) { - return ptzCmd(deviceId, channelId, 0, 0, inOut, 0, config.getSpeed()); + public boolean ptzZoomCmd(Device device, String channelId, int inOut) { + return ptzCmd(device, channelId, 0, 0, inOut, 0, config.getSpeed()); } /** * 云台缩放控制 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 * @param zoomSpeed 镜头缩放速度 */ @Override - public boolean ptzZoomCmd(String deviceId, String channelId, int inOut, int zoomSpeed) { - return ptzCmd(deviceId, channelId, 0, 0, inOut, 0, zoomSpeed); + public boolean ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) { + return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed); } /** * 云台控制,支持方向与缩放控制 * - * @param deviceId 控制设备 + * @param device 控制设备 * @param channelId 预览通道 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 @@ -102,10 +100,9 @@ public class SIPCommander implements ISIPCommander { * @param zoomSpeed 镜头缩放速度 */ @Override - public boolean ptzCmd(String deviceId, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, + public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) { try { - Device device = storager.queryVideoDevice(deviceId); StringBuffer ptzXml = new StringBuffer(200); ptzXml.append(""); ptzXml.append(""); @@ -119,7 +116,7 @@ public class SIPCommander implements ISIPCommander { Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtzTag", "ToPtzTag"); - transmitRequest(device.getTransport(), request); + transmitRequest(device, request); return true; } catch (SipException | ParseException | InvalidArgumentException e) { @@ -131,15 +128,13 @@ public class SIPCommander implements ISIPCommander { /** * 请求预览视频流 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ @Override - public String playStreamCmd(String deviceId, String channelId) { + public String playStreamCmd(Device device, String channelId) { try { - Device device = storager.queryVideoDevice(deviceId); - //生成ssrc标识数据流 10位数字 String ssrc = ""; Random random = new Random(); @@ -170,7 +165,7 @@ public class SIPCommander implements ISIPCommander { Request request = headerProvider.createInviteRequest(device, content.toString(), null, "live", null); - transmitRequest(device.getTransport(), request); + transmitRequest(device, request); return ssrc; } catch ( SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); @@ -181,81 +176,81 @@ public class SIPCommander implements ISIPCommander { /** * 语音广播 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ @Override - public String audioBroadcastCmd(String deviceId, String channelId) { + public boolean audioBroadcastCmd(Device device, String channelId) { // TODO Auto-generated method stub - return null; + return false; } /** * 音视频录像控制 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ @Override - public String recordCmd(String deviceId, String channelId) { + public boolean recordCmd(Device device, String channelId) { // TODO Auto-generated method stub - return null; + return false; } /** * 报警布防/撤防命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ @Override - public String guardCmd(String deviceId) { + public boolean guardCmd(Device device) { // TODO Auto-generated method stub - return null; + return false; } /** * 报警复位命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ @Override - public String alarmCmd(String deviceId) { + public boolean alarmCmd(Device device) { // TODO Auto-generated method stub - return null; + return false; } /** * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 * - * @param deviceId 视频设备 + * @param device 视频设备 * @param channelId 预览通道 */ @Override - public String iFameCmd(String deviceId, String channelId) { + public boolean iFameCmd(Device device, String channelId) { // TODO Auto-generated method stub - return null; + return false; } /** * 看守位控制命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ @Override - public String homePositionCmd(String deviceId) { + public boolean homePositionCmd(Device device) { // TODO Auto-generated method stub - return null; + return false; } /** * 设备配置命令 * - * @param deviceId 视频设备 + * @param device 视频设备 */ @Override - public String deviceConfigCmd(String deviceId) { + public boolean deviceConfigCmd(Device device) { // TODO Auto-generated method stub - return null; + return false; } /** @@ -286,8 +281,8 @@ public class SIPCommander implements ISIPCommander { catalogXml.append(""); Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDeviceInfoTag", "ToDeviceInfoTag"); + transmitRequest(device, request); - transmitRequest(device.getTransport(), request); } catch (SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); return false; @@ -312,9 +307,7 @@ public class SIPCommander implements ISIPCommander { catalogXml.append(""); Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCatalogTag", "ToCatalogTag"); - - transmitRequest(device.getTransport(), request); - + transmitRequest(device, request); } catch (SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); return false; @@ -326,11 +319,32 @@ public class SIPCommander implements ISIPCommander { * 查询录像信息 * * @param device 视频设备 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ @Override - public boolean recordInfoQuery(Device device) { - // TODO Auto-generated method stub - return false; + public boolean recordInfoQuery(Device device, String startTime, String endTime) { + + try { + StringBuffer catalogXml = new StringBuffer(200); + catalogXml.append(""); + catalogXml.append(""); + catalogXml.append("RecordInfo"); + catalogXml.append("" + (int)((Math.random()*9+1)*100000) + ""); + catalogXml.append("" + device.getDeviceId() + ""); + catalogXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + ""); + catalogXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + ""); + // 大华NVR要求必须增加一个值为all的文本元素节点Type + catalogXml.append("all"); + catalogXml.append(""); + + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", "ToRecordInfoTag"); + transmitRequest(device, request); + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + return true; } /** @@ -377,10 +391,10 @@ public class SIPCommander implements ISIPCommander { return false; } - private void transmitRequest(String transport, Request request) throws SipException { - if(transport.equals("TCP")) { + private void transmitRequest(Device device, Request request) throws SipException { + if(device.getTransport().equals("TCP")) { sipLayer.getTcpSipProvider().sendRequest(request); - } else if(transport.equals("UDP")) { + } else if(device.getTransport().equals("UDP")) { sipLayer.getUdpSipProvider().sendRequest(request); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java index 9a5551b5..453420a8 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java @@ -2,8 +2,10 @@ package com.genersoft.iot.vmp.gb28181.transmit.request.impl; import java.io.ByteArrayInputStream; import java.text.ParseException; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import javax.sip.InvalidArgumentException; @@ -24,9 +26,14 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; +import com.genersoft.iot.vmp.gb28181.bean.RecordItem; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor; +import com.genersoft.iot.vmp.gb28181.utils.DateUtil; import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @@ -51,6 +58,9 @@ public class MessageRequestProcessor implements ISIPRequestProcessor { @Autowired private EventPublisher publisher; + @Autowired + private DeferredResultHolder deferredResultHolder; + /** * 处理MESSAGE请求 * @@ -74,21 +84,49 @@ public class MessageRequestProcessor implements ISIPRequestProcessor { processMessageDeviceInfo(evt); } else if (new String(request.getRawContent()).contains("Alarm")) { processMessageAlarm(evt); + } else if (new String(request.getRawContent()).contains("recordInfo")) { + processMessageRecordInfo(evt); } } + /** + * 收到deviceInfo设备信息请求 处理 + * @param evt + */ + private void processMessageDeviceInfo(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + Element deviceIdElement = rootElement.element("DeviceID"); + String deviceId = deviceIdElement.getText().toString(); + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + device.setName(XmlUtil.getText(rootElement,"DeviceName")); + device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer")); + device.setModel(XmlUtil.getText(rootElement,"Model")); + device.setFirmware(XmlUtil.getText(rootElement,"Firmware")); + storager.update(device); + + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICEINFO); + msg.setData(device); + deferredResultHolder.invokeResult(msg); + } catch (DocumentException e) { + e.printStackTrace(); + } + } + /*** * 收到catalog设备目录列表请求 处理 * @param evt */ private void processMessageCatalogList(RequestEvent evt) { try { - Request request = evt.getRequest(); - SAXReader reader = new SAXReader(); - reader.setEncoding("GB2312"); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); - Element rootElement = xml.getRootElement(); + Element rootElement = getRootElement(evt); Element deviceIdElement = rootElement.element("DeviceID"); String deviceId = deviceIdElement.getText().toString(); Element deviceListElement = rootElement.element("DeviceList"); @@ -152,6 +190,11 @@ public class MessageRequestProcessor implements ISIPRequestProcessor { } // 更新 storager.update(device); + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG); + msg.setData(device); + deferredResultHolder.invokeResult(msg); } } catch (DocumentException e) { e.printStackTrace(); @@ -159,16 +202,12 @@ public class MessageRequestProcessor implements ISIPRequestProcessor { } /*** - * 收到deviceInfo设备信息请求 处理 + * 收到alarm设备报警信息 处理 * @param evt */ - private void processMessageDeviceInfo(RequestEvent evt) { + private void processMessageAlarm(RequestEvent evt) { try { - Request request = evt.getRequest(); - SAXReader reader = new SAXReader(); - // reader.setEncoding("GB2312"); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); - Element rootElement = xml.getRootElement(); + Element rootElement = getRootElement(evt); Element deviceIdElement = rootElement.element("DeviceID"); String deviceId = deviceIdElement.getText().toString(); @@ -188,52 +227,83 @@ public class MessageRequestProcessor implements ISIPRequestProcessor { } /*** - * 收到alarm设备报警信息 处理 + * 收到keepalive请求 处理 * @param evt */ - private void processMessageAlarm(RequestEvent evt) { + private void processMessageKeepAlive(RequestEvent evt){ try { Request request = evt.getRequest(); - SAXReader reader = new SAXReader(); - // reader.setEncoding("GB2312"); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); - Element rootElement = xml.getRootElement(); + Response response = layer.getMessageFactory().createResponse(Response.OK,request); + Element rootElement = getRootElement(evt); Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText().toString(); - - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - device.setName(XmlUtil.getText(rootElement,"DeviceName")); - device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer")); - device.setModel(XmlUtil.getText(rootElement,"Model")); - device.setFirmware(XmlUtil.getText(rootElement,"Firmware")); - storager.update(device); - cmder.catalogQuery(device); - } catch (DocumentException e) { + transaction.sendResponse(response); + publisher.onlineEventPublish(deviceIdElement.getText(), VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { e.printStackTrace(); } } /*** - * 收到keepalive请求 处理 + * 收到catalog设备目录列表请求 处理 * @param evt */ - private void processMessageKeepAlive(RequestEvent evt){ + private void processMessageRecordInfo(RequestEvent evt) { try { - Request request = evt.getRequest(); - Response response = layer.getMessageFactory().createResponse(Response.OK,request); - SAXReader reader = new SAXReader(); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); - // reader.setEncoding("GB2312"); - Element rootElement = xml.getRootElement(); + RecordInfo recordInfo = new RecordInfo(); + Element rootElement = getRootElement(evt); Element deviceIdElement = rootElement.element("DeviceID"); - transaction.sendResponse(response); - publisher.onlineEventPublish(deviceIdElement.getText(), VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + String deviceId = deviceIdElement.getText().toString(); + recordInfo.setDeviceId(deviceId); + recordInfo.setName(XmlUtil.getText(rootElement,"Name")); + recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement,"SumNum"))); + Element recordListElement = rootElement.element("RecordList"); + if (recordListElement == null) { + return; + } + + Iterator recordListIterator = recordListElement.elementIterator(); + if (recordListIterator != null) { + + List recordList = new ArrayList(); + RecordItem record = new RecordItem(); + // 遍历DeviceList + while (recordListIterator.hasNext()) { + Element itemRecord = recordListIterator.next(); + Element recordElement = itemRecord.element("DeviceID"); + if (recordElement == null) { + continue; + } + record.setDeviceId(XmlUtil.getText(itemRecord,"DeviceID")); + record.setName(XmlUtil.getText(itemRecord,"Name")); + record.setFilePath(XmlUtil.getText(itemRecord,"FilePath")); + record.setAddress(XmlUtil.getText(itemRecord,"Address")); + record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord,"StartTime"))); + record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord,"EndTime"))); + record.setSecrecy(itemRecord.element("Secrecy") == null? 0:Integer.parseInt(XmlUtil.getText(itemRecord,"Secrecy"))); + record.setType(XmlUtil.getText(itemRecord,"Type")); + record.setRecordId(XmlUtil.getText(itemRecord,"RecordID")); + recordList.add(record); + } + recordInfo.setRecordList(recordList); + } + + + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); + msg.setData(recordInfo); + deferredResultHolder.invokeResult(msg); + } catch (DocumentException e) { e.printStackTrace(); } } + + private Element getRootElement(RequestEvent evt) throws DocumentException { + Request request = evt.getRequest(); + SAXReader reader = new SAXReader(); + reader.setEncoding("GB2312"); + Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); + return xml.getRootElement(); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java index 1c7a957f..ebb6db43 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java @@ -25,10 +25,10 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; +import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Host; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @@ -48,7 +48,7 @@ public class RegisterRequestProcessor implements ISIPRequestProcessor { private SipConfig config; @Autowired - private SIPCommander cmder; + private RegisterLogicHandler handler; @Autowired private IVideoManagerStorager storager; @@ -149,7 +149,7 @@ public class RegisterRequestProcessor implements ISIPRequestProcessor { System.out.println("注册成功! deviceId:" + device.getDeviceId()); storager.update(device); publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER); - cmder.deviceInfoQuery(device); + handler.onRegister(device); } else if (registerFlag == 2) { System.out.println("注销成功! deviceId:" + device.getDeviceId()); publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java new file mode 100644 index 00000000..e241bd50 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java @@ -0,0 +1,40 @@ +package com.genersoft.iot.vmp.gb28181.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Locale; + +/** + * @Description:时间工具类,主要处理ISO 8601格式转换 + * @author: songww + * @date: 2020年5月8日 下午3:24:42 + */ +public class DateUtil { + + private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; + private static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; + + public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { + + SimpleDateFormat oldsdf = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); + SimpleDateFormat newsdf = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()); + try { + return newsdf.format(oldsdf.parse(formatTime)); + } catch (ParseException e) { + e.printStackTrace(); + } + return ""; + } + + public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { + + SimpleDateFormat oldsdf = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()); + SimpleDateFormat newsdf = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); + try { + return newsdf.format(oldsdf.parse(formatTime)); + } catch (ParseException e) { + e.printStackTrace(); + } + return ""; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java new file mode 100644 index 00000000..0ea162ee --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -0,0 +1,152 @@ +package com.genersoft.iot.vmp.media.zlm; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Description:针对 ZLMediaServer的hook事件监听 + * @author: songww + * @date: 2020年5月8日 上午10:46:48 + */ +@RestController +@RequestMapping("/hook/zlm") +public class ZLMHttpHookListener { + + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); + + /** + * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 + * + */ + @PostMapping("/on_flow_report") + public ResponseEntity onFlowReport(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * 访问http文件服务器上hls之外的文件时触发。 + * + */ + @PostMapping("/on_http_access") + public ResponseEntity onHttpAccess(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 + * + */ + @PostMapping("/on_play") + public ResponseEntity onPlay(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * rtsp/rtmp/rtp推流鉴权事件。 + * + */ + @PostMapping("/on_publish") + public ResponseEntity onPublish(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * 录制mp4完成后通知事件;此事件对回复不敏感。 + * + */ + @PostMapping("/on_record_mp4") + public ResponseEntity onRecordMp4(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 + * + */ + @PostMapping("/on_rtsp_auth") + public ResponseEntity onRtspAuth(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 + * + */ + @PostMapping("/on_rtsp_realm") + public ResponseEntity onRtspRealm(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。 + * + */ + @PostMapping("/on_shell_login") + public ResponseEntity onShellLogin(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 + * + */ + @PostMapping("/on_stream_changed") + public ResponseEntity onStreamChanged(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 + * + */ + @PostMapping("/on_stream_none_reader") + public ResponseEntity onStreamNoneReader(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 + * + */ + @PostMapping("/on_stream_not_found") + public ResponseEntity onStreamNotFound(){ + // TODO Auto-generated method stub + + return null; + } + + /** + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 + * + */ + @PostMapping("/on_server_started") + public ResponseEntity onServerStarted(){ + // TODO Auto-generated method stub + + return null; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java index 993ddf79..ce869108 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java @@ -1,7 +1,8 @@ package com.genersoft.iot.vmp.vmanager.device; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,10 +11,14 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @RestController @@ -25,16 +30,21 @@ public class DeviceController { @Autowired private IVideoManagerStorager storager; + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + @GetMapping("/devices/{deviceId}") - public ResponseEntity> devices(@PathVariable String deviceId){ + public ResponseEntity devices(@PathVariable String deviceId){ if (logger.isDebugEnabled()) { logger.debug("查询视频设备API调用,deviceId:" + deviceId); } - List deviceList = new ArrayList<>(); - deviceList.add(storager.queryVideoDevice(deviceId)); - return new ResponseEntity<>(deviceList,HttpStatus.OK); + Device device = storager.queryVideoDevice(deviceId); + return new ResponseEntity<>(device,HttpStatus.OK); } @GetMapping("/devices") @@ -47,4 +57,18 @@ public class DeviceController { List deviceList = storager.queryVideoDeviceList(null); return new ResponseEntity<>(deviceList,HttpStatus.OK); } + + @PostMapping("/devices/{deviceId}/sync") + public DeferredResult> devicesSync(@PathVariable String deviceId){ + + if (logger.isDebugEnabled()) { + logger.debug("设备信息同步API调用,deviceId:" + deviceId); + } + + Device device = storager.queryVideoDevice(deviceId); + cmder.catalogQuery(device); + DeferredResult> result = new DeferredResult>(); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result); + return result; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java index d8b7305a..9fe78c39 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java @@ -10,7 +10,9 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @RestController @RequestMapping("/api") @@ -21,10 +23,14 @@ public class PlayController { @Autowired private SIPCommander cmder; + @Autowired + private IVideoManagerStorager storager; + @GetMapping("/play/{deviceId}_{channelId}") public ResponseEntity play(@PathVariable String deviceId,@PathVariable String channelId){ - String ssrc = cmder.playStreamCmd(deviceId, channelId); + Device device = storager.queryVideoDevice(deviceId); + String ssrc = cmder.playStreamCmd(device, channelId); if (logger.isDebugEnabled()) { logger.debug(String.format("设备预览 API调用,deviceId:%s ,channelId:%s",deviceId, channelId)); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java index 842d2a89..bc9792e9 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java @@ -10,7 +10,9 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @RestController @RequestMapping("/api") @@ -20,6 +22,9 @@ public class PtzController { @Autowired private SIPCommander cmder; + + @Autowired + private IVideoManagerStorager storager; /*** * http://localhost:8080/api/ptz/34020000001320000002_34020000001320000008?leftRight=1&upDown=0&inOut=0&moveSpeed=50&zoomSpeed=0 @@ -38,8 +43,9 @@ public class PtzController { if (logger.isDebugEnabled()) { logger.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,leftRight:%d ,upDown:%d ,inOut:%d ,moveSpeed:%d ,zoomSpeed:%d",deviceId, channelId, leftRight, upDown, inOut, moveSpeed, zoomSpeed)); } + Device device = storager.queryVideoDevice(deviceId); - cmder.ptzCmd(deviceId, channelId, leftRight, upDown, inOut, moveSpeed, zoomSpeed); + cmder.ptzCmd(device, channelId, leftRight, upDown, inOut, moveSpeed, zoomSpeed); return new ResponseEntity("success",HttpStatus.OK); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java new file mode 100644 index 00000000..c115dd06 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java @@ -0,0 +1,47 @@ +package com.genersoft.iot.vmp.vmanager.record; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; + +@RestController +@RequestMapping("/api") +public class RecordController { + + private final static Logger logger = LoggerFactory.getLogger(RecordController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorager storager; + + @Autowired + private DeferredResultHolder resultHolder; + + @GetMapping("/recordinfo/{deviceId}") + public DeferredResult> recordinfo(@PathVariable String deviceId, String startTime, String endTime){ + + if (logger.isDebugEnabled()) { + logger.debug(String.format("录像信息 API调用,deviceId:%s ,startTime:%s, startTime:%s",deviceId, startTime, endTime)); + } + + Device device = storager.queryVideoDevice(deviceId); + cmder.recordInfoQuery(device, startTime, endTime); + DeferredResult> result = new DeferredResult>(); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result); + return result; + } +}