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; |
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.Logger; |
||||
import org.slf4j.LoggerFactory; |
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.boot.CommandLineRunner; |
||||
import org.springframework.core.annotation.Order; |
import org.springframework.core.annotation.Order; |
||||
import org.springframework.stereotype.Component; |
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.Set; |
||||
|
|
||||
|
|
||||
/** |
/** |
||||
* 对配置文件进行校验 |
* 对配置文件进行校验 |
||||
*/ |
*/ |
||||
@Component |
@Component |
||||
@Order(value=2) |
@Order(value = 0) |
||||
public class ApplicationCheckRunner implements CommandLineRunner { |
public class ApplicationCheckRunner implements CommandLineRunner { |
||||
|
|
||||
private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); |
private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); |
||||
|
@Autowired |
||||
@Value("${sip.ip}") |
SipConfig sipConfig; |
||||
private String sipIp; |
@Autowired |
||||
|
MediaConfig mediaConfig; |
||||
@Value("${media.ip}") |
@Autowired |
||||
private String mediaIp; |
JedisUtil jedisUtil; |
||||
|
|
||||
@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; |
|
||||
|
|
||||
|
|
||||
@Override |
@Override |
||||
public void run(String... args) throws Exception { |
public void run(String... args) { |
||||
if (sipIP.equals("localhost") || sipIP.equals("127.0.0.1")) { |
String sipIp = sipConfig.getSipIp(); |
||||
logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIP ); |
if (sipIp.equals("localhost") || sipIp.equals("127.0.0.1")) { |
||||
|
logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIp); |
||||
System.exit(1); |
System.exit(1); |
||||
} |
} |
||||
|
String mediaIp = mediaConfig.getMediaIp(); |
||||
if (mediaIp.equals("localhost") || mediaIp.equals("127.0.0.1")) { |
String[] mediaIpArr = mediaIp.split(","); |
||||
logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); |
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; |
||||
|
|
||||
|
} |
@ -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; |
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 javax.sip.ClientTransaction; |
||||
|
import java.util.List; |
||||
import org.springframework.stereotype.Component; |
import java.util.Map; |
||||
|
import java.util.Random; |
||||
|
import java.util.Set; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
|
||||
/** |
/** |
||||
* @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 |
* @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 |
||||
* @author: swwheihei |
* @author: swwheihei |
||||
* @date: 2020年5月13日 下午4:03:02 |
* @date: 2020年5月13日 下午4:03:02 |
||||
*/ |
*/ |
||||
|
@Slf4j |
||||
@Component |
@Component |
||||
public class VideoStreamSessionManager { |
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); |
||||
|
} |
||||
|
|
||||
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); |
public ClientTransaction getClientTransaction(String channelId, String streamId) { |
||||
private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>(); |
String streamKey = getStreamKey(channelId, streamId); |
||||
|
return sessionMap.get(streamKey); |
||||
|
} |
||||
|
|
||||
public String createPlaySsrc(){ |
public void remove(String channelId, String streamId) { |
||||
return SsrcUtil.getPlaySsrc(); |
StreamInfo streamInfo = this.getStreamInfo(channelId, streamId); |
||||
} |
if (null == streamId) { |
||||
|
return; |
||||
|
} |
||||
|
this.remove(streamInfo); |
||||
|
} |
||||
|
|
||||
public String createPlayBackSsrc(){ |
/** |
||||
return SsrcUtil.getPlayBackSsrc(); |
* 移除会话并释放ssrc,主要用完的ssrc一定要释放,否则会耗尽 |
||||
} |
*/ |
||||
|
public void remove(StreamInfo streamInfo) { |
||||
|
String streamKey = getStreamKey(streamInfo.getChannelId(), streamInfo.getStreamId()); |
||||
|
// 移除会话
|
||||
|
sessionMap.remove(streamKey); |
||||
|
|
||||
public void put(String streamId,String ssrc,ClientTransaction transaction){ |
String ssrc = streamInfo.getSsrc(); |
||||
sessionMap.put(streamId, transaction); |
String sn = ssrc.substring(6); |
||||
ssrcMap.put(streamId, ssrc); |
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); |
||||
|
|
||||
public ClientTransaction get(String streamId){ |
// 会话句柄和ZLM服务器的对应关系,从redis移除
|
||||
return sessionMap.get(streamId); |
if (ssrc.startsWith(PlayTypeEnum.PLAY.getValue())) { |
||||
} |
redisCatchStorage.stopPlay(streamInfo); |
||||
|
} else { |
||||
|
redisCatchStorage.stopPlayback(streamInfo); |
||||
|
} |
||||
|
} |
||||
|
|
||||
public void remove(String streamId) { |
private static String getStreamKey(String channelId, String streamId) { |
||||
sessionMap.remove(streamId); |
return channelId + "_" + streamId; |
||||
SsrcUtil.releaseSsrc(ssrcMap.get(streamId)); |
} |
||||
ssrcMap.remove(streamId); |
|
||||
} |
|
||||
} |
} |
||||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -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; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
@ -1,110 +1,101 @@ |
|||||
package com.genersoft.iot.vmp.vmanager.playback; |
package com.genersoft.iot.vmp.vmanager.playback; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
import com.genersoft.iot.vmp.common.StreamInfo; |
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.DeferredResultHolder; |
||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
||||
//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
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 com.genersoft.iot.vmp.vmanager.service.IPlayService; |
||||
import org.slf4j.Logger; |
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
import org.slf4j.LoggerFactory; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.http.HttpStatus; |
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.ResponseEntity; |
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.web.bind.annotation.CrossOrigin; |
import org.springframework.web.bind.annotation.*; |
||||
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.context.request.async.DeferredResult; |
import org.springframework.web.context.request.async.DeferredResult; |
||||
|
|
||||
import javax.sip.message.Response; |
import javax.sip.message.Response; |
||||
import java.util.UUID; |
|
||||
|
|
||||
@CrossOrigin |
@CrossOrigin |
||||
@RestController |
@RestController |
||||
@RequestMapping("/api") |
@RequestMapping("/api") |
||||
public class PlaybackController { |
public class PlaybackController { |
||||
|
|
||||
private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); |
private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); |
||||
|
|
||||
@Autowired |
@Autowired |
||||
private SIPCommander cmder; |
private SIPCommander cmder; |
||||
|
|
||||
@Autowired |
@Autowired |
||||
private IVideoManagerStorager storager; |
private IVideoManagerStorager storager; |
||||
|
|
||||
@Autowired |
@Autowired |
||||
private IRedisCatchStorage redisCatchStorage; |
private VideoStreamSessionManager streamSession; |
||||
|
|
||||
// @Autowired
|
@Autowired |
||||
// private ZLMRESTfulUtils zlmresTfulUtils;
|
private IPlayService playService; |
||||
|
|
||||
@Autowired |
@Autowired |
||||
private IPlayService playService; |
private DeferredResultHolder resultHolder; |
||||
|
|
||||
@Autowired |
@GetMapping("/playback/{deviceId}/{channelId}") |
||||
private DeferredResultHolder resultHolder; |
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, |
||||
|
String endTime) { |
||||
@GetMapping("/playback/{deviceId}/{channelId}") |
|
||||
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, |
if (logger.isDebugEnabled()) { |
||||
String endTime) { |
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); |
||||
|
} |
||||
if (logger.isDebugEnabled()) { |
RequestMessage msg = playService.createCallbackPlayMsg(); |
||||
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); |
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); |
||||
} |
// 超时处理
|
||||
UUID uuid = UUID.randomUUID(); |
result.onTimeout(() -> { |
||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); |
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
||||
// 超时处理
|
StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId); |
||||
result.onTimeout(()->{ |
streamSession.remove(streamInfo); |
||||
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
msg.setData("Timeout"); |
||||
RequestMessage msg = new RequestMessage(); |
resultHolder.invokeResult(msg); |
||||
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); |
}); |
||||
msg.setData("Timeout"); |
Device device = storager.queryVideoDevice(deviceId); |
||||
resultHolder.invokeResult(msg); |
StreamInfo oldStreamInfo = streamSession.getPlayBackStreamInfo(channelId); |
||||
}); |
if (oldStreamInfo != null) { |
||||
Device device = storager.queryVideoDevice(deviceId); |
// TODO 只能停止自己之前的回放,不能停止别人的回放,否则会导致别人无法播放
|
||||
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); |
cmder.stopStreamByeCmd(oldStreamInfo, null); |
||||
if (streamInfo != null) { |
} |
||||
// 停止之前的回放
|
resultHolder.put(msg.getId(), result); |
||||
cmder.streamByeCmd(streamInfo.getStreamId()); |
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { |
||||
} |
logger.info("收到订阅消息: " + response.toJSONString()); |
||||
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); |
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, msg); |
||||
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { |
}, event -> { |
||||
logger.info("收到订阅消息: " + response.toJSONString()); |
StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId); |
||||
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); |
streamSession.remove(streamInfo); |
||||
}, event -> { |
Response response = event.getResponse(); |
||||
Response response = event.getResponse(); |
msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
||||
RequestMessage msg = new RequestMessage(); |
resultHolder.invokeResult(msg); |
||||
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); |
}); |
||||
msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); |
|
||||
resultHolder.invokeResult(msg); |
return result; |
||||
}); |
} |
||||
|
|
||||
return result; |
@RequestMapping("/playback/{channelId}/{ssrc}/stop") |
||||
} |
public ResponseEntity<String> playStop(@PathVariable String channelId, @PathVariable String ssrc) { |
||||
|
if (logger.isDebugEnabled()) { |
||||
@RequestMapping("/playback/{ssrc}/stop") |
logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); |
||||
public ResponseEntity<String> playStop(@PathVariable String ssrc) { |
} |
||||
|
if (ssrc == null) { |
||||
cmder.streamByeCmd(ssrc); |
logger.warn("设备录像回放停止API调用失败!"); |
||||
|
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); |
||||
if (logger.isDebugEnabled()) { |
} |
||||
logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); |
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, ssrc); |
||||
} |
if (streamInfo == null) { |
||||
|
logger.warn("回放播流不存在!"); |
||||
if (ssrc != null) { |
return new ResponseEntity<>(HttpStatus.NOT_FOUND); |
||||
JSONObject json = new JSONObject(); |
} |
||||
json.put("ssrc", ssrc); |
cmder.stopStreamByeCmd(streamInfo, null); |
||||
return new ResponseEntity<String>(json.toString(), HttpStatus.OK); |
JSONObject json = new JSONObject(); |
||||
} else { |
json.put("ssrc", ssrc); |
||||
logger.warn("设备录像回放停止API调用失败!"); |
return new ResponseEntity<>(json.toString(), HttpStatus.OK); |
||||
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); |
} |
||||
} |
|
||||
} |
|
||||
} |
} |
||||
|
Loading…
Reference in new issue