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,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); |
|||
} |
|||
String mediaIp = mediaConfig.getMediaIp(); |
|||
String[] mediaIpArr = mediaIp.split(","); |
|||
mediaConfig.setMediaIpArr(mediaIpArr); |
|||
|
|||
if (mediaIp.equals("localhost") || mediaIp.equals("127.0.0.1")) { |
|||
logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); |
|||
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; |
|||
|
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 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 org.springframework.stereotype.Component; |
|||
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 ConcurrentHashMap<String, String> ssrcMap = 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); |
|||
} |
|||
} |
|||
|
|||
public String createPlaySsrc(){ |
|||
return SsrcUtil.getPlaySsrc(); |
|||
/** |
|||
* 存储会话 |
|||
* |
|||
* @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 String createPlayBackSsrc(){ |
|||
return SsrcUtil.getPlayBackSsrc(); |
|||
public ClientTransaction getClientTransaction(String channelId, String streamId) { |
|||
String streamKey = getStreamKey(channelId, streamId); |
|||
return sessionMap.get(streamKey); |
|||
} |
|||
|
|||
public void put(String streamId,String ssrc,ClientTransaction transaction){ |
|||
sessionMap.put(streamId, transaction); |
|||
ssrcMap.put(streamId, ssrc); |
|||
public void remove(String channelId, String streamId) { |
|||
StreamInfo streamInfo = this.getStreamInfo(channelId, streamId); |
|||
if (null == streamId) { |
|||
return; |
|||
} |
|||
this.remove(streamInfo); |
|||
} |
|||
|
|||
public ClientTransaction get(String streamId){ |
|||
return sessionMap.get(streamId); |
|||
/** |
|||
* 移除会话并释放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); |
|||
} |
|||
} |
|||
|
|||
public void remove(String streamId) { |
|||
sessionMap.remove(streamId); |
|||
SsrcUtil.releaseSsrc(ssrcMap.get(streamId)); |
|||
ssrcMap.remove(streamId); |
|||
private static String getStreamKey(String channelId, String streamId) { |
|||
return channelId + "_" + streamId; |
|||
} |
|||
} |
|||
|
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
|
|||
|
|||
} |
|||
Loading…
Reference in new issue