Browse Source
# Conflicts: # src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java # src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java # src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java # src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java # src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java # src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java # src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java # src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java # src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java # src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java # src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java # src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java # src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java # src/main/resources/application-dev.ymlpull/76/head

53 changed files with 7144 additions and 7160 deletions
@ -1,23 +1,21 @@ |
|||
package com.genersoft.iot.vmp; |
|||
|
|||
import java.util.logging.LogManager; |
|||
|
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.context.ConfigurableApplicationContext; |
|||
|
|||
@SpringBootApplication |
|||
public class VManageBootstrap extends LogManager { |
|||
private static String[] args; |
|||
private static ConfigurableApplicationContext context; |
|||
public static void main(String[] args) { |
|||
VManageBootstrap.args = args; |
|||
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); |
|||
} |
|||
// 项目重启
|
|||
public static void restart() { |
|||
context.close(); |
|||
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); |
|||
|
|||
} |
|||
} |
|||
package com.genersoft.iot.vmp; |
|||
|
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.context.ConfigurableApplicationContext; |
|||
|
|||
@SpringBootApplication |
|||
public class VManageBootstrap extends LogManager { |
|||
private static String[] args; |
|||
private static ConfigurableApplicationContext context; |
|||
public static void main(String[] args) { |
|||
VManageBootstrap.args = args; |
|||
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); |
|||
} |
|||
// 项目重启
|
|||
public static void restart() { |
|||
context.close(); |
|||
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); |
|||
|
|||
} |
|||
} |
|||
|
@ -1,46 +1,48 @@ |
|||
package com.genersoft.iot.vmp.common; |
|||
|
|||
/** |
|||
* @Description: 定义常量 |
|||
* @author: swwheihei |
|||
* @date: 2019年5月30日 下午3:04:04 |
|||
* |
|||
*/ |
|||
public class VideoManagerConstants { |
|||
|
|||
public static final String MEDIA_SERVER_PREFIX = "VMP_media_server"; |
|||
|
|||
public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream"; |
|||
|
|||
public static final String DEVICE_PREFIX = "VMP_device_"; |
|||
|
|||
public static final String CACHEKEY_PREFIX = "VMP_channel_"; |
|||
|
|||
public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_"; |
|||
|
|||
public static final String PLAYER_PREFIX = "VMP_player_"; |
|||
|
|||
public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; |
|||
|
|||
public static final String PLATFORM_PREFIX = "VMP_platform"; |
|||
|
|||
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_platform_keeplive_"; |
|||
|
|||
public static final String PLATFORM_CATCH_PREFIX = "VMP_platform_catch_"; |
|||
|
|||
public static final String PLATFORM_REGISTER_PREFIX = "VMP_platform_register_"; |
|||
|
|||
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_platform_register_info_"; |
|||
|
|||
public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_platform_send_rtp_info_"; |
|||
|
|||
public static final String Pattern_Topic = "VMP_keeplive_platform_"; |
|||
|
|||
public static final String EVENT_ONLINE_REGISTER = "1"; |
|||
|
|||
public static final String EVENT_ONLINE_KEEPLIVE = "2"; |
|||
|
|||
public static final String EVENT_OUTLINE_UNREGISTER = "1"; |
|||
|
|||
public static final String EVENT_OUTLINE_TIMEOUT = "2"; |
|||
} |
|||
package com.genersoft.iot.vmp.common; |
|||
|
|||
/** |
|||
* @Description: 定义常量 |
|||
* @author: swwheihei |
|||
* @date: 2019年5月30日 下午3:04:04 |
|||
* |
|||
*/ |
|||
public class VideoManagerConstants { |
|||
|
|||
public static final String MEDIA_SERVER_PREFIX = "VMP_media_server"; |
|||
|
|||
public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream"; |
|||
|
|||
public static final String DEVICE_PREFIX = "VMP_device_"; |
|||
|
|||
public static final String CACHEKEY_PREFIX = "VMP_channel_"; |
|||
|
|||
public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_"; |
|||
|
|||
public static final String PLAYER_PREFIX = "VMP_player_"; |
|||
|
|||
public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; |
|||
|
|||
public static final String PLATFORM_PREFIX = "VMP_platform"; |
|||
|
|||
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_platform_keeplive_"; |
|||
|
|||
public static final String PLATFORM_CATCH_PREFIX = "VMP_platform_catch_"; |
|||
|
|||
public static final String PLATFORM_REGISTER_PREFIX = "VMP_platform_register_"; |
|||
|
|||
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_platform_register_info_"; |
|||
|
|||
public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_platform_send_rtp_info_"; |
|||
|
|||
public static final String Pattern_Topic = "VMP_keeplive_platform_"; |
|||
|
|||
public static final String EVENT_ONLINE_REGISTER = "1"; |
|||
|
|||
public static final String EVENT_ONLINE_KEEPLIVE = "2"; |
|||
|
|||
public static final String EVENT_OUTLINE_UNREGISTER = "1"; |
|||
|
|||
public static final String EVENT_OUTLINE_TIMEOUT = "2"; |
|||
|
|||
public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_"; |
|||
} |
|||
|
@ -1,63 +1,58 @@ |
|||
package com.genersoft.iot.vmp.conf; |
|||
|
|||
import com.genersoft.iot.vmp.common.VideoManagerConstants; |
|||
import com.genersoft.iot.vmp.utils.redis.JedisUtil; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Set; |
|||
|
|||
|
|||
/** |
|||
* 对配置文件进行校验 |
|||
* 对配置文件进行校验 |
|||
*/ |
|||
@Component |
|||
@Order(value=2) |
|||
@Order(value = 0) |
|||
public class ApplicationCheckRunner implements CommandLineRunner { |
|||
|
|||
private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); |
|||
|
|||
@Value("${sip.ip}") |
|||
private String sipIp; |
|||
|
|||
@Value("${media.ip}") |
|||
private String mediaIp; |
|||
|
|||
@Value("${media.wanIp}") |
|||
private String mediaWanIp; |
|||
|
|||
@Value("${media.hookIp}") |
|||
private String mediaHookIp; |
|||
|
|||
@Value("${media.port}") |
|||
private int mediaPort; |
|||
|
|||
@Value("${media.secret}") |
|||
private String mediaSecret; |
|||
|
|||
@Value("${media.streamNoneReaderDelayMS}") |
|||
private String streamNoneReaderDelayMS; |
|||
|
|||
@Value("${sip.ip}") |
|||
private String sipIP; |
|||
|
|||
@Value("${server.port}") |
|||
private String serverPort; |
|||
|
|||
@Value("${media.autoConfig}") |
|||
private boolean autoConfig; |
|||
|
|||
@Autowired |
|||
SipConfig sipConfig; |
|||
@Autowired |
|||
MediaConfig mediaConfig; |
|||
@Autowired |
|||
JedisUtil jedisUtil; |
|||
|
|||
@Override |
|||
public void run(String... args) throws Exception { |
|||
if (sipIP.equals("localhost") || sipIP.equals("127.0.0.1")) { |
|||
logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIP ); |
|||
public void run(String... args) { |
|||
String sipIp = sipConfig.getSipIp(); |
|||
if (sipIp.equals("localhost") || sipIp.equals("127.0.0.1")) { |
|||
logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIp); |
|||
System.exit(1); |
|||
} |
|||
|
|||
if (mediaIp.equals("localhost") || mediaIp.equals("127.0.0.1")) { |
|||
logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); |
|||
String mediaIp = mediaConfig.getMediaIp(); |
|||
String[] mediaIpArr = mediaIp.split(","); |
|||
mediaConfig.setMediaIpArr(mediaIpArr); |
|||
|
|||
for (String mId : mediaIpArr) { |
|||
if (mId.equals("localhost") || mId.equals("127.0.0.1")) { |
|||
logger.warn("mediaIp.ip使用localhost或127.0.0.1,将无法收到网络内其他设备的推流!!!"); |
|||
} |
|||
} |
|||
|
|||
HashMap mediaServerSsrcMap = new HashMap<>(mediaIpArr.length); |
|||
for (int i = 0; i < mediaIpArr.length; i++) { |
|||
String mIp = mediaIpArr[i]; |
|||
SsrcConfig ssrcConfig = new SsrcConfig(); |
|||
Set<String> usedSet = jedisUtil.smembers(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mIp); |
|||
ssrcConfig.init(mIp, usedSet); |
|||
mediaServerSsrcMap.put(mIp, ssrcConfig); |
|||
} |
|||
mediaConfig.setMediaServerSsrcMap(mediaServerSsrcMap); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,53 @@ |
|||
package com.genersoft.iot.vmp.conf; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
import java.util.HashMap; |
|||
|
|||
|
|||
/** |
|||
* 对配置文件进行校验 |
|||
*/ |
|||
@Configuration("mediaConfig") |
|||
@Data |
|||
public class MediaConfig { |
|||
@Value("${media.ip}") |
|||
private String mediaIp; |
|||
private String[] mediaIpArr; |
|||
|
|||
@Value("${media.hookIp}") |
|||
private String mediaHookIp; |
|||
|
|||
@Value("${media.port}") |
|||
private Integer mediaPort; |
|||
|
|||
@Value("${media.autoConfig}") |
|||
private Boolean autoConfig; |
|||
|
|||
@Value("${media.secret}") |
|||
private String mediaSecret; |
|||
|
|||
@Value("${media.streamNoneReaderDelayMS}") |
|||
private String streamNoneReaderDelayMS; |
|||
|
|||
@Value("${media.autoApplyPlay}") |
|||
private Boolean autoApplyPlay; |
|||
|
|||
@Value("${media.seniorSdp}") |
|||
private Boolean seniorSdp; |
|||
|
|||
@Value("${media.rtp.enable}") |
|||
private Boolean rtpEnable; |
|||
|
|||
@Value("${media.rtp.udpPortRange}") |
|||
private String udpPortRange; |
|||
|
|||
/** |
|||
* 每一台ZLM都有一套独立的SSRC列表 |
|||
* 在ApplicationCheckRunner里对mediaServerSsrcMap进行初始化 |
|||
*/ |
|||
private HashMap<String, SsrcConfig> mediaServerSsrcMap; |
|||
|
|||
} |
@ -1,57 +1,89 @@ |
|||
package com.genersoft.iot.vmp.conf; |
|||
|
|||
import org.springframework.cache.annotation.CachingConfigurerSupport; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
import com.alibaba.fastjson.parser.ParserConfig; |
|||
import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; |
|||
|
|||
/** |
|||
* @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 |
|||
* @author: swwheihei |
|||
* @date: 2019年5月30日 上午10:58:25 |
|||
* |
|||
*/ |
|||
@Configuration |
|||
public class RedisConfig extends CachingConfigurerSupport { |
|||
|
|||
@Bean("redisTemplate") |
|||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { |
|||
RedisTemplate<Object, Object> template = new RedisTemplate<>(); |
|||
template.setConnectionFactory(redisConnectionFactory); |
|||
// 使用fastjson进行序列化处理,提高解析效率
|
|||
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class); |
|||
// value值的序列化采用fastJsonRedisSerializer
|
|||
template.setValueSerializer(serializer); |
|||
template.setHashValueSerializer(serializer); |
|||
// key的序列化采用StringRedisSerializer
|
|||
template.setKeySerializer(new StringRedisSerializer()); |
|||
template.setHashKeySerializer(new StringRedisSerializer()); |
|||
template.setConnectionFactory(redisConnectionFactory); |
|||
// 使用fastjson时需设置此项,否则会报异常not support type
|
|||
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); |
|||
return template; |
|||
} |
|||
|
|||
/** |
|||
* redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 |
|||
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 |
|||
* |
|||
* @param connectionFactory |
|||
* @param listenerAdapter |
|||
* @return |
|||
*/ |
|||
@Bean |
|||
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { |
|||
|
|||
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
|||
container.setConnectionFactory(connectionFactory); |
|||
return container; |
|||
} |
|||
|
|||
} |
|||
package com.genersoft.iot.vmp.conf; |
|||
|
|||
import com.alibaba.fastjson.parser.ParserConfig; |
|||
import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.cache.annotation.CachingConfigurerSupport; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
import redis.clients.jedis.JedisPool; |
|||
import redis.clients.jedis.JedisPoolConfig; |
|||
|
|||
/** |
|||
* @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 |
|||
* @author: swwheihei |
|||
* @date: 2019年5月30日 上午10:58:25 |
|||
*/ |
|||
@Configuration |
|||
public class RedisConfig extends CachingConfigurerSupport { |
|||
|
|||
@Value("${spring.redis.host}") |
|||
private String host; |
|||
@Value("${spring.redis.port}") |
|||
private int port; |
|||
@Value("${spring.redis.database}") |
|||
private int database; |
|||
@Value("${spring.redis.password}") |
|||
private String password; |
|||
@Value("${spring.redis.timeout}") |
|||
private int timeout; |
|||
@Value("${spring.redis.poolMaxTotal}") |
|||
private int poolMaxTotal; |
|||
@Value("${spring.redis.poolMaxIdle}") |
|||
private int poolMaxIdle; |
|||
@Value("${spring.redis.poolMaxWait}") |
|||
private int poolMaxWait; |
|||
|
|||
@Bean |
|||
public JedisPool jedisPool() { |
|||
if (StringUtils.isBlank(password)) { |
|||
password = null; |
|||
} |
|||
JedisPoolConfig poolConfig = new JedisPoolConfig(); |
|||
poolConfig.setMaxIdle(poolMaxIdle); |
|||
poolConfig.setMaxTotal(poolMaxTotal); |
|||
// 秒转毫秒
|
|||
poolConfig.setMaxWaitMillis(poolMaxWait * 1000); |
|||
JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database); |
|||
return jp; |
|||
} |
|||
|
|||
@Bean("redisTemplate") |
|||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { |
|||
RedisTemplate<Object, Object> template = new RedisTemplate<>(); |
|||
template.setConnectionFactory(redisConnectionFactory); |
|||
// 使用fastjson进行序列化处理,提高解析效率
|
|||
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class); |
|||
// value值的序列化采用fastJsonRedisSerializer
|
|||
template.setValueSerializer(serializer); |
|||
template.setHashValueSerializer(serializer); |
|||
// key的序列化采用StringRedisSerializer
|
|||
template.setKeySerializer(new StringRedisSerializer()); |
|||
template.setHashKeySerializer(new StringRedisSerializer()); |
|||
template.setConnectionFactory(redisConnectionFactory); |
|||
// 使用fastjson时需设置此项,否则会报异常not support type
|
|||
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); |
|||
return template; |
|||
} |
|||
|
|||
/** |
|||
* redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 |
|||
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 |
|||
* |
|||
* @param connectionFactory |
|||
* @return |
|||
*/ |
|||
@Bean |
|||
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { |
|||
|
|||
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
|||
container.setConnectionFactory(connectionFactory); |
|||
return container; |
|||
} |
|||
|
|||
} |
|||
|
@ -0,0 +1,51 @@ |
|||
package com.genersoft.iot.vmp.conf; |
|||
|
|||
import com.genersoft.iot.vmp.utils.ConfigConst; |
|||
import lombok.Data; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
|
|||
/** |
|||
* 每一个zlm流媒体服务器,都设置MAX_STRTEAM_COUNT个可用同步信源(SSRC) |
|||
*/ |
|||
@Data |
|||
public class SsrcConfig { |
|||
/** |
|||
* zlm流媒体服务器IP |
|||
*/ |
|||
String mediaServerIp; |
|||
/** |
|||
* zlm流媒体服务器已用会话句柄 |
|||
*/ |
|||
private List<String> isUsed; |
|||
/** |
|||
* zlm流媒体服务器可用会话句柄 |
|||
*/ |
|||
private List<String> notUsed; |
|||
|
|||
public void init(String mediaServerIp, Set<String> usedSet) { |
|||
this.mediaServerIp = mediaServerIp; |
|||
this.isUsed = new ArrayList<>(); |
|||
|
|||
this.notUsed = new ArrayList<>(); |
|||
for (int i = 1; i < ConfigConst.MAX_STRTEAM_COUNT; i++) { |
|||
String ssrc; |
|||
if (i < 10) { |
|||
ssrc = "000" + i; |
|||
} else if (i < 100) { |
|||
ssrc = "00" + i; |
|||
} else if (i < 1000) { |
|||
ssrc = "0" + i; |
|||
} else { |
|||
ssrc = String.valueOf(i); |
|||
} |
|||
if (null == usedSet || !usedSet.contains(ssrc)) { |
|||
this.notUsed.add(ssrc); |
|||
} else { |
|||
this.isUsed.add(ssrc); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,236 +1,260 @@ |
|||
package com.genersoft.iot.vmp.gb28181; |
|||
|
|||
import java.text.ParseException; |
|||
import java.util.Properties; |
|||
import java.util.TooManyListenersException; |
|||
import java.util.concurrent.LinkedBlockingQueue; |
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
import javax.sip.*; |
|||
import javax.sip.header.CallIdHeader; |
|||
import javax.sip.message.Response; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Bean; |
|||
// import org.springframework.context.annotation.ComponentScan;
|
|||
import org.springframework.context.annotation.DependsOn; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; |
|||
|
|||
import gov.nist.javax.sip.SipStackImpl; |
|||
|
|||
@Component |
|||
public class SipLayer implements SipListener { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); |
|||
|
|||
@Autowired |
|||
private SipConfig sipConfig; |
|||
|
|||
@Autowired |
|||
private SIPProcessorFactory processorFactory; |
|||
|
|||
@Autowired |
|||
private SipSubscribe sipSubscribe; |
|||
|
|||
private SipStack sipStack; |
|||
|
|||
private SipFactory sipFactory; |
|||
|
|||
/** |
|||
* 消息处理器线程池 |
|||
*/ |
|||
private ThreadPoolExecutor processThreadPool; |
|||
|
|||
@Bean("initSipServer") |
|||
private ThreadPoolExecutor initSipServer() { |
|||
|
|||
int processThreadNum = Runtime.getRuntime().availableProcessors() * 10; |
|||
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<Runnable>(10000); |
|||
processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum, |
|||
0L,TimeUnit.MILLISECONDS,processQueue, |
|||
new ThreadPoolExecutor.CallerRunsPolicy()); |
|||
return processThreadPool; |
|||
} |
|||
|
|||
@Bean("sipFactory") |
|||
@DependsOn("initSipServer") |
|||
private SipFactory createSipFactory() { |
|||
sipFactory = SipFactory.getInstance(); |
|||
sipFactory.setPathName("gov.nist"); |
|||
return sipFactory; |
|||
} |
|||
|
|||
@Bean("sipStack") |
|||
@DependsOn({"initSipServer", "sipFactory"}) |
|||
private SipStack createSipStack() throws PeerUnavailableException { |
|||
Properties properties = new Properties(); |
|||
properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); |
|||
properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.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); |
|||
return sipStack; |
|||
} |
|||
|
|||
@Bean("tcpSipProvider") |
|||
@DependsOn("sipStack") |
|||
private SipProvider startTcpListener() { |
|||
ListeningPoint tcpListeningPoint = null; |
|||
SipProvider tcpSipProvider = null; |
|||
try { |
|||
tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP"); |
|||
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); |
|||
tcpSipProvider.addSipListener(this); |
|||
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); |
|||
} catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) { |
|||
logger.error(String.format("创建SIP服务失败: %s", e.getMessage())); |
|||
} |
|||
return tcpSipProvider; |
|||
} |
|||
|
|||
@Bean("udpSipProvider") |
|||
@DependsOn("sipStack") |
|||
private SipProvider startUdpListener() throws Exception { |
|||
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP"); |
|||
SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); |
|||
udpSipProvider.addSipListener(this); |
|||
logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); |
|||
return udpSipProvider; |
|||
} |
|||
|
|||
/** |
|||
* SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a |
|||
* new request arrives. |
|||
*/ |
|||
@Override |
|||
public void processRequest(RequestEvent evt) { |
|||
logger.debug(evt.getRequest().toString()); |
|||
// 由于jainsip是单线程程序,为提高性能并发处理
|
|||
processThreadPool.execute(() -> { |
|||
if (processorFactory != null) { |
|||
processorFactory.createRequestProcessor(evt).process(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public void processResponse(ResponseEvent evt) { |
|||
Response response = evt.getResponse(); |
|||
logger.debug(evt.getResponse().toString()); |
|||
int status = response.getStatusCode(); |
|||
if (((status >= 200) && (status < 300)) || status == 401) { // Success!
|
|||
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); |
|||
try { |
|||
processor.process(evt, this, sipConfig); |
|||
} catch (ParseException e) { |
|||
// TODO Auto-generated catch block
|
|||
e.printStackTrace(); |
|||
} |
|||
if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { |
|||
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); |
|||
if (callIdHeader != null) { |
|||
SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); |
|||
if (subscribe != null) { |
|||
subscribe.response(evt); |
|||
} |
|||
} |
|||
} |
|||
} else if ((status >= 100) && (status < 200)) { |
|||
// 增加其它无需回复的响应,如101、180等
|
|||
} else { |
|||
logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); |
|||
if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { |
|||
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); |
|||
if (callIdHeader != null) { |
|||
SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); |
|||
if (subscribe != null) { |
|||
subscribe.response(evt); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
/** |
|||
* <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 |
|||
*/ |
|||
@Override |
|||
public void processIOException(IOExceptionEvent exceptionEvent) { |
|||
// TODO Auto-generated method stub
|
|||
|
|||
} |
|||
|
|||
/** |
|||
* <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 |
|||
*/ |
|||
@Override |
|||
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { |
|||
// TODO Auto-generated method stub
|
|||
|
|||
} |
|||
|
|||
} |
|||
package com.genersoft.iot.vmp.gb28181; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; |
|||
import gov.nist.javax.sip.SipStackImpl; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.DependsOn; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.sip.*; |
|||
import javax.sip.header.CallIdHeader; |
|||
import javax.sip.message.Response; |
|||
import java.text.ParseException; |
|||
import java.util.Properties; |
|||
import java.util.TooManyListenersException; |
|||
import java.util.concurrent.LinkedBlockingQueue; |
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Component |
|||
public class SipLayer implements SipListener { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); |
|||
|
|||
@Autowired |
|||
private SipConfig sipConfig; |
|||
|
|||
@Autowired |
|||
private SIPProcessorFactory processorFactory; |
|||
|
|||
@Autowired |
|||
private SipSubscribe sipSubscribe; |
|||
|
|||
private SipStack sipStack; |
|||
|
|||
private SipFactory sipFactory; |
|||
|
|||
/** |
|||
* 消息处理器线程池 |
|||
*/ |
|||
private ThreadPoolExecutor processThreadPool; |
|||
|
|||
@Bean("initSipServer") |
|||
private ThreadPoolExecutor initSipServer() { |
|||
|
|||
int processThreadNum = Runtime.getRuntime().availableProcessors() * 10; |
|||
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000); |
|||
processThreadPool = new ThreadPoolExecutor(processThreadNum, processThreadNum, |
|||
0L, TimeUnit.MILLISECONDS, processQueue, |
|||
new ThreadPoolExecutor.CallerRunsPolicy()); |
|||
return processThreadPool; |
|||
} |
|||
|
|||
@Bean("sipFactory") |
|||
@DependsOn("initSipServer") |
|||
private SipFactory createSipFactory() { |
|||
sipFactory = SipFactory.getInstance(); |
|||
sipFactory.setPathName("gov.nist"); |
|||
return sipFactory; |
|||
} |
|||
|
|||
@Bean("sipStack") |
|||
@DependsOn({"initSipServer", "sipFactory"}) |
|||
private SipStack createSipStack() throws PeerUnavailableException { |
|||
Properties properties = new Properties(); |
|||
properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); |
|||
properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.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); |
|||
return sipStack; |
|||
} |
|||
|
|||
@Bean("tcpSipProvider") |
|||
@DependsOn("sipStack") |
|||
private SipProvider startTcpListener() { |
|||
ListeningPoint tcpListeningPoint = null; |
|||
SipProvider tcpSipProvider = null; |
|||
try { |
|||
tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP"); |
|||
} catch (TransportNotSupportedException e) { |
|||
logger.error("创建SIP服务失败!", e); |
|||
System.exit(1); |
|||
} catch (InvalidArgumentException e) { |
|||
if ("Cannot assign requested address: JVM_Bind".equals(e.getMessage())) { |
|||
logger.error("创建SIP服务失败,请检查sip.ip必须是本地网卡上的IP!", e); |
|||
} else { |
|||
logger.error("创建SIP服务失败!", e); |
|||
} |
|||
System.exit(1); |
|||
} |
|||
try { |
|||
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); |
|||
} catch (ObjectInUseException e) { |
|||
logger.error("创建SIP服务失败!", e); |
|||
System.exit(1); |
|||
} |
|||
try { |
|||
tcpSipProvider.addSipListener(this); |
|||
} catch (TooManyListenersException e) { |
|||
logger.error("创建SIP服务失败!", e); |
|||
System.exit(1); |
|||
} |
|||
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); |
|||
return tcpSipProvider; |
|||
|
|||
|
|||
// try {
|
|||
// tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP");
|
|||
// tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
|
|||
// tcpSipProvider.addSipListener(this);
|
|||
// logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}");
|
|||
// } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
|
|||
// logger.error(String.format("创建SIP服务失败: %s", e.getMessage()));
|
|||
// }
|
|||
// return tcpSipProvider;
|
|||
} |
|||
|
|||
@Bean("udpSipProvider") |
|||
@DependsOn("sipStack") |
|||
private SipProvider startUdpListener() throws Exception { |
|||
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP"); |
|||
SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); |
|||
udpSipProvider.addSipListener(this); |
|||
logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); |
|||
return udpSipProvider; |
|||
} |
|||
|
|||
/** |
|||
* SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a |
|||
* new request arrives. |
|||
*/ |
|||
@Override |
|||
public void processRequest(RequestEvent evt) { |
|||
logger.debug(evt.getRequest().toString()); |
|||
// 由于jainsip是单线程程序,为提高性能并发处理
|
|||
processThreadPool.execute(() -> { |
|||
if (processorFactory != null) { |
|||
processorFactory.createRequestProcessor(evt).process(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public void processResponse(ResponseEvent evt) { |
|||
Response response = evt.getResponse(); |
|||
logger.debug(evt.getResponse().toString()); |
|||
int status = response.getStatusCode(); |
|||
if (((status >= 200) && (status < 300)) || status == 401) { // Success!
|
|||
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); |
|||
try { |
|||
processor.process(evt, this, sipConfig); |
|||
} catch (ParseException e) { |
|||
// TODO Auto-generated catch block
|
|||
e.printStackTrace(); |
|||
} |
|||
if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { |
|||
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); |
|||
if (callIdHeader != null) { |
|||
SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); |
|||
if (subscribe != null) { |
|||
subscribe.response(evt); |
|||
} |
|||
} |
|||
} |
|||
} else if ((status >= 100) && (status < 200)) { |
|||
// 增加其它无需回复的响应,如101、180等
|
|||
} else { |
|||
logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); |
|||
if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { |
|||
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); |
|||
if (callIdHeader != null) { |
|||
SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); |
|||
if (subscribe != null) { |
|||
subscribe.response(evt); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
/** |
|||
* <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 |
|||
*/ |
|||
@Override |
|||
public void processIOException(IOExceptionEvent exceptionEvent) { |
|||
// TODO Auto-generated method stub
|
|||
|
|||
} |
|||
|
|||
/** |
|||
* <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 |
|||
*/ |
|||
@Override |
|||
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { |
|||
// TODO Auto-generated method stub
|
|||
|
|||
} |
|||
|
|||
} |
|||
|
@ -1,55 +1,53 @@ |
|||
package com.genersoft.iot.vmp.gb28181.bean; |
|||
|
|||
|
|||
//import gov.nist.javax.sip.header.SIPDate;
|
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @Description:设备录像信息bean |
|||
* @author: swwheihei |
|||
* @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; |
|||
} |
|||
|
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.bean; |
|||
|
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @Description:设备录像信息bean |
|||
* @author: swwheihei |
|||
* @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; |
|||
} |
|||
|
|||
} |
|||
|
@ -1,24 +1,24 @@ |
|||
package com.genersoft.iot.vmp.gb28181.event; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.common.VideoManagerConstants; |
|||
import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
|||
|
|||
/** |
|||
* @Description:设备离在线状态检测器,用于检测设备状态 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月13日 下午2:40:29 |
|||
*/ |
|||
@Component |
|||
public class DeviceOffLineDetector { |
|||
|
|||
@Autowired |
|||
private RedisUtil redis; |
|||
|
|||
public boolean isOnline(String deviceId) { |
|||
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + deviceId; |
|||
return redis.hasKey(key); |
|||
} |
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.event; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.common.VideoManagerConstants; |
|||
import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
|||
|
|||
/** |
|||
* @Description:设备离在线状态检测器,用于检测设备状态 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月13日 下午2:40:29 |
|||
*/ |
|||
@Component |
|||
public class DeviceOffLineDetector { |
|||
|
|||
@Autowired |
|||
private RedisUtil redis; |
|||
|
|||
public Boolean isOnline(String deviceId) { |
|||
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + deviceId; |
|||
return redis.hasKey(key); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,23 @@ |
|||
package com.genersoft.iot.vmp.gb28181.session; |
|||
|
|||
public enum PlayTypeEnum { |
|||
|
|||
PLAY("0", "直播"), |
|||
PLAY_BACK("1", "回放"); |
|||
|
|||
private String value; |
|||
private String name; |
|||
|
|||
PlayTypeEnum(String value, String name) { |
|||
this.value = value; |
|||
this.name = name; |
|||
} |
|||
|
|||
public String getValue() { |
|||
return value; |
|||
} |
|||
|
|||
public String getName() { |
|||
return name; |
|||
} |
|||
} |
@ -1,93 +0,0 @@ |
|||
package com.genersoft.iot.vmp.gb28181.session; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Random; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.utils.SpringBeanFactory; |
|||
|
|||
/** |
|||
* @Description:SIP信令中的SSRC工具类。SSRC值由10位十进制整数组成的字符串,第一位为0代表实况,为1则代表回放;第二位至第六位由监控域ID的第4位到第8位组成;最后4位为不重复的4个整数 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月10日 上午11:57:57 |
|||
*/ |
|||
public class SsrcUtil { |
|||
|
|||
private static String ssrcPrefix; |
|||
|
|||
private static List<String> isUsed; |
|||
|
|||
private static List<String> notUsed; |
|||
|
|||
private static void init() { |
|||
SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig"); |
|||
ssrcPrefix = sipConfig.getSipDomain().substring(3, 8); |
|||
isUsed = new ArrayList<String>(); |
|||
notUsed = new ArrayList<String>(); |
|||
for (int i = 1; i < 10000; i++) { |
|||
if (i < 10) { |
|||
notUsed.add("000" + i); |
|||
} else if (i < 100) { |
|||
notUsed.add("00" + i); |
|||
} else if (i < 1000) { |
|||
notUsed.add("0" + i); |
|||
} else { |
|||
notUsed.add(String.valueOf(i)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取视频预览的SSRC值,第一位固定为0 |
|||
* |
|||
*/ |
|||
public static String getPlaySsrc() { |
|||
return "0" + getSsrcPrefix() + getSN(); |
|||
} |
|||
|
|||
/** |
|||
* 获取录像回放的SSRC值,第一位固定为1 |
|||
* |
|||
*/ |
|||
public static String getPlayBackSsrc() { |
|||
return "1" + getSsrcPrefix() + getSN(); |
|||
} |
|||
|
|||
/** |
|||
* 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽 |
|||
* |
|||
*/ |
|||
public static void releaseSsrc(String ssrc) { |
|||
String sn = ssrc.substring(6); |
|||
isUsed.remove(sn); |
|||
notUsed.add(sn); |
|||
} |
|||
|
|||
/** |
|||
* 获取后四位数SN,随机数 |
|||
* |
|||
*/ |
|||
private static String getSN() { |
|||
String sn = null; |
|||
int index = 0; |
|||
if (notUsed.size() == 0) { |
|||
throw new RuntimeException("ssrc已经用完"); |
|||
} else if (notUsed.size() == 1) { |
|||
sn = notUsed.get(0); |
|||
} else { |
|||
index = new Random().nextInt(notUsed.size() - 1); |
|||
sn = notUsed.get(index); |
|||
} |
|||
notUsed.remove(index); |
|||
isUsed.add(sn); |
|||
return sn; |
|||
} |
|||
|
|||
private static String getSsrcPrefix() { |
|||
if (ssrcPrefix == null) { |
|||
init(); |
|||
} |
|||
return ssrcPrefix; |
|||
} |
|||
} |
@ -1,42 +1,285 @@ |
|||
package com.genersoft.iot.vmp.gb28181.session; |
|||
|
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
import javax.sip.ClientTransaction; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月13日 下午4:03:02 |
|||
*/ |
|||
@Component |
|||
public class VideoStreamSessionManager { |
|||
|
|||
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); |
|||
private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>(); |
|||
|
|||
public String createPlaySsrc(){ |
|||
return SsrcUtil.getPlaySsrc(); |
|||
} |
|||
|
|||
public String createPlayBackSsrc(){ |
|||
return SsrcUtil.getPlayBackSsrc(); |
|||
} |
|||
|
|||
public void put(String streamId,String ssrc,ClientTransaction transaction){ |
|||
sessionMap.put(streamId, transaction); |
|||
ssrcMap.put(streamId, ssrc); |
|||
} |
|||
|
|||
public ClientTransaction get(String streamId){ |
|||
return sessionMap.get(streamId); |
|||
} |
|||
|
|||
public void remove(String streamId) { |
|||
sessionMap.remove(streamId); |
|||
SsrcUtil.releaseSsrc(ssrcMap.get(streamId)); |
|||
ssrcMap.remove(streamId); |
|||
} |
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.session; |
|||
|
|||
import com.genersoft.iot.vmp.common.StreamInfo; |
|||
import com.genersoft.iot.vmp.common.VideoManagerConstants; |
|||
import com.genersoft.iot.vmp.conf.MediaConfig; |
|||
import com.genersoft.iot.vmp.conf.MediaServerConfig; |
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.conf.SsrcConfig; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
|||
import com.genersoft.iot.vmp.utils.redis.JedisUtil; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.sip.ClientTransaction; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Random; |
|||
import java.util.Set; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
/** |
|||
* @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月13日 下午4:03:02 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
public class VideoStreamSessionManager { |
|||
/** |
|||
* key: ssrc 播流会话句柄(streamId)和同步信源(SSRC)的对应关系 |
|||
* value: 流媒体服务器 |
|||
*/ |
|||
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); |
|||
private String ssrcPrefix; |
|||
|
|||
@Autowired |
|||
private SipConfig sipConfig; |
|||
@Autowired |
|||
private MediaConfig mediaConfig; |
|||
@Autowired |
|||
private JedisUtil jedisUtil; |
|||
@Autowired |
|||
private IRedisCatchStorage redisCatchStorage; |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
this.ssrcPrefix = sipConfig.getSipDomain().substring(3, 8); |
|||
} |
|||
|
|||
/** |
|||
* 获取视频预览的会话信息。 |
|||
*/ |
|||
public StreamInfo createPlayStreamInfo(Device device, String channelId) { |
|||
// SSRC值,第一位固定为0
|
|||
StreamInfo streamInfo = createStreamInfo(device, channelId, PlayTypeEnum.PLAY); |
|||
// 会话句柄和ZLM服务器的对应关系,存到redis,防止服务器宕机数据丢失。
|
|||
redisCatchStorage.startPlay(streamInfo); |
|||
return streamInfo; |
|||
} |
|||
|
|||
/** |
|||
* 获取录像回放的会话信息 |
|||
* 会话句柄和ZLM服务器的对应关系,存到redis,防止服务器宕机数据丢失 |
|||
*/ |
|||
public StreamInfo createPlayBackStreamInfo(Device device, String channelId) { |
|||
// SSRC值,第一位固定为1
|
|||
StreamInfo streamInfo = createStreamInfo(device, channelId, PlayTypeEnum.PLAY_BACK); |
|||
// 会话句柄和ZLM服务器的对应关系,存到redis,防止服务器宕机数据丢失。
|
|||
redisCatchStorage.startPlayback(streamInfo); |
|||
return streamInfo; |
|||
} |
|||
|
|||
/** |
|||
* 1、选举ZLM服务器 |
|||
* 2、分配SSRC |
|||
* 3、生成streamId和播流RUL,如果此时未连接ZLM,会抛出运行时异常 |
|||
* 4、已分配SSRC存储到redis,防止服务器宕机后数据丢失。 |
|||
*/ |
|||
private StreamInfo createStreamInfo(Device device, String channelId, PlayTypeEnum playType) { |
|||
// 1、选举ZLM服务器
|
|||
SsrcConfig ssrcConfig = elect(); |
|||
List<String> isUsed = ssrcConfig.getIsUsed(); |
|||
List<String> notUsed = ssrcConfig.getNotUsed(); |
|||
|
|||
// 2、分配SSRC
|
|||
String sn; |
|||
int index = 0; |
|||
if (notUsed.size() == 0) { |
|||
throw new RuntimeException("ssrc已经用完"); |
|||
} else if (notUsed.size() == 1) { |
|||
sn = notUsed.get(0); |
|||
} else { |
|||
index = new Random().nextInt(notUsed.size() - 1); |
|||
sn = notUsed.get(index); |
|||
} |
|||
String ssrc = playType.getValue() + ssrcPrefix + sn; |
|||
String mediaServerIp = ssrcConfig.getMediaServerIp(); |
|||
|
|||
// 3、生成streamId和播流RUL,如果此时未连接ZLM,会抛出运行时异常
|
|||
StreamInfo streamInfo = initStreamInfo(device, channelId, ssrc, mediaServerIp); |
|||
|
|||
// 4、已分配SSRC存储到redis,防止服务器宕机后数据丢失。
|
|||
jedisUtil.sadd(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mediaServerIp, sn); |
|||
notUsed.remove(index); |
|||
isUsed.add(sn); |
|||
return streamInfo; |
|||
} |
|||
|
|||
/** |
|||
* 流媒体服务器选举算法 |
|||
* |
|||
* @return |
|||
*/ |
|||
private SsrcConfig elect() { |
|||
Set<Map.Entry<String, SsrcConfig>> entries = mediaConfig.getMediaServerSsrcMap().entrySet(); |
|||
SsrcConfig min = null; |
|||
for (Map.Entry<String, SsrcConfig> e : entries) { |
|||
SsrcConfig vo = e.getValue(); |
|||
if (null == min) { |
|||
min = vo; |
|||
continue; |
|||
} |
|||
if (vo.getNotUsed().size() > min.getNotUsed().size()) { |
|||
min = vo; |
|||
} |
|||
} |
|||
return min; |
|||
} |
|||
|
|||
/** |
|||
* 生成streamId和播流RUL,如果此时未连接ZLM,会抛出运行时异常 |
|||
* |
|||
* @param device |
|||
* @param channelId |
|||
* @param ssrc |
|||
* @param mediaServerIp |
|||
* @return |
|||
*/ |
|||
private StreamInfo initStreamInfo(Device device, String channelId, String ssrc, String mediaServerIp) { |
|||
String streamId; |
|||
if (ssrc.startsWith(PlayTypeEnum.PLAY.getValue()) && mediaConfig.getRtpEnable()) { |
|||
streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); |
|||
} else { |
|||
streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); |
|||
} |
|||
StreamInfo streamInfo = new StreamInfo(); |
|||
streamInfo.setMediaServerIp(mediaServerIp); |
|||
streamInfo.setSsrc(ssrc); |
|||
streamInfo.setStreamId(streamId); |
|||
streamInfo.setDeviceID(device.getDeviceId()); |
|||
streamInfo.setChannelId(channelId); |
|||
MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo(); |
|||
if (null == mediaServerConfig) { |
|||
throw new RuntimeException("点播时发现ZLM尚未连接..."); |
|||
} |
|||
|
|||
streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
|
|||
streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
|
|||
streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
|
|||
streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); |
|||
|
|||
streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerIp, mediaServerConfig.getRtmpPort(), streamId)); |
|||
streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerIp, mediaServerConfig.getRtspPort(), streamId)); |
|||
|
|||
return streamInfo; |
|||
} |
|||
|
|||
/** |
|||
* 查找IPC通道播流使用流媒体服务器的IP |
|||
* |
|||
* @param channelId |
|||
* @param streamId |
|||
* @return |
|||
*/ |
|||
public String getMediaServerIp(String channelId, String streamId) { |
|||
StreamInfo streamInfo = this.getStreamInfo(channelId, streamId); |
|||
return null == streamInfo ? null : streamInfo.getMediaServerIp(); |
|||
} |
|||
|
|||
public StreamInfo getPlayStreamInfo(String channelId) { |
|||
if (StringUtils.isBlank(channelId)) { |
|||
log.error("getPlayStreamInfo channelId can not be null!!!"); |
|||
return null; |
|||
} |
|||
return redisCatchStorage.queryPlayByChannel(channelId); |
|||
} |
|||
|
|||
public StreamInfo getPlayBackStreamInfo(String channelId) { |
|||
if (StringUtils.isBlank(channelId)) { |
|||
log.error("getPlayBackStreamInfo channelId can not be null!!!"); |
|||
return null; |
|||
} |
|||
return redisCatchStorage.queryPlaybackByChannel(channelId); |
|||
} |
|||
|
|||
public StreamInfo getStreamInfo(String channelId, String streamId) { |
|||
if (StringUtils.isBlank(channelId) || StringUtils.isBlank(streamId)) { |
|||
log.error("getStreamInfo channelId and streamId can not be null!!!"); |
|||
return null; |
|||
} |
|||
StreamInfo streamInfo = getStreamInfo(channelId, streamId, PlayTypeEnum.PLAY); |
|||
if (null == streamInfo) { |
|||
streamInfo = getStreamInfo(channelId, streamId, PlayTypeEnum.PLAY_BACK); |
|||
} |
|||
return streamInfo; |
|||
} |
|||
|
|||
private StreamInfo getStreamInfo(String channelId, String streamId, PlayTypeEnum playType) { |
|||
if (StringUtils.isBlank(channelId) || StringUtils.isBlank(streamId)) { |
|||
log.error("getStreamInfo channelId and streamId can not be null!!!"); |
|||
return null; |
|||
} |
|||
// TODO channelId
|
|||
if (null == playType || PlayTypeEnum.PLAY.equals(playType)) { |
|||
return redisCatchStorage.queryPlayByStreamId(channelId, streamId); |
|||
} else { |
|||
return redisCatchStorage.queryPlaybackByStreamId(channelId, streamId); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 存储会话 |
|||
* |
|||
* @param channelId |
|||
* @param streamId |
|||
* @param transaction |
|||
*/ |
|||
public void putClientTransaction(String channelId, String streamId, ClientTransaction transaction) { |
|||
String streamKey = getStreamKey(channelId, streamId); |
|||
sessionMap.put(streamKey, transaction); |
|||
} |
|||
|
|||
public ClientTransaction getClientTransaction(String channelId, String streamId) { |
|||
String streamKey = getStreamKey(channelId, streamId); |
|||
return sessionMap.get(streamKey); |
|||
} |
|||
|
|||
public void remove(String channelId, String streamId) { |
|||
StreamInfo streamInfo = this.getStreamInfo(channelId, streamId); |
|||
if (null == streamId) { |
|||
return; |
|||
} |
|||
this.remove(streamInfo); |
|||
} |
|||
|
|||
/** |
|||
* 移除会话并释放ssrc,主要用完的ssrc一定要释放,否则会耗尽 |
|||
*/ |
|||
public void remove(StreamInfo streamInfo) { |
|||
String streamKey = getStreamKey(streamInfo.getChannelId(), streamInfo.getStreamId()); |
|||
// 移除会话
|
|||
sessionMap.remove(streamKey); |
|||
|
|||
String ssrc = streamInfo.getSsrc(); |
|||
String sn = ssrc.substring(6); |
|||
String mediaServerIp = streamInfo.getMediaServerIp(); |
|||
// 释放ssrc,并从redis移除
|
|||
jedisUtil.srem(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mediaServerIp, sn); |
|||
SsrcConfig ssrcConfig = mediaConfig.getMediaServerSsrcMap().get(mediaServerIp); |
|||
ssrcConfig.getIsUsed().remove(sn); |
|||
ssrcConfig.getNotUsed().add(sn); |
|||
|
|||
// 会话句柄和ZLM服务器的对应关系,从redis移除
|
|||
if (ssrc.startsWith(PlayTypeEnum.PLAY.getValue())) { |
|||
redisCatchStorage.stopPlay(streamInfo); |
|||
} else { |
|||
redisCatchStorage.stopPlayback(streamInfo); |
|||
} |
|||
} |
|||
|
|||
private static String getStreamKey(String channelId, String streamId) { |
|||
return channelId + "_" + streamId; |
|||
} |
|||
} |
|||
|
@ -1,231 +1,231 @@ |
|||
package com.genersoft.iot.vmp.gb28181.transmit; |
|||
|
|||
import javax.sip.RequestEvent; |
|||
import javax.sip.ResponseEvent; |
|||
import javax.sip.SipProvider; |
|||
import javax.sip.header.CSeqHeader; |
|||
import javax.sip.message.Request; |
|||
import javax.sip.message.Response; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
|||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
|||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*; |
|||
import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
|||
// import org.slf4j.Logger;
|
|||
// import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler; |
|||
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; |
|||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
|||
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.transmit.request.impl.AckRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
import com.genersoft.iot.vmp.utils.SpringBeanFactory; |
|||
import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
|||
|
|||
/** |
|||
* @Description: SIP信令处理分配 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月3日 下午4:24:37 |
|||
*/ |
|||
@Component |
|||
public class SIPProcessorFactory { |
|||
|
|||
// private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
|
|||
|
|||
@Autowired |
|||
private SipConfig sipConfig; |
|||
|
|||
@Autowired |
|||
private RegisterLogicHandler handler; |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private IRedisCatchStorage redisCatchStorage; |
|||
|
|||
@Autowired |
|||
private EventPublisher publisher; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private SIPCommanderFroPlatform cmderFroPlatform; |
|||
|
|||
@Autowired |
|||
private RedisUtil redis; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder deferredResultHolder; |
|||
|
|||
@Autowired |
|||
private DeviceOffLineDetector offLineDetector; |
|||
|
|||
@Autowired |
|||
private InviteResponseProcessor inviteResponseProcessor; |
|||
|
|||
@Autowired |
|||
private ByeResponseProcessor byeResponseProcessor; |
|||
|
|||
@Autowired |
|||
private CancelResponseProcessor cancelResponseProcessor; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private RegisterResponseProcessor registerResponseProcessor; |
|||
|
|||
|
|||
@Autowired |
|||
private OtherResponseProcessor otherResponseProcessor; |
|||
|
|||
@Autowired |
|||
private IPlayService playService; |
|||
|
|||
@Autowired |
|||
private ZLMRTPServerFactory zlmrtpServerFactory; |
|||
|
|||
|
|||
// 注:这里使用注解会导致循环依赖注入,暂用springBean
|
|||
private SipProvider tcpSipProvider; |
|||
|
|||
// 注:这里使用注解会导致循环依赖注入,暂用springBean
|
|||
private SipProvider udpSipProvider; |
|||
|
|||
public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) { |
|||
Request request = evt.getRequest(); |
|||
String method = request.getMethod(); |
|||
// logger.info("接收到消息:"+request.getMethod());
|
|||
// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
|
|||
if (Request.INVITE.equals(method)) { |
|||
InviteRequestProcessor processor = new InviteRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
|
|||
processor.setCmder(cmder); |
|||
processor.setCmderFroPlatform(cmderFroPlatform); |
|||
processor.setPlayService(playService); |
|||
processor.setStorager(storager); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
processor.setZlmrtpServerFactory(zlmrtpServerFactory); |
|||
return processor; |
|||
} else if (Request.REGISTER.equals(method)) { |
|||
RegisterRequestProcessor processor = new RegisterRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
processor.setHandler(handler); |
|||
processor.setPublisher(publisher); |
|||
processor.setSipConfig(sipConfig); |
|||
processor.setVideoManagerStorager(storager); |
|||
return processor; |
|||
} else if (Request.SUBSCRIBE.equals(method)) { |
|||
SubscribeRequestProcessor processor = new SubscribeRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
return processor; |
|||
} else if (Request.ACK.equals(method)) { |
|||
AckRequestProcessor processor = new AckRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
processor.setZlmrtpServerFactory(zlmrtpServerFactory); |
|||
return processor; |
|||
} else if (Request.BYE.equals(method)) { |
|||
ByeRequestProcessor processor = new ByeRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
processor.setZlmrtpServerFactory(zlmrtpServerFactory); |
|||
processor.setSIPCommander(cmder); |
|||
return processor; |
|||
} else if (Request.CANCEL.equals(method)) { |
|||
CancelRequestProcessor processor = new CancelRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
return processor; |
|||
} else if (Request.MESSAGE.equals(method)) { |
|||
MessageRequestProcessor processor = new MessageRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
processor.setPublisher(publisher); |
|||
processor.setRedis(redis); |
|||
processor.setDeferredResultHolder(deferredResultHolder); |
|||
processor.setOffLineDetector(offLineDetector); |
|||
processor.setCmder(cmder); |
|||
processor.setCmderFroPlatform(cmderFroPlatform); |
|||
processor.setStorager(storager); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
return processor; |
|||
} else if (Request.NOTIFY.equalsIgnoreCase(method)) { |
|||
NotifyRequestProcessor processor = new NotifyRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
processor.setPublisher(publisher); |
|||
processor.setRedis(redis); |
|||
processor.setDeferredResultHolder(deferredResultHolder); |
|||
processor.setOffLineDetector(offLineDetector); |
|||
processor.setCmder(cmder); |
|||
processor.setStorager(storager); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
return processor; |
|||
} else { |
|||
OtherRequestProcessor processor = new OtherRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
return processor; |
|||
} |
|||
} |
|||
|
|||
public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) { |
|||
|
|||
Response response = evt.getResponse(); |
|||
CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); |
|||
String method = cseqHeader.getMethod(); |
|||
if(Request.INVITE.equals(method)){ |
|||
return inviteResponseProcessor; |
|||
} else if (Request.BYE.equals(method)) { |
|||
return byeResponseProcessor; |
|||
} else if (Request.CANCEL.equals(method)) { |
|||
return cancelResponseProcessor; |
|||
}else if (Request.REGISTER.equals(method)) { |
|||
return registerResponseProcessor; |
|||
} else { |
|||
return otherResponseProcessor; |
|||
} |
|||
} |
|||
|
|||
private SipProvider getTcpSipProvider() { |
|||
if (tcpSipProvider == null) { |
|||
tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider"); |
|||
} |
|||
return tcpSipProvider; |
|||
} |
|||
|
|||
private SipProvider getUdpSipProvider() { |
|||
if (udpSipProvider == null) { |
|||
udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); |
|||
} |
|||
return udpSipProvider; |
|||
} |
|||
|
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.transmit; |
|||
|
|||
import javax.sip.RequestEvent; |
|||
import javax.sip.ResponseEvent; |
|||
import javax.sip.SipProvider; |
|||
import javax.sip.header.CSeqHeader; |
|||
import javax.sip.message.Request; |
|||
import javax.sip.message.Response; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
|||
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
|||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*; |
|||
import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
|||
// import org.slf4j.Logger;
|
|||
// import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler; |
|||
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; |
|||
import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
|||
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.transmit.request.impl.AckRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
import com.genersoft.iot.vmp.utils.SpringBeanFactory; |
|||
import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
|||
|
|||
/** |
|||
* @Description: SIP信令处理分配 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月3日 下午4:24:37 |
|||
*/ |
|||
@Component |
|||
public class SIPProcessorFactory { |
|||
|
|||
// private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
|
|||
|
|||
@Autowired |
|||
private SipConfig sipConfig; |
|||
|
|||
@Autowired |
|||
private RegisterLogicHandler handler; |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private IRedisCatchStorage redisCatchStorage; |
|||
|
|||
@Autowired |
|||
private EventPublisher publisher; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private SIPCommanderFroPlatform cmderFroPlatform; |
|||
|
|||
@Autowired |
|||
private RedisUtil redis; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder deferredResultHolder; |
|||
|
|||
@Autowired |
|||
private DeviceOffLineDetector offLineDetector; |
|||
|
|||
@Autowired |
|||
private InviteResponseProcessor inviteResponseProcessor; |
|||
|
|||
@Autowired |
|||
private ByeResponseProcessor byeResponseProcessor; |
|||
|
|||
@Autowired |
|||
private CancelResponseProcessor cancelResponseProcessor; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private RegisterResponseProcessor registerResponseProcessor; |
|||
|
|||
|
|||
@Autowired |
|||
private OtherResponseProcessor otherResponseProcessor; |
|||
|
|||
@Autowired |
|||
private IPlayService playService; |
|||
|
|||
@Autowired |
|||
private ZLMRTPServerFactory zlmrtpServerFactory; |
|||
|
|||
|
|||
// 注:这里使用注解会导致循环依赖注入,暂用springBean
|
|||
private SipProvider tcpSipProvider; |
|||
|
|||
// 注:这里使用注解会导致循环依赖注入,暂用springBean
|
|||
private SipProvider udpSipProvider; |
|||
|
|||
public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) { |
|||
Request request = evt.getRequest(); |
|||
String method = request.getMethod(); |
|||
// logger.info("接收到消息:"+request.getMethod());
|
|||
// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
|
|||
if (Request.INVITE.equals(method)) { |
|||
InviteRequestProcessor processor = new InviteRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
|
|||
processor.setCmder(cmder); |
|||
processor.setCmderFroPlatform(cmderFroPlatform); |
|||
processor.setPlayService(playService); |
|||
processor.setStorager(storager); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
processor.setZlmrtpServerFactory(zlmrtpServerFactory); |
|||
return processor; |
|||
} else if (Request.REGISTER.equals(method)) { |
|||
RegisterRequestProcessor processor = new RegisterRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
processor.setHandler(handler); |
|||
processor.setPublisher(publisher); |
|||
processor.setSipConfig(sipConfig); |
|||
processor.setVideoManagerStorager(storager); |
|||
return processor; |
|||
} else if (Request.SUBSCRIBE.equals(method)) { |
|||
SubscribeRequestProcessor processor = new SubscribeRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
return processor; |
|||
} else if (Request.ACK.equals(method)) { |
|||
AckRequestProcessor processor = new AckRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
processor.setZlmrtpServerFactory(zlmrtpServerFactory); |
|||
return processor; |
|||
} else if (Request.BYE.equals(method)) { |
|||
ByeRequestProcessor processor = new ByeRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
processor.setZlmrtpServerFactory(zlmrtpServerFactory); |
|||
processor.setSIPCommander(cmder); |
|||
return processor; |
|||
} else if (Request.CANCEL.equals(method)) { |
|||
CancelRequestProcessor processor = new CancelRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
return processor; |
|||
} else if (Request.MESSAGE.equals(method)) { |
|||
MessageRequestProcessor processor = new MessageRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
processor.setPublisher(publisher); |
|||
processor.setRedis(redis); |
|||
processor.setDeferredResultHolder(deferredResultHolder); |
|||
processor.setOffLineDetector(offLineDetector); |
|||
processor.setCmder(cmder); |
|||
processor.setCmderFroPlatform(cmderFroPlatform); |
|||
processor.setStorager(storager); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
return processor; |
|||
} else if (Request.NOTIFY.equalsIgnoreCase(method)) { |
|||
NotifyRequestProcessor processor = new NotifyRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
processor.setTcpSipProvider(getTcpSipProvider()); |
|||
processor.setUdpSipProvider(getUdpSipProvider()); |
|||
processor.setPublisher(publisher); |
|||
processor.setRedis(redis); |
|||
processor.setDeferredResultHolder(deferredResultHolder); |
|||
processor.setOffLineDetector(offLineDetector); |
|||
processor.setCmder(cmder); |
|||
processor.setStorager(storager); |
|||
processor.setRedisCatchStorage(redisCatchStorage); |
|||
return processor; |
|||
} else { |
|||
OtherRequestProcessor processor = new OtherRequestProcessor(); |
|||
processor.setRequestEvent(evt); |
|||
return processor; |
|||
} |
|||
} |
|||
|
|||
public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) { |
|||
|
|||
Response response = evt.getResponse(); |
|||
CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); |
|||
String method = cseqHeader.getMethod(); |
|||
if(Request.INVITE.equals(method)){ |
|||
return inviteResponseProcessor; |
|||
} else if (Request.BYE.equals(method)) { |
|||
return byeResponseProcessor; |
|||
} else if (Request.CANCEL.equals(method)) { |
|||
return cancelResponseProcessor; |
|||
}else if (Request.REGISTER.equals(method)) { |
|||
return registerResponseProcessor; |
|||
} else { |
|||
return otherResponseProcessor; |
|||
} |
|||
} |
|||
|
|||
private SipProvider getTcpSipProvider() { |
|||
if (tcpSipProvider == null) { |
|||
tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider"); |
|||
} |
|||
return tcpSipProvider; |
|||
} |
|||
|
|||
private SipProvider getUdpSipProvider() { |
|||
if (udpSipProvider == null) { |
|||
udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); |
|||
} |
|||
return udpSipProvider; |
|||
} |
|||
|
|||
} |
|||
|
@ -1,63 +1,60 @@ |
|||
package com.genersoft.iot.vmp.gb28181.transmit.callback; |
|||
|
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
/** |
|||
* @Description: 异步请求处理 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月8日 下午7:59:05 |
|||
*/ |
|||
@SuppressWarnings(value = {"rawtypes", "unchecked"}) |
|||
@Component |
|||
public class DeferredResultHolder { |
|||
|
|||
public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS"; |
|||
|
|||
public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO"; |
|||
|
|||
public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL"; |
|||
|
|||
public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG"; |
|||
|
|||
public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD"; |
|||
|
|||
public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG"; |
|||
|
|||
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; |
|||
|
|||
public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; |
|||
|
|||
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; |
|||
|
|||
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; |
|||
|
|||
public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; |
|||
|
|||
public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; |
|||
|
|||
public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; |
|||
|
|||
private Map<String, DeferredResult> map = new ConcurrentHashMap<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)); |
|||
} |
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.transmit.callback; |
|||
|
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
/** |
|||
* @Description: 异步请求处理 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月8日 下午7:59:05 |
|||
*/ |
|||
@Component |
|||
public class DeferredResultHolder { |
|||
|
|||
public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS"; |
|||
|
|||
public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO"; |
|||
|
|||
public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL"; |
|||
|
|||
public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG"; |
|||
|
|||
public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD"; |
|||
|
|||
public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG"; |
|||
|
|||
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; |
|||
|
|||
public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; |
|||
|
|||
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; |
|||
|
|||
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; |
|||
|
|||
public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; |
|||
|
|||
public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; |
|||
|
|||
public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; |
|||
|
|||
private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>(); |
|||
|
|||
public void put(String key, DeferredResult result) { |
|||
map.put(key, result); |
|||
} |
|||
|
|||
public void invokeResult(RequestMessage msg) { |
|||
// DeferredResult result = map.get(msg.getId());
|
|||
// 获取并移除
|
|||
DeferredResult result = map.remove(msg.getId()); |
|||
if (result == null) { |
|||
return; |
|||
} |
|||
result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK)); |
|||
} |
|||
} |
|||
|
@ -1,300 +1,301 @@ |
|||
package com.genersoft.iot.vmp.gb28181.transmit.cmd; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
|||
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
|||
|
|||
/** |
|||
* @Description:设备能力接口,用于定义设备的控制、查询能力 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月3日 下午9:16:34 |
|||
*/ |
|||
public interface ISIPCommander { |
|||
|
|||
/** |
|||
* 云台方向放控制,使用配置文件中的默认镜头移动速度 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 |
|||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
|||
* @param moveSpeed 镜头移动速度 |
|||
*/ |
|||
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); |
|||
|
|||
/** |
|||
* 云台方向放控制 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 |
|||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
|||
* @param moveSpeed 镜头移动速度 |
|||
*/ |
|||
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); |
|||
|
|||
/** |
|||
* 云台缩放控制,使用配置文件中的默认镜头缩放速度 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
|||
*/ |
|||
boolean ptzZoomCmd(Device device,String channelId,int inOut); |
|||
|
|||
/** |
|||
* 云台缩放控制 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
|||
* @param zoomSpeed 镜头缩放速度 |
|||
*/ |
|||
boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); |
|||
|
|||
/** |
|||
* 云台控制,支持方向与缩放控制 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 |
|||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
|||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
|||
* @param moveSpeed 镜头移动速度 |
|||
* @param zoomSpeed 镜头缩放速度 |
|||
*/ |
|||
boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); |
|||
|
|||
/** |
|||
* 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param cmdCode 指令码 |
|||
* @param parameter1 数据1 |
|||
* @param parameter2 数据2 |
|||
* @param combineCode2 组合码2 |
|||
*/ |
|||
boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); |
|||
|
|||
/** |
|||
* 前端控制指令(用于转发上级指令) |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param cmdString 前端控制指令串 |
|||
*/ |
|||
boolean fronEndCmd(Device device, String channelId, String cmdString); |
|||
|
|||
/** |
|||
* 请求预览视频流 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
*/ |
|||
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 请求回放视频流 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
*/ |
|||
void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 视频流停止 |
|||
* |
|||
* @param ssrc ssrc |
|||
*/ |
|||
void streamByeCmd(String ssrc, SipSubscribe.Event okEvent); |
|||
void streamByeCmd(String ssrc); |
|||
|
|||
/** |
|||
* 语音广播 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
*/ |
|||
boolean audioBroadcastCmd(Device device,String channelId); |
|||
|
|||
/** |
|||
* 语音广播 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent); |
|||
boolean audioBroadcastCmd(Device device); |
|||
|
|||
/** |
|||
* 音视频录像控制 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
* @param recordCmdStr 录像命令:Record / StopRecord |
|||
*/ |
|||
boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 远程启动控制命令 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean teleBootCmd(Device device); |
|||
|
|||
/** |
|||
* 报警布防/撤防命令 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param setGuard true: SetGuard, false: ResetGuard |
|||
*/ |
|||
boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 报警复位命令 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param alarmMethod 报警方式(可选) |
|||
* @param alarmType 报警类型(可选) |
|||
*/ |
|||
boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
*/ |
|||
boolean iFrameCmd(Device device, String channelId); |
|||
|
|||
/** |
|||
* 看守位控制命令 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param enabled 看守位使能:1 = 开启,0 = 关闭 |
|||
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) |
|||
* @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 |
|||
*/ |
|||
boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 设备配置命令 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean deviceConfigCmd(Device device); |
|||
|
|||
/** |
|||
* 设备配置命令:basicParam |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 通道编码(可选) |
|||
* @param name 设备/通道名称(可选) |
|||
* @param expiration 注册过期时间(可选) |
|||
* @param heartBeatInterval 心跳间隔时间(可选) |
|||
* @param heartBeatCount 心跳超时次数(可选) |
|||
*/ |
|||
boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备状态 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备信息 |
|||
* |
|||
* @param device 视频设备 |
|||
* @return |
|||
*/ |
|||
boolean deviceInfoQuery(Device device); |
|||
|
|||
/** |
|||
* 查询目录列表 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean catalogQuery(Device device, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询录像信息 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
*/ |
|||
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); |
|||
|
|||
/** |
|||
* 查询报警信息 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param startPriority 报警起始级别(可选) |
|||
* @param endPriority 报警终止级别(可选) |
|||
* @param alarmMethod 报警方式条件(可选) |
|||
* @param alarmType 报警类型 |
|||
* @param startTime 报警发生起始时间(可选) |
|||
* @param endTime 报警发生终止时间(可选) |
|||
* @return true = 命令发送成功 |
|||
*/ |
|||
boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, |
|||
String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备配置 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 通道编码(可选) |
|||
* @param configType 配置类型: |
|||
*/ |
|||
boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备预置位置 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询移动设备位置数据 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 订阅、取消订阅移动位置 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param expires 订阅超时时间(值=0时为取消订阅) |
|||
* @param interval 上报时间间隔 |
|||
* @return true = 命令发送成功 |
|||
*/ |
|||
boolean mobilePositionSubscribe(Device device, int expires, int interval); |
|||
|
|||
/** |
|||
* 订阅、取消订阅报警信息 |
|||
* @param device 视频设备 |
|||
* @param expires 订阅过期时间(0 = 取消订阅) |
|||
* @param startPriority 报警起始级别(可选) |
|||
* @param endPriority 报警终止级别(可选) |
|||
* @param alarmMethods 报警方式条件(可选) |
|||
* @param alarmType 报警类型 |
|||
* @param startTime 报警发生起始时间(可选) |
|||
* @param endTime 报警发生终止时间(可选) |
|||
* @return true = 命令发送成功 |
|||
*/ |
|||
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); |
|||
|
|||
|
|||
/** |
|||
* 释放rtpserver |
|||
* @param device |
|||
* @param channelId |
|||
*/ |
|||
void closeRTPServer(Device device, String channelId); |
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.transmit.cmd; |
|||
|
|||
import com.genersoft.iot.vmp.common.StreamInfo; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
|||
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
|||
|
|||
/** |
|||
* @Description:设备能力接口,用于定义设备的控制、查询能力 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月3日 下午9:16:34 |
|||
*/ |
|||
public interface ISIPCommander { |
|||
|
|||
/** |
|||
* 云台方向放控制,使用配置文件中的默认镜头移动速度 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 |
|||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
|||
* @param moveSpeed 镜头移动速度 |
|||
*/ |
|||
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); |
|||
|
|||
/** |
|||
* 云台方向放控制 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 |
|||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
|||
* @param moveSpeed 镜头移动速度 |
|||
*/ |
|||
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); |
|||
|
|||
/** |
|||
* 云台缩放控制,使用配置文件中的默认镜头缩放速度 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
|||
*/ |
|||
boolean ptzZoomCmd(Device device,String channelId,int inOut); |
|||
|
|||
/** |
|||
* 云台缩放控制 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
|||
* @param zoomSpeed 镜头缩放速度 |
|||
*/ |
|||
boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); |
|||
|
|||
/** |
|||
* 云台控制,支持方向与缩放控制 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 |
|||
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 |
|||
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 |
|||
* @param moveSpeed 镜头移动速度 |
|||
* @param zoomSpeed 镜头缩放速度 |
|||
*/ |
|||
boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); |
|||
|
|||
/** |
|||
* 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 |
|||
* |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param cmdCode 指令码 |
|||
* @param parameter1 数据1 |
|||
* @param parameter2 数据2 |
|||
* @param combineCode2 组合码2 |
|||
*/ |
|||
boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); |
|||
|
|||
/** |
|||
* 前端控制指令(用于转发上级指令) |
|||
* @param device 控制设备 |
|||
* @param channelId 预览通道 |
|||
* @param cmdString 前端控制指令串 |
|||
*/ |
|||
boolean fronEndCmd(Device device, String channelId, String cmdString); |
|||
|
|||
/** |
|||
* 请求预览视频流 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
*/ |
|||
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 请求回放视频流 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
*/ |
|||
void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 视频流停止 |
|||
* |
|||
* @param streamInfo streamInfo |
|||
* @param okEvent okEvent |
|||
*/ |
|||
void stopStreamByeCmd(StreamInfo streamInfo, SipSubscribe.Event okEvent); |
|||
|
|||
/** |
|||
* 语音广播 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
*/ |
|||
boolean audioBroadcastCmd(Device device,String channelId); |
|||
|
|||
/** |
|||
* 语音广播 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent); |
|||
boolean audioBroadcastCmd(Device device); |
|||
|
|||
/** |
|||
* 音视频录像控制 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
* @param recordCmdStr 录像命令:Record / StopRecord |
|||
*/ |
|||
boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 远程启动控制命令 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean teleBootCmd(Device device); |
|||
|
|||
/** |
|||
* 报警布防/撤防命令 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param setGuard true: SetGuard, false: ResetGuard |
|||
*/ |
|||
boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 报警复位命令 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param alarmMethod 报警方式(可选) |
|||
* @param alarmType 报警类型(可选) |
|||
*/ |
|||
boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 预览通道 |
|||
*/ |
|||
boolean iFrameCmd(Device device, String channelId); |
|||
|
|||
/** |
|||
* 看守位控制命令 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param enabled 看守位使能:1 = 开启,0 = 关闭 |
|||
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) |
|||
* @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 |
|||
*/ |
|||
boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 设备配置命令 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean deviceConfigCmd(Device device); |
|||
|
|||
/** |
|||
* 设备配置命令:basicParam |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 通道编码(可选) |
|||
* @param name 设备/通道名称(可选) |
|||
* @param expiration 注册过期时间(可选) |
|||
* @param heartBeatInterval 心跳间隔时间(可选) |
|||
* @param heartBeatCount 心跳超时次数(可选) |
|||
*/ |
|||
boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备状态 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备信息 |
|||
* |
|||
* @param device 视频设备 |
|||
* @return |
|||
*/ |
|||
boolean deviceInfoQuery(Device device); |
|||
|
|||
/** |
|||
* 查询目录列表 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean catalogQuery(Device device, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询录像信息 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
|||
*/ |
|||
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); |
|||
|
|||
/** |
|||
* 查询报警信息 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param startPriority 报警起始级别(可选) |
|||
* @param endPriority 报警终止级别(可选) |
|||
* @param alarmMethod 报警方式条件(可选) |
|||
* @param alarmType 报警类型 |
|||
* @param startTime 报警发生起始时间(可选) |
|||
* @param endTime 报警发生终止时间(可选) |
|||
* @return true = 命令发送成功 |
|||
*/ |
|||
boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, |
|||
String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备配置 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param channelId 通道编码(可选) |
|||
* @param configType 配置类型: |
|||
*/ |
|||
boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询设备预置位置 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 查询移动设备位置数据 |
|||
* |
|||
* @param device 视频设备 |
|||
*/ |
|||
boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent); |
|||
|
|||
/** |
|||
* 订阅、取消订阅移动位置 |
|||
* |
|||
* @param device 视频设备 |
|||
* @param expires 订阅超时时间(值=0时为取消订阅) |
|||
* @param interval 上报时间间隔 |
|||
* @return true = 命令发送成功 |
|||
*/ |
|||
boolean mobilePositionSubscribe(Device device, int expires, int interval); |
|||
|
|||
/** |
|||
* 订阅、取消订阅报警信息 |
|||
* @param device 视频设备 |
|||
* @param expires 订阅过期时间(0 = 取消订阅) |
|||
* @param startPriority 报警起始级别(可选) |
|||
* @param endPriority 报警终止级别(可选) |
|||
* @param alarmMethods 报警方式条件(可选) |
|||
* @param alarmType 报警类型 |
|||
* @param startTime 报警发生起始时间(可选) |
|||
* @param endTime 报警发生终止时间(可选) |
|||
* @return true = 命令发送成功 |
|||
*/ |
|||
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); |
|||
|
|||
|
|||
/** |
|||
* 释放rtpserver |
|||
* @param device |
|||
* @param channelId |
|||
*/ |
|||
void closeRTPServer(Device device, String channelId); |
|||
} |
|||
|
@ -1,183 +1,183 @@ |
|||
package com.genersoft.iot.vmp.gb28181.transmit.cmd; |
|||
|
|||
import java.text.ParseException; |
|||
import java.util.ArrayList; |
|||
|
|||
import javax.sip.InvalidArgumentException; |
|||
import javax.sip.PeerUnavailableException; |
|||
import javax.sip.SipFactory; |
|||
// import javax.sip.SipProvider;
|
|||
import javax.sip.address.Address; |
|||
import javax.sip.address.SipURI; |
|||
import javax.sip.header.*; |
|||
import javax.sip.message.Request; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
// import org.springframework.beans.factory.annotation.Qualifier;
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
|
|||
/** |
|||
* @Description:摄像头命令request创造器 TODO 冗余代码太多待优化 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月6日 上午9:29:02 |
|||
*/ |
|||
@Component |
|||
public class SIPRequestHeaderProvider { |
|||
|
|||
@Autowired |
|||
private SipConfig sipConfig; |
|||
|
|||
@Autowired |
|||
private SipFactory sipFactory; |
|||
|
|||
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
// sipuri
|
|||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); |
|||
// via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), |
|||
device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
// from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), |
|||
sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); |
|||
// to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); |
|||
|
|||
// Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
// ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); |
|||
|
|||
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, |
|||
toHeader, viaHeaders, maxForwards); |
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
|
|||
public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
//请求行
|
|||
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); |
|||
//via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
|
|||
//from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
|
|||
//to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); |
|||
|
|||
//Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
|
|||
//ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); |
|||
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); |
|||
|
|||
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); |
|||
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
|
|||
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); |
|||
// Subject
|
|||
SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0)); |
|||
request.addHeader(subjectHeader); |
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
|
|||
public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
//请求行
|
|||
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); |
|||
// via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
//from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
|
|||
//to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); |
|||
|
|||
//Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
|
|||
//ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); |
|||
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); |
|||
|
|||
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); |
|||
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
|
|||
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); |
|||
|
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
|
|||
public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
// sipuri
|
|||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); |
|||
// via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), |
|||
device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
// from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), |
|||
sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); |
|||
// to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); |
|||
|
|||
// Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
// ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); |
|||
|
|||
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, |
|||
toHeader, viaHeaders, maxForwards); |
|||
|
|||
|
|||
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); |
|||
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); |
|||
|
|||
// Expires
|
|||
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires); |
|||
request.addHeader(expireHeader); |
|||
|
|||
// Event
|
|||
EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event); |
|||
request.addHeader(eventHeader); |
|||
|
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.transmit.cmd; |
|||
|
|||
import java.text.ParseException; |
|||
import java.util.ArrayList; |
|||
|
|||
import javax.sip.InvalidArgumentException; |
|||
import javax.sip.PeerUnavailableException; |
|||
import javax.sip.SipFactory; |
|||
// import javax.sip.SipProvider;
|
|||
import javax.sip.address.Address; |
|||
import javax.sip.address.SipURI; |
|||
import javax.sip.header.*; |
|||
import javax.sip.message.Request; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
// import org.springframework.beans.factory.annotation.Qualifier;
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
|
|||
/** |
|||
* @Description:摄像头命令request创造器 TODO 冗余代码太多待优化 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月6日 上午9:29:02 |
|||
*/ |
|||
@Component |
|||
public class SIPRequestHeaderProvider { |
|||
|
|||
@Autowired |
|||
private SipConfig sipConfig; |
|||
|
|||
@Autowired |
|||
private SipFactory sipFactory; |
|||
|
|||
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
// sipuri
|
|||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); |
|||
// via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), |
|||
device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
// from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), |
|||
sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); |
|||
// to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); |
|||
|
|||
// Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
// ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); |
|||
|
|||
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, |
|||
toHeader, viaHeaders, maxForwards); |
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
|
|||
public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
//请求行
|
|||
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); |
|||
//via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
|
|||
//from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
|
|||
//to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); |
|||
|
|||
//Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
|
|||
//ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); |
|||
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); |
|||
|
|||
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); |
|||
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
|
|||
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); |
|||
// Subject
|
|||
SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0)); |
|||
request.addHeader(subjectHeader); |
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
|
|||
public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
//请求行
|
|||
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); |
|||
// via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
//from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
|
|||
//to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); |
|||
|
|||
//Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
|
|||
//ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); |
|||
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); |
|||
|
|||
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); |
|||
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
|
|||
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); |
|||
|
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
|
|||
public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
|||
Request request = null; |
|||
// sipuri
|
|||
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); |
|||
// via
|
|||
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
|||
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), |
|||
device.getTransport(), viaTag); |
|||
viaHeader.setRPort(); |
|||
viaHeaders.add(viaHeader); |
|||
// from
|
|||
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), |
|||
sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); |
|||
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
|||
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); |
|||
// to
|
|||
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); |
|||
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); |
|||
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); |
|||
|
|||
// Forwards
|
|||
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
|||
// ceq
|
|||
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); |
|||
|
|||
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, |
|||
toHeader, viaHeaders, maxForwards); |
|||
|
|||
|
|||
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); |
|||
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); |
|||
|
|||
// Expires
|
|||
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires); |
|||
request.addHeader(expireHeader); |
|||
|
|||
// Event
|
|||
EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event); |
|||
request.addHeader(eventHeader); |
|||
|
|||
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); |
|||
request.setContent(content, contentTypeHeader); |
|||
return request; |
|||
} |
|||
} |
|||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,68 +1,62 @@ |
|||
package com.genersoft.iot.vmp.gb28181.transmit.response.impl; |
|||
|
|||
import java.text.ParseException; |
|||
|
|||
import javax.sip.Dialog; |
|||
import javax.sip.InvalidArgumentException; |
|||
import javax.sip.ResponseEvent; |
|||
import javax.sip.SipException; |
|||
import javax.sip.address.SipURI; |
|||
import javax.sip.header.CSeqHeader; |
|||
import javax.sip.header.ViaHeader; |
|||
import javax.sip.message.Request; |
|||
import javax.sip.message.Response; |
|||
|
|||
// import org.slf4j.Logger;
|
|||
// import org.slf4j.LoggerFactory;
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.SipLayer; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; |
|||
|
|||
|
|||
/** |
|||
* @Description:处理INVITE响应 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月3日 下午4:43:52 |
|||
*/ |
|||
@Component |
|||
public class InviteResponseProcessor implements ISIPResponseProcessor { |
|||
|
|||
// private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
|
|||
|
|||
/** |
|||
* 处理invite响应 |
|||
* |
|||
* @param evt 响应消息 |
|||
* @throws ParseException |
|||
*/ |
|||
@Override |
|||
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException { |
|||
try { |
|||
Response response = evt.getResponse(); |
|||
int statusCode = response.getStatusCode(); |
|||
// trying不会回复
|
|||
if (statusCode == Response.TRYING) { |
|||
} |
|||
// 成功响应
|
|||
// 下发ack
|
|||
if (statusCode == Response.OK) { |
|||
Dialog dialog = evt.getDialog(); |
|||
CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); |
|||
Request reqAck = dialog.createAck(cseq.getSeqNumber()); |
|||
|
|||
SipURI requestURI = (SipURI) reqAck.getRequestURI(); |
|||
ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); |
|||
requestURI.setHost(viaHeader.getHost()); |
|||
requestURI.setPort(viaHeader.getPort()); |
|||
reqAck.setRequestURI(requestURI); |
|||
|
|||
dialog.sendAck(reqAck); |
|||
} |
|||
} catch (InvalidArgumentException | SipException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
} |
|||
package com.genersoft.iot.vmp.gb28181.transmit.response.impl; |
|||
|
|||
import com.genersoft.iot.vmp.conf.SipConfig; |
|||
import com.genersoft.iot.vmp.gb28181.SipLayer; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.sip.Dialog; |
|||
import javax.sip.InvalidArgumentException; |
|||
import javax.sip.ResponseEvent; |
|||
import javax.sip.SipException; |
|||
import javax.sip.address.SipURI; |
|||
import javax.sip.header.CSeqHeader; |
|||
import javax.sip.header.ViaHeader; |
|||
import javax.sip.message.Request; |
|||
import javax.sip.message.Response; |
|||
import java.text.ParseException; |
|||
|
|||
|
|||
/** |
|||
* @Description:处理INVITE响应 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月3日 下午4:43:52 |
|||
*/ |
|||
@Component |
|||
public class InviteResponseProcessor implements ISIPResponseProcessor { |
|||
|
|||
/** |
|||
* 处理invite响应 |
|||
* |
|||
* @param evt 响应消息 |
|||
* @throws ParseException |
|||
*/ |
|||
@Override |
|||
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException { |
|||
try { |
|||
Response response = evt.getResponse(); |
|||
int statusCode = response.getStatusCode(); |
|||
// trying不会回复
|
|||
if (statusCode == Response.TRYING) { |
|||
} |
|||
// 成功响应
|
|||
// 下发ack
|
|||
if (statusCode == Response.OK) { |
|||
Dialog dialog = evt.getDialog(); |
|||
CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); |
|||
Request reqAck = dialog.createAck(cseq.getSeqNumber()); |
|||
|
|||
SipURI requestURI = (SipURI) reqAck.getRequestURI(); |
|||
ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); |
|||
requestURI.setHost(viaHeader.getHost()); |
|||
requestURI.setPort(viaHeader.getPort()); |
|||
reqAck.setRequestURI(requestURI); |
|||
|
|||
dialog.sendAck(reqAck); |
|||
} |
|||
} catch (InvalidArgumentException | SipException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
@ -1,362 +1,360 @@ |
|||
package com.genersoft.iot.vmp.media.zlm; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
import com.alibaba.fastjson.JSON; |
|||
import com.genersoft.iot.vmp.common.StreamInfo; |
|||
import com.genersoft.iot.vmp.conf.MediaServerConfig; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
|
|||
/** |
|||
* @Description:针对 ZLMediaServer的hook事件监听 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月8日 上午10:46:48 |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("/index/hook") |
|||
public class ZLMHttpHookListener { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); |
|||
|
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private IPlayService playService; |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private IRedisCatchStorage redisCatchStorage; |
|||
|
|||
@Autowired |
|||
private ZLMMediaListManager zlmMediaListManager; |
|||
|
|||
@Autowired |
|||
private ZLMHttpHookSubscribe subscribe; |
|||
|
|||
@Value("${media.autoApplyPlay}") |
|||
private boolean autoApplyPlay; |
|||
|
|||
@Value("${media.ip}") |
|||
private String mediaIp; |
|||
|
|||
@Value("${media.wanIp}") |
|||
private String mediaWanIp; |
|||
|
|||
@Value("${media.port}") |
|||
private int mediaPort; |
|||
|
|||
/** |
|||
* 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 访问http文件服务器上hls之外的文件时触发。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onHttpAccess(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("err", ""); |
|||
ret.put("path", ""); |
|||
ret.put("second", 600); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onPlay(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* rtsp/rtmp/rtp推流鉴权事件。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onPublish(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); |
|||
if (subscribe != null) subscribe.response(json); |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
ret.put("enableHls", true); |
|||
ret.put("enableMP4", false); |
|||
ret.put("enableRtxp", true); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 录制mp4完成后通知事件;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onRtspRealm(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("realm", ""); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onRtspAuth(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("encrypted", false); |
|||
ret.put("passwd", "test"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString()); |
|||
} |
|||
// TODO 如果是带有rtpstream则开启按需拉流
|
|||
// String app = json.getString("app");
|
|||
// String stream = json.getString("stream");
|
|||
|
|||
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); |
|||
if (subscribe != null) subscribe.response(json); |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); |
|||
} |
|||
// 流消失移除redis play
|
|||
String app = json.getString("app"); |
|||
String streamId = json.getString("stream"); |
|||
String schema = json.getString("schema"); |
|||
boolean regist = json.getBoolean("regist"); |
|||
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); |
|||
if ("rtp".equals(app) && !regist ) { |
|||
if (streamInfo!=null){ |
|||
redisCatchStorage.stopPlay(streamInfo); |
|||
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
|||
}else{ |
|||
streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); |
|||
redisCatchStorage.stopPlayback(streamInfo); |
|||
} |
|||
}else { |
|||
if (!"rtp".equals(app) && "rtsp".equals(schema)){ |
|||
zlmMediaListManager.updateMediaList(); |
|||
} |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onStreamNoneReader(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
String streamId = json.getString("stream"); |
|||
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("close", true); |
|||
|
|||
if (streamInfo != null) { |
|||
if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) { |
|||
ret.put("close", false); |
|||
} else { |
|||
cmder.streamByeCmd(streamId); |
|||
redisCatchStorage.stopPlay(streamInfo); |
|||
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
|||
} |
|||
}else{ |
|||
cmder.streamByeCmd(streamId); |
|||
streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); |
|||
redisCatchStorage.stopPlayback(streamInfo); |
|||
} |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); |
|||
} |
|||
if (autoApplyPlay) { |
|||
String app = json.getString("app"); |
|||
String streamId = json.getString("stream"); |
|||
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); |
|||
if ("rtp".equals(app) && streamId.indexOf("gb_play") > -1 && streamInfo == null) { |
|||
String[] s = streamId.split("_"); |
|||
if (s.length == 4) { |
|||
String deviceId = s[2]; |
|||
String channelId = s[3]; |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
if (device != null) { |
|||
UUID uuid = UUID.randomUUID(); |
|||
cmder.playStreamCmd(device, channelId, (JSONObject response) -> { |
|||
logger.info("收到订阅消息: " + response.toJSONString()); |
|||
playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); |
|||
}, null); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
// String data = json.getString("data");
|
|||
// List<MediaServerConfig> mediaServerConfigs = JSON.parseArray(JSON.toJSONString(json), MediaServerConfig.class);
|
|||
// MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0);
|
|||
MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); |
|||
mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp); |
|||
mediaServerConfig.setLocalIP(mediaIp); |
|||
redisCatchStorage.updateMediaInfo(mediaServerConfig); |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
} |
|||
package com.genersoft.iot.vmp.media.zlm; |
|||
|
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.common.StreamInfo; |
|||
import com.genersoft.iot.vmp.conf.MediaConfig; |
|||
import com.genersoft.iot.vmp.conf.MediaServerConfig; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
|||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
|
|||
/** |
|||
* @Description:针对 ZLMediaServer的hook事件监听 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月8日 上午10:46:48 |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("/index/hook") |
|||
public class ZLMHttpHookListener { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); |
|||
|
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private IPlayService playService; |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private IRedisCatchStorage redisCatchStorage; |
|||
|
|||
@Autowired |
|||
private ZLMMediaListManager zlmMediaListManager; |
|||
|
|||
@Autowired |
|||
private ZLMHttpHookSubscribe subscribe; |
|||
@Autowired |
|||
private ZLMHttpHookSubscribe subscribe; |
|||
|
|||
@Autowired |
|||
MediaConfig mediaConfig; |
|||
|
|||
@Autowired |
|||
private VideoStreamSessionManager streamSession; |
|||
|
|||
/** |
|||
* 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 访问http文件服务器上hls之外的文件时触发。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onHttpAccess(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("err", ""); |
|||
ret.put("path", ""); |
|||
ret.put("second", 600); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onPlay(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* rtsp/rtmp/rtp推流鉴权事件。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onPublish(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); |
|||
if (subscribe != null) { |
|||
subscribe.response(json); |
|||
} |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
ret.put("enableHls", true); |
|||
ret.put("enableMP4", false); |
|||
ret.put("enableRtxp", true); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 录制mp4完成后通知事件;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onRtspRealm(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("realm", ""); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onRtspAuth(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString()); |
|||
} |
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("encrypted", false); |
|||
ret.put("passwd", "test"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString()); |
|||
} |
|||
// TODO 如果是带有rtpstream则开启按需拉流
|
|||
// String app = json.getString("app");
|
|||
// String stream = json.getString("stream");
|
|||
|
|||
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); |
|||
if (subscribe != null) subscribe.response(json); |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
|
|||
// 流消失移除redis play
|
|||
String app = json.getString("app"); |
|||
String streamId = json.getString("stream"); |
|||
boolean regist = json.getBoolean("regist"); |
|||
if (!"rtp".equals(app) || regist) { |
|||
if (!"rtp".equals(app) && "rtsp".equals(schema)){ |
|||
zlmMediaListManager.updateMediaList(); |
|||
} |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
|
|||
String[] s = streamId.split("_"); |
|||
if (s.length != 4) { |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
String channelId = s[3]; |
|||
// TODO channelId
|
|||
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); |
|||
if (null != streamInfo) { |
|||
cmder.stopStreamByeCmd(streamInfo, null); |
|||
} |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onStreamNoneReader(@RequestBody JSONObject json) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("close", true); |
|||
|
|||
String app = json.getString("app"); |
|||
String streamId = json.getString("stream"); |
|||
if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) { |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
String[] s = streamId.split("_"); |
|||
if (s.length != 4) { |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
String channelId = s[3]; |
|||
// TODO channelId
|
|||
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); |
|||
if (null != streamInfo) { |
|||
if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) { |
|||
ret.put("close", false); |
|||
} else { |
|||
cmder.stopStreamByeCmd(streamInfo, null); |
|||
} |
|||
} |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
if (!mediaConfig.getAutoApplyPlay()) { |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
String app = json.getString("app"); |
|||
String streamId = json.getString("stream"); |
|||
|
|||
if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) { |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
String[] s = streamId.split("_"); |
|||
if (s.length != 4) { |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
String deviceId = s[2]; |
|||
String channelId = s[3]; |
|||
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); |
|||
|
|||
if (streamInfo != null) { |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
if (device != null) { |
|||
RequestMessage msg = playService.createCallbackPlayMsg(); |
|||
cmder.playStreamCmd(device, channelId, (JSONObject response) -> { |
|||
logger.info("收到订阅消息: " + response.toJSONString()); |
|||
playService.onPublishHandlerForPlay(response, deviceId, channelId, msg); |
|||
}, null); |
|||
} |
|||
return new ResponseEntity<>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 |
|||
* |
|||
*/ |
|||
@ResponseBody |
|||
@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") |
|||
public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); |
|||
} |
|||
|
|||
MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); |
|||
redisCatchStorage.updateMediaInfo(mediaServerConfig); |
|||
// TODO Auto-generated method stub
|
|||
|
|||
JSONObject ret = new JSONObject(); |
|||
ret.put("code", 0); |
|||
ret.put("msg", "success"); |
|||
return new ResponseEntity<String>(ret.toString(), HttpStatus.OK); |
|||
} |
|||
} |
|||
|
@ -1,264 +1,278 @@ |
|||
package com.genersoft.iot.vmp.storager; |
|||
|
|||
import java.util.List; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
|||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
|||
import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce; |
|||
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; |
|||
import com.github.pagehelper.PageInfo; |
|||
|
|||
/** |
|||
* @Description:视频设备数据存储接口 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月6日 下午2:14:31 |
|||
*/ |
|||
@SuppressWarnings("rawtypes") |
|||
public interface IVideoManagerStorager { |
|||
|
|||
/** |
|||
* 根据设备ID判断设备是否存在 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:存在 false:不存在 |
|||
*/ |
|||
public boolean exists(String deviceId); |
|||
|
|||
/** |
|||
* 视频设备创建 |
|||
* |
|||
* @param device 设备对象 |
|||
* @return true:创建成功 false:创建失败 |
|||
*/ |
|||
public boolean create(Device device); |
|||
|
|||
/** |
|||
* 视频设备更新 |
|||
* |
|||
* @param device 设备对象 |
|||
* @return true:创建成功 false:创建失败 |
|||
*/ |
|||
public boolean updateDevice(Device device); |
|||
|
|||
/** |
|||
* 添加设备通道 |
|||
* |
|||
* @param deviceId 设备id |
|||
* @param channel 通道 |
|||
*/ |
|||
public void updateChannel(String deviceId, DeviceChannel channel); |
|||
|
|||
/** |
|||
* 开始播放 |
|||
* @param deviceId 设备id |
|||
* @param channelId 通道ID |
|||
* @param streamId 流地址 |
|||
*/ |
|||
public void startPlay(String deviceId, String channelId, String streamId); |
|||
|
|||
/** |
|||
* 停止播放 |
|||
* @param deviceId 设备id |
|||
* @param channelId 通道ID |
|||
*/ |
|||
public void stopPlay(String deviceId, String channelId); |
|||
|
|||
/** |
|||
* 获取设备 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return DShadow 设备对象 |
|||
*/ |
|||
public Device queryVideoDevice(String deviceId); |
|||
|
|||
/** |
|||
* 获取某个设备的通道列表 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @param page 分页 当前页 |
|||
* @param count 每页数量 |
|||
* @return |
|||
*/ |
|||
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count); |
|||
|
|||
/** |
|||
* 获取某个设备的通道列表 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return |
|||
*/ |
|||
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId); |
|||
|
|||
/** |
|||
* 获取某个设备的通道 |
|||
* @param deviceId 设备ID |
|||
* @param channelId 通道ID |
|||
*/ |
|||
public DeviceChannel queryChannel(String deviceId, String channelId); |
|||
|
|||
/** |
|||
* 获取多个设备 |
|||
* @param page 当前页数 |
|||
* @param count 每页数量 |
|||
* @return List<Device> 设备对象数组 |
|||
*/ |
|||
public PageInfo<Device> queryVideoDeviceList(int page, int count); |
|||
|
|||
/** |
|||
* 获取多个设备 |
|||
* |
|||
* @return List<Device> 设备对象数组 |
|||
*/ |
|||
public List<Device> queryVideoDeviceList(); |
|||
|
|||
/** |
|||
* 删除设备 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:删除成功 false:删除失败 |
|||
*/ |
|||
public boolean delete(String deviceId); |
|||
|
|||
/** |
|||
* 更新设备在线 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:更新成功 false:更新失败 |
|||
*/ |
|||
public boolean online(String deviceId); |
|||
|
|||
/** |
|||
* 更新设备离线 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:更新成功 false:更新失败 |
|||
*/ |
|||
public boolean outline(String deviceId); |
|||
|
|||
|
|||
/** |
|||
* 查询子设备 |
|||
* |
|||
* @param deviceId |
|||
* @param channelId |
|||
* @param page |
|||
* @param count |
|||
* @return |
|||
*/ |
|||
PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count); |
|||
|
|||
|
|||
/** |
|||
* 清空通道 |
|||
* @param deviceId |
|||
*/ |
|||
void cleanChannelsForDevice(String deviceId); |
|||
|
|||
|
|||
/** |
|||
* 更新上级平台 |
|||
* @param parentPlatform |
|||
*/ |
|||
boolean updateParentPlatform(ParentPlatform parentPlatform); |
|||
|
|||
|
|||
/** |
|||
* 添加上级平台 |
|||
* @param parentPlatform |
|||
*/ |
|||
boolean addParentPlatform(ParentPlatform parentPlatform); |
|||
|
|||
/** |
|||
* 删除上级平台 |
|||
* @param parentPlatform |
|||
*/ |
|||
boolean deleteParentPlatform(ParentPlatform parentPlatform); |
|||
|
|||
|
|||
/** |
|||
* 分页获取上级平台 |
|||
* @param page |
|||
* @param count |
|||
* @return |
|||
*/ |
|||
PageInfo<ParentPlatform> queryParentPlatformList(int page, int count); |
|||
|
|||
/** |
|||
* 获取所有已启用的平台 |
|||
* @return |
|||
*/ |
|||
List<ParentPlatform> queryEnableParentPlatformList(boolean enable); |
|||
|
|||
/** |
|||
* 获取上级平台 |
|||
* @param platformGbId |
|||
* @return |
|||
*/ |
|||
ParentPlatform queryParentPlatById(String platformGbId); |
|||
|
|||
/** |
|||
* 所有平台离线 |
|||
*/ |
|||
void outlineForAllParentPlatform(); |
|||
|
|||
/** |
|||
* 查询通道信息,不区分设备(已关联平台或全部) |
|||
*/ |
|||
PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, Boolean inPlatform); |
|||
|
|||
/** |
|||
* 查询设备的通道信息 |
|||
*/ |
|||
List<ChannelReduce> queryChannelListInParentPlatform(String platformId); |
|||
|
|||
|
|||
/** |
|||
* 更新上级平台的通道信息 |
|||
* @param platformId |
|||
* @param channelReduces |
|||
* @return |
|||
*/ |
|||
int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces); |
|||
|
|||
/** |
|||
* 移除上级平台的通道信息 |
|||
* @param platformId |
|||
* @param channelReduces |
|||
* @return |
|||
*/ |
|||
int delChannelForGB(String platformId, List<ChannelReduce> channelReduces); |
|||
|
|||
|
|||
DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); |
|||
|
|||
Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); |
|||
|
|||
|
|||
/** |
|||
* 添加Mobile Position设备移动位置 |
|||
* @param MobilePosition |
|||
* @return |
|||
*/ |
|||
public boolean insertMobilePosition(MobilePosition mobilePosition); |
|||
|
|||
/** |
|||
* 查询移动位置轨迹 |
|||
* @param deviceId |
|||
* @param startTime |
|||
* @param endTime |
|||
*/ |
|||
public List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime); |
|||
|
|||
/** |
|||
* 查询最新移动位置 |
|||
* @param deviceId |
|||
*/ |
|||
public MobilePosition queryLatestPosition(String deviceId); |
|||
|
|||
/** |
|||
* 删除指定设备的所有移动位置 |
|||
* @param deviceId |
|||
*/ |
|||
public int clearMobilePositionsByDeviceId(String deviceId); |
|||
} |
|||
package com.genersoft.iot.vmp.storager; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
|||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
|||
import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce; |
|||
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; |
|||
import com.github.pagehelper.PageInfo; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @Description:视频设备数据存储接口 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月6日 下午2:14:31 |
|||
*/ |
|||
public interface IVideoManagerStorager { |
|||
|
|||
/** |
|||
* 根据设备ID判断设备是否存在 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:存在 false:不存在 |
|||
*/ |
|||
public boolean exists(String deviceId); |
|||
|
|||
/** |
|||
* 视频设备创建 |
|||
* |
|||
* @param device 设备对象 |
|||
* @return true:创建成功 false:创建失败 |
|||
*/ |
|||
public boolean create(Device device); |
|||
|
|||
/** |
|||
* 视频设备更新 |
|||
* |
|||
* @param device 设备对象 |
|||
* @return true:创建成功 false:创建失败 |
|||
*/ |
|||
public boolean updateDevice(Device device); |
|||
|
|||
/** |
|||
* 添加设备通道 |
|||
* |
|||
* @param deviceId 设备id |
|||
* @param channel 通道 |
|||
*/ |
|||
public void updateChannel(String deviceId, DeviceChannel channel); |
|||
|
|||
/** |
|||
* 开始播放 |
|||
* |
|||
* @param deviceId 设备id |
|||
* @param channelId 通道ID |
|||
* @param streamId 流地址 |
|||
*/ |
|||
public void startPlay(String deviceId, String channelId, String streamId); |
|||
|
|||
/** |
|||
* 停止播放 |
|||
* |
|||
* @param deviceId 设备id |
|||
* @param channelId 通道ID |
|||
*/ |
|||
public void stopPlay(String deviceId, String channelId); |
|||
|
|||
/** |
|||
* 获取设备 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return DShadow 设备对象 |
|||
*/ |
|||
public Device queryVideoDevice(String deviceId); |
|||
|
|||
/** |
|||
* 获取某个设备的通道列表 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @param page 分页 当前页 |
|||
* @param count 每页数量 |
|||
* @return |
|||
*/ |
|||
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count); |
|||
|
|||
/** |
|||
* 获取某个设备的通道列表 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return |
|||
*/ |
|||
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId); |
|||
|
|||
/** |
|||
* 获取某个设备的通道 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @param channelId 通道ID |
|||
*/ |
|||
public DeviceChannel queryChannel(String deviceId, String channelId); |
|||
|
|||
/** |
|||
* 获取多个设备 |
|||
* |
|||
* @param page 当前页数 |
|||
* @param count 每页数量 |
|||
* @return List<Device> 设备对象数组 |
|||
*/ |
|||
public PageInfo<Device> queryVideoDeviceList(int page, int count); |
|||
|
|||
/** |
|||
* 获取多个设备 |
|||
* |
|||
* @return List<Device> 设备对象数组 |
|||
*/ |
|||
public List<Device> queryVideoDeviceList(); |
|||
|
|||
/** |
|||
* 删除设备 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:删除成功 false:删除失败 |
|||
*/ |
|||
public boolean delete(String deviceId); |
|||
|
|||
/** |
|||
* 更新设备在线 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:更新成功 false:更新失败 |
|||
*/ |
|||
public boolean online(String deviceId); |
|||
|
|||
/** |
|||
* 更新设备离线 |
|||
* |
|||
* @param deviceId 设备ID |
|||
* @return true:更新成功 false:更新失败 |
|||
*/ |
|||
public boolean outline(String deviceId); |
|||
|
|||
|
|||
/** |
|||
* 查询子设备 |
|||
* |
|||
* @param deviceId |
|||
* @param channelId |
|||
* @param page |
|||
* @param count |
|||
* @return |
|||
*/ |
|||
PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count); |
|||
|
|||
|
|||
/** |
|||
* 清空通道 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
void cleanChannelsForDevice(String deviceId); |
|||
|
|||
/** |
|||
* 添加Mobile Position设备移动位置 |
|||
* |
|||
* @param MobilePosition |
|||
* @return |
|||
*/ |
|||
public boolean insertMobilePosition(MobilePosition mobilePosition); |
|||
|
|||
/** |
|||
* 更新上级平台 |
|||
* @param parentPlatform |
|||
*/ |
|||
boolean updateParentPlatform(ParentPlatform parentPlatform); |
|||
|
|||
|
|||
/** |
|||
* 添加上级平台 |
|||
* @param parentPlatform |
|||
*/ |
|||
boolean addParentPlatform(ParentPlatform parentPlatform); |
|||
|
|||
/** |
|||
* 删除上级平台 |
|||
* @param parentPlatform |
|||
*/ |
|||
boolean deleteParentPlatform(ParentPlatform parentPlatform); |
|||
|
|||
|
|||
/** |
|||
* 分页获取上级平台 |
|||
* @param page |
|||
* @param count |
|||
* @return |
|||
*/ |
|||
PageInfo<ParentPlatform> queryParentPlatformList(int page, int count); |
|||
|
|||
/** |
|||
* 获取所有已启用的平台 |
|||
* @return |
|||
*/ |
|||
List<ParentPlatform> queryEnableParentPlatformList(boolean enable); |
|||
|
|||
/** |
|||
* 获取上级平台 |
|||
* @param platformGbId |
|||
* @return |
|||
*/ |
|||
ParentPlatform queryParentPlatById(String platformGbId); |
|||
|
|||
/** |
|||
* 所有平台离线 |
|||
*/ |
|||
void outlineForAllParentPlatform(); |
|||
|
|||
/** |
|||
* 查询通道信息,不区分设备(已关联平台或全部) |
|||
*/ |
|||
PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, Boolean inPlatform); |
|||
|
|||
/** |
|||
* 查询设备的通道信息 |
|||
*/ |
|||
List<ChannelReduce> queryChannelListInParentPlatform(String platformId); |
|||
|
|||
|
|||
/** |
|||
* 更新上级平台的通道信息 |
|||
* @param platformId |
|||
* @param channelReduces |
|||
* @return |
|||
*/ |
|||
int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces); |
|||
|
|||
/** |
|||
* 移除上级平台的通道信息 |
|||
* @param platformId |
|||
* @param channelReduces |
|||
* @return |
|||
*/ |
|||
int delChannelForGB(String platformId, List<ChannelReduce> channelReduces); |
|||
|
|||
|
|||
DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); |
|||
|
|||
Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); |
|||
|
|||
|
|||
/** |
|||
* 添加Mobile Position设备移动位置 |
|||
* @param MobilePosition |
|||
* @return |
|||
*/ |
|||
public boolean insertMobilePosition(MobilePosition mobilePosition); |
|||
|
|||
/** |
|||
* 查询移动位置轨迹 |
|||
* |
|||
* @param deviceId |
|||
* @param startTime |
|||
* @param endTime |
|||
*/ |
|||
public List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime); |
|||
|
|||
/** |
|||
* 查询最新移动位置 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
public MobilePosition queryLatestPosition(String deviceId); |
|||
|
|||
/** |
|||
* 删除指定设备的所有移动位置 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
public int clearMobilePositionsByDeviceId(String deviceId); |
|||
} |
|||
|
@ -0,0 +1,8 @@ |
|||
package com.genersoft.iot.vmp.utils; |
|||
|
|||
public class ConfigConst { |
|||
/** |
|||
* 播流最大并发个数 |
|||
*/ |
|||
public static final Integer MAX_STRTEAM_COUNT = 10000; |
|||
} |
@ -0,0 +1,97 @@ |
|||
package com.genersoft.iot.vmp.utils.redis; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import redis.clients.jedis.Jedis; |
|||
import redis.clients.jedis.JedisPool; |
|||
|
|||
import java.util.Set; |
|||
|
|||
/** |
|||
* @Description:Jedis工具类 |
|||
* @author: wangshaopeng@sunnybs.com |
|||
* @date: 2021年03月22日 下午8:27:29 |
|||
*/ |
|||
@Component |
|||
public class JedisUtil { |
|||
|
|||
@Autowired |
|||
private JedisPool jedisPool; |
|||
|
|||
// ============================== Key ==============================
|
|||
|
|||
/** |
|||
* 检查给定 key 是否存在。 |
|||
* |
|||
* @param key |
|||
* @return |
|||
*/ |
|||
public Boolean exists(String key) { |
|||
Jedis jedis = null; |
|||
try { |
|||
jedis = jedisPool.getResource(); |
|||
Boolean exists = jedis.exists(key); |
|||
return exists; |
|||
} finally { |
|||
returnToPool(jedis); |
|||
} |
|||
} |
|||
|
|||
|
|||
// ============================== Set ==============================
|
|||
|
|||
/** |
|||
* SADD key member [member ...] |
|||
* 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。 |
|||
* 假如 key 不存在,则创建一个只包含 member 元素作成员的集合。 |
|||
* 当 key 不是集合类型时,返回一个错误。 |
|||
*/ |
|||
public Long sadd(String key, String... members) { |
|||
Jedis jedis = null; |
|||
try { |
|||
jedis = jedisPool.getResource(); |
|||
Long smove = jedis.sadd(key, members); |
|||
return smove; |
|||
} finally { |
|||
returnToPool(jedis); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* SMEMBERS key |
|||
* 返回集合 key 中的所有成员。 |
|||
* 不存在的 key 被视为空集合。 |
|||
*/ |
|||
public Set<String> smembers(String key) { |
|||
Jedis jedis = null; |
|||
try { |
|||
jedis = jedisPool.getResource(); |
|||
Set<String> smembers = jedis.smembers(key); |
|||
return smembers; |
|||
} finally { |
|||
returnToPool(jedis); |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* SREM key member1 [member2] |
|||
* 移除集合中一个或多个成员 |
|||
*/ |
|||
public Long srem(String key, String... member) { |
|||
Jedis jedis = null; |
|||
try { |
|||
jedis = jedisPool.getResource(); |
|||
Long srem = jedis.srem(key, member); |
|||
return srem; |
|||
} finally { |
|||
returnToPool(jedis); |
|||
} |
|||
} |
|||
|
|||
private void returnToPool(Jedis jedis) { |
|||
if (jedis != null) { |
|||
jedis.close(); |
|||
} |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -1,120 +0,0 @@ |
|||
/** |
|||
* 设备设置命令API接口 |
|||
* |
|||
* @author lawrencehj |
|||
* @date 2021年2月2日 |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.vmanager.device; |
|||
|
|||
import javax.sip.message.Response; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
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.utils.XmlUtil; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
|
|||
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.*; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
@CrossOrigin |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class DeviceConfig { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
/** |
|||
* 看守位控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param enabled 看守位使能1:开启,0:关闭 |
|||
* @param resetTime 自动归位时间间隔(可选) |
|||
* @param presetIndex 调用预置位编号(可选) |
|||
* @param channelId 通道编码(可选) |
|||
*/ |
|||
@GetMapping("/config/{deviceId}/basicParam") |
|||
public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String channelId, |
|||
@RequestParam(required = false) String name, |
|||
@RequestParam(required = false) String expiration, |
|||
@RequestParam(required = false) String heartBeatInterval, |
|||
@RequestParam(required = false) String heartBeatCount) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("报警复位API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("Status", "Timeout"); |
|||
json.put("Description", "设备配置操作超时, 设备未返回应答指令"); |
|||
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
|
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 设备配置查询请求API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/config/{deviceId}/query/{configType}") |
|||
public DeferredResult<ResponseEntity<String>> configDownloadApi(@PathVariable String deviceId, |
|||
@PathVariable String configType, |
|||
@RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备状态查询API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.deviceConfigQuery(device, channelId, configType, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L); |
|||
result.onTimeout(()->{ |
|||
logger.warn(String.format("获取设备配置超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,119 @@ |
|||
/** |
|||
* 设备设置命令API接口 |
|||
* |
|||
* @author lawrencehj |
|||
* @date 2021年2月2日 |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.vmanager.device; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
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.utils.XmlUtil; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
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.*; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
import javax.sip.message.Response; |
|||
|
|||
@CrossOrigin |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class DeviceConfigController { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(DeviceConfigController.class); |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
/** |
|||
* 看守位控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param enabled 看守位使能1:开启,0:关闭 |
|||
* @param resetTime 自动归位时间间隔(可选) |
|||
* @param presetIndex 调用预置位编号(可选) |
|||
* @param channelId 通道编码(可选) |
|||
*/ |
|||
@GetMapping("/config/{deviceId}/basicParam") |
|||
public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String channelId, |
|||
@RequestParam(required = false) String name, |
|||
@RequestParam(required = false) String expiration, |
|||
@RequestParam(required = false) String heartBeatInterval, |
|||
@RequestParam(required = false) String heartBeatCount) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("报警复位API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("Status", "Timeout"); |
|||
json.put("Description", "设备配置操作超时, 设备未返回应答指令"); |
|||
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
|
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 设备配置查询请求API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/config/{deviceId}/query/{configType}") |
|||
public DeferredResult<ResponseEntity<String>> configDownloadApi(@PathVariable String deviceId, |
|||
@PathVariable String configType, |
|||
@RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备状态查询API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.deviceConfigQuery(device, channelId, configType, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("获取设备配置超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
|
|||
} |
@ -1,238 +0,0 @@ |
|||
/** |
|||
* 设备控制命令API接口 |
|||
* |
|||
* @author lawrencehj |
|||
* @date 2021年2月1日 |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.vmanager.device; |
|||
|
|||
import javax.sip.message.Response; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
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.utils.XmlUtil; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
|
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
@CrossOrigin |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class DeviceControl { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
/** |
|||
* 远程启动控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/teleboot") |
|||
@PostMapping("/control/{deviceId}/teleboot") |
|||
public ResponseEntity<String> teleBootApi(@PathVariable String deviceId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备远程启动API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
boolean sucsess = cmder.teleBootCmd(device); |
|||
if (sucsess) { |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("Result", "OK"); |
|||
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK); |
|||
} else { |
|||
logger.warn("设备远程启动API调用失败!"); |
|||
return new ResponseEntity<String>("设备远程启动API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 录像控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 |
|||
* @param channelId 通道编码(可选) |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/record/{recordCmdStr}") |
|||
public DeferredResult<ResponseEntity<String>> recordApi(@PathVariable String deviceId, |
|||
@PathVariable String recordCmdStr, @RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("开始/停止录像API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.recordCmd(device, channelId, recordCmdStr, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 报警布防/撤防命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param guardCmdStr SetGuard:布防,ResetGuard:撤防 |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/guard/{guardCmdStr}") |
|||
public DeferredResult<ResponseEntity<String>> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("布防/撤防API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.guardCmd(device, guardCmdStr, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 报警复位API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param alarmMethod 报警方式(可选) |
|||
* @param alarmType 报警类型(可选) |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/resetAlarm") |
|||
public DeferredResult<ResponseEntity<String>> resetAlarmApi(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String alarmMethod, |
|||
@RequestParam(required = false) String alarmType) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("报警复位API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.alarmCmd(device, alarmMethod, alarmType, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData(String.format("报警复位操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("报警复位操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 强制关键帧API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param channelId |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/iFrame") |
|||
@PostMapping("/control/{deviceId}/iFrame") |
|||
public ResponseEntity<String> iFrame(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("强制关键帧API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
boolean sucsess = cmder.iFrameCmd(device, channelId); |
|||
if (sucsess) { |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("ChannelID", channelId); |
|||
json.put("Result", "OK"); |
|||
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK); |
|||
} else { |
|||
logger.warn("强制关键帧API调用失败!"); |
|||
return new ResponseEntity<String>("强制关键帧API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 看守位控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param enabled 看守位使能1:开启,0:关闭 |
|||
* @param resetTime 自动归位时间间隔(可选) |
|||
* @param presetIndex 调用预置位编号(可选) |
|||
* @param channelId 通道编码(可选) |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/homePosition/{enabled}") |
|||
public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId, |
|||
@PathVariable String enabled, |
|||
@RequestParam(required = false) String resetTime, |
|||
@RequestParam(required = false) String presetIndex, |
|||
@RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("报警复位API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("Status", "Timeout"); |
|||
json.put("Description", "看守位控制操作超时, 设备未返回应答指令"); |
|||
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
|
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
} |
@ -0,0 +1,237 @@ |
|||
/** |
|||
* 设备控制命令API接口 |
|||
* |
|||
* @author lawrencehj |
|||
* @date 2021年2月1日 |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.vmanager.device; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
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.utils.XmlUtil; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
import javax.sip.message.Response; |
|||
|
|||
@CrossOrigin |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class DeviceControlController { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(DeviceControlController.class); |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
/** |
|||
* 远程启动控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/teleboot") |
|||
@PostMapping("/control/{deviceId}/teleboot") |
|||
public ResponseEntity<String> teleBootApi(@PathVariable String deviceId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备远程启动API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
boolean sucsess = cmder.teleBootCmd(device); |
|||
if (sucsess) { |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("Result", "OK"); |
|||
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK); |
|||
} else { |
|||
logger.warn("设备远程启动API调用失败!"); |
|||
return new ResponseEntity<String>("设备远程启动API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 录像控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 |
|||
* @param channelId 通道编码(可选) |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/record/{recordCmdStr}") |
|||
public DeferredResult<ResponseEntity<String>> recordApi(@PathVariable String deviceId, |
|||
@PathVariable String recordCmdStr, @RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("开始/停止录像API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.recordCmd(device, channelId, recordCmdStr, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 报警布防/撤防命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param guardCmdStr SetGuard:布防,ResetGuard:撤防 |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/guard/{guardCmdStr}") |
|||
public DeferredResult<ResponseEntity<String>> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("布防/撤防API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.guardCmd(device, guardCmdStr, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 报警复位API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param alarmMethod 报警方式(可选) |
|||
* @param alarmType 报警类型(可选) |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/resetAlarm") |
|||
public DeferredResult<ResponseEntity<String>> resetAlarmApi(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String alarmMethod, |
|||
@RequestParam(required = false) String alarmType) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("报警复位API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.alarmCmd(device, alarmMethod, alarmType, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData(String.format("报警复位操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("报警复位操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 强制关键帧API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param channelId |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/iFrame") |
|||
@PostMapping("/control/{deviceId}/iFrame") |
|||
public ResponseEntity<String> iFrame(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("强制关键帧API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
boolean sucsess = cmder.iFrameCmd(device, channelId); |
|||
if (sucsess) { |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("ChannelID", channelId); |
|||
json.put("Result", "OK"); |
|||
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK); |
|||
} else { |
|||
logger.warn("强制关键帧API调用失败!"); |
|||
return new ResponseEntity<String>("强制关键帧API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 看守位控制命令API接口 |
|||
* |
|||
* @param deviceId |
|||
* @param enabled 看守位使能1:开启,0:关闭 |
|||
* @param resetTime 自动归位时间间隔(可选) |
|||
* @param presetIndex 调用预置位编号(可选) |
|||
* @param channelId 通道编码(可选) |
|||
*/ |
|||
@GetMapping("/control/{deviceId}/homePosition/{enabled}") |
|||
public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId, |
|||
@PathVariable String enabled, |
|||
@RequestParam(required = false) String resetTime, |
|||
@RequestParam(required = false) String presetIndex, |
|||
@RequestParam(required = false) String channelId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("报警复位API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId)); |
|||
JSONObject json = new JSONObject(); |
|||
json.put("DeviceID", deviceId); |
|||
json.put("Status", "Timeout"); |
|||
json.put("Description", "看守位控制操作超时, 设备未返回应答指令"); |
|||
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
|
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result); |
|||
return result; |
|||
} |
|||
} |
@ -0,0 +1,261 @@ |
|||
package com.genersoft.iot.vmp.vmanager.device; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
|||
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; |
|||
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.storager.IVideoManagerStorager; |
|||
import com.github.pagehelper.PageInfo; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
import javax.sip.message.Response; |
|||
|
|||
@CrossOrigin |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class DeviceController { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(DeviceController.class); |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
@Autowired |
|||
private DeviceOffLineDetector offLineDetector; |
|||
|
|||
@GetMapping("/devices/{deviceId}") |
|||
public ResponseEntity<Device> devices(@PathVariable String deviceId) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询视频设备API调用,deviceId:" + deviceId); |
|||
} |
|||
|
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
return new ResponseEntity<>(device, HttpStatus.OK); |
|||
} |
|||
|
|||
@GetMapping("/devices") |
|||
public PageInfo<Device> devices(Integer page, Integer count) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询所有视频设备API调用"); |
|||
} |
|||
if (null == page) { |
|||
page = 1; |
|||
} |
|||
if (null == count) { |
|||
count = 10; |
|||
} |
|||
return storager.queryVideoDeviceList(page, count); |
|||
} |
|||
|
|||
/** |
|||
* 分页查询通道数 |
|||
* |
|||
* @param deviceId 设备id |
|||
* @param page 当前页 |
|||
* @param count 每页条数 |
|||
* @param query 查询内容 |
|||
* @param online 是否在线 在线 true / 离线 false |
|||
* @param channelType 设备 false/子目录 true |
|||
* @return 通道列表 |
|||
*/ |
|||
@GetMapping("/devices/{deviceId}/channels") |
|||
public ResponseEntity<PageInfo> channels(@PathVariable String deviceId, |
|||
int page, int count, |
|||
@RequestParam(required = false) String query, |
|||
@RequestParam(required = false) Boolean online, |
|||
@RequestParam(required = false) Boolean channelType |
|||
) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询所有视频设备API调用"); |
|||
} |
|||
if (StringUtils.isEmpty(query)) { |
|||
query = null; |
|||
} |
|||
|
|||
PageInfo pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count); |
|||
return new ResponseEntity<>(pageResult, 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, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId); |
|||
msg.setData(String.format("同步通道失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>(2 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("设备通道信息同步超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId); |
|||
msg.setData("Timeout"); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
@PostMapping("/devices/{deviceId}/delete") |
|||
public ResponseEntity<String> delete(@PathVariable String deviceId) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备信息删除API调用,deviceId:" + deviceId); |
|||
} |
|||
|
|||
if (offLineDetector.isOnline(deviceId)) { |
|||
return new ResponseEntity<String>("不允许删除在线设备!", HttpStatus.NOT_ACCEPTABLE); |
|||
} |
|||
boolean isSuccess = storager.delete(deviceId); |
|||
if (isSuccess) { |
|||
JSONObject json = new JSONObject(); |
|||
json.put("deviceId", deviceId); |
|||
return new ResponseEntity<>(json.toString(), HttpStatus.OK); |
|||
} else { |
|||
logger.warn("设备信息删除API调用失败!"); |
|||
return new ResponseEntity<>("设备信息删除API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 分页查询通道数 |
|||
* |
|||
* @param channelId 通道id |
|||
* @param page 当前页 |
|||
* @param count 每页条数 |
|||
* @return 子通道列表 |
|||
*/ |
|||
@GetMapping("/subChannels/{deviceId}/{channelId}/channels") |
|||
public ResponseEntity<PageInfo> subChannels(@PathVariable String deviceId, |
|||
@PathVariable String channelId, |
|||
int page, |
|||
int count, |
|||
@RequestParam(required = false) String query, |
|||
@RequestParam(required = false) String online, |
|||
@RequestParam(required = false) Boolean channelType) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询所有视频通道API调用"); |
|||
} |
|||
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); |
|||
if (deviceChannel == null) { |
|||
PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>(); |
|||
return new ResponseEntity<>(deviceChannelPageResult, HttpStatus.OK); |
|||
} |
|||
|
|||
PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); |
|||
return new ResponseEntity<>(pageResult, HttpStatus.OK); |
|||
} |
|||
|
|||
@PostMapping("/channel/update/{deviceId}") |
|||
public ResponseEntity<PageInfo> updateChannel(@PathVariable String deviceId, DeviceChannel channel) { |
|||
storager.updateChannel(deviceId, channel); |
|||
return new ResponseEntity<>(null, HttpStatus.OK); |
|||
} |
|||
|
|||
@GetMapping("/devices/{deviceId}/transport/{streamMode}") |
|||
@PostMapping("/devices/{deviceId}/transport/{streamMode}") |
|||
public ResponseEntity<PageInfo> updateTransport(@PathVariable String deviceId, @PathVariable String streamMode) { |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
device.setStreamMode(streamMode); |
|||
storager.updateDevice(device); |
|||
return new ResponseEntity<>(null, HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 设备状态查询请求API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/devices/{deviceId}/status") |
|||
public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备状态查询API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.deviceStatusQuery(device, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId); |
|||
msg.setData(String.format("获取设备状态失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("获取设备状态超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 设备报警查询请求API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/alarm/{deviceId}") |
|||
public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String startPriority, |
|||
@RequestParam(required = false) String endPriority, |
|||
@RequestParam(required = false) String alarmMethod, |
|||
@RequestParam(required = false) String alarmType, |
|||
@RequestParam(required = false) String startTime, |
|||
@RequestParam(required = false) String endTime) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备报警查询API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId); |
|||
msg.setData(String.format("设备报警查询失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); |
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("设备报警查询超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId); |
|||
msg.setData("设备报警查询超时"); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
|
|||
} |
@ -1,255 +0,0 @@ |
|||
package com.genersoft.iot.vmp.vmanager.device; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
|||
import com.github.pagehelper.PageInfo; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; |
|||
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; |
|||
|
|||
import javax.sip.message.Response; |
|||
|
|||
@SuppressWarnings("rawtypes") |
|||
@CrossOrigin |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class DeviceQuery { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
@Autowired |
|||
private DeviceOffLineDetector offLineDetector; |
|||
|
|||
@GetMapping("/devices/{deviceId}") |
|||
public ResponseEntity<Device> devices(@PathVariable String deviceId){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询视频设备API调用,deviceId:" + deviceId); |
|||
} |
|||
|
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
return new ResponseEntity<>(device,HttpStatus.OK); |
|||
} |
|||
|
|||
@GetMapping("/devices") |
|||
public PageInfo<Device> devices(int page, int count){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询所有视频设备API调用"); |
|||
} |
|||
|
|||
return storager.queryVideoDeviceList(page, count); |
|||
} |
|||
|
|||
/** |
|||
* 分页查询通道数 |
|||
* |
|||
* @param deviceId 设备id |
|||
* @param page 当前页 |
|||
* @param count 每页条数 |
|||
* @param query 查询内容 |
|||
* @param online 是否在线 在线 true / 离线 false |
|||
* @param channelType 设备 false/子目录 true |
|||
* @return 通道列表 |
|||
*/ |
|||
@GetMapping("/devices/{deviceId}/channels") |
|||
public ResponseEntity<PageInfo> channels(@PathVariable String deviceId, |
|||
int page, int count, |
|||
@RequestParam(required = false) String query, |
|||
@RequestParam(required = false) Boolean online, |
|||
@RequestParam(required = false) Boolean channelType) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询视频设备通道API调用"); |
|||
} |
|||
if (StringUtils.isEmpty(query)) { |
|||
query = null; |
|||
} |
|||
|
|||
PageInfo pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count); |
|||
return new ResponseEntity<>(pageResult,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, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId); |
|||
msg.setData(String.format("同步通道失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>(2*1000L); |
|||
result.onTimeout(()->{ |
|||
logger.warn(String.format("设备通道信息同步超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId); |
|||
msg.setData("Timeout"); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
@PostMapping("/devices/{deviceId}/delete") |
|||
public ResponseEntity<String> delete(@PathVariable String deviceId){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备信息删除API调用,deviceId:" + deviceId); |
|||
} |
|||
|
|||
if (offLineDetector.isOnline(deviceId)) { |
|||
return new ResponseEntity<String>("不允许删除在线设备!", HttpStatus.NOT_ACCEPTABLE); |
|||
} |
|||
boolean isSuccess = storager.delete(deviceId); |
|||
if (isSuccess) { |
|||
JSONObject json = new JSONObject(); |
|||
json.put("deviceId", deviceId); |
|||
return new ResponseEntity<>(json.toString(),HttpStatus.OK); |
|||
} else { |
|||
logger.warn("设备信息删除API调用失败!"); |
|||
return new ResponseEntity<String>("设备信息删除API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 分页查询通道数 |
|||
* @param channelId 通道id |
|||
* @param page 当前页 |
|||
* @param count 每页条数 |
|||
* @return 子通道列表 |
|||
*/ |
|||
@GetMapping("/subChannels/{deviceId}/{channelId}/channels") |
|||
public ResponseEntity<PageInfo> subChannels(@PathVariable String deviceId, |
|||
@PathVariable String channelId, |
|||
int page, |
|||
int count, |
|||
@RequestParam(required = false) String query, |
|||
@RequestParam(required = false) String online, |
|||
@RequestParam(required = false) Boolean channelType){ |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("查询所有视频通道API调用"); |
|||
} |
|||
DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId); |
|||
if (deviceChannel == null) { |
|||
PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>(); |
|||
return new ResponseEntity<>(deviceChannelPageResult,HttpStatus.OK); |
|||
} |
|||
|
|||
PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); |
|||
return new ResponseEntity<>(pageResult,HttpStatus.OK); |
|||
} |
|||
|
|||
@PostMapping("/channel/update/{deviceId}") |
|||
public ResponseEntity<PageInfo> updateChannel(@PathVariable String deviceId,DeviceChannel channel){ |
|||
storager.updateChannel(deviceId, channel); |
|||
return new ResponseEntity<>(null,HttpStatus.OK); |
|||
} |
|||
|
|||
@GetMapping("/devices/{deviceId}/transport/{streamMode}") |
|||
@PostMapping("/devices/{deviceId}/transport/{streamMode}") |
|||
public ResponseEntity<PageInfo> updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
device.setStreamMode(streamMode); |
|||
storager.updateDevice(device); |
|||
return new ResponseEntity<>(null,HttpStatus.OK); |
|||
} |
|||
|
|||
/** |
|||
* 设备状态查询请求API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/devices/{deviceId}/status") |
|||
public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备状态查询API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.deviceStatusQuery(device, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId); |
|||
msg.setData(String.format("获取设备状态失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L); |
|||
result.onTimeout(()->{ |
|||
logger.warn(String.format("获取设备状态超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId); |
|||
msg.setData("Timeout. Device did not response to this command."); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 设备报警查询请求API接口 |
|||
* |
|||
* @param deviceId |
|||
*/ |
|||
@GetMapping("/alarm/{deviceId}") |
|||
public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId, |
|||
@RequestParam(required = false) String startPriority, |
|||
@RequestParam(required = false) String endPriority, |
|||
@RequestParam(required = false) String alarmMethod, |
|||
@RequestParam(required = false) String alarmType, |
|||
@RequestParam(required = false) String startTime, |
|||
@RequestParam(required = false) String endTime) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug("设备报警查询API调用"); |
|||
} |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId); |
|||
msg.setData(String.format("设备报警查询失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L); |
|||
result.onTimeout(()->{ |
|||
logger.warn(String.format("设备报警查询超时")); |
|||
// 释放rtpserver
|
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId); |
|||
msg.setData("设备报警查询超时"); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, result); |
|||
return result; |
|||
} |
|||
|
|||
|
|||
} |
@ -1,110 +1,101 @@ |
|||
package com.genersoft.iot.vmp.vmanager.playback; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.genersoft.iot.vmp.common.StreamInfo; |
|||
import com.genersoft.iot.vmp.gb28181.bean.Device; |
|||
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
|||
//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
|||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
|||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.CrossOrigin; |
|||
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 com.alibaba.fastjson.JSONObject; |
|||
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; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.springframework.web.context.request.async.DeferredResult; |
|||
|
|||
import javax.sip.message.Response; |
|||
import java.util.UUID; |
|||
|
|||
@CrossOrigin |
|||
@RestController |
|||
@RequestMapping("/api") |
|||
public class PlaybackController { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private IRedisCatchStorage redisCatchStorage; |
|||
|
|||
// @Autowired
|
|||
// private ZLMRESTfulUtils zlmresTfulUtils;
|
|||
|
|||
@Autowired |
|||
private IPlayService playService; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
@GetMapping("/playback/{deviceId}/{channelId}") |
|||
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, |
|||
String endTime) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); |
|||
} |
|||
UUID uuid = UUID.randomUUID(); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); |
|||
// 超时处理
|
|||
result.onTimeout(()->{ |
|||
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); |
|||
msg.setData("Timeout"); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); |
|||
if (streamInfo != null) { |
|||
// 停止之前的回放
|
|||
cmder.streamByeCmd(streamInfo.getStreamId()); |
|||
} |
|||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); |
|||
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { |
|||
logger.info("收到订阅消息: " + response.toJSONString()); |
|||
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); |
|||
}, event -> { |
|||
Response response = event.getResponse(); |
|||
RequestMessage msg = new RequestMessage(); |
|||
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); |
|||
msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
@RequestMapping("/playback/{ssrc}/stop") |
|||
public ResponseEntity<String> playStop(@PathVariable String ssrc) { |
|||
|
|||
cmder.streamByeCmd(ssrc); |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); |
|||
} |
|||
|
|||
if (ssrc != null) { |
|||
JSONObject json = new JSONObject(); |
|||
json.put("ssrc", ssrc); |
|||
return new ResponseEntity<String>(json.toString(), HttpStatus.OK); |
|||
} else { |
|||
logger.warn("设备录像回放停止API调用失败!"); |
|||
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
} |
|||
private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); |
|||
|
|||
@Autowired |
|||
private SIPCommander cmder; |
|||
|
|||
@Autowired |
|||
private IVideoManagerStorager storager; |
|||
|
|||
@Autowired |
|||
private VideoStreamSessionManager streamSession; |
|||
|
|||
@Autowired |
|||
private IPlayService playService; |
|||
|
|||
@Autowired |
|||
private DeferredResultHolder resultHolder; |
|||
|
|||
@GetMapping("/playback/{deviceId}/{channelId}") |
|||
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, |
|||
String endTime) { |
|||
|
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); |
|||
} |
|||
RequestMessage msg = playService.createCallbackPlayMsg(); |
|||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); |
|||
// 超时处理
|
|||
result.onTimeout(() -> { |
|||
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
|||
StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId); |
|||
streamSession.remove(streamInfo); |
|||
msg.setData("Timeout"); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
Device device = storager.queryVideoDevice(deviceId); |
|||
StreamInfo oldStreamInfo = streamSession.getPlayBackStreamInfo(channelId); |
|||
if (oldStreamInfo != null) { |
|||
// TODO 只能停止自己之前的回放,不能停止别人的回放,否则会导致别人无法播放
|
|||
cmder.stopStreamByeCmd(oldStreamInfo, null); |
|||
} |
|||
resultHolder.put(msg.getId(), result); |
|||
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { |
|||
logger.info("收到订阅消息: " + response.toJSONString()); |
|||
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, msg); |
|||
}, event -> { |
|||
StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId); |
|||
streamSession.remove(streamInfo); |
|||
Response response = event.getResponse(); |
|||
msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|||
resultHolder.invokeResult(msg); |
|||
}); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
@RequestMapping("/playback/{channelId}/{ssrc}/stop") |
|||
public ResponseEntity<String> playStop(@PathVariable String channelId, @PathVariable String ssrc) { |
|||
if (logger.isDebugEnabled()) { |
|||
logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); |
|||
} |
|||
if (ssrc == null) { |
|||
logger.warn("设备录像回放停止API调用失败!"); |
|||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); |
|||
} |
|||
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, ssrc); |
|||
if (streamInfo == null) { |
|||
logger.warn("回放播流不存在!"); |
|||
return new ResponseEntity<>(HttpStatus.NOT_FOUND); |
|||
} |
|||
cmder.stopStreamByeCmd(streamInfo, null); |
|||
JSONObject json = new JSONObject(); |
|||
json.put("ssrc", ssrc); |
|||
return new ResponseEntity<>(json.toString(), HttpStatus.OK); |
|||
} |
|||
} |
|||
|
Loading…
Reference in new issue