songww
2020-05-08 bf7ab3fe2ba246208d729901c33a9402209ee26a
优化代码结构,提供NVR录像检索接口
10个文件已修改
8个文件已添加
1108 ■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java 156 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
@@ -12,20 +12,19 @@
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<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        // 使用fastjson进行序列化处理,提高解析效率
        FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class);
        // value值的序列化采用fastJsonRedisSerializer
        template.setValueSerializer(serializer);
@@ -33,8 +32,9 @@
        // 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 @@
        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");
//    }
}
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
@@ -16,7 +16,6 @@
    String sipPassword;
    @Value("${media.ip}")
    String mediaIp;
    @Value("${media.port}")
    Integer mediaPort;
    
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
@@ -2,6 +2,7 @@
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 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 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 @@
        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) {
        }
    }
    /**
     * <p>Title: processTimeout</p>
     * <p>Description: </p>
     * @param timeoutEvent
     */
    /**
     * <p>
     * Title: processTimeout
     * </p>
     * <p>
     * Description:
     * </p>
     *
     * @param timeoutEvent
     */
    @Override
    public void processTimeout(TimeoutEvent timeoutEvent) {
        // TODO Auto-generated method stub
    }
    /**
     * <p>Title: processIOException</p>
     * <p>Description: </p>
     * @param exceptionEvent
     */
    /**
     * <p>
     * Title: processIOException
     * </p>
     * <p>
     * Description:
     * </p>
     *
     * @param exceptionEvent
     */
    @Override
    public void processIOException(IOExceptionEvent exceptionEvent) {
        // TODO Auto-generated method stub
    }
    /**
     * <p>Title: processTransactionTerminated</p>
     * <p>Description: </p>
     * @param transactionTerminatedEvent
     */
    /**
     * <p>
     * Title: processTransactionTerminated
     * </p>
     * <p>
     * Description:
     * </p>
     *
     * @param transactionTerminatedEvent
     */
    @Override
    public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
        // TODO Auto-generated method stub
    }
    /**
     * <p>Title: processDialogTerminated</p>
     * <p>Description: </p>
     * @param dialogTerminatedEvent
     */
    /**
     * <p>
     * Title: processDialogTerminated
     * </p>
     * <p>
     * Description:
     * </p>
     *
     * @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 @@
        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) {
@@ -198,7 +226,6 @@
        }
        return serverTransaction;
    }
    public AddressFactory getAddressFactory() {
        return addressFactory;
@@ -219,5 +246,5 @@
    public SipProvider getUdpSipProvider() {
        return udpSipProvider;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java
New file
@@ -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);
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
New file
@@ -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<RecordItem> 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<RecordItem> getRecordList() {
        return recordList;
    }
    public void setRecordList(List<RecordItem> recordList) {
        this.recordList = recordList;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
New file
@@ -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;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
New file
@@ -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<String, DeferredResult> map = new HashMap<String, DeferredResult>();
    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));
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java
New file
@@ -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;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -12,48 +12,48 @@
    /**
     * 云台方向放控制,使用配置文件中的默认镜头移动速度
     * 
     * @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 @@
     * @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 @@
     * 查询录像信息
     * 
     * @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);
    
    /**
     * 查询报警信息
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -8,6 +8,7 @@
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.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 @@
    @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 @@
     * @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("<?xml version=\"1.0\" ?>");
            ptzXml.append("<Control>");
@@ -119,7 +116,7 @@
            
            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,14 +128,12 @@
    /**
     * 请求预览视频流
     * 
     * @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 = "";
@@ -170,7 +165,7 @@
            
            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 @@
    /**
     * 语音广播
     * 
     * @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 @@
            catalogXml.append("</Query>");
            
            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 @@
            catalogXml.append("</Query>");
            
            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 @@
     * 查询录像信息
     * 
     * @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("<?xml version=\"1.0\" encoding=\"GB2312\"?>");
            catalogXml.append("<Query>");
            catalogXml.append("<CmdType>RecordInfo</CmdType>");
            catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>");
            catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>");
            catalogXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>");
            catalogXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>");
            // 大华NVR要求必须增加一个值为all的文本元素节点Type
            catalogXml.append("<Type>all</Type>");
            catalogXml.append("</Query>");
            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 @@
        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);
        }
    }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
@@ -2,8 +2,10 @@
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.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 @@
    @Autowired
    private EventPublisher publisher;
    
    @Autowired
    private DeferredResultHolder deferredResultHolder;
    /**   
     * 处理MESSAGE请求
     *  
@@ -74,8 +84,40 @@
            processMessageDeviceInfo(evt);
        } else if (new String(request.getRawContent()).contains("<CmdType>Alarm</CmdType>")) {
            processMessageAlarm(evt);
        } else if (new String(request.getRawContent()).contains("<CmdType>recordInfo</CmdType>")) {
            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();
        }
    }
    
    /***
@@ -84,11 +126,7 @@
     */
    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,36 +190,12 @@
                }
                // 更新
                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();
        }
    }
    /***
     * 收到deviceInfo设备信息请求 处理
     * @param evt
     */
    private void processMessageDeviceInfo(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 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) {
            e.printStackTrace();
        }
@@ -193,11 +207,7 @@
     */
    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();
            
@@ -224,10 +234,7 @@
        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();
            Element rootElement = getRootElement(evt);
            Element deviceIdElement = rootElement.element("DeviceID");
            transaction.sendResponse(response);
            publisher.onlineEventPublish(deviceIdElement.getText(), VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
@@ -235,5 +242,68 @@
            e.printStackTrace();
        }
    }
    /***
     * 收到catalog设备目录列表请求 处理
     * @param evt
     */
    private void processMessageRecordInfo(RequestEvent evt) {
        try {
            RecordInfo recordInfo = new RecordInfo();
            Element rootElement = getRootElement(evt);
            Element deviceIdElement = rootElement.element("DeviceID");
            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<Element> recordListIterator = recordListElement.elementIterator();
            if (recordListIterator != null) {
                List<RecordItem> recordList = new ArrayList<RecordItem>();
                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();
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
@@ -25,10 +25,10 @@
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 @@
    private SipConfig config;
    
    @Autowired
    private SIPCommander cmder;
    private RegisterLogicHandler handler;
    
    @Autowired
    private IVideoManagerStorager storager;
@@ -149,7 +149,7 @@
                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);
src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java
New file
@@ -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 "";
    }
}
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
New file
@@ -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;
    }
}
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.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 @@
    @Autowired
    private IVideoManagerStorager storager;
    
    @Autowired
    private SIPCommander cmder;
    @Autowired
    private DeferredResultHolder resultHolder;
    @GetMapping("/devices/{deviceId}")
    public ResponseEntity<List<Device>> devices(@PathVariable String deviceId){
    public ResponseEntity<Device> devices(@PathVariable String deviceId){
        
        if (logger.isDebugEnabled()) {
            logger.debug("查询视频设备API调用,deviceId:" + deviceId);
        }
        
        List<Device> 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 @@
        List<Device> deviceList = storager.queryVideoDeviceList(null);
        return new ResponseEntity<>(deviceList,HttpStatus.OK);
    }
    @PostMapping("/devices/{deviceId}/sync")
    public DeferredResult<ResponseEntity<Device>> devicesSync(@PathVariable String deviceId){
        if (logger.isDebugEnabled()) {
            logger.debug("设备信息同步API调用,deviceId:" + deviceId);
        }
        Device device = storager.queryVideoDevice(deviceId);
        cmder.catalogQuery(device);
        DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>();
        resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result);
        return result;
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -10,7 +10,9 @@
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 @@
    @Autowired
    private SIPCommander cmder;
    
    @Autowired
    private IVideoManagerStorager storager;
    @GetMapping("/play/{deviceId}_{channelId}")
    public ResponseEntity<String> 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));
src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java
@@ -10,7 +10,9 @@
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 @@
    
    @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 @@
        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<String>("success",HttpStatus.OK);
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java
New file
@@ -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<ResponseEntity<RecordInfo>> 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<ResponseEntity<RecordInfo>> result = new DeferredResult<ResponseEntity<RecordInfo>>();
        resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result);
        return result;
    }
}