From 2d82d1b8eec67df9195b7ebc4b4318a7cdcc1f7f Mon Sep 17 00:00:00 2001 From: panlinlin <648540858@qq.com> Date: Wed, 3 Mar 2021 10:30:06 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dssrc=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../session/VideoStreamSessionManager.java | 17 ++++++++++------- .../gb28181/transmit/cmd/impl/SIPCommander.java | 4 ++-- web_src/.postcssrc.js | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java index c69faf96..65e1e5f6 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java @@ -15,6 +15,7 @@ import org.springframework.stereotype.Component; public class VideoStreamSessionManager { private ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); + private ConcurrentHashMap ssrcMap = new ConcurrentHashMap<>(); public String createPlaySsrc(){ return SsrcUtil.getPlaySsrc(); @@ -24,16 +25,18 @@ public class VideoStreamSessionManager { return SsrcUtil.getPlayBackSsrc(); } - public void put(String ssrc,ClientTransaction transaction){ - sessionMap.put(ssrc, transaction); + public void put(String streamId,String ssrc,ClientTransaction transaction){ + sessionMap.put(streamId, transaction); + ssrcMap.put(streamId, ssrc); } - public ClientTransaction get(String ssrc){ - return sessionMap.get(ssrc); + public ClientTransaction get(String streamId){ + return sessionMap.get(streamId); } - public void remove(String ssrc) { - sessionMap.remove(ssrc); - SsrcUtil.releaseSsrc(ssrc); + public void remove(String streamId) { + sessionMap.remove(streamId); + SsrcUtil.releaseSsrc(ssrcMap.get(streamId)); + ssrcMap.remove(streamId); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 6bab8090..6561763b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -388,7 +388,7 @@ public class SIPCommander implements ISIPCommander { Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc); ClientTransaction transaction = transmitRequest(device, request, errorEvent); - streamSession.put(streamId, transaction); + streamSession.put(streamId,ssrc, transaction); @@ -488,7 +488,7 @@ public class SIPCommander implements ISIPCommander { Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null); ClientTransaction transaction = transmitRequest(device, request, errorEvent); - streamSession.put(streamId, transaction); + streamSession.put(streamId, ssrc, transaction); } catch ( SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); diff --git a/web_src/.postcssrc.js b/web_src/.postcssrc.js index 406b4d24..f8737a12 100644 --- a/web_src/.postcssrc.js +++ b/web_src/.postcssrc.js @@ -7,7 +7,7 @@ module.exports = { // to edit target browsers: use "browserslist" field in package.json "autoprefixer": {}, 'postcss-pxtorem': { - rootValue: 24, + rootValue: 16, propList: ['font-size'] // 只转化font-size } } From 21f69db96f28b7830db470d3ae2f073ba674a8ba Mon Sep 17 00:00:00 2001 From: "wangshaopeng@sunnybs.com" Date: Thu, 25 Mar 2021 14:09:40 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix=20#61=201=E3=80=81=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=8E=A5=E5=85=A5=E5=A4=9A=E5=8F=B0ZLM?= =?UTF-8?q?=E3=80=822=E3=80=81=E6=92=AD=E6=B5=81=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=AD=98=E5=82=A8=E5=88=B0redis=EF=BC=8C?= =?UTF-8?q?=E9=98=B2=E6=AD=A2wvp=E5=AE=95=E6=9C=BA=E5=90=8E=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E6=95=B0=E6=8D=AE=E4=B8=A2=E5=A4=B1=E3=80=823?= =?UTF-8?q?=E3=80=81=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + README.md | 2 +- pom.xml | 90 +- .../genersoft/iot/vmp/VManageBootstrap.java | 24 +- .../genersoft/iot/vmp/common/StreamInfo.java | 119 +- .../iot/vmp/common/VideoManagerConstants.java | 62 +- .../iot/vmp/conf/ApplicationCheckRunner.java | 75 +- .../genersoft/iot/vmp/conf/MediaConfig.java | 53 + .../iot/vmp/conf/MediaServerConfig.java | 535 +--- .../genersoft/iot/vmp/conf/RedisConfig.java | 146 +- .../com/genersoft/iot/vmp/conf/SipConfig.java | 51 +- .../genersoft/iot/vmp/conf/SsrcConfig.java | 51 + .../genersoft/iot/vmp/gb28181/SipLayer.java | 497 ++-- .../iot/vmp/gb28181/bean/Device.java | 12 +- .../iot/vmp/gb28181/bean/DeviceChannel.java | 6 +- .../iot/vmp/gb28181/bean/RecordInfo.java | 108 +- .../gb28181/event/DeviceOffLineDetector.java | 48 +- .../iot/vmp/gb28181/session/PlayTypeEnum.java | 23 + .../iot/vmp/gb28181/session/SsrcUtil.java | 93 - .../session/VideoStreamSessionManager.java | 327 ++- .../gb28181/transmit/SIPProcessorFactory.java | 384 ++- .../callback/DeferredResultHolder.java | 119 +- .../gb28181/transmit/cmd/ISIPCommander.java | 569 ++-- .../cmd/SIPRequestHeaderProvider.java | 424 ++- .../transmit/cmd/impl/SIPCommander.java | 2461 ++++++++--------- .../request/impl/MessageRequestProcessor.java | 1646 ++++++----- .../impl/InviteResponseProcessor.java | 130 +- .../vmp/media/zlm/ZLMHTTPProxyController.java | 43 +- .../vmp/media/zlm/ZLMHttpHookListener.java | 689 +++-- .../iot/vmp/media/zlm/ZLMRESTfulUtils.java | 138 +- .../vmp/media/zlm/ZLMRTPServerFactory.java | 51 +- .../iot/vmp/media/zlm/ZLMRunner.java | 132 +- .../iot/vmp/storager/IRedisCatchStorage.java | 44 +- .../vmp/storager/IVideoManagerStorager.java | 372 +-- .../vmp/storager/dao/DeviceChannelMapper.java | 2 +- .../storager/impl/RedisCatchStorageImpl.java | 214 +- .../impl/VideoManagerStoragerImpl.java | 462 ++-- .../genersoft/iot/vmp/utils/ConfigConst.java | 8 + .../iot/vmp/utils/redis/JedisUtil.java | 97 + .../iot/vmp/utils/redis/RedisUtil.java | 1502 +++++----- .../iot/vmp/vmanager/device/DeviceConfig.java | 120 - .../device/DeviceConfigController.java | 119 + .../vmp/vmanager/device/DeviceControl.java | 238 -- .../device/DeviceControlController.java | 237 ++ .../vmp/vmanager/device/DeviceController.java | 261 ++ .../iot/vmp/vmanager/device/DeviceQuery.java | 255 -- .../iot/vmp/vmanager/play/PlayController.java | 431 ++- .../vmanager/playback/PlaybackController.java | 169 +- .../vmp/vmanager/service/IPlayService.java | 8 +- .../service/impl/PlayServiceImpl.java | 65 +- .../iot/vmp/web/ApiStreamController.java | 126 +- src/main/resources/application-dev.yml | 9 +- web_src/src/components/channelList.vue | 2 +- .../src/components/gb28181/devicePlayer.vue | 10 +- 54 files changed, 6844 insertions(+), 7017 deletions(-) create mode 100644 src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java create mode 100644 src/main/java/com/genersoft/iot/vmp/conf/SsrcConfig.java create mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/session/PlayTypeEnum.java delete mode 100644 src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java create mode 100644 src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java create mode 100644 src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java delete mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java create mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfigController.java delete mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java create mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControlController.java create mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java delete mode 100644 src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java diff --git a/.gitignore b/.gitignore index 54054a0f..d1cb0635 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ # Mobile Tools for Java (J2ME) .mtj.tmp/ src/main/resources/application-*.yml +src/main/resources/static/ +web_src/package-lock.json # Package Files # #*.jar *.war diff --git a/README.md b/README.md index 03ee2764..06bf4abc 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ https://gitee.com/18010473990/wvp-GB28181.git 8. 支持udp/tcp国标流传输模式; 9. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址 10. 支持国标网络校时 -11. 支持公网部署, 支持wvp与zlm分开部署 +11. 支持公网部署, 支持wvp与zlm分开部署,支持配置多台zlm 12. 支持播放h265, g.711格式的流 13. 支持固定流地址和自动点播,同时支持未点播时直接播放流地址,代码自动发起点播. ( [查看WIKI](https://github.com/648540858/wvp-GB28181-pro/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E5%9B%BA%E5%AE%9A%E6%92%AD%E6%94%BE%E5%9C%B0%E5%9D%80%E4%B8%8E%E8%87%AA%E5%8A%A8%E7%82%B9%E6%92%AD)) 14. 报警信息处理,支持向前端推送报警信息 diff --git a/pom.xml b/pom.xml index c2f5d82f..53f3198b 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,34 @@ wvp web video platform + + UTF-8 + + 2.1.4 + 1.2.3 + 8.0.22 + 3.32.3.2 + 3.1.0 + 1.2.10 + 2.9.2 + 2.6.1 + 1.3.0-91 + 1.2.17 + 2.1.3 + 1.2.73 + 30.0-jre + 1.18.12 + 3.7 + 2.6 + 4.9.0 + + ${project.build.directory}/generated-snippets + ${project.basedir}/docs/asciidoc + ${project.build.directory}/asciidoc + ${project.build.directory}/asciidoc/html + ${project.build.directory}/asciidoc/pdf + + nexus-aliyun @@ -41,17 +69,6 @@ - - UTF-8 - - - 5.2.0 - ${project.build.directory}/generated-snippets - ${project.basedir}/docs/asciidoc - ${project.build.directory}/asciidoc - ${project.build.directory}/asciidoc/html - ${project.build.directory}/asciidoc/pdf - @@ -65,54 +82,54 @@ org.mybatis.spring.boot mybatis-spring-boot-starter - 2.1.4 + ${mybatis-spring-boot-starter-version} com.alibaba druid - 1.2.3 + ${druid-version} mysql mysql-connector-java - 8.0.22 + ${mysql-connector-java-version} org.xerial sqlite-jdbc - 3.32.3.2 + ${sqlite-jdbc-version} com.github.pagehelper pagehelper-spring-boot-starter - 1.2.10 + ${pagehelper-spring-boot-starter-version} - - - - - + + redis.clients + jedis + ${jedis-version} + io.springfox springfox-swagger2 - 2.9.2 + ${springfox-swagger2-version} io.springfox springfox-swagger-ui - 2.6.1 + ${springfox-swagger-ui-version} javax.validation @@ -129,41 +146,56 @@ javax.sip jain-sip-ri - 1.3.0-91 + ${jain-sip-ri-version} log4j log4j - 1.2.17 + ${log4j-version} org.dom4j dom4j - 2.1.3 + ${dom4j-version} com.alibaba fastjson - 1.2.73 + ${fastjson-version} com.google.guava guava - 30.0-jre + ${guava-version} + + + org.projectlombok + lombok + ${lombok-version} + + + org.apache.commons + commons-lang3 + ${commons-lang3-version} + + + commons-io + commons-io + ${commons-io-version} com.squareup.okhttp3 okhttp - 4.9.0 + ${okhttp-version} diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java index 4a92e093..5e895e08 100644 --- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java +++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java @@ -1,13 +1,11 @@ -package com.genersoft.iot.vmp; - -import java.util.logging.LogManager; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class VManageBootstrap extends LogManager { - public static void main(String[] args) { - SpringApplication.run(VManageBootstrap.class, args); - } -} +package com.genersoft.iot.vmp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class VManageBootstrap { + public static void main(String[] args) { + SpringApplication.run(VManageBootstrap.class, args); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java index 0082a27d..272afba7 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -1,9 +1,16 @@ package com.genersoft.iot.vmp.common; import com.alibaba.fastjson.JSONArray; +import lombok.Data; +@Data public class StreamInfo { + /** + * zlm流媒体服务器IP + */ + private String mediaServerIp; + private String ssrc; private String streamId; private String deviceID; private String channelId; @@ -18,116 +25,4 @@ public class StreamInfo { private String rtmp; private String rtsp; private JSONArray tracks; - - public String getDeviceID() { - return deviceID; - } - - public void setDeviceID(String deviceID) { - this.deviceID = deviceID; - } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public String getFlv() { - return flv; - } - - public void setFlv(String flv) { - this.flv = flv; - } - - public String getWs_flv() { - return ws_flv; - } - - public void setWs_flv(String ws_flv) { - this.ws_flv = ws_flv; - } - - public String getRtmp() { - return rtmp; - } - - public void setRtmp(String rtmp) { - this.rtmp = rtmp; - } - - public String getHls() { - return hls; - } - - public void setHls(String hls) { - this.hls = hls; - } - - public String getRtsp() { - return rtsp; - } - - public void setRtsp(String rtsp) { - this.rtsp = rtsp; - } - - public JSONArray getTracks() { - return tracks; - } - - public void setTracks(JSONArray tracks) { - this.tracks = tracks; - } - - public String getFmp4() { - return fmp4; - } - - public void setFmp4(String fmp4) { - this.fmp4 = fmp4; - } - - public String getWs_fmp4() { - return ws_fmp4; - } - - public void setWs_fmp4(String ws_fmp4) { - this.ws_fmp4 = ws_fmp4; - } - - public String getWs_hls() { - return ws_hls; - } - - public void setWs_hls(String ws_hls) { - this.ws_hls = ws_hls; - } - - public String getTs() { - return ts; - } - - public void setTs(String ts) { - this.ts = ts; - } - - public String getWs_ts() { - return ws_ts; - } - - public void setWs_ts(String ws_ts) { - this.ws_ts = ws_ts; - } - - public String getStreamId() { - return streamId; - } - - public void setStreamId(String streamId) { - this.streamId = streamId; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java index 846d3d01..938f674a 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -1,30 +1,32 @@ -package com.genersoft.iot.vmp.common; - -/** - * @Description: 定义常量 - * @author: swwheihei - * @date: 2019年5月30日 下午3:04:04 - * - */ -public class VideoManagerConstants { - - public static final String MEDIA_SERVER_PREFIX = "VMP_media_server"; - - public static final String DEVICE_PREFIX = "VMP_device_"; - - public static final String CACHEKEY_PREFIX = "VMP_channel_"; - - public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_"; - - public static final String PLAYER_PREFIX = "VMP_player_"; - - public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; - - public static final String EVENT_ONLINE_REGISTER = "1"; - - public static final String EVENT_ONLINE_KEEPLIVE = "2"; - - public static final String EVENT_OUTLINE_UNREGISTER = "1"; - - public static final String EVENT_OUTLINE_TIMEOUT = "2"; -} +package com.genersoft.iot.vmp.common; + +/** + * @Description: 定义常量 + * @author: swwheihei + * @date: 2019年5月30日 下午3:04:04 + * + */ +public class VideoManagerConstants { + + public static final String MEDIA_SERVER_PREFIX = "VMP_media_server"; + + public static final String DEVICE_PREFIX = "VMP_device_"; + + public static final String CACHEKEY_PREFIX = "VMP_channel_"; + + public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_"; + + public static final String PLAYER_PREFIX = "VMP_player_"; + + public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; + + public static final String EVENT_ONLINE_REGISTER = "1"; + + public static final String EVENT_ONLINE_KEEPLIVE = "2"; + + public static final String EVENT_OUTLINE_UNREGISTER = "1"; + + public static final String EVENT_OUTLINE_TIMEOUT = "2"; + + public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_"; +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java index faa9ef12..f03d49ad 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java @@ -1,63 +1,58 @@ package com.genersoft.iot.vmp.conf; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.utils.redis.JedisUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; +import java.util.HashMap; +import java.util.Set; + /** - * 对配置文件进行校验 + * 对配置文件进行校验 */ @Component -@Order(value=2) +@Order(value = 0) public class ApplicationCheckRunner implements CommandLineRunner { private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); - - @Value("${sip.ip}") - private String sipIp; - - @Value("${media.ip}") - private String mediaIp; - - @Value("${media.wanIp}") - private String mediaWanIp; - - @Value("${media.hookIp}") - private String mediaHookIp; - - @Value("${media.port}") - private int mediaPort; - - @Value("${media.secret}") - private String mediaSecret; - - @Value("${media.streamNoneReaderDelayMS}") - private String streamNoneReaderDelayMS; - - @Value("${sip.ip}") - private String sipIP; - - @Value("${server.port}") - private String serverPort; - - @Value("${media.autoConfig}") - private boolean autoConfig; - + @Autowired + SipConfig sipConfig; + @Autowired + MediaConfig mediaConfig; + @Autowired + JedisUtil jedisUtil; @Override - public void run(String... args) throws Exception { - if (sipIP.equals("localhost") || sipIP.equals("127.0.0.1")) { - logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIP ); + public void run(String... args) { + String sipIp = sipConfig.getSipIp(); + if (sipIp.equals("localhost") || sipIp.equals("127.0.0.1")) { + logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIp); System.exit(1); } - - if (mediaIp.equals("localhost") || mediaIp.equals("127.0.0.1")) { - logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); + String mediaIp = mediaConfig.getMediaIp(); + String[] mediaIpArr = mediaIp.split(","); + mediaConfig.setMediaIpArr(mediaIpArr); + + for (String mId : mediaIpArr) { + if (mId.equals("localhost") || mId.equals("127.0.0.1")) { + logger.warn("mediaIp.ip使用localhost或127.0.0.1,将无法收到网络内其他设备的推流!!!"); + } } + HashMap mediaServerSsrcMap = new HashMap<>(mediaIpArr.length); + for (int i = 0; i < mediaIpArr.length; i++) { + String mIp = mediaIpArr[i]; + SsrcConfig ssrcConfig = new SsrcConfig(); + Set usedSet = jedisUtil.smembers(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mIp); + ssrcConfig.init(mIp, usedSet); + mediaServerSsrcMap.put(mIp, ssrcConfig); + } + mediaConfig.setMediaServerSsrcMap(mediaServerSsrcMap); } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java new file mode 100644 index 00000000..73cad4c6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java @@ -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 mediaServerSsrcMap; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java index 76390e43..16629393 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java @@ -1,7 +1,9 @@ package com.genersoft.iot.vmp.conf; import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; +@Data public class MediaServerConfig { @JSONField(name = "api.apiDebug") @@ -31,10 +33,6 @@ public class MediaServerConfig { @JSONField(name = "general.streamNoneReaderDelayMS") private String generalStreamNoneReaderDelayMS; - private String localIP; - - private String wanIp; - @JSONField(name = "hls.fileBufSize") private String hlsFileBufSize; @@ -199,533 +197,4 @@ public class MediaServerConfig { @JSONField(name = "shell.shell") private String shellPhell; - - - public String getApiDebug() { - return apiDebug; - } - - public void setApiDebug(String apiDebug) { - this.apiDebug = apiDebug; - } - - public String getApiSecret() { - return apiSecret; - } - - public void setApiSecret(String apiSecret) { - this.apiSecret = apiSecret; - } - - public String getFfmpegBin() { - return ffmpegBin; - } - - public void setFfmpegBin(String ffmpegBin) { - this.ffmpegBin = ffmpegBin; - } - - public String getFfmpegCmd() { - return ffmpegCmd; - } - - public void setFfmpegCmd(String ffmpegCmd) { - this.ffmpegCmd = ffmpegCmd; - } - - public String getFfmpegLog() { - return ffmpegLog; - } - - public void setFfmpegLog(String ffmpegLog) { - this.ffmpegLog = ffmpegLog; - } - - public String getGeneralEnableVhost() { - return generalEnableVhost; - } - - public void setGeneralEnableVhost(String generalEnableVhost) { - this.generalEnableVhost = generalEnableVhost; - } - - public String getGeneralFlowThreshold() { - return generalFlowThreshold; - } - - public void setGeneralFlowThreshold(String generalFlowThreshold) { - this.generalFlowThreshold = generalFlowThreshold; - } - - public String getGeneralMaxStreamWaitMS() { - return generalMaxStreamWaitMS; - } - - public void setGeneralMaxStreamWaitMS(String generalMaxStreamWaitMS) { - this.generalMaxStreamWaitMS = generalMaxStreamWaitMS; - } - - public String getGeneralStreamNoneReaderDelayMS() { - return generalStreamNoneReaderDelayMS; - } - - public void setGeneralStreamNoneReaderDelayMS(String generalStreamNoneReaderDelayMS) { - this.generalStreamNoneReaderDelayMS = generalStreamNoneReaderDelayMS; - } - - public String getLocalIP() { - return localIP; - } - - public void setLocalIP(String localIP) { - this.localIP = localIP; - } - - public String getHlsFileBufSize() { - return hlsFileBufSize; - } - - public void setHlsFileBufSize(String hlsFileBufSize) { - this.hlsFileBufSize = hlsFileBufSize; - } - - public String getHlsFilePath() { - return hlsFilePath; - } - - public void setHlsFilePath(String hlsFilePath) { - this.hlsFilePath = hlsFilePath; - } - - public String getHlsSegDur() { - return hlsSegDur; - } - - public void setHlsSegDur(String hlsSegDur) { - this.hlsSegDur = hlsSegDur; - } - - public String getHlsSegNum() { - return hlsSegNum; - } - - public void setHlsSegNum(String hlsSegNum) { - this.hlsSegNum = hlsSegNum; - } - - public String getHookAccessFileExceptHLS() { - return hookAccessFileExceptHLS; - } - - public void setHookAccessFileExceptHLS(String hookAccessFileExceptHLS) { - this.hookAccessFileExceptHLS = hookAccessFileExceptHLS; - } - - public String getHookAdminParams() { - return hookAdminParams; - } - - public void setHookAdminParams(String hookAdminParams) { - this.hookAdminParams = hookAdminParams; - } - - public String getHookEnable() { - return hookEnable; - } - - public void setHookEnable(String hookEnable) { - this.hookEnable = hookEnable; - } - - public String getHookOnFlowReport() { - return hookOnFlowReport; - } - - public void setHookOnFlowReport(String hookOnFlowReport) { - this.hookOnFlowReport = hookOnFlowReport; - } - - public String getHookOnHttpAccess() { - return hookOnHttpAccess; - } - - public void setHookOnHttpAccess(String hookOnHttpAccess) { - this.hookOnHttpAccess = hookOnHttpAccess; - } - - public String getHookOnPlay() { - return hookOnPlay; - } - - public void setHookOnPlay(String hookOnPlay) { - this.hookOnPlay = hookOnPlay; - } - - public String getHookOnPublish() { - return hookOnPublish; - } - - public void setHookOnPublish(String hookOnPublish) { - this.hookOnPublish = hookOnPublish; - } - - public String getHookOnRecordMp4() { - return hookOnRecordMp4; - } - - public void setHookOnRecordMp4(String hookOnRecordMp4) { - this.hookOnRecordMp4 = hookOnRecordMp4; - } - - public String getHookOnRtspAuth() { - return hookOnRtspAuth; - } - - public void setHookOnRtspAuth(String hookOnRtspAuth) { - this.hookOnRtspAuth = hookOnRtspAuth; - } - - public String getHookOnRtspRealm() { - return hookOnRtspRealm; - } - - public void setHookOnRtspRealm(String hookOnRtspRealm) { - this.hookOnRtspRealm = hookOnRtspRealm; - } - - public String getHookOnShellLogin() { - return hookOnShellLogin; - } - - public void setHookOnShellLogin(String hookOnShellLogin) { - this.hookOnShellLogin = hookOnShellLogin; - } - - public String getHookOnStreamChanged() { - return hookOnStreamChanged; - } - - public void setHookOnStreamChanged(String hookOnStreamChanged) { - this.hookOnStreamChanged = hookOnStreamChanged; - } - - public String getHookOnStreamNoneReader() { - return hookOnStreamNoneReader; - } - - public void setHookOnStreamNoneReader(String hookOnStreamNoneReader) { - this.hookOnStreamNoneReader = hookOnStreamNoneReader; - } - - public String getHookOnStreamNotFound() { - return hookOnStreamNotFound; - } - - public void setHookOnStreamNotFound(String hookOnStreamNotFound) { - this.hookOnStreamNotFound = hookOnStreamNotFound; - } - - public String getHookTimeoutSec() { - return hookTimeoutSec; - } - - public void setHookTimeoutSec(String hookTimeoutSec) { - this.hookTimeoutSec = hookTimeoutSec; - } - - public String getHttpCharSet() { - return httpCharSet; - } - - public void setHttpCharSet(String httpCharSet) { - this.httpCharSet = httpCharSet; - } - - public String getHttpKeepAliveSecond() { - return httpKeepAliveSecond; - } - - public void setHttpKeepAliveSecond(String httpKeepAliveSecond) { - this.httpKeepAliveSecond = httpKeepAliveSecond; - } - - public String getHttpMaxReqCount() { - return httpMaxReqCount; - } - - public void setHttpMaxReqCount(String httpMaxReqCount) { - this.httpMaxReqCount = httpMaxReqCount; - } - - public String getHttpMaxReqSize() { - return httpMaxReqSize; - } - - public void setHttpMaxReqSize(String httpMaxReqSize) { - this.httpMaxReqSize = httpMaxReqSize; - } - - public String getHttpNotFound() { - return httpNotFound; - } - - public void setHttpNotFound(String httpNotFound) { - this.httpNotFound = httpNotFound; - } - - public String getHttpPort() { - return httpPort; - } - - public void setHttpPort(String httpPort) { - this.httpPort = httpPort; - } - - public String getHttpRootPath() { - return httpRootPath; - } - - public void setHttpRootPath(String httpRootPath) { - this.httpRootPath = httpRootPath; - } - - public String getHttpSendBufSize() { - return httpSendBufSize; - } - - public void setHttpSendBufSize(String httpSendBufSize) { - this.httpSendBufSize = httpSendBufSize; - } - - public String getHttpSSLport() { - return httpSSLport; - } - - public void setHttpSSLport(String httpSSLport) { - this.httpSSLport = httpSSLport; - } - - public String getMulticastAddrMax() { - return multicastAddrMax; - } - - public void setMulticastAddrMax(String multicastAddrMax) { - this.multicastAddrMax = multicastAddrMax; - } - - public String getMulticastAddrMin() { - return multicastAddrMin; - } - - public void setMulticastAddrMin(String multicastAddrMin) { - this.multicastAddrMin = multicastAddrMin; - } - - public String getMulticastUdpTTL() { - return multicastUdpTTL; - } - - public void setMulticastUdpTTL(String multicastUdpTTL) { - this.multicastUdpTTL = multicastUdpTTL; - } - - public String getRecordAppName() { - return recordAppName; - } - - public void setRecordAppName(String recordAppName) { - this.recordAppName = recordAppName; - } - - public String getRecordFilePath() { - return recordFilePath; - } - - public void setRecordFilePath(String recordFilePath) { - this.recordFilePath = recordFilePath; - } - - public String getRecordFileSecond() { - return recordFileSecond; - } - - public void setRecordFileSecond(String recordFileSecond) { - this.recordFileSecond = recordFileSecond; - } - - public String getRecordFileSampleMS() { - return recordFileSampleMS; - } - - public void setRecordFileSampleMS(String recordFileSampleMS) { - this.recordFileSampleMS = recordFileSampleMS; - } - - public String getRtmpHandshakeSecond() { - return rtmpHandshakeSecond; - } - - public void setRtmpHandshakeSecond(String rtmpHandshakeSecond) { - this.rtmpHandshakeSecond = rtmpHandshakeSecond; - } - - public String getRtmpKeepAliveSecond() { - return rtmpKeepAliveSecond; - } - - public void setRtmpKeepAliveSecond(String rtmpKeepAliveSecond) { - this.rtmpKeepAliveSecond = rtmpKeepAliveSecond; - } - - public String getRtmpModifyStamp() { - return rtmpModifyStamp; - } - - public void setRtmpModifyStamp(String rtmpModifyStamp) { - this.rtmpModifyStamp = rtmpModifyStamp; - } - - public String getRtmpPort() { - return rtmpPort; - } - - public void setRtmpPort(String rtmpPort) { - this.rtmpPort = rtmpPort; - } - - public String getRtpAudioMtuSize() { - return rtpAudioMtuSize; - } - - public void setRtpAudioMtuSize(String rtpAudioMtuSize) { - this.rtpAudioMtuSize = rtpAudioMtuSize; - } - - public String getRtpClearCount() { - return rtpClearCount; - } - - public void setRtpClearCount(String rtpClearCount) { - this.rtpClearCount = rtpClearCount; - } - - public String getRtpCycleMS() { - return rtpCycleMS; - } - - public void setRtpCycleMS(String rtpCycleMS) { - this.rtpCycleMS = rtpCycleMS; - } - - public String getRtpMaxRtpCount() { - return rtpMaxRtpCount; - } - - public void setRtpMaxRtpCount(String rtpMaxRtpCount) { - this.rtpMaxRtpCount = rtpMaxRtpCount; - } - - public String getRtpVideoMtuSize() { - return rtpVideoMtuSize; - } - - public void setRtpVideoMtuSize(String rtpVideoMtuSize) { - this.rtpVideoMtuSize = rtpVideoMtuSize; - } - - public String getRtpProxyCheckSource() { - return rtpProxyCheckSource; - } - - public void setRtpProxyCheckSource(String rtpProxyCheckSource) { - this.rtpProxyCheckSource = rtpProxyCheckSource; - } - - public String getRtpProxyDumpDir() { - return rtpProxyDumpDir; - } - - public void setRtpProxyDumpDir(String rtpProxyDumpDir) { - this.rtpProxyDumpDir = rtpProxyDumpDir; - } - - public String getRtpProxyPort() { - return rtpProxyPort; - } - - public void setRtpProxyPort(String rtpProxyPort) { - this.rtpProxyPort = rtpProxyPort; - } - - public String getRtpProxyTimeoutSec() { - return rtpProxyTimeoutSec; - } - - public void setRtpProxyTimeoutSec(String rtpProxyTimeoutSec) { - this.rtpProxyTimeoutSec = rtpProxyTimeoutSec; - } - - public String getRtspAuthBasic() { - return rtspAuthBasic; - } - - public void setRtspAuthBasic(String rtspAuthBasic) { - this.rtspAuthBasic = rtspAuthBasic; - } - - public String getRtspHandshakeSecond() { - return rtspHandshakeSecond; - } - - public void setRtspHandshakeSecond(String rtspHandshakeSecond) { - this.rtspHandshakeSecond = rtspHandshakeSecond; - } - - public String getRtspKeepAliveSecond() { - return rtspKeepAliveSecond; - } - - public void setRtspKeepAliveSecond(String rtspKeepAliveSecond) { - this.rtspKeepAliveSecond = rtspKeepAliveSecond; - } - - public String getRtspPort() { - return rtspPort; - } - - public void setRtspPort(String rtspPort) { - this.rtspPort = rtspPort; - } - - public String getRtspSSlport() { - return rtspSSlport; - } - - public void setRtspSSlport(String rtspSSlport) { - this.rtspSSlport = rtspSSlport; - } - - public String getShellMaxReqSize() { - return shellMaxReqSize; - } - - public void setShellMaxReqSize(String shellMaxReqSize) { - this.shellMaxReqSize = shellMaxReqSize; - } - - public String getShellPhell() { - return shellPhell; - } - - public void setShellPhell(String shellPhell) { - this.shellPhell = shellPhell; - } - - public String getWanIp() { - return wanIp; - } - - public void setWanIp(String wanIp) { - this.wanIp = wanIp; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java index 47d2a02b..64e7c287 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java @@ -1,57 +1,89 @@ -package com.genersoft.iot.vmp.conf; - -import org.springframework.cache.annotation.CachingConfigurerSupport; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -import com.alibaba.fastjson.parser.ParserConfig; -import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; - -/** - * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 - * @author: swwheihei - * @date: 2019年5月30日 上午10:58:25 - * - */ -@Configuration -public class RedisConfig extends CachingConfigurerSupport { - - @Bean("redisTemplate") - public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(redisConnectionFactory); - // 使用fastjson进行序列化处理,提高解析效率 - FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); - // value值的序列化采用fastJsonRedisSerializer - template.setValueSerializer(serializer); - template.setHashValueSerializer(serializer); - // key的序列化采用StringRedisSerializer - template.setKeySerializer(new StringRedisSerializer()); - template.setHashKeySerializer(new StringRedisSerializer()); - template.setConnectionFactory(redisConnectionFactory); - // 使用fastjson时需设置此项,否则会报异常not support type - ParserConfig.getGlobalInstance().setAutoTypeSupport(true); - return template; - } - - /** - * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 - * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 - * - * @param connectionFactory - * @param listenerAdapter - * @return - */ - @Bean - RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { - - RedisMessageListenerContainer container = new RedisMessageListenerContainer(); - container.setConnectionFactory(connectionFactory); - return container; - } - -} +package com.genersoft.iot.vmp.conf; + +import com.alibaba.fastjson.parser.ParserConfig; +import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +/** + * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 + * @author: swwheihei + * @date: 2019年5月30日 上午10:58:25 + */ +@Configuration +public class RedisConfig extends CachingConfigurerSupport { + + @Value("${spring.redis.host}") + private String host; + @Value("${spring.redis.port}") + private int port; + @Value("${spring.redis.database}") + private int database; + @Value("${spring.redis.password}") + private String password; + @Value("${spring.redis.timeout}") + private int timeout; + @Value("${spring.redis.poolMaxTotal}") + private int poolMaxTotal; + @Value("${spring.redis.poolMaxIdle}") + private int poolMaxIdle; + @Value("${spring.redis.poolMaxWait}") + private int poolMaxWait; + + @Bean + public JedisPool jedisPool() { + if (StringUtils.isBlank(password)) { + password = null; + } + JedisPoolConfig poolConfig = new JedisPoolConfig(); + poolConfig.setMaxIdle(poolMaxIdle); + poolConfig.setMaxTotal(poolMaxTotal); + // 秒转毫秒 + poolConfig.setMaxWaitMillis(poolMaxWait * 1000); + JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database); + return jp; + } + + @Bean("redisTemplate") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory); + // 使用fastjson进行序列化处理,提高解析效率 + FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); + // value值的序列化采用fastJsonRedisSerializer + template.setValueSerializer(serializer); + template.setHashValueSerializer(serializer); + // key的序列化采用StringRedisSerializer + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setConnectionFactory(redisConnectionFactory); + // 使用fastjson时需设置此项,否则会报异常not support type + ParserConfig.getGlobalInstance().setAutoTypeSupport(true); + return template; + } + + /** + * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 + * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 + * + * @param connectionFactory + * @return + */ + @Bean + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { + + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + return container; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java index 52d80890..0e92cc78 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java @@ -1,10 +1,12 @@ package com.genersoft.iot.vmp.conf; +import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; @Configuration("sipConfig") +@Data public class SipConfig { @Value("${sip.ip}") @@ -17,55 +19,6 @@ public class SipConfig { String sipId; @Value("${sip.password}") String sipPassword; - @Value("${sip.ptz.speed:50}") Integer speed; - - public String getSipIp() { - return sipIp; - } - - public void setSipIp(String sipIp) { - this.sipIp = sipIp; - } - - public Integer getSipPort() { - return sipPort; - } - - public void setSipPort(Integer sipPort) { - this.sipPort = sipPort; - } - - public String getSipDomain() { - return sipDomain; - } - - public void setSipDomain(String sipDomain) { - this.sipDomain = sipDomain; - } - - public String getSipId() { - return sipId; - } - - public void setSipId(String sipId) { - this.sipId = sipId; - } - - public String getSipPassword() { - return sipPassword; - } - - public void setSipPassword(String sipPassword) { - this.sipPassword = sipPassword; - } - - public Integer getSpeed() { - return speed; - } - - public void setSpeed(Integer speed) { - this.speed = speed; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SsrcConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SsrcConfig.java new file mode 100644 index 00000000..55979592 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/SsrcConfig.java @@ -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 isUsed; + /** + * zlm流媒体服务器可用会话句柄 + */ + private List notUsed; + + public void init(String mediaServerIp, Set 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); + } + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java index c5dc1e5c..a18bf45f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -1,235 +1,262 @@ -package com.genersoft.iot.vmp.gb28181; - -import java.text.ParseException; -import java.util.Properties; -import java.util.TooManyListenersException; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import javax.sip.*; -import javax.sip.header.CallIdHeader; -import javax.sip.message.Response; - -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; - -import gov.nist.javax.sip.SipStackImpl; - -@Component -public class SipLayer implements SipListener { - - private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); - - @Autowired - private SipConfig sipConfig; - - @Autowired - private SIPProcessorFactory processorFactory; - - @Autowired - private SipSubscribe sipSubscribe; - - private SipStack sipStack; - - private SipFactory sipFactory; - - /** - * 消息处理器线程池 - */ - private ThreadPoolExecutor processThreadPool; - - @Bean("initSipServer") - private ThreadPoolExecutor initSipServer() { - - int processThreadNum = Runtime.getRuntime().availableProcessors() * 10; - LinkedBlockingQueue processQueue = new LinkedBlockingQueue(10000); - processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum, - 0L,TimeUnit.MILLISECONDS,processQueue, - new ThreadPoolExecutor.CallerRunsPolicy()); - return processThreadPool; - } - - @Bean("sipFactory") - @DependsOn("initSipServer") - private SipFactory createSipFactory() { - sipFactory = SipFactory.getInstance(); - sipFactory.setPathName("gov.nist"); - return sipFactory; - } - - @Bean("sipStack") - @DependsOn({"initSipServer", "sipFactory"}) - private SipStack createSipStack() throws PeerUnavailableException { - Properties properties = new Properties(); - properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); - properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getSipIp()); - properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false"); - /** - * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE = - * 0; public static final int TRACE_MESSAGES = 16; public static final int - * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32; - */ - properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0"); - properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log"); - properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log"); - sipStack = (SipStackImpl) sipFactory.createSipStack(properties); - return sipStack; - } - - @Bean("tcpSipProvider") - @DependsOn("sipStack") - private SipProvider startTcpListener() { - ListeningPoint tcpListeningPoint = null; - SipProvider tcpSipProvider = null; - try { - tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP"); - tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); - tcpSipProvider.addSipListener(this); - logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); - } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) { - logger.error(String.format("创建SIP服务失败: %s", e.getMessage())); - } - return tcpSipProvider; - } - - @Bean("udpSipProvider") - @DependsOn("sipStack") - private SipProvider startUdpListener() throws Exception { - ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP"); - SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); - udpSipProvider.addSipListener(this); - logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); - return udpSipProvider; - } - - /** - * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a - * new request arrives. - */ - @Override - public void processRequest(RequestEvent evt) { - logger.debug(evt.getRequest().toString()); - // 由于jainsip是单线程程序,为提高性能并发处理 - processThreadPool.execute(() -> { - if (processorFactory != null) { - processorFactory.createRequestProcessor(evt).process(); - } - }); - } - - @Override - public void processResponse(ResponseEvent evt) { - Response response = evt.getResponse(); - logger.debug(evt.getResponse().toString()); - int status = response.getStatusCode(); - if ((status >= 200) && (status < 300)) { // Success! - ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); - try { - processor.process(evt, this, sipConfig); - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { - CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); - if (callIdHeader != null) { - SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); - if (subscribe != null) { - subscribe.response(evt); - } - } - } - } else if ((status >= 100) && (status < 200)) { - // 增加其它无需回复的响应,如101、180等 - } else { - logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); - if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { - CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); - if (callIdHeader != null) { - SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); - if (subscribe != null) { - subscribe.response(evt); - } - } - } - } - - - - } - - /** - *

- * Title: processTimeout - *

- *

- * Description: - *

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

- * Title: processIOException - *

- *

- * Description: - *

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

- * Title: processTransactionTerminated - *

- *

- * Description: - *

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

- * Title: processDialogTerminated - *

- *

- * Description: - *

- * - * @param dialogTerminatedEvent - */ - @Override - public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { - // TODO Auto-generated method stub - - } - -} +package com.genersoft.iot.vmp.gb28181; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory; +import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; +import gov.nist.javax.sip.SipStackImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; + +import javax.sip.*; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.Properties; +import java.util.TooManyListenersException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +@Component +public class SipLayer implements SipListener { + + private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SIPProcessorFactory processorFactory; + + @Autowired + private SipSubscribe sipSubscribe; + + private SipStack sipStack; + + private SipFactory sipFactory; + + /** + * 消息处理器线程池 + */ + private ThreadPoolExecutor processThreadPool; + + @Bean("initSipServer") + private ThreadPoolExecutor initSipServer() { + + int processThreadNum = Runtime.getRuntime().availableProcessors() * 10; + LinkedBlockingQueue processQueue = new LinkedBlockingQueue<>(10000); + processThreadPool = new ThreadPoolExecutor(processThreadNum, processThreadNum, + 0L, TimeUnit.MILLISECONDS, processQueue, + new ThreadPoolExecutor.CallerRunsPolicy()); + return processThreadPool; + } + + @Bean("sipFactory") + @DependsOn("initSipServer") + private SipFactory createSipFactory() { + sipFactory = SipFactory.getInstance(); + sipFactory.setPathName("gov.nist"); + return sipFactory; + } + + @Bean("sipStack") + @DependsOn({"initSipServer", "sipFactory"}) + private SipStack createSipStack() throws PeerUnavailableException { + Properties properties = new Properties(); + properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); + properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getSipIp()); + properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false"); + /** + * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE = + * 0; public static final int TRACE_MESSAGES = 16; public static final int + * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32; + */ + properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0"); + properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log"); + properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log"); + sipStack = (SipStackImpl) sipFactory.createSipStack(properties); + return sipStack; + } + + @Bean("tcpSipProvider") + @DependsOn("sipStack") + private SipProvider startTcpListener() { + ListeningPoint tcpListeningPoint = null; + SipProvider tcpSipProvider = null; + try { + tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP"); + } catch (TransportNotSupportedException e) { + logger.error("创建SIP服务失败!", e); + System.exit(1); + } catch (InvalidArgumentException e) { + if ("Cannot assign requested address: JVM_Bind".equals(e.getMessage())) { + logger.error("创建SIP服务失败,请检查sip.ip必须是本地网卡上的IP!", e); + } else { + logger.error("创建SIP服务失败!", e); + } + System.exit(1); + } + try { + tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); + } catch (ObjectInUseException e) { + logger.error("创建SIP服务失败!", e); + System.exit(1); + } + try { + tcpSipProvider.addSipListener(this); + } catch (TooManyListenersException e) { + logger.error("创建SIP服务失败!", e); + System.exit(1); + } + logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); + return tcpSipProvider; + + +// try { +// tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP"); +// tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); +// tcpSipProvider.addSipListener(this); +// logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); +// } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) { +// logger.error(String.format("创建SIP服务失败: %s", e.getMessage())); +// } +// return tcpSipProvider; + } + + @Bean("udpSipProvider") + @DependsOn("sipStack") + private SipProvider startUdpListener() throws Exception { + ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP"); + SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); + udpSipProvider.addSipListener(this); + logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); + return udpSipProvider; + } + + /** + * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a + * new request arrives. + */ + @Override + public void processRequest(RequestEvent evt) { + logger.debug(evt.getRequest().toString()); + // 由于jainsip是单线程程序,为提高性能并发处理 + processThreadPool.execute(() -> { + if (processorFactory != null) { + processorFactory.createRequestProcessor(evt).process(); + } + }); + } + + @Override + public void processResponse(ResponseEvent evt) { + Response response = evt.getResponse(); + logger.debug(evt.getResponse().toString()); + int status = response.getStatusCode(); + if ((status >= 200) && (status < 300)) { // Success! + ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); + try { + processor.process(evt, this, sipConfig); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0) { + CallIdHeader callIdHeader = (CallIdHeader) evt.getResponse().getHeader(CallIdHeader.NAME); + if (callIdHeader != null) { + SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); + if (subscribe != null) { + subscribe.response(evt); + } + } + } + // } else if (status == Response.TRYING) { + // trying不会回复 + } else if ((status >= 100) && (status < 200)) { + // 增加其它无需回复的响应,如101、180等 + } else { + logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); + if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0) { + CallIdHeader callIdHeader = (CallIdHeader) evt.getResponse().getHeader(CallIdHeader.NAME); + if (callIdHeader != null) { + SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); + if (subscribe != null) { + subscribe.response(evt); + } + } + } + } + + + } + + /** + *

+ * Title: processTimeout + *

+ *

+ * Description: + *

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

+ * Title: processIOException + *

+ *

+ * Description: + *

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

+ * Title: processTransactionTerminated + *

+ *

+ * Description: + *

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

+ * Title: processDialogTerminated + *

+ *

+ * Description: + *

+ * + * @param dialogTerminatedEvent + */ + @Override + public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java index 9393106e..fa6f62fe 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java @@ -12,17 +12,17 @@ public class Device { * 设备名 */ private String name; - + /** * 生产厂商 */ private String manufacturer; - + /** * 型号 */ private String model; - + /** * 固件版本 */ @@ -45,7 +45,7 @@ public class Device { /** * wan地址_ip */ - private String ip; + private String ip; /** * wan地址_port @@ -55,8 +55,8 @@ public class Device { /** * wan地址 */ - private String hostAddress; - + private String hostAddress; + /** * 在线 */ diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java index ca6ef60f..64666c2e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java @@ -153,7 +153,7 @@ public class DeviceChannel { /** * 是否含有音频 */ - private boolean hasAudio; + private Boolean hasAudio; public String getDeviceId() { return deviceId; @@ -388,11 +388,11 @@ public class DeviceChannel { this.subCount = subCount; } - public boolean isHasAudio() { + public Boolean isHasAudio() { return hasAudio; } - public void setHasAudio(boolean hasAudio) { + public void setHasAudio(Boolean hasAudio) { this.hasAudio = hasAudio; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java index 4ec182af..fdd4bfa9 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java @@ -1,55 +1,53 @@ -package com.genersoft.iot.vmp.gb28181.bean; - - -//import gov.nist.javax.sip.header.SIPDate; - -import java.util.List; - -/** - * @Description:设备录像信息bean - * @author: swwheihei - * @date: 2020年5月8日 下午2:05:56 - */ -public class RecordInfo { - - private String deviceId; - - private String name; - - private int sumNum; - - private List recordList; - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getSumNum() { - return sumNum; - } - - public void setSumNum(int sumNum) { - this.sumNum = sumNum; - } - - public List getRecordList() { - return recordList; - } - - public void setRecordList(List recordList) { - this.recordList = recordList; - } - -} +package com.genersoft.iot.vmp.gb28181.bean; + + +import java.util.List; + +/** + * @Description:设备录像信息bean + * @author: swwheihei + * @date: 2020年5月8日 下午2:05:56 + */ +public class RecordInfo { + + private String deviceId; + + private String name; + + private int sumNum; + + private List recordList; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSumNum() { + return sumNum; + } + + public void setSumNum(int sumNum) { + this.sumNum = sumNum; + } + + public List getRecordList() { + return recordList; + } + + public void setRecordList(List recordList) { + this.recordList = recordList; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java index 60998ea3..b65de09f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java @@ -1,24 +1,24 @@ -package com.genersoft.iot.vmp.gb28181.event; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -/** - * @Description:设备离在线状态检测器,用于检测设备状态 - * @author: swwheihei - * @date: 2020年5月13日 下午2:40:29 - */ -@Component -public class DeviceOffLineDetector { - - @Autowired - private RedisUtil redis; - - public boolean isOnline(String deviceId) { - String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + deviceId; - return redis.hasKey(key); - } -} +package com.genersoft.iot.vmp.gb28181.event; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; + +/** + * @Description:设备离在线状态检测器,用于检测设备状态 + * @author: swwheihei + * @date: 2020年5月13日 下午2:40:29 + */ +@Component +public class DeviceOffLineDetector { + + @Autowired + private RedisUtil redis; + + public Boolean isOnline(String deviceId) { + String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + deviceId; + return redis.hasKey(key); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/PlayTypeEnum.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/PlayTypeEnum.java new file mode 100644 index 00000000..50352ef5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/PlayTypeEnum.java @@ -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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java deleted file mode 100644 index d6b75f85..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java +++ /dev/null @@ -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 isUsed; - - private static List notUsed; - - private static void init() { - SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig"); - ssrcPrefix = sipConfig.getSipDomain().substring(3, 8); - isUsed = new ArrayList(); - notUsed = new ArrayList(); - 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; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java index 65e1e5f6..5a8dfb3f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java @@ -1,42 +1,285 @@ -package com.genersoft.iot.vmp.gb28181.session; - -import java.util.concurrent.ConcurrentHashMap; - -import javax.sip.ClientTransaction; - -import org.springframework.stereotype.Component; - -/** - * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 - * @author: swwheihei - * @date: 2020年5月13日 下午4:03:02 - */ -@Component -public class VideoStreamSessionManager { - - private ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); - private ConcurrentHashMap ssrcMap = new ConcurrentHashMap<>(); - - public String createPlaySsrc(){ - return SsrcUtil.getPlaySsrc(); - } - - public String createPlayBackSsrc(){ - return SsrcUtil.getPlayBackSsrc(); - } - - public void put(String streamId,String ssrc,ClientTransaction transaction){ - sessionMap.put(streamId, transaction); - ssrcMap.put(streamId, ssrc); - } - - public ClientTransaction get(String streamId){ - return sessionMap.get(streamId); - } - - public void remove(String streamId) { - sessionMap.remove(streamId); - SsrcUtil.releaseSsrc(ssrcMap.get(streamId)); - ssrcMap.remove(streamId); - } -} +package com.genersoft.iot.vmp.gb28181.session; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.MediaConfig; +import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.SsrcConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.utils.redis.JedisUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.sip.ClientTransaction; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 + * @author: swwheihei + * @date: 2020年5月13日 下午4:03:02 + */ +@Slf4j +@Component +public class VideoStreamSessionManager { + /** + * key: ssrc 播流会话句柄(streamId)和同步信源(SSRC)的对应关系 + * value: 流媒体服务器 + */ + private ConcurrentHashMap 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 isUsed = ssrcConfig.getIsUsed(); + List 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> entries = mediaConfig.getMediaServerSsrcMap().entrySet(); + SsrcConfig min = null; + for (Map.Entry e : entries) { + SsrcConfig vo = e.getValue(); + if (null == min) { + min = vo; + continue; + } + if (vo.getNotUsed().size() > min.getNotUsed().size()) { + min = vo; + } + } + return min; + } + + /** + * 生成streamId和播流RUL,如果此时未连接ZLM,会抛出运行时异常 + * + * @param device + * @param channelId + * @param ssrc + * @param mediaServerIp + * @return + */ + private StreamInfo initStreamInfo(Device device, String channelId, String ssrc, String mediaServerIp) { + String streamId; + if (ssrc.startsWith(PlayTypeEnum.PLAY.getValue()) && mediaConfig.getRtpEnable()) { + streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); + } else { + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); + } + StreamInfo streamInfo = new StreamInfo(); + streamInfo.setMediaServerIp(mediaServerIp); + streamInfo.setSsrc(ssrc); + streamInfo.setStreamId(streamId); + streamInfo.setDeviceID(device.getDeviceId()); + streamInfo.setChannelId(channelId); + MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo(); + if (null == mediaServerConfig) { + throw new RuntimeException("点播时发现ZLM尚未连接..."); + } + + streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerIp, mediaServerConfig.getHttpPort(), streamId)); + + streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerIp, mediaServerConfig.getRtmpPort(), streamId)); + streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerIp, mediaServerConfig.getRtspPort(), streamId)); + + return streamInfo; + } + + /** + * 查找IPC通道播流使用流媒体服务器的IP + * + * @param channelId + * @param streamId + * @return + */ + public String getMediaServerIp(String channelId, String streamId) { + StreamInfo streamInfo = this.getStreamInfo(channelId, streamId); + return null == streamInfo ? null : streamInfo.getMediaServerIp(); + } + + public StreamInfo getPlayStreamInfo(String channelId) { + if (StringUtils.isBlank(channelId)) { + log.error("getPlayStreamInfo channelId can not be null!!!"); + return null; + } + return redisCatchStorage.queryPlayByChannel(channelId); + } + + public StreamInfo getPlayBackStreamInfo(String channelId) { + if (StringUtils.isBlank(channelId)) { + log.error("getPlayBackStreamInfo channelId can not be null!!!"); + return null; + } + return redisCatchStorage.queryPlaybackByChannel(channelId); + } + + public StreamInfo getStreamInfo(String channelId, String streamId) { + if (StringUtils.isBlank(channelId) || StringUtils.isBlank(streamId)) { + log.error("getStreamInfo channelId and streamId can not be null!!!"); + return null; + } + StreamInfo streamInfo = getStreamInfo(channelId, streamId, PlayTypeEnum.PLAY); + if (null == streamInfo) { + streamInfo = getStreamInfo(channelId, streamId, PlayTypeEnum.PLAY_BACK); + } + return streamInfo; + } + + private StreamInfo getStreamInfo(String channelId, String streamId, PlayTypeEnum playType) { + if (StringUtils.isBlank(channelId) || StringUtils.isBlank(streamId)) { + log.error("getStreamInfo channelId and streamId can not be null!!!"); + return null; + } + // TODO channelId + if (null == playType || PlayTypeEnum.PLAY.equals(playType)) { + return redisCatchStorage.queryPlayByStreamId(channelId, streamId); + } else { + return redisCatchStorage.queryPlaybackByStreamId(channelId, streamId); + } + } + + /** + * 存储会话 + * + * @param channelId + * @param streamId + * @param transaction + */ + public void putClientTransaction(String channelId, String streamId, ClientTransaction transaction) { + String streamKey = getStreamKey(channelId, streamId); + sessionMap.put(streamKey, transaction); + } + + public ClientTransaction getClientTransaction(String channelId, String streamId) { + String streamKey = getStreamKey(channelId, streamId); + return sessionMap.get(streamKey); + } + + public void remove(String channelId, String streamId) { + StreamInfo streamInfo = this.getStreamInfo(channelId, streamId); + if (null == streamId) { + return; + } + this.remove(streamInfo); + } + + /** + * 移除会话并释放ssrc,主要用完的ssrc一定要释放,否则会耗尽 + */ + public void remove(StreamInfo streamInfo) { + String streamKey = getStreamKey(streamInfo.getChannelId(), streamInfo.getStreamId()); + // 移除会话 + sessionMap.remove(streamKey); + + String ssrc = streamInfo.getSsrc(); + String sn = ssrc.substring(6); + String mediaServerIp = streamInfo.getMediaServerIp(); + // 释放ssrc,并从redis移除 + jedisUtil.srem(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mediaServerIp, sn); + SsrcConfig ssrcConfig = mediaConfig.getMediaServerSsrcMap().get(mediaServerIp); + ssrcConfig.getIsUsed().remove(sn); + ssrcConfig.getNotUsed().add(sn); + + // 会话句柄和ZLM服务器的对应关系,从redis移除 + if (ssrc.startsWith(PlayTypeEnum.PLAY.getValue())) { + redisCatchStorage.stopPlay(streamInfo); + } else { + redisCatchStorage.stopPlayback(streamInfo); + } + } + + private static String getStreamKey(String channelId, String streamId) { + return channelId + "_" + streamId; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java index 77e6bd95..f3618681 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java @@ -1,196 +1,188 @@ -package com.genersoft.iot.vmp.gb28181.transmit; - -import javax.sip.RequestEvent; -import javax.sip.ResponseEvent; -import javax.sip.SipProvider; -import javax.sip.header.CSeqHeader; -import javax.sip.message.Request; -import javax.sip.message.Response; - -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -// import org.slf4j.Logger; -// import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler; -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor; -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.SpringBeanFactory; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -/** - * @Description: SIP信令处理分配 - * @author: swwheihei - * @date: 2020年5月3日 下午4:24:37 - */ -@Component -public class SIPProcessorFactory { - - // private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class); - - @Autowired - private SipConfig sipConfig; - - @Autowired - private RegisterLogicHandler handler; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private EventPublisher publisher; - - @Autowired - private SIPCommander cmder; - - @Autowired - private RedisUtil redis; - - @Autowired - private DeferredResultHolder deferredResultHolder; - - @Autowired - private DeviceOffLineDetector offLineDetector; - - @Autowired - private InviteResponseProcessor inviteResponseProcessor; - - @Autowired - private ByeResponseProcessor byeResponseProcessor; - - @Autowired - private CancelResponseProcessor cancelResponseProcessor; - - @Autowired - private OtherResponseProcessor otherResponseProcessor; - - - // 注:这里使用注解会导致循环依赖注入,暂用springBean - private SipProvider tcpSipProvider; - - // 注:这里使用注解会导致循环依赖注入,暂用springBean - private SipProvider udpSipProvider; - - public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) { - Request request = evt.getRequest(); - String method = request.getMethod(); -// logger.info("接收到消息:"+request.getMethod()); -// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt); - if (Request.INVITE.equals(method)) { - InviteRequestProcessor processor = new InviteRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - return processor; - } else if (Request.REGISTER.equals(method)) { - RegisterRequestProcessor processor = new RegisterRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - processor.setHandler(handler); - processor.setPublisher(publisher); - processor.setSipConfig(sipConfig); - processor.setVideoManagerStorager(storager); - return processor; - } else if (Request.SUBSCRIBE.equals(method)) { - SubscribeRequestProcessor processor = new SubscribeRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.ACK.equals(method)) { - AckRequestProcessor processor = new AckRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.BYE.equals(method)) { - ByeRequestProcessor processor = new ByeRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.CANCEL.equals(method)) { - CancelRequestProcessor processor = new CancelRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } else if (Request.MESSAGE.equals(method)) { - MessageRequestProcessor processor = new MessageRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - processor.setPublisher(publisher); - processor.setRedis(redis); - processor.setDeferredResultHolder(deferredResultHolder); - processor.setOffLineDetector(offLineDetector); - processor.setCmder(cmder); - processor.setStorager(storager); - processor.setRedisCatchStorage(redisCatchStorage); - return processor; - } else if (Request.NOTIFY.equalsIgnoreCase(method)) { - NotifyRequestProcessor processor = new NotifyRequestProcessor(); - processor.setRequestEvent(evt); - processor.setTcpSipProvider(getTcpSipProvider()); - processor.setUdpSipProvider(getUdpSipProvider()); - processor.setPublisher(publisher); - processor.setRedis(redis); - processor.setDeferredResultHolder(deferredResultHolder); - processor.setOffLineDetector(offLineDetector); - processor.setCmder(cmder); - processor.setStorager(storager); - processor.setRedisCatchStorage(redisCatchStorage); - return processor; - } else { - OtherRequestProcessor processor = new OtherRequestProcessor(); - processor.setRequestEvent(evt); - return processor; - } - } - - public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) { - Response response = evt.getResponse(); - CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); - String method = cseqHeader.getMethod(); - if(Request.INVITE.equals(method)){ - return inviteResponseProcessor; - } else if (Request.BYE.equals(method)) { - return byeResponseProcessor; - } else if (Request.CANCEL.equals(method)) { - return cancelResponseProcessor; - } else { - return otherResponseProcessor; - } - } - - private SipProvider getTcpSipProvider() { - if (tcpSipProvider == null) { - tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider"); - } - return tcpSipProvider; - } - - private SipProvider getUdpSipProvider() { - if (udpSipProvider == null) { - udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); - } - return udpSipProvider; - } - -} +package com.genersoft.iot.vmp.gb28181.transmit; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler; +import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.request.impl.*; +import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor; +import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.utils.SpringBeanFactory; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; +import javax.sip.ResponseEvent; +import javax.sip.SipProvider; +import javax.sip.header.CSeqHeader; +import javax.sip.message.Request; +import javax.sip.message.Response; + +/** + * @Description: SIP信令处理分配 + * @author: swwheihei + * @date: 2020年5月3日 下午4:24:37 + */ +@Component +public class SIPProcessorFactory { + + + @Autowired + private SipConfig sipConfig; + + @Autowired + private RegisterLogicHandler handler; + + @Autowired + private IVideoManagerStorager storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private EventPublisher publisher; + + @Autowired + private SIPCommander cmder; + + @Autowired + private RedisUtil redis; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Autowired + private DeviceOffLineDetector offLineDetector; + + @Autowired + private InviteResponseProcessor inviteResponseProcessor; + + @Autowired + private ByeResponseProcessor byeResponseProcessor; + + @Autowired + private CancelResponseProcessor cancelResponseProcessor; + + @Autowired + private OtherResponseProcessor otherResponseProcessor; + + + // 注:这里使用注解会导致循环依赖注入,暂用springBean + private SipProvider tcpSipProvider; + + // 注:这里使用注解会导致循环依赖注入,暂用springBean + private SipProvider udpSipProvider; + + public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) { + Request request = evt.getRequest(); + String method = request.getMethod(); +// logger.info("接收到消息:"+request.getMethod()); +// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt); + if (Request.INVITE.equals(method)) { + InviteRequestProcessor processor = new InviteRequestProcessor(); + processor.setRequestEvent(evt); + processor.setTcpSipProvider(getTcpSipProvider()); + processor.setUdpSipProvider(getUdpSipProvider()); + return processor; + } else if (Request.REGISTER.equals(method)) { + RegisterRequestProcessor processor = new RegisterRequestProcessor(); + processor.setRequestEvent(evt); + processor.setTcpSipProvider(getTcpSipProvider()); + processor.setUdpSipProvider(getUdpSipProvider()); + processor.setHandler(handler); + processor.setPublisher(publisher); + processor.setSipConfig(sipConfig); + processor.setVideoManagerStorager(storager); + return processor; + } else if (Request.SUBSCRIBE.equals(method)) { + SubscribeRequestProcessor processor = new SubscribeRequestProcessor(); + processor.setRequestEvent(evt); + return processor; + } else if (Request.ACK.equals(method)) { + AckRequestProcessor processor = new AckRequestProcessor(); + processor.setRequestEvent(evt); + return processor; + } else if (Request.BYE.equals(method)) { + ByeRequestProcessor processor = new ByeRequestProcessor(); + processor.setRequestEvent(evt); + return processor; + } else if (Request.CANCEL.equals(method)) { + CancelRequestProcessor processor = new CancelRequestProcessor(); + processor.setRequestEvent(evt); + return processor; + } else if (Request.MESSAGE.equals(method)) { + MessageRequestProcessor processor = new MessageRequestProcessor(); + processor.setRequestEvent(evt); + processor.setTcpSipProvider(getTcpSipProvider()); + processor.setUdpSipProvider(getUdpSipProvider()); + processor.setPublisher(publisher); + processor.setRedis(redis); + processor.setDeferredResultHolder(deferredResultHolder); + processor.setOffLineDetector(offLineDetector); + processor.setCmder(cmder); + processor.setStorager(storager); + processor.setRedisCatchStorage(redisCatchStorage); + return processor; + } else if (Request.NOTIFY.equalsIgnoreCase(method)) { + NotifyRequestProcessor processor = new NotifyRequestProcessor(); + processor.setRequestEvent(evt); + processor.setTcpSipProvider(getTcpSipProvider()); + processor.setUdpSipProvider(getUdpSipProvider()); + processor.setPublisher(publisher); + processor.setRedis(redis); + processor.setDeferredResultHolder(deferredResultHolder); + processor.setOffLineDetector(offLineDetector); + processor.setCmder(cmder); + processor.setStorager(storager); + processor.setRedisCatchStorage(redisCatchStorage); + return processor; + } else { + OtherRequestProcessor processor = new OtherRequestProcessor(); + processor.setRequestEvent(evt); + return processor; + } + } + + public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) { + Response response = evt.getResponse(); + CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); + String method = cseqHeader.getMethod(); + if (Request.INVITE.equals(method)) { + return inviteResponseProcessor; + } else if (Request.BYE.equals(method)) { + return byeResponseProcessor; + } else if (Request.CANCEL.equals(method)) { + return cancelResponseProcessor; + } else { + return otherResponseProcessor; + } + } + + private SipProvider getTcpSipProvider() { + if (tcpSipProvider == null) { + tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider"); + } + return tcpSipProvider; + } + + private SipProvider getUdpSipProvider() { + if (udpSipProvider == null) { + udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); + } + return udpSipProvider; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java index 6fcc2e02..c2ab53f1 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java @@ -1,61 +1,58 @@ -package com.genersoft.iot.vmp.gb28181.transmit.callback; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.async.DeferredResult; - -/** - * @Description: 异步请求处理 - * @author: swwheihei - * @date: 2020年5月8日 下午7:59:05 - */ -@SuppressWarnings(value = {"rawtypes", "unchecked"}) -@Component -public class DeferredResultHolder { - - public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS"; - - public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO"; - - public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL"; - - public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG"; - - public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD"; - - public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG"; - - public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; - - public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; - - public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; - - public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; - - public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; - - public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; - - private Map map = new ConcurrentHashMap(); - - public void put(String key, DeferredResult result) { - map.put(key, result); - } - - public DeferredResult get(String key) { - return map.get(key); - } - - public void invokeResult(RequestMessage msg) { - DeferredResult result = map.get(msg.getId()); - if (result == null) { - return; - } - result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK)); - } -} +package com.genersoft.iot.vmp.gb28181.transmit.callback; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.async.DeferredResult; + +/** + * @Description: 异步请求处理 + * @author: swwheihei + * @date: 2020年5月8日 下午7:59:05 + */ +@Component +public class DeferredResultHolder { + + public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS"; + + public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO"; + + public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL"; + + public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG"; + + public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD"; + + public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG"; + + public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; + + public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; + + public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; + + public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; + + public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; + + public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; + + private Map map = new ConcurrentHashMap(); + + public void put(String key, DeferredResult result) { + map.put(key, result); + } + + public void invokeResult(RequestMessage msg) { +// DeferredResult result = map.get(msg.getId()); + // 获取并移除 + DeferredResult result = map.remove(msg.getId()); + if (result == null) { + return; + } + result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK)); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index 6fa4eca9..e1bdca8b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -1,284 +1,285 @@ -package com.genersoft.iot.vmp.gb28181.transmit.cmd; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; - -/** - * @Description:设备能力接口,用于定义设备的控制、查询能力 - * @author: swwheihei - * @date: 2020年5月3日 下午9:16:34 - */ -public interface ISIPCommander { - - /** - * 云台方向放控制,使用配置文件中的默认镜头移动速度 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param moveSpeed 镜头移动速度 - */ - boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); - - /** - * 云台方向放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param moveSpeed 镜头移动速度 - */ - boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); - - /** - * 云台缩放控制,使用配置文件中的默认镜头缩放速度 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - */ - boolean ptzZoomCmd(Device device,String channelId,int inOut); - - /** - * 云台缩放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param zoomSpeed 镜头缩放速度 - */ - boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); - - /** - * 云台控制,支持方向与缩放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param moveSpeed 镜头移动速度 - * @param zoomSpeed 镜头缩放速度 - */ - boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); - - /** - * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param cmdCode 指令码 - * @param parameter1 数据1 - * @param parameter2 数据2 - * @param combineCode2 组合码2 - */ - boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); - - /** - * 请求预览视频流 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); - - /** - * 请求回放视频流 - * - * @param device 视频设备 - * @param channelId 预览通道 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss - */ - void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); - - /** - * 视频流停止 - * - * @param ssrc ssrc - */ - void streamByeCmd(String ssrc, SipSubscribe.Event okEvent); - void streamByeCmd(String ssrc); - - /** - * 语音广播 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - boolean audioBroadcastCmd(Device device,String channelId); - - /** - * 音视频录像控制 - * - * @param device 视频设备 - * @param channelId 预览通道 - * @param recordCmdStr 录像命令:Record / StopRecord - */ - boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent); - - /** - * 远程启动控制命令 - * - * @param device 视频设备 - */ - boolean teleBootCmd(Device device); - - /** - * 报警布防/撤防命令 - * - * @param device 视频设备 - * @param setGuard true: SetGuard, false: ResetGuard - */ - boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent); - - /** - * 报警复位命令 - * - * @param device 视频设备 - * @param alarmMethod 报警方式(可选) - * @param alarmType 报警类型(可选) - */ - boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent); - - /** - * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - boolean iFrameCmd(Device device, String channelId); - - /** - * 看守位控制命令 - * - * @param device 视频设备 - * @param enabled 看守位使能:1 = 开启,0 = 关闭 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 - */ - boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent); - - /** - * 设备配置命令 - * - * @param device 视频设备 - */ - boolean deviceConfigCmd(Device device); - - /** - * 设备配置命令:basicParam - * - * @param device 视频设备 - * @param channelId 通道编码(可选) - * @param name 设备/通道名称(可选) - * @param expiration 注册过期时间(可选) - * @param heartBeatInterval 心跳间隔时间(可选) - * @param heartBeatCount 心跳超时次数(可选) - */ - boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent); - - /** - * 查询设备状态 - * - * @param device 视频设备 - */ - boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent); - - /** - * 查询设备信息 - * - * @param device 视频设备 - * @return - */ - boolean deviceInfoQuery(Device device); - - /** - * 查询目录列表 - * - * @param device 视频设备 - */ - boolean catalogQuery(Device device, SipSubscribe.Event errorEvent); - - /** - * 查询录像信息 - * - * @param device 视频设备 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss - */ - boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); - - /** - * 查询报警信息 - * - * @param device 视频设备 - * @param startPriority 报警起始级别(可选) - * @param endPriority 报警终止级别(可选) - * @param alarmMethod 报警方式条件(可选) - * @param alarmType 报警类型 - * @param startTime 报警发生起始时间(可选) - * @param endTime 报警发生终止时间(可选) - * @return true = 命令发送成功 - */ - boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, - String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent); - - /** - * 查询设备配置 - * - * @param device 视频设备 - * @param channelId 通道编码(可选) - * @param configType 配置类型: - */ - boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent); - - /** - * 查询设备预置位置 - * - * @param device 视频设备 - */ - boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent); - - /** - * 查询移动设备位置数据 - * - * @param device 视频设备 - */ - boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent); - - /** - * 订阅、取消订阅移动位置 - * - * @param device 视频设备 - * @param expires 订阅超时时间(值=0时为取消订阅) - * @param interval 上报时间间隔 - * @return true = 命令发送成功 - */ - boolean mobilePositionSubscribe(Device device, int expires, int interval); - - /** - * 订阅、取消订阅报警信息 - * @param device 视频设备 - * @param expires 订阅过期时间(0 = 取消订阅) - * @param startPriority 报警起始级别(可选) - * @param endPriority 报警终止级别(可选) - * @param alarmMethods 报警方式条件(可选) - * @param alarmType 报警类型 - * @param startTime 报警发生起始时间(可选) - * @param endTime 报警发生终止时间(可选) - * @return true = 命令发送成功 - */ - boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); - - - /** - * 释放rtpserver - * @param device - * @param channelId - */ - void closeRTPServer(Device device, String channelId); -} +package com.genersoft.iot.vmp.gb28181.transmit.cmd; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; + +/** + * @Description:设备能力接口,用于定义设备的控制、查询能力 + * @author: swwheihei + * @date: 2020年5月3日 下午9:16:34 + */ +public interface ISIPCommander { + + /** + * 云台方向放控制,使用配置文件中的默认镜头移动速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param moveSpeed 镜头移动速度 + */ + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); + + /** + * 云台方向放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param moveSpeed 镜头移动速度 + */ + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); + + /** + * 云台缩放控制,使用配置文件中的默认镜头缩放速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + */ + boolean ptzZoomCmd(Device device,String channelId,int inOut); + + /** + * 云台缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param zoomSpeed 镜头缩放速度 + */ + boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); + + /** + * 云台控制,支持方向与缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 + * @param zoomSpeed 镜头缩放速度 + */ + boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); + + /** + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); + + /** + * 请求预览视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + + /** + * 请求回放视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + + /** + * 视频流停止 + * + * @param streamInfo streamInfo + * @param okEvent okEvent + */ + void stopStreamByeCmd(StreamInfo streamInfo, SipSubscribe.Event okEvent); + + /** + * 语音广播 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + boolean audioBroadcastCmd(Device device,String channelId); + + /** + * 音视频录像控制 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param recordCmdStr 录像命令:Record / StopRecord + */ + boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent); + + /** + * 远程启动控制命令 + * + * @param device 视频设备 + */ + boolean teleBootCmd(Device device); + + /** + * 报警布防/撤防命令 + * + * @param device 视频设备 + * @param setGuard true: SetGuard, false: ResetGuard + */ + boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent); + + /** + * 报警复位命令 + * + * @param device 视频设备 + * @param alarmMethod 报警方式(可选) + * @param alarmType 报警类型(可选) + */ + boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent); + + /** + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + boolean iFrameCmd(Device device, String channelId); + + /** + * 看守位控制命令 + * + * @param device 视频设备 + * @param enabled 看守位使能:1 = 开启,0 = 关闭 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 + */ + boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent); + + /** + * 设备配置命令 + * + * @param device 视频设备 + */ + boolean deviceConfigCmd(Device device); + + /** + * 设备配置命令:basicParam + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param name 设备/通道名称(可选) + * @param expiration 注册过期时间(可选) + * @param heartBeatInterval 心跳间隔时间(可选) + * @param heartBeatCount 心跳超时次数(可选) + */ + boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent); + + /** + * 查询设备状态 + * + * @param device 视频设备 + */ + boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent); + + /** + * 查询设备信息 + * + * @param device 视频设备 + * @return + */ + boolean deviceInfoQuery(Device device); + + /** + * 查询目录列表 + * + * @param device 视频设备 + */ + boolean catalogQuery(Device device, SipSubscribe.Event errorEvent); + + /** + * 查询录像信息 + * + * @param device 视频设备 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); + + /** + * 查询报警信息 + * + * @param device 视频设备 + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, + String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent); + + /** + * 查询设备配置 + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param configType 配置类型: + */ + boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent); + + /** + * 查询设备预置位置 + * + * @param device 视频设备 + */ + boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent); + + /** + * 查询移动设备位置数据 + * + * @param device 视频设备 + */ + boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent); + + /** + * 订阅、取消订阅移动位置 + * + * @param device 视频设备 + * @param expires 订阅超时时间(值=0时为取消订阅) + * @param interval 上报时间间隔 + * @return true = 命令发送成功 + */ + boolean mobilePositionSubscribe(Device device, int expires, int interval); + + /** + * 订阅、取消订阅报警信息 + * @param device 视频设备 + * @param expires 订阅过期时间(0 = 取消订阅) + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethods 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); + + + /** + * 释放rtpserver + * @param device + * @param channelId + */ + void closeRTPServer(Device device, String channelId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java index 11e3e16e..1e239886 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java @@ -1,213 +1,211 @@ -package com.genersoft.iot.vmp.gb28181.transmit.cmd; - -import java.text.ParseException; -import java.util.ArrayList; - -import javax.sip.InvalidArgumentException; -import javax.sip.PeerUnavailableException; -import javax.sip.SipFactory; -import javax.sip.SipProvider; -import javax.sip.address.Address; -import javax.sip.address.SipURI; -import javax.sip.header.*; -import javax.sip.message.Request; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.Device; - -/** - * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化 - * @author: swwheihei - * @date: 2020年5月6日 上午9:29:02 - */ -@Component -public class SIPRequestHeaderProvider { - - @Autowired - private SipConfig sipConfig; - - @Autowired - private SipFactory sipFactory; - - @Autowired - @Qualifier(value="tcpSipProvider") - private SipProvider tcpSipProvider; - - @Autowired - @Qualifier(value="udpSipProvider") - private SipProvider udpSipProvider; - - public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { - Request request = null; - // sipuri - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); - // via - ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), - device.getTransport(), viaTag); - viaHeader.setRPort(); - viaHeaders.add(viaHeader); - // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), - sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); - // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); - // callid - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); - - request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, - toHeader, viaHeaders, maxForwards); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); - request.setContent(content, contentTypeHeader); - return request; - } - - public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException { - Request request = null; - //请求行 - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); - //via - ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); - viaHeader.setRPort(); - viaHeaders.add(viaHeader); - - //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack - //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); - - //callid - CallIdHeader callIdHeader = null; - if(device.getTransport().equals("TCP")) { - callIdHeader = tcpSipProvider.getNewCallId(); - } - if(device.getTransport().equals("UDP")) { - callIdHeader = udpSipProvider.getNewCallId(); - } - - //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - - //ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); - request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); - - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); - // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); - // Subject - SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0)); - request.addHeader(subjectHeader); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); - request.setContent(content, contentTypeHeader); - return request; - } - - public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { - Request request = null; - //请求行 - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); - // via - ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); - viaHeader.setRPort(); - viaHeaders.add(viaHeader); - //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack - //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); - - //callid - CallIdHeader callIdHeader = null; - if(device.getTransport().equals("TCP")) { - callIdHeader = tcpSipProvider.getNewCallId(); - } - if(device.getTransport().equals("UDP")) { - callIdHeader = udpSipProvider.getNewCallId(); - } - - //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - - //ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); - request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); - - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); - // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); - - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); - request.setContent(content, contentTypeHeader); - return request; - } - - public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event) throws ParseException, InvalidArgumentException, PeerUnavailableException { - Request request = null; - // sipuri - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); - // via - ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), - device.getTransport(), viaTag); - viaHeader.setRPort(); - viaHeaders.add(viaHeader); - // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), - sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); - // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); - // callid - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); - - request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, - toHeader, viaHeaders, maxForwards); - - - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); - - // Expires - ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires); - request.addHeader(expireHeader); - - // Event - EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event); - request.addHeader(eventHeader); - - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); - request.setContent(content, contentTypeHeader); - return request; - } -} +package com.genersoft.iot.vmp.gb28181.transmit.cmd; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.PeerUnavailableException; +import javax.sip.SipFactory; +import javax.sip.SipProvider; +import javax.sip.address.Address; +import javax.sip.address.SipURI; +import javax.sip.header.*; +import javax.sip.message.Request; +import java.text.ParseException; +import java.util.ArrayList; + +/** + * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化 + * @author: swwheihei + * @date: 2020年5月6日 上午9:29:02 + */ +@Component +public class SIPRequestHeaderProvider { + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SipFactory sipFactory; + + @Autowired + @Qualifier(value = "tcpSipProvider") + private SipProvider tcpSipProvider; + + @Autowired + @Qualifier(value = "udpSipProvider") + private SipProvider udpSipProvider; + + public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + // sipuri + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), + device.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), + sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + // to + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); + // callid + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() + : udpSipProvider.getNewCallId(); + // Forwards + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); + + request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); + request.setContent(content, contentTypeHeader); + return request; + } + + public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + //请求行 + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); + //via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + + //from + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipDomain()); + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + //to + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getSipDomain()); + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null); + + //callid + CallIdHeader callIdHeader = null; + if (device.getTransport().equals("TCP")) { + callIdHeader = tcpSipProvider.getNewCallId(); + } + if (device.getTransport().equals("UDP")) { + callIdHeader = udpSipProvider.getNewCallId(); + } + + //Forwards + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); + request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); + + Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp() + ":" + sipConfig.getSipPort())); + // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + // Subject + SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0)); + request.addHeader(subjectHeader); + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + request.setContent(content, contentTypeHeader); + return request; + } + + public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + //请求行 + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipDomain()); + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + //to + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getSipDomain()); + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null); + + //callid + CallIdHeader callIdHeader = null; + if (device.getTransport().equals("TCP")) { + callIdHeader = tcpSipProvider.getNewCallId(); + } + if (device.getTransport().equals("UDP")) { + callIdHeader = udpSipProvider.getNewCallId(); + } + + //Forwards + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); + request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); + + Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp() + ":" + sipConfig.getSipPort())); + // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + request.setContent(content, contentTypeHeader); + return request; + } + + public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + // sipuri + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), + device.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), + sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + // to + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); + // callid + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() + : udpSipProvider.getNewCallId(); + // Forwards + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); + + request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + + Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp() + ":" + sipConfig.getSipPort())); + request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + + // Expires + ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires); + request.addHeader(expireHeader); + + // Event + EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event); + request.addHeader(eventHeader); + + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); + request.setContent(content, contentTypeHeader); + return request; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 6561763b..7e933103 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -1,1243 +1,1218 @@ -package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; - -import java.text.ParseException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.sip.*; -import javax.sip.address.SipURI; -import javax.sip.header.CallIdHeader; -import javax.sip.header.ViaHeader; -import javax.sip.message.Request; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -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.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; -import com.genersoft.iot.vmp.gb28181.utils.DateUtil; -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; - -/** - * @Description:设备能力接口,用于定义设备的控制、查询能力 - * @author: swwheihei - * @date: 2020年5月3日 下午9:22:48 - */ -@Component -public class SIPCommander implements ISIPCommander { - - private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); - - @Autowired - private SipConfig sipConfig; - - @Autowired - private SIPRequestHeaderProvider headerProvider; - - @Autowired - private VideoStreamSessionManager streamSession; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - @Qualifier(value="tcpSipProvider") - private SipProvider tcpSipProvider; - - @Autowired - @Qualifier(value="udpSipProvider") - private SipProvider udpSipProvider; - - @Autowired - private ZLMRTPServerFactory zlmrtpServerFactory; - - @Value("${media.rtp.enable}") - private boolean rtpEnable; - - @Value("${media.seniorSdp}") - private boolean seniorSdp; - - @Value("${media.autoApplyPlay}") - private boolean autoApplyPlay; - - @Autowired - private ZLMHttpHookSubscribe subscribe; - - @Autowired - private SipSubscribe sipSubscribe; - - - - /** - * 云台方向放控制,使用配置文件中的默认镜头移动速度 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - */ - @Override - public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) { - return ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getSpeed(), 0); - } - - /** - * 云台方向放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param moveSpeed 镜头移动速度 - */ - @Override - public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) { - return ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0); - } - - /** - * 云台缩放控制,使用配置文件中的默认镜头缩放速度 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - */ - @Override - public boolean ptzZoomCmd(Device device, String channelId, int inOut) { - return ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getSpeed()); - } - - /** - * 云台缩放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param zoomSpeed 镜头缩放速度 - */ - @Override - public boolean ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) { - return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed); - } - - /** - * 云台指令码计算 - * - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param moveSpeed 镜头移动速度 默认 0XFF (0-255) - * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255) - */ - public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) { - int cmdCode = 0; - if (leftRight == 2) { - cmdCode|=0x01; // 右移 - } else if(leftRight == 1) { - cmdCode|=0x02; // 左移 - } - if (upDown == 2) { - cmdCode|=0x04; // 下移 - } else if(upDown == 1) { - cmdCode|=0x08; // 上移 - } - if (inOut == 2) { - cmdCode |= 0x10; // 放大 - } else if(inOut == 1) { - cmdCode |= 0x20; // 缩小 - } - StringBuilder builder = new StringBuilder("A50F01"); - String strTmp; - strTmp = String.format("%02X", cmdCode); - builder.append(strTmp, 0, 2); - strTmp = String.format("%02X", moveSpeed); - builder.append(strTmp, 0, 2); - builder.append(strTmp, 0, 2); - strTmp = String.format("%X", zoomSpeed); - builder.append(strTmp, 0, 1).append("0"); - //计算校验码 - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100; - strTmp = String.format("%02X", checkCode); - builder.append(strTmp, 0, 2); - return builder.toString(); -} - - /** - * 云台指令码计算 - * - * @param cmdCode 指令码 - * @param parameter1 数据1 - * @param parameter2 数据2 - * @param combineCode2 组合码2 - */ - public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) { - StringBuilder builder = new StringBuilder("A50F01"); - String strTmp; - strTmp = String.format("%02X", cmdCode); - builder.append(strTmp, 0, 2); - strTmp = String.format("%02X", parameter1); - builder.append(strTmp, 0, 2); - strTmp = String.format("%02X", parameter2); - builder.append(strTmp, 0, 2); - strTmp = String.format("%X", combineCode2); - builder.append(strTmp, 0, 1).append("0"); - //计算校验码 - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100; - strTmp = String.format("%02X", checkCode); - builder.append(strTmp, 0, 2); - return builder.toString(); - } - - /** - * 云台控制,支持方向与缩放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param moveSpeed 镜头移动速度 - * @param zoomSpeed 镜头缩放速度 - */ - @Override - public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, - int zoomSpeed) { - try { - String cmdStr= cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); - StringBuffer ptzXml = new StringBuffer(200); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("DeviceControl\r\n"); - ptzXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - ptzXml.append("" + channelId + "\r\n"); - ptzXml.append("" + cmdStr + "\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); - - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - return false; - } - - /** - * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param cmdCode 指令码 - * @param parameter1 数据1 - * @param parameter2 数据2 - * @param combineCode2 组合码2 - */ - @Override - public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) { - try { - String cmdStr= frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2); - System.out.println("控制字符串:" + cmdStr); - StringBuffer ptzXml = new StringBuffer(200); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("DeviceControl\r\n"); - ptzXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - ptzXml.append("" + channelId + "\r\n"); - ptzXml.append("" + cmdStr + "\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - return false; - } - - /** - * 请求预览视频流 - * @param device 视频设备 - * @param channelId 预览通道 - * @param event hook订阅 - * @param errorEvent sip错误订阅 - */ - @Override - public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { - try { - - String ssrc = streamSession.createPlaySsrc(); - String streamId = null; - if (rtpEnable) { - streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); - }else { - streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); - } - String streamMode = device.getStreamMode().toUpperCase(); - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - if (mediaInfo == null) { - logger.warn("点播时发现ZLM尚未连接..."); - return; - } - String mediaPort = null; - // 使用动态udp端口 - if (rtpEnable) { - mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; - }else { - mediaPort = mediaInfo.getRtpProxyPort(); - } - - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("id", streamId); - - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); - // - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); -// content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("s=Play\r\n"); - content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("t=0 0\r\n"); - - if (seniorSdp) { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:126 H264/90000\r\n"); - content.append("a=rtpmap:125 H264S/90000\r\n"); - content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); - content.append("a=fmtp:99 profile-level-id=3\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - }else { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - } - } - - content.append("y="+ssrc+"\r\n");//ssrc - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc); - - ClientTransaction transaction = transmitRequest(device, request, errorEvent); - streamSession.put(streamId,ssrc, transaction); - - - - } catch ( SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - /** - * 请求回放视频流 - * - * @param device 视频设备 - * @param channelId 预览通道 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss - */ - @Override - public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event - , SipSubscribe.Event errorEvent) { - try { - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - String ssrc = streamSession.createPlayBackSsrc(); - String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("id", streamId); - - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); - - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o="+sipConfig.getSipId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n"); - content.append("s=Playback\r\n"); - content.append("u="+channelId+":0\r\n"); - content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); - content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" " - +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); - String mediaPort = null; - // 使用动态udp端口 - if (rtpEnable) { - mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; - }else { - mediaPort = mediaInfo.getRtpProxyPort(); - } - String streamMode = device.getStreamMode().toUpperCase(); - - if (seniorSdp) { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:126 H264/90000\r\n"); - content.append("a=rtpmap:125 H264S/90000\r\n"); - content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); - content.append("a=fmtp:99 profile-level-id=3\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - }else { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - } - - content.append("y="+ssrc+"\r\n");//ssrc - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null); - - ClientTransaction transaction = transmitRequest(device, request, errorEvent); - streamSession.put(streamId, ssrc, transaction); - - } catch ( SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - - - /** - * 视频流停止 - * - */ - @Override - public void streamByeCmd(String ssrc) { - streamByeCmd(ssrc, null); - } - @Override - public void streamByeCmd(String streamId, SipSubscribe.Event okEvent) { - - try { - ClientTransaction transaction = streamSession.get(streamId); - // 服务重启后 - if (transaction == null) { - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo != null) { - - } - return; - } - - Dialog dialog = transaction.getDialog(); - if (dialog == null) { - return; - } - Request byeRequest = dialog.createRequest(Request.BYE); - SipURI byeURI = (SipURI) byeRequest.getRequestURI(); - String vh = transaction.getRequest().getHeader(ViaHeader.NAME).toString(); - Pattern p = Pattern.compile("(\\d+\\.\\d+\\.\\d+\\.\\d+)\\:(\\d+)"); - Matcher matcher = p.matcher(vh); - if (matcher.find()) { - String ip = matcher.group(1); - byeURI.setHost(ip); - String port = matcher.group(2); - byeURI.setPort(Integer.parseInt(port)); - } - ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME); - String protocol = viaHeader.getTransport().toUpperCase(); - ClientTransaction clientTransaction = null; - if("TCP".equals(protocol)) { - clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest); - } else if("UDP".equals(protocol)) { - clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); - } - - CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME); - if (okEvent != null) { - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); - } - - dialog.sendRequest(clientTransaction); - - streamSession.remove(streamId); - zlmrtpServerFactory.closeRTPServer(streamId); - } catch (TransactionDoesNotExistException e) { - e.printStackTrace(); - } catch (SipException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } - } - - /** - * 语音广播 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - @Override - public boolean audioBroadcastCmd(Device device, String channelId) { - // TODO Auto-generated method stub - return false; - } - - /** - * 音视频录像控制 - * - * @param device 视频设备 - * @param channelId 预览通道 - * @param recordCmdStr 录像命令:Record / StopRecord - */ - @Override - public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("" + recordCmdStr + "\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 远程启动控制命令 - * - * @param device 视频设备 - */ - @Override - public boolean teleBootCmd(Device device) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - cmdXml.append("Boot\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 报警布防/撤防命令 - * - * @param device 视频设备 - * @param guardCmdStr "SetGuard"/"ResetGuard" - */ - @Override - public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - cmdXml.append("" + guardCmdStr + "\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 报警复位命令 - * - * @param device 视频设备 - */ - @Override - public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - cmdXml.append("ResetAlarm\r\n"); - if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { - cmdXml.append("\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { - cmdXml.append("\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - @Override - public boolean iFrameCmd(Device device, String channelId) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("Send\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 看守位控制命令 - * - * @param device 视频设备 - * @param enabled 看守位使能:1 = 开启,0 = 关闭 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 - */ - @Override - public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) { - cmdXml.append("1\r\n"); - if (NumericUtil.isInteger(resetTime)) { - cmdXml.append("" + resetTime + "\r\n"); - } else { - cmdXml.append("0\r\n"); - } - if (NumericUtil.isInteger(presetIndex)) { - cmdXml.append("" + presetIndex + "\r\n"); - } else { - cmdXml.append("0\r\n"); - } - } else { - cmdXml.append("0\r\n"); - } - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 设备配置命令 - * - * @param device 视频设备 - */ - @Override - public boolean deviceConfigCmd(Device device) { - // TODO Auto-generated method stub - return false; - } - - /** - * 设备配置命令:basicParam - * - * @param device 视频设备 - * @param channelId 通道编码(可选) - * @param name 设备/通道名称(可选) - * @param expiration 注册过期时间(可选) - * @param heartBeatInterval 心跳间隔时间(可选) - * @param heartBeatCount 心跳超时次数(可选) - */ - @Override - public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, - String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceConfig\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - if (!XmlUtil.isEmpty(name)) { - cmdXml.append("" + name + "\r\n"); - } - if (NumericUtil.isInteger(expiration)) { - if (Integer.valueOf(expiration) > 0) { - cmdXml.append("" + expiration + "\r\n"); - } - } - if (NumericUtil.isInteger(heartBeatInterval)) { - if (Integer.valueOf(heartBeatInterval) > 0) { - cmdXml.append("" + heartBeatInterval + "\r\n"); - } - } - if (NumericUtil.isInteger(heartBeatCount)) { - if (Integer.valueOf(heartBeatCount) > 0) { - cmdXml.append("" + heartBeatCount + "\r\n"); - } - } - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备状态 - * - * @param device 视频设备 - */ - @Override - public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) { - try { - StringBuffer catalogXml = new StringBuffer(200); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("DeviceStatus\r\n"); - catalogXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - catalogXml.append("" + device.getDeviceId() + "\r\n"); - catalogXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null); - - transmitRequest(device, request, errorEvent); - return true; - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备信息 - * - * @param device 视频设备 - */ - @Override - public boolean deviceInfoQuery(Device device) { - try { - StringBuffer catalogXml = new StringBuffer(200); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("DeviceInfo\r\n"); - catalogXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - catalogXml.append("" + device.getDeviceId() + "\r\n"); - catalogXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDev" + tm, null); - - transmitRequest(device, request); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 查询目录列表 - * - * @param device 视频设备 - */ - @Override - public boolean catalogQuery(Device device, SipSubscribe.Event errorEvent) { - // 清空通道 - storager.cleanChannelsForDevice(device.getDeviceId()); - try { - StringBuffer catalogXml = new StringBuffer(200); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("Catalog\r\n"); - catalogXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - catalogXml.append("" + device.getDeviceId() + "\r\n"); - catalogXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCat" + tm, null); - - transmitRequest(device, request, errorEvent); - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 查询录像信息 - * - * @param device 视频设备 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss - */ - @Override - public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime) { - - try { - StringBuffer recordInfoXml = new StringBuffer(200); - recordInfoXml.append("\r\n"); - recordInfoXml.append("\r\n"); - recordInfoXml.append("RecordInfo\r\n"); - recordInfoXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - recordInfoXml.append("" + channelId + "\r\n"); - recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "\r\n"); - recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "\r\n"); - recordInfoXml.append("0\r\n"); - // 大华NVR要求必须增加一个值为all的文本元素节点Type - recordInfoXml.append("all\r\n"); - recordInfoXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "fromRec" + tm, null); - - transmitRequest(device, request); - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 查询报警信息 - * - * @param device 视频设备 - * @param startPriority 报警起始级别(可选) - * @param endPriority 报警终止级别(可选) - * @param alarmMethods 报警方式条件(可选) - * @param alarmType 报警类型 - * @param startTime 报警发生起始时间(可选) - * @param endTime 报警发生终止时间(可选) - * @return true = 命令发送成功 - */ - @Override - public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType, - String startTime, String endTime, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("Alarm\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - if (!XmlUtil.isEmpty(startPriority)) { - cmdXml.append("" + startPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(endPriority)) { - cmdXml.append("" + endPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!XmlUtil.isEmpty(startTime)) { - cmdXml.append("" + startTime + "\r\n"); - } - if (!XmlUtil.isEmpty(endTime)) { - cmdXml.append("" + endTime + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备配置 - * - * @param device 视频设备 - * @param channelId 通道编码(可选) - * @param configType 配置类型: - */ - @Override - public boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("ConfigDownload\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("" + configType + "\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备预置位置 - * - * @param device 视频设备 - */ - @Override - public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("PresetQuery\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (XmlUtil.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询移动设备位置数据 - * - * @param device 视频设备 - */ - @Override - public boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) { - try { - StringBuffer mobilePostitionXml = new StringBuffer(200); - mobilePostitionXml.append("\r\n"); - mobilePostitionXml.append("\r\n"); - mobilePostitionXml.append("MobilePosition\r\n"); - mobilePostitionXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - mobilePostitionXml.append("" + device.getDeviceId() + "\r\n"); - mobilePostitionXml.append("60\r\n"); - mobilePostitionXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null); - - transmitRequest(device, request, errorEvent); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 订阅、取消订阅移动位置 - * - * @param device 视频设备 - * @param expires 订阅超时时间 - * @param interval 上报时间间隔 - * @return true = 命令发送成功 - */ - public boolean mobilePositionSubscribe(Device device, int expires, int interval) { - try { - StringBuffer subscribePostitionXml = new StringBuffer(200); - subscribePostitionXml.append("\r\n"); - subscribePostitionXml.append("\r\n"); - subscribePostitionXml.append("MobilePosition\r\n"); - subscribePostitionXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - subscribePostitionXml.append("" + device.getDeviceId() + "\r\n"); - if (expires > 0) { - subscribePostitionXml.append("" + String.valueOf(interval) + "\r\n"); - } - subscribePostitionXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); //Position;id=" + tm.substring(tm.length() - 4)); - transmitRequest(device, request); - - return true; - - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 订阅、取消订阅报警信息 - * - * @param device 视频设备 - * @param expires 订阅过期时间(0 = 取消订阅) - * @param startPriority 报警起始级别(可选) - * @param endPriority 报警终止级别(可选) - * @param alarmMethod 报警方式条件(可选) - * @param alarmType 报警类型 - * @param startTime 报警发生起始时间(可选) - * @param endTime 报警发生终止时间(可选) - * @return true = 命令发送成功 - */ - public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("Alarm\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - if (!XmlUtil.isEmpty(startPriority)) { - cmdXml.append("" + startPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(endPriority)) { - cmdXml.append("" + endPriority + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!XmlUtil.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!XmlUtil.isEmpty(startTime)) { - cmdXml.append("" + startTime + "\r\n"); - } - if (!XmlUtil.isEmpty(endTime)) { - cmdXml.append("" + endTime + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence" ); - transmitRequest(device, request); - - return true; - - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) { - e.printStackTrace(); - return false; - } - } - - - private ClientTransaction transmitRequest(Device device, Request request) throws SipException { - return transmitRequest(device, request, null, null); - } - - private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent) throws SipException { - return transmitRequest(device, request, errorEvent, null); - } - - private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException { - ClientTransaction clientTransaction = null; - if("TCP".equals(device.getTransport())) { - clientTransaction = tcpSipProvider.getNewClientTransaction(request); - } else if("UDP".equals(device.getTransport())) { - clientTransaction = udpSipProvider.getNewClientTransaction(request); - } - - CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME); - // 添加错误订阅 - if (errorEvent != null) { - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), errorEvent); - } - // 添加订阅 - if (okEvent != null) { - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); - } - - clientTransaction.sendRequest(); - return clientTransaction; - } - - - - - @Override - public void closeRTPServer(Device device, String channelId) { - if (rtpEnable) { - String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); - zlmrtpServerFactory.closeRTPServer(streamId); - } - } -} +package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; + +import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.MediaConfig; +import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; +import com.genersoft.iot.vmp.gb28181.utils.DateUtil; +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.sip.*; +import javax.sip.address.SipURI; +import javax.sip.header.CallIdHeader; +import javax.sip.header.ViaHeader; +import javax.sip.message.Request; +import java.text.ParseException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Description:设备能力接口,用于定义设备的控制、查询能力 + * @author: swwheihei + * @date: 2020年5月3日 下午9:22:48 + */ +@Slf4j +@Component +public class SIPCommander implements ISIPCommander { + + private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SIPRequestHeaderProvider headerProvider; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private IVideoManagerStorager storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + @Qualifier(value = "tcpSipProvider") + private SipProvider tcpSipProvider; + + @Autowired + @Qualifier(value = "udpSipProvider") + private SipProvider udpSipProvider; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + MediaConfig mediaConfig; + + @Autowired + private ZLMHttpHookSubscribe subscribe; + + @Autowired + private SipSubscribe sipSubscribe; + + + /** + * 云台方向放控制,使用配置文件中的默认镜头移动速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + */ + @Override + public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) { + return ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getSpeed(), 0); + } + + /** + * 云台方向放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param moveSpeed 镜头移动速度 + */ + @Override + public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) { + return ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0); + } + + /** + * 云台缩放控制,使用配置文件中的默认镜头缩放速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + */ + @Override + public boolean ptzZoomCmd(Device device, String channelId, int inOut) { + return ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getSpeed()); + } + + /** + * 云台缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param zoomSpeed 镜头缩放速度 + */ + @Override + public boolean ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) { + return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed); + } + + /** + * 云台指令码计算 + * + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 默认 0XFF (0-255) + * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255) + */ + public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) { + int cmdCode = 0; + if (leftRight == 2) { + cmdCode |= 0x01; // 右移 + } else if (leftRight == 1) { + cmdCode |= 0x02; // 左移 + } + if (upDown == 2) { + cmdCode |= 0x04; // 下移 + } else if (upDown == 1) { + cmdCode |= 0x08; // 上移 + } + if (inOut == 2) { + cmdCode |= 0x10; // 放大 + } else if (inOut == 1) { + cmdCode |= 0x20; // 缩小 + } + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", moveSpeed); + builder.append(strTmp, 0, 2); + builder.append(strTmp, 0, 2); + strTmp = String.format("%X", zoomSpeed); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + + /** + * 云台指令码计算 + * + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + private static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) { + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter1); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter2); + builder.append(strTmp, 0, 2); + strTmp = String.format("%X", combineCode2); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + + /** + * 云台控制,支持方向与缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 + * @param zoomSpeed 镜头缩放速度 + */ + @Override + public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, + int zoomSpeed) { + try { + String cmdStr = cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); + StringBuffer ptzXml = new StringBuffer(200); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdStr + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); + + transmitRequest(device, request); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + } + return false; + } + + /** + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + @Override + public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) { + try { + String cmdStr = frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2); + System.out.println("控制字符串:" + cmdStr); + StringBuffer ptzXml = new StringBuffer(200); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdStr + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtz" + tm, null); + transmitRequest(device, request); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + } + return false; + } + + /** + * 请求预览视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param event hook订阅 + * @param errorEvent sip错误订阅 + */ + @Override + public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { + StreamInfo streamInfo = streamSession.createPlayStreamInfo(device, channelId); + String mediaServerIp = streamInfo.getMediaServerIp(); + String streamId = streamInfo.getStreamId(); + String ssrc = streamInfo.getSsrc(); + String streamMode = device.getStreamMode().toUpperCase(); + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); + String mediaPort; + // 使用动态udp端口 + if (mediaConfig.getRtpEnable()) { + mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerIp, streamId) + ""; + } else { + mediaPort = mediaInfo.getRtpProxyPort(); + } + + // 添加订阅 + JSONObject subscribeKey = new JSONObject(); + subscribeKey.put("app", "rtp"); + subscribeKey.put("id", streamId); + + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); + // + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + "00000" + " 0 0 IN IP4 " + mediaServerIp + "\r\n"); + content.append("s=Play\r\n"); + content.append("c=IN IP4 " + mediaServerIp + "\r\n"); + content.append("t=0 0\r\n"); + + if (mediaConfig.getSeniorSdp()) { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + mediaPort + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); + content.append("a=fmtp:99 profile-level-id=3\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 98 97\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 98 97\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + mediaPort + " RTP/AVP 96 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + } + + content.append("y=" + ssrc + "\r\n");//ssrc + try { + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc); + ClientTransaction transaction = transmitRequest(device, request, errorEvent); + streamSession.putClientTransaction(channelId, streamId, transaction); + } catch (SipException | ParseException | InvalidArgumentException e) { + logger.error("请求直播失败", e); + streamSession.remove(streamInfo); + } + } + + /** + * 请求回放视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + @Override + public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event + , SipSubscribe.Event errorEvent) { + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); + StreamInfo streamInfo = streamSession.createPlayBackStreamInfo(device, channelId); + String mediaServerIp = streamInfo.getMediaServerIp(); + String streamId = streamInfo.getStreamId(); + String ssrc = streamInfo.getSsrc(); + // 添加订阅 + JSONObject subscribeKey = new JSONObject(); + subscribeKey.put("app", "rtp"); + subscribeKey.put("id", streamId); + + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, subscribeKey, event); + + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + sipConfig.getSipId() + " 0 0 IN IP4 " + sipConfig.getSipIp() + "\r\n"); + content.append("s=Playback\r\n"); + content.append("u=" + channelId + ":0\r\n"); + content.append("c=IN IP4 " + mediaServerIp + "\r\n"); + content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " " + + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n"); + String mediaPort = null; + // 使用动态udp端口 + if (mediaConfig.getRtpEnable()) { + mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerIp, streamId) + ""; + } else { + mediaPort = mediaInfo.getRtpProxyPort(); + } + String streamMode = device.getStreamMode().toUpperCase(); + + if (mediaConfig.getSeniorSdp()) { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + mediaPort + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); + content.append("a=fmtp:99 profile-level-id=3\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 98 97\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + mediaPort + " TCP/RTP/AVP 96 98 97\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + mediaPort + " RTP/AVP 96 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + + content.append("y=" + ssrc + "\r\n");//ssrc + try { + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null); + ClientTransaction transaction = transmitRequest(device, request, errorEvent); + streamSession.putClientTransaction(channelId, streamId, transaction); + } catch (SipException | ParseException | InvalidArgumentException e) { + logger.error("请求回放失败", e); + streamSession.remove(streamInfo); + } + } + + + /** + * 视频流停止 + */ + @Override + public void stopStreamByeCmd(StreamInfo streamInfo, SipSubscribe.Event okEvent) { + String channelId = streamInfo.getChannelId(); + String streamId = streamInfo.getStreamId(); + ClientTransaction transaction = streamSession.getClientTransaction(channelId, streamId); + // wvp信令服务重启后 + if (transaction == null) { + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + streamSession.remove(streamInfo); + okEvent.response(null); + return; + } + + Dialog dialog = transaction.getDialog(); + if (dialog == null) { + return; + } + try { + Request byeRequest = dialog.createRequest(Request.BYE); + SipURI byeURI = (SipURI) byeRequest.getRequestURI(); + String vh = transaction.getRequest().getHeader(ViaHeader.NAME).toString(); + Pattern p = Pattern.compile("(\\d+\\.\\d+\\.\\d+\\.\\d+)\\:(\\d+)"); + Matcher matcher = p.matcher(vh); + if (matcher.find()) { + String ip = matcher.group(1); + byeURI.setHost(ip); + String port = matcher.group(2); + byeURI.setPort(Integer.parseInt(port)); + } + ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME); + String protocol = viaHeader.getTransport().toUpperCase(); + ClientTransaction clientTransaction = null; + if ("TCP".equals(protocol)) { + clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest); + } else if ("UDP".equals(protocol)) { + clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); + } + + CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME); + if (okEvent != null) { + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); + } + + dialog.sendRequest(clientTransaction); + + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + streamSession.remove(streamInfo); + zlmrtpServerFactory.closeRTPServer(streamInfo.getMediaServerIp(), streamId); + } catch (TransactionDoesNotExistException e) { + e.printStackTrace(); + } catch (SipException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + /** + * 语音广播 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + @Override + public boolean audioBroadcastCmd(Device device, String channelId) { + // TODO Auto-generated method stub + return false; + } + + /** + * 音视频录像控制 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param recordCmdStr 录像命令:Record / StopRecord + */ + @Override + public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (XmlUtil.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("" + recordCmdStr + "\r\n"); + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 远程启动控制命令 + * + * @param device 视频设备 + */ + @Override + public boolean teleBootCmd(Device device) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("Boot\r\n"); + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null); + transmitRequest(device, request); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 报警布防/撤防命令 + * + * @param device 视频设备 + * @param guardCmdStr "SetGuard"/"ResetGuard" + */ + @Override + public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("" + guardCmdStr + "\r\n"); + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 报警复位命令 + * + * @param device 视频设备 + */ + @Override + public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("ResetAlarm\r\n"); + if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { + cmdXml.append("\r\n"); + } + if (!XmlUtil.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!XmlUtil.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!XmlUtil.isEmpty(alarmMethod) || !XmlUtil.isEmpty(alarmType)) { + cmdXml.append("\r\n"); + } + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + @Override + public boolean iFrameCmd(Device device, String channelId) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (XmlUtil.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("Send\r\n"); + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null); + transmitRequest(device, request); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 看守位控制命令 + * + * @param device 视频设备 + * @param enabled 看守位使能:1 = 开启,0 = 关闭 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 + */ + @Override + public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (XmlUtil.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) { + cmdXml.append("1\r\n"); + if (NumericUtil.isInteger(resetTime)) { + cmdXml.append("" + resetTime + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } + if (NumericUtil.isInteger(presetIndex)) { + cmdXml.append("" + presetIndex + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } + } else { + cmdXml.append("0\r\n"); + } + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 设备配置命令 + * + * @param device 视频设备 + */ + @Override + public boolean deviceConfigCmd(Device device) { + // TODO Auto-generated method stub + return false; + } + + /** + * 设备配置命令:basicParam + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param name 设备/通道名称(可选) + * @param expiration 注册过期时间(可选) + * @param heartBeatInterval 心跳间隔时间(可选) + * @param heartBeatCount 心跳超时次数(可选) + */ + @Override + public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, + String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceConfig\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (XmlUtil.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + if (!XmlUtil.isEmpty(name)) { + cmdXml.append("" + name + "\r\n"); + } + if (NumericUtil.isInteger(expiration)) { + if (Integer.valueOf(expiration) > 0) { + cmdXml.append("" + expiration + "\r\n"); + } + } + if (NumericUtil.isInteger(heartBeatInterval)) { + if (Integer.valueOf(heartBeatInterval) > 0) { + cmdXml.append("" + heartBeatInterval + "\r\n"); + } + } + if (NumericUtil.isInteger(heartBeatCount)) { + if (Integer.valueOf(heartBeatCount) > 0) { + cmdXml.append("" + heartBeatCount + "\r\n"); + } + } + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 查询设备状态 + * + * @param device 视频设备 + */ + @Override + public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) { + try { + StringBuffer catalogXml = new StringBuffer(200); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append("DeviceStatus\r\n"); + catalogXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + catalogXml.append("" + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null); + + transmitRequest(device, request, errorEvent); + return true; + + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 查询设备信息 + * + * @param device 视频设备 + */ + @Override + public boolean deviceInfoQuery(Device device) { + try { + StringBuffer catalogXml = new StringBuffer(200); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append("DeviceInfo\r\n"); + catalogXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + catalogXml.append("" + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDev" + tm, null); + + transmitRequest(device, request); + + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 查询目录列表 + * + * @param device 视频设备 + */ + @Override + public boolean catalogQuery(Device device, SipSubscribe.Event errorEvent) { + // 清空通道 + storager.cleanChannelsForDevice(device.getDeviceId()); + try { + StringBuffer catalogXml = new StringBuffer(200); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append("Catalog\r\n"); + catalogXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + catalogXml.append("" + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCat" + tm, null); + + transmitRequest(device, request, errorEvent); + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 查询录像信息 + * + * @param device 视频设备 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + @Override + public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime) { + + try { + StringBuffer recordInfoXml = new StringBuffer(200); + recordInfoXml.append("\r\n"); + recordInfoXml.append("\r\n"); + recordInfoXml.append("RecordInfo\r\n"); + recordInfoXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + recordInfoXml.append("" + channelId + "\r\n"); + recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "\r\n"); + recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "\r\n"); + recordInfoXml.append("0\r\n"); + // 大华NVR要求必须增加一个值为all的文本元素节点Type + recordInfoXml.append("all\r\n"); + recordInfoXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "fromRec" + tm, null); + + transmitRequest(device, request); + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 查询报警信息 + * + * @param device 视频设备 + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethods 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + @Override + public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType, + String startTime, String endTime, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Alarm\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + if (!XmlUtil.isEmpty(startPriority)) { + cmdXml.append("" + startPriority + "\r\n"); + } + if (!XmlUtil.isEmpty(endPriority)) { + cmdXml.append("" + endPriority + "\r\n"); + } + if (!XmlUtil.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!XmlUtil.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!XmlUtil.isEmpty(startTime)) { + cmdXml.append("" + startTime + "\r\n"); + } + if (!XmlUtil.isEmpty(endTime)) { + cmdXml.append("" + endTime + "\r\n"); + } + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 查询设备配置 + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param configType 配置类型: + */ + @Override + public boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("ConfigDownload\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (XmlUtil.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("" + configType + "\r\n"); + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 查询设备预置位置 + * + * @param device 视频设备 + */ + @Override + public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("PresetQuery\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (XmlUtil.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null); + transmitRequest(device, request, errorEvent); + return true; + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 查询移动设备位置数据 + * + * @param device 视频设备 + */ + @Override + public boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) { + try { + StringBuffer mobilePostitionXml = new StringBuffer(200); + mobilePostitionXml.append("\r\n"); + mobilePostitionXml.append("\r\n"); + mobilePostitionXml.append("MobilePosition\r\n"); + mobilePostitionXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + mobilePostitionXml.append("" + device.getDeviceId() + "\r\n"); + mobilePostitionXml.append("60\r\n"); + mobilePostitionXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null); + + transmitRequest(device, request, errorEvent); + + } catch (SipException | ParseException | InvalidArgumentException e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 订阅、取消订阅移动位置 + * + * @param device 视频设备 + * @param expires 订阅超时时间 + * @param interval 上报时间间隔 + * @return true = 命令发送成功 + */ + public boolean mobilePositionSubscribe(Device device, int expires, int interval) { + try { + StringBuffer subscribePostitionXml = new StringBuffer(200); + subscribePostitionXml.append("\r\n"); + subscribePostitionXml.append("\r\n"); + subscribePostitionXml.append("MobilePosition\r\n"); + subscribePostitionXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + subscribePostitionXml.append("" + device.getDeviceId() + "\r\n"); + if (expires > 0) { + subscribePostitionXml.append("" + String.valueOf(interval) + "\r\n"); + } + subscribePostitionXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence"); //Position;id=" + tm.substring(tm.length() - 4)); + transmitRequest(device, request); + + return true; + + } catch (NumberFormatException | ParseException | InvalidArgumentException | SipException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 订阅、取消订阅报警信息 + * + * @param device 视频设备 + * @param expires 订阅过期时间(0 = 取消订阅) + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) { + try { + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Alarm\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + if (!XmlUtil.isEmpty(startPriority)) { + cmdXml.append("" + startPriority + "\r\n"); + } + if (!XmlUtil.isEmpty(endPriority)) { + cmdXml.append("" + endPriority + "\r\n"); + } + if (!XmlUtil.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!XmlUtil.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!XmlUtil.isEmpty(startTime)) { + cmdXml.append("" + startTime + "\r\n"); + } + if (!XmlUtil.isEmpty(endTime)) { + cmdXml.append("" + endTime + "\r\n"); + } + cmdXml.append("\r\n"); + + String tm = Long.toString(System.currentTimeMillis()); + Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "viaTagPos" + tm, "fromTagPos" + tm, null, expires, "presence"); + transmitRequest(device, request); + + return true; + + } catch (NumberFormatException | ParseException | InvalidArgumentException | SipException e) { + e.printStackTrace(); + return false; + } + } + + + private ClientTransaction transmitRequest(Device device, Request request) throws SipException { + return transmitRequest(device, request, null, null); + } + + private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent) throws SipException { + return transmitRequest(device, request, errorEvent, null); + } + + private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException { + ClientTransaction clientTransaction = null; + if ("TCP".equals(device.getTransport())) { + clientTransaction = tcpSipProvider.getNewClientTransaction(request); + } else if ("UDP".equals(device.getTransport())) { + clientTransaction = udpSipProvider.getNewClientTransaction(request); + } + + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + // 添加错误订阅 + if (errorEvent != null) { + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), errorEvent); + } + // 添加订阅 + if (okEvent != null) { + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); + } + + clientTransaction.sendRequest(); + return clientTransaction; + } + + + @Override + public void closeRTPServer(Device device, String channelId) { + if (mediaConfig.getRtpEnable()) { + String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); + String mediaServerIp = streamSession.getMediaServerIp(channelId, streamId); + zlmrtpServerFactory.closeRTPServer(mediaServerIp, streamId); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java index e7fbfe0d..7c9a5521 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java @@ -1,825 +1,823 @@ -package com.genersoft.iot.vmp.gb28181.transmit.request.impl; - -import java.io.ByteArrayInputStream; -import java.text.ParseException; -import java.util.*; - -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.SipException; -import javax.sip.message.Request; -import javax.sip.message.Response; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; -import com.genersoft.iot.vmp.gb28181.bean.RecordItem; -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread; -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; -import com.genersoft.iot.vmp.gb28181.utils.DateUtil; -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.GpsUtil; -import com.genersoft.iot.vmp.utils.SpringBeanFactory; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.dom4j.io.SAXReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -/** - * @Description:MESSAGE请求处理器 - * @author: swwheihei - * @date: 2020年5月3日 下午5:32:41 - */ -@SuppressWarnings(value={"unchecked", "rawtypes"}) -public class MessageRequestProcessor extends SIPRequestAbstractProcessor { - - public static volatile List threadNameList = new ArrayList(); - - private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup"); - - private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); - - private SIPCommander cmder; - - private IVideoManagerStorager storager; - - private IRedisCatchStorage redisCatchStorage; - - private EventPublisher publisher; - - private RedisUtil redis; - - private DeferredResultHolder deferredResultHolder; - - private DeviceOffLineDetector offLineDetector; - - private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_"; - - private static final String MESSAGE_KEEP_ALIVE = "Keepalive"; - private static final String MESSAGE_CONFIG_DOWNLOAD = "ConfigDownload"; - private static final String MESSAGE_CATALOG = "Catalog"; - private static final String MESSAGE_DEVICE_INFO = "DeviceInfo"; - private static final String MESSAGE_ALARM = "Alarm"; - private static final String MESSAGE_RECORD_INFO = "RecordInfo"; - private static final String MESSAGE_MEDIA_STATUS = "MediaStatus"; - // private static final String MESSAGE_BROADCAST = "Broadcast"; - private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus"; - private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl"; - private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig"; - private static final String MESSAGE_MOBILE_POSITION = "MobilePosition"; - // private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval"; - private static final String MESSAGE_PRESET_QUERY = "PresetQuery"; - - /** - * 处理MESSAGE请求 - * - * @param evt - */ - @Override - public void process(RequestEvent evt) { - - try { - Element rootElement = getRootElement(evt); - String cmd = XmlUtil.getText(rootElement, "CmdType"); - - if (MESSAGE_KEEP_ALIVE.equals(cmd)) { - logger.info("接收到KeepAlive消息"); - processMessageKeepAlive(evt); - } else if (MESSAGE_CONFIG_DOWNLOAD.equals(cmd)) { - logger.info("接收到ConfigDownload消息"); - processMessageConfigDownload(evt); - } else if (MESSAGE_CATALOG.equals(cmd)) { - logger.info("接收到Catalog消息"); - processMessageCatalogList(evt); - } else if (MESSAGE_DEVICE_INFO.equals(cmd)) { - logger.info("接收到DeviceInfo消息"); - processMessageDeviceInfo(evt); - } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) { - logger.info("接收到DeviceStatus消息"); - processMessageDeviceStatus(evt); - } else if (MESSAGE_DEVICE_CONTROL.equals(cmd)) { - logger.info("接收到DeviceControl消息"); - processMessageDeviceControl(evt); - } else if (MESSAGE_DEVICE_CONFIG.equals(cmd)) { - logger.info("接收到DeviceConfig消息"); - processMessageDeviceConfig(evt); - } else if (MESSAGE_ALARM.equals(cmd)) { - logger.info("接收到Alarm消息"); - processMessageAlarm(evt); - } else if (MESSAGE_RECORD_INFO.equals(cmd)) { - logger.info("接收到RecordInfo消息"); - processMessageRecordInfo(evt); - }else if (MESSAGE_MEDIA_STATUS.equals(cmd)) { - logger.info("接收到MediaStatus消息"); - processMessageMediaStatus(evt); - } else if (MESSAGE_MOBILE_POSITION.equals(cmd)) { - logger.info("接收到MobilePosition消息"); - processMessageMobilePosition(evt); - } else if (MESSAGE_PRESET_QUERY.equals(cmd)) { - logger.info("接收到PresetQuery消息"); - processMessagePresetQuery(evt); - } else { - logger.info("接收到消息:" + cmd); - responseAck(evt); - } - } catch (DocumentException | SipException |InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 处理MobilePosition移动位置消息 - * - * @param evt - */ - private void processMessageMobilePosition(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - MobilePosition mobilePosition = new MobilePosition(); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getTextTrim().toString(); - Device device = storager.queryVideoDevice(deviceId); - if (device != null) { - if (!StringUtils.isEmpty(device.getName())) { - mobilePosition.setDeviceName(device.getName()); - } - } - mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID")); - mobilePosition.setTime(XmlUtil.getText(rootElement, "Time")); - mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); - mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { - mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed"))); - } else { - mobilePosition.setSpeed(0.0); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) { - mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction"))); - } else { - mobilePosition.setDirection(0.0); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) { - mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude"))); - } else { - mobilePosition.setAltitude(0.0); - } - mobilePosition.setReportSource("Mobile Position"); - BaiduPoint bp = new BaiduPoint(); - bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); - logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); - mobilePosition.setGeodeticSystem("BD-09"); - mobilePosition.setCnLng(bp.getBdLng()); - mobilePosition.setCnLat(bp.getBdLat()); - if (!userSetup.getSavePositionHistory()) { - storager.clearMobilePositionsByDeviceId(deviceId); - } - storager.insertMobilePosition(mobilePosition); - //回复 200 OK - responseAck(evt); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceStatus设备状态Message - * - * @param evt - */ - private void processMessageDeviceStatus(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 检查设备是否存在, 不存在则不回复 - if (storager.exists(deviceId)) { - // 回复200 OK - responseAck(evt); - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } else { - } - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceControl设备状态Message - * - * @param evt - */ - private void processMessageDeviceControl(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - //String result = XmlUtil.getText(rootElement, "Result"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) {//} !XmlUtil.isEmpty(result)) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceControl指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceConfig设备状态Message - * - * @param evt - */ - private void processMessageDeviceConfig(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceConfig指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理ConfigDownload设备状态Message - * - * @param evt - */ - private void processMessageConfigDownload(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceConfig指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理PresetQuery预置位列表Message - * - * @param evt - */ - private void processMessagePresetQuery(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 回复200 OK - responseAck(evt); - if (rootElement.getName().equals("Response")) {// !XmlUtil.isEmpty(result)) { - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } else { - // 此处是上级发出的DeviceControl指令 - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /** - * 处理DeviceInfo设备信息Message - * - * @param evt - */ - private void processMessageDeviceInfo(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getTextTrim().toString(); - - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - device.setName(XmlUtil.getText(rootElement, "DeviceName")); - device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer")); - device.setModel(XmlUtil.getText(rootElement, "Model")); - device.setFirmware(XmlUtil.getText(rootElement, "Firmware")); - if (StringUtils.isEmpty(device.getStreamMode())) { - device.setStreamMode("UDP"); - } - storager.updateDevice(device); - - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICEINFO); - msg.setData(device); - deferredResultHolder.invokeResult(msg); - // 回复200 OK - responseAck(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /*** - * 收到catalog设备目录列表请求 处理 - * - * @param evt - */ - private void processMessageCatalogList(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText(); - Element deviceListElement = rootElement.element("DeviceList"); - if (deviceListElement == null) { - return; - } - Iterator deviceListIterator = deviceListElement.elementIterator(); - if (deviceListIterator != null) { - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - // 遍历DeviceList - while (deviceListIterator.hasNext()) { - Element itemDevice = deviceListIterator.next(); - Element channelDeviceElement = itemDevice.element("DeviceID"); - if (channelDeviceElement == null) { - continue; - } - String channelDeviceId = channelDeviceElement.getTextTrim(); - Element channdelNameElement = itemDevice.element("Name"); - String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : ""; - Element statusElement = itemDevice.element("Status"); - String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON"; - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setName(channelName); - deviceChannel.setChannelId(channelDeviceId); - // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 - if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) { - deviceChannel.setStatus(1); - } - if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { - deviceChannel.setStatus(0); - } - - deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer")); - deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model")); - deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner")); - deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode")); - deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block")); - deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address")); - if (XmlUtil.getText(itemDevice, "Parental") == null || XmlUtil.getText(itemDevice, "Parental") == "") { - deviceChannel.setParental(0); - } else { - deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental"))); - } - deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID")); - if (XmlUtil.getText(itemDevice, "SafetyWay") == null || XmlUtil.getText(itemDevice, "SafetyWay")== "") { - deviceChannel.setSafetyWay(0); - } else { - deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay"))); - } - if (XmlUtil.getText(itemDevice, "RegisterWay") == null || XmlUtil.getText(itemDevice, "RegisterWay") =="") { - deviceChannel.setRegisterWay(1); - } else { - deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay"))); - } - deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum")); - if (XmlUtil.getText(itemDevice, "Certifiable") == null || XmlUtil.getText(itemDevice, "Certifiable") == "") { - deviceChannel.setCertifiable(0); - } else { - deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable"))); - } - if (XmlUtil.getText(itemDevice, "ErrCode") == null || XmlUtil.getText(itemDevice, "ErrCode") == "") { - deviceChannel.setErrCode(0); - } else { - deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode"))); - } - deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime")); - deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy")); - deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress")); - if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") =="") { - deviceChannel.setPort(0); - } else { - deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port"))); - } - deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password")); - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) { - deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude"))); - } else { - deviceChannel.setLongitude(0.00); - } - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) { - deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude"))); - } else { - deviceChannel.setLatitude(0.00); - } - if (XmlUtil.getText(itemDevice, "PTZType") == null || XmlUtil.getText(itemDevice, "PTZType") == "") { - deviceChannel.setPTZType(0); - } else { - deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType"))); - } - deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC - storager.updateChannel(device.getDeviceId(), deviceChannel); - } - - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG); - msg.setData(device); - deferredResultHolder.invokeResult(msg); - // 回复200 OK - responseAck(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /*** - * alarm设备报警信息处理 - * @param evt - */ - private void processMessageAlarm(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText().toString(); - // 回复200 OK - responseAck(evt); - - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - return; - } - - if (rootElement.getName().equals("Notify")) { // 处理报警通知 - DeviceAlarm deviceAlarm = new DeviceAlarm(); - deviceAlarm.setDeviceId(deviceId); - deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority")); - deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); - deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime")); - if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { - deviceAlarm.setAlarmDescription(""); - } else { - deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription")); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) { - deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); - } else { - deviceAlarm.setLongitude(0.00); - } - if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) { - deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); - } else { - deviceAlarm.setLatitude(0.00); - } - - if (!XmlUtil.isEmpty(deviceAlarm.getAlarmMethod())) { - if ( deviceAlarm.getAlarmMethod().equals("4")) { - MobilePosition mobilePosition = new MobilePosition(); - mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); - mobilePosition.setTime(deviceAlarm.getAlarmTime()); - mobilePosition.setLongitude(deviceAlarm.getLongitude()); - mobilePosition.setLatitude(deviceAlarm.getLatitude()); - mobilePosition.setReportSource("GPS Alarm"); - BaiduPoint bp = new BaiduPoint(); - bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); - logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); - mobilePosition.setGeodeticSystem("BD-09"); - mobilePosition.setCnLng(bp.getBdLng()); - mobilePosition.setCnLat(bp.getBdLat()); - if (!userSetup.getSavePositionHistory()) { - storager.clearMobilePositionsByDeviceId(deviceId); - } - storager.insertMobilePosition(mobilePosition); - } - } - // TODO: 需要实现存储报警信息、报警分类 - - if (offLineDetector.isOnline(deviceId)) { - publisher.deviceAlarmEventPublish(deviceAlarm); - } - } else if (rootElement.getName().equals("Response")) { // 处理报警查询响应 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(rootElement, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_ALARM); - msg.setData(json); - deferredResultHolder.invokeResult(msg); - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - // } catch (DocumentException e) { - e.printStackTrace(); - } - } - - /*** - * 收到keepalive请求 处理 - * - * @param evt - */ - private void processMessageKeepAlive(RequestEvent evt) { - try { - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - // 检查设备是否存在, 不存在则不回复 - if (storager.exists(deviceId)) { - // 回复200 OK - responseAck(evt); - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } else { - } - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - /*** - * 处理RecordInfo设备录像列表Message请求 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致 - * - * @param evt - */ - private void processMessageRecordInfo(RequestEvent evt) { - try { - // 回复200 OK - responseAck(evt); - String uuid = UUID.randomUUID().toString().replace("-", ""); - RecordInfo recordInfo = new RecordInfo(); - Element rootElement = getRootElement(evt); - Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText().toString(); - recordInfo.setDeviceId(deviceId); - recordInfo.setName(XmlUtil.getText(rootElement, "Name")); - if (XmlUtil.getText(rootElement, "SumNum")== null || XmlUtil.getText(rootElement, "SumNum") =="") { - recordInfo.setSumNum(0); - } else { - recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement, "SumNum"))); - } - String sn = XmlUtil.getText(rootElement, "SN"); - Element recordListElement = rootElement.element("RecordList"); - if (recordListElement == null || recordInfo.getSumNum() == 0) { - logger.info("无录像数据"); - RequestMessage msg = new RequestMessage(); - msg.setDeviceId(deviceId); - msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); - msg.setData(recordInfo); - deferredResultHolder.invokeResult(msg); - } else { - Iterator recordListIterator = recordListElement.elementIterator(); - List recordList = new ArrayList(); - if (recordListIterator != null) { - RecordItem record = new RecordItem(); - logger.info("处理录像列表数据..."); - // 遍历DeviceList - while (recordListIterator.hasNext()) { - Element itemRecord = recordListIterator.next(); - Element recordElement = itemRecord.element("DeviceID"); - if (recordElement == null) { - logger.info("记录为空,下一个..."); - continue; - } - record = new RecordItem(); - record.setDeviceId(XmlUtil.getText(itemRecord, "DeviceID")); - record.setName(XmlUtil.getText(itemRecord, "Name")); - record.setFilePath(XmlUtil.getText(itemRecord, "FilePath")); - record.setAddress(XmlUtil.getText(itemRecord, "Address")); - record.setStartTime( - DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "StartTime"))); - record.setEndTime( - DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "EndTime"))); - record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 - : Integer.parseInt(XmlUtil.getText(itemRecord, "Secrecy"))); - record.setType(XmlUtil.getText(itemRecord, "Type")); - record.setRecorderId(XmlUtil.getText(itemRecord, "RecorderID")); - recordList.add(record); - } - recordInfo.setRecordList(recordList); - } - - // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题 - String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn; - redis.set(cacheKey + "_" + uuid, recordList, 90); - if (!threadNameList.contains(cacheKey)) { - threadNameList.add(cacheKey); - CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo); - chk.setName(cacheKey); - chk.setDeferredResultHolder(deferredResultHolder); - chk.setRedis(redis); - chk.setLogger(logger); - chk.start(); - if (logger.isDebugEnabled()) { - logger.debug("Start Thread " + cacheKey + "."); - } - } else { - if (logger.isDebugEnabled()) { - logger.debug("Thread " + cacheKey + " already started."); - } - } - - // 存在录像且如果当前录像明细个数小于总条数,说明拆包返回,需要组装,暂不返回 - // if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) { - // // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分 - // String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn; - - // redis.set(cacheKey + "_" + uuid, recordList, 90); - // List cacheKeys = redis.scan(cacheKey + "_*"); - // List totalRecordList = new ArrayList(); - // for (int i = 0; i < cacheKeys.size(); i++) { - // totalRecordList.addAll((List) redis.get(cacheKeys.get(i).toString())); - // } - // if (totalRecordList.size() < recordInfo.getSumNum()) { - // logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项"); - // return; - // } - // logger.info("录像数据已全部获取,共" + recordInfo.getSumNum() + "项"); - // recordInfo.setRecordList(totalRecordList); - // for (int i = 0; i < cacheKeys.size(); i++) { - // redis.del(cacheKeys.get(i).toString()); - // } - // } - // // 自然顺序排序, 元素进行升序排列 - // recordInfo.getRecordList().sort(Comparator.naturalOrder()); - } - // 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作 - // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作 - // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据 - - // RequestMessage msg = new RequestMessage(); - // msg.setDeviceId(deviceId); - // msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); - // msg.setData(recordInfo); - // deferredResultHolder.invokeResult(msg); - // logger.info("处理完成,返回结果"); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 收到MediaStatus消息处理 - * - * @param evt - */ - private void processMessageMediaStatus(RequestEvent evt){ - try { - // 回复200 OK - responseAck(evt); - Element rootElement = getRootElement(evt); - String deviceId = XmlUtil.getText(rootElement, "DeviceID"); - String NotifyType =XmlUtil.getText(rootElement, "NotifyType"); - if (NotifyType.equals("121")){ - logger.info("媒体播放完毕,通知关流"); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, "*"); - if (streamInfo != null) { - redisCatchStorage.stopPlayback(streamInfo); - cmder.streamByeCmd(streamInfo.getStreamId()); - } - } - } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { - e.printStackTrace(); - } - } - - - /*** - * 回复200 OK - * @param evt - * @throws SipException - * @throws InvalidArgumentException - * @throws ParseException - */ - private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { - Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); - getServerTransaction(evt).sendResponse(response); - } - - private Element getRootElement(RequestEvent evt) throws DocumentException { - Request request = evt.getRequest(); - SAXReader reader = new SAXReader(); - reader.setEncoding("gbk"); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); - return xml.getRootElement(); - } - - public void setCmder(SIPCommander cmder) { - this.cmder = cmder; - } - - public void setStorager(IVideoManagerStorager storager) { - this.storager = storager; - } - - public void setPublisher(EventPublisher publisher) { - this.publisher = publisher; - } - - public void setRedis(RedisUtil redis) { - this.redis = redis; - } - - public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { - this.deferredResultHolder = deferredResultHolder; - } - - public void setOffLineDetector(DeviceOffLineDetector offLineDetector) { - this.offLineDetector = offLineDetector; - } - - public IRedisCatchStorage getRedisCatchStorage() { - return redisCatchStorage; - } - - public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { - this.redisCatchStorage = redisCatchStorage; - } +package com.genersoft.iot.vmp.gb28181.transmit.request.impl; + +import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.UserSetup; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread; +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor; +import com.genersoft.iot.vmp.gb28181.utils.DateUtil; +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.utils.GpsUtil; +import com.genersoft.iot.vmp.utils.SpringBeanFactory; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.io.ByteArrayInputStream; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +/** + * @Description:MESSAGE请求处理器 + * @author: swwheihei + * @date: 2020年5月3日 下午5:32:41 + */ +@Slf4j +@Data +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +public class MessageRequestProcessor extends SIPRequestAbstractProcessor { + + public static volatile List threadNameList = new ArrayList(); + + private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup"); + + private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); + + private SIPCommander cmder; + + private IVideoManagerStorager storager; + + private IRedisCatchStorage redisCatchStorage; + + private VideoStreamSessionManager streamSession; + + private EventPublisher publisher; + + private RedisUtil redis; + + private DeferredResultHolder deferredResultHolder; + + private DeviceOffLineDetector offLineDetector; + + private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_"; + + private static final String MESSAGE_KEEP_ALIVE = "Keepalive"; + private static final String MESSAGE_CONFIG_DOWNLOAD = "ConfigDownload"; + private static final String MESSAGE_CATALOG = "Catalog"; + private static final String MESSAGE_DEVICE_INFO = "DeviceInfo"; + private static final String MESSAGE_ALARM = "Alarm"; + private static final String MESSAGE_RECORD_INFO = "RecordInfo"; + private static final String MESSAGE_MEDIA_STATUS = "MediaStatus"; + // private static final String MESSAGE_BROADCAST = "Broadcast"; + private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus"; + private static final String MESSAGE_DEVICE_CONTROL = "DeviceControl"; + private static final String MESSAGE_DEVICE_CONFIG = "DeviceConfig"; + private static final String MESSAGE_MOBILE_POSITION = "MobilePosition"; + // private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval"; + private static final String MESSAGE_PRESET_QUERY = "PresetQuery"; + + /** + * 处理MESSAGE请求 + * + * @param evt + */ + @Override + public void process(RequestEvent evt) { + + try { + Element rootElement = getRootElement(evt); + String cmd = XmlUtil.getText(rootElement, "CmdType"); + String deviceID = XmlUtil.getText(rootElement, "DeviceID"); + if (MESSAGE_KEEP_ALIVE.equals(cmd) || MESSAGE_ALARM.equals(cmd)) { + // KEEP_ALIVE 和 ALARM 消息太多,改成debug级别 + logger.debug("接收到{}消息,deviceID :{}", cmd, deviceID); + } else { + logger.info("接收到{}消息,deviceID :{}", cmd, deviceID); + } + + if (MESSAGE_KEEP_ALIVE.equals(cmd)) { + processMessageKeepAlive(evt); + } else if (MESSAGE_CONFIG_DOWNLOAD.equals(cmd)) { + processMessageConfigDownload(evt); + } else if (MESSAGE_CATALOG.equals(cmd)) { + processMessageCatalogList(evt); + } else if (MESSAGE_DEVICE_INFO.equals(cmd)) { + processMessageDeviceInfo(evt); + } else if (MESSAGE_DEVICE_STATUS.equals(cmd)) { + processMessageDeviceStatus(evt); + } else if (MESSAGE_DEVICE_CONTROL.equals(cmd)) { + processMessageDeviceControl(evt); + } else if (MESSAGE_DEVICE_CONFIG.equals(cmd)) { + processMessageDeviceConfig(evt); + } else if (MESSAGE_ALARM.equals(cmd)) { + processMessageAlarm(evt); + } else if (MESSAGE_RECORD_INFO.equals(cmd)) { + processMessageRecordInfo(evt); + } else if (MESSAGE_MEDIA_STATUS.equals(cmd)) { + // TODO channelId + processMessageMediaStatus(evt); + } else if (MESSAGE_MOBILE_POSITION.equals(cmd)) { + processMessageMobilePosition(evt); + } else if (MESSAGE_PRESET_QUERY.equals(cmd)) { + processMessagePresetQuery(evt); + } else { + responseAck(evt); + } + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + log.error("MessageRequestProcessor.process error!", e); + } + } + + /** + * 处理MobilePosition移动位置消息 + * + * @param evt + */ + private void processMessageMobilePosition(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + MobilePosition mobilePosition = new MobilePosition(); + Element deviceIdElement = rootElement.element("DeviceID"); + String deviceId = deviceIdElement.getTextTrim().toString(); + Device device = storager.queryVideoDevice(deviceId); + if (device != null) { + if (!StringUtils.isEmpty(device.getName())) { + mobilePosition.setDeviceName(device.getName()); + } + } + mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID")); + mobilePosition.setTime(XmlUtil.getText(rootElement, "Time")); + mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); + mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { + mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed"))); + } else { + mobilePosition.setSpeed(0.0); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) { + mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction"))); + } else { + mobilePosition.setDirection(0.0); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) { + mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude"))); + } else { + mobilePosition.setAltitude(0.0); + } + mobilePosition.setReportSource("Mobile Position"); + BaiduPoint bp = new BaiduPoint(); + bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); + logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); + mobilePosition.setGeodeticSystem("BD-09"); + mobilePosition.setCnLng(bp.getBdLng()); + mobilePosition.setCnLat(bp.getBdLat()); + if (!userSetup.getSavePositionHistory()) { + storager.clearMobilePositionsByDeviceId(deviceId); + } + storager.insertMobilePosition(mobilePosition); + //回复 200 OK + responseAck(evt); + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + e.printStackTrace(); + } + } + + /** + * 处理DeviceStatus设备状态Message + * + * @param evt + */ + private void processMessageDeviceStatus(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + // 检查设备是否存在, 不存在则不回复 + if (storager.exists(deviceId)) { + // 回复200 OK + responseAck(evt); + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS); + msg.setData(json); + deferredResultHolder.invokeResult(msg); + + if (offLineDetector.isOnline(deviceId)) { + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); + } else { + } + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + e.printStackTrace(); + } + } + + /** + * 处理DeviceControl设备状态Message + * + * @param evt + */ + private void processMessageDeviceControl(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + //String result = XmlUtil.getText(rootElement, "Result"); + // 回复200 OK + responseAck(evt); + if (rootElement.getName().equals("Response")) {//} !XmlUtil.isEmpty(result)) { + // 此处是对本平台发出DeviceControl指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL); + msg.setData(json); + deferredResultHolder.invokeResult(msg); + } else { + // 此处是上级发出的DeviceControl指令 + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + e.printStackTrace(); + } + } + + /** + * 处理DeviceConfig设备状态Message + * + * @param evt + */ + private void processMessageDeviceConfig(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + // 回复200 OK + responseAck(evt); + if (rootElement.getName().equals("Response")) { + // 此处是对本平台发出DeviceControl指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG); + msg.setData(json); + deferredResultHolder.invokeResult(msg); + } else { + // 此处是上级发出的DeviceConfig指令 + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + e.printStackTrace(); + } + } + + /** + * 处理ConfigDownload设备状态Message + * + * @param evt + */ + private void processMessageConfigDownload(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + // 回复200 OK + responseAck(evt); + if (rootElement.getName().equals("Response")) { + // 此处是对本平台发出DeviceControl指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD); + msg.setData(json); + deferredResultHolder.invokeResult(msg); + } else { + // 此处是上级发出的DeviceConfig指令 + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + e.printStackTrace(); + } + } + + /** + * 处理PresetQuery预置位列表Message + * + * @param evt + */ + private void processMessagePresetQuery(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + // 回复200 OK + responseAck(evt); + if (rootElement.getName().equals("Response")) {// !XmlUtil.isEmpty(result)) { + // 此处是对本平台发出DeviceControl指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_PRESETQUERY); + msg.setData(json); + deferredResultHolder.invokeResult(msg); + } else { + // 此处是上级发出的DeviceControl指令 + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + e.printStackTrace(); + } + } + + /** + * 处理DeviceInfo设备信息Message + * + * @param evt + */ + private void processMessageDeviceInfo(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + Element deviceIdElement = rootElement.element("DeviceID"); + String deviceId = deviceIdElement.getTextTrim().toString(); + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + device.setName(XmlUtil.getText(rootElement, "DeviceName")); + device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer")); + device.setModel(XmlUtil.getText(rootElement, "Model")); + device.setFirmware(XmlUtil.getText(rootElement, "Firmware")); + if (StringUtils.isEmpty(device.getStreamMode())) { + device.setStreamMode("UDP"); + } + storager.updateDevice(device); + + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICEINFO); + msg.setData(device); + deferredResultHolder.invokeResult(msg); + // 回复200 OK + responseAck(evt); + if (offLineDetector.isOnline(deviceId)) { + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); + } + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + log.error("MessageRequestProcessor.processMessageDeviceInfo error!", e); + } + } + + /*** + * 收到catalog设备目录列表请求 处理 + * + * @param evt + */ + private void processMessageCatalogList(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + Element deviceIdElement = rootElement.element("DeviceID"); + String deviceId = deviceIdElement.getText(); + Element deviceListElement = rootElement.element("DeviceList"); + if (deviceListElement == null) { + return; + } + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + String channelDeviceId = channelDeviceElement.getTextTrim(); + Element channdelNameElement = itemDevice.element("Name"); + String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : ""; + Element statusElement = itemDevice.element("Status"); + String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON"; + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setName(channelName); + deviceChannel.setChannelId(channelDeviceId); + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) { + deviceChannel.setStatus(1); + } + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { + deviceChannel.setStatus(0); + } + + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer")); + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model")); + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner")); + deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode")); + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block")); + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address")); + if (XmlUtil.getText(itemDevice, "Parental") == null || XmlUtil.getText(itemDevice, "Parental") == "") { + deviceChannel.setParental(0); + } else { + deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental"))); + } + deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID")); + if (XmlUtil.getText(itemDevice, "SafetyWay") == null || XmlUtil.getText(itemDevice, "SafetyWay") == "") { + deviceChannel.setSafetyWay(0); + } else { + deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay"))); + } + if (XmlUtil.getText(itemDevice, "RegisterWay") == null || XmlUtil.getText(itemDevice, "RegisterWay") == "") { + deviceChannel.setRegisterWay(1); + } else { + deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay"))); + } + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum")); + if (XmlUtil.getText(itemDevice, "Certifiable") == null || XmlUtil.getText(itemDevice, "Certifiable") == "") { + deviceChannel.setCertifiable(0); + } else { + deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable"))); + } + if (XmlUtil.getText(itemDevice, "ErrCode") == null || XmlUtil.getText(itemDevice, "ErrCode") == "") { + deviceChannel.setErrCode(0); + } else { + deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode"))); + } + deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime")); + deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy")); + deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress")); + if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") { + deviceChannel.setPort(0); + } else { + deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port"))); + } + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password")); + if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) { + deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude"))); + } else { + deviceChannel.setLongitude(0.00); + } + if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) { + deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude"))); + } else { + deviceChannel.setLatitude(0.00); + } + if (XmlUtil.getText(itemDevice, "PTZType") == null || XmlUtil.getText(itemDevice, "PTZType") == "") { + deviceChannel.setPTZType(0); + } else { + deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType"))); + } + deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC + storager.updateChannel(device.getDeviceId(), deviceChannel); + } + + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG); + msg.setData(device); + deferredResultHolder.invokeResult(msg); + // 回复200 OK + responseAck(evt); + if (offLineDetector.isOnline(deviceId)) { + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); + } + } + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + log.error("MessageRequestProcessor.processMessageCatalogList error!", e); + } + } + + /*** + * alarm设备报警信息处理 + * @param evt + */ + private void processMessageAlarm(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + Element deviceIdElement = rootElement.element("DeviceID"); + String deviceId = deviceIdElement.getText().toString(); + // 回复200 OK + responseAck(evt); + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + + if (rootElement.getName().equals("Notify")) { // 处理报警通知 + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setDeviceId(deviceId); + deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority")); + deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); + deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime")); + if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { + deviceAlarm.setAlarmDescription(""); + } else { + deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription")); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) { + deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); + } else { + deviceAlarm.setLongitude(0.00); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) { + deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); + } else { + deviceAlarm.setLatitude(0.00); + } + + if (!XmlUtil.isEmpty(deviceAlarm.getAlarmMethod())) { + if (deviceAlarm.getAlarmMethod().equals("4")) { + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); + mobilePosition.setTime(deviceAlarm.getAlarmTime()); + mobilePosition.setLongitude(deviceAlarm.getLongitude()); + mobilePosition.setLatitude(deviceAlarm.getLatitude()); + mobilePosition.setReportSource("GPS Alarm"); + BaiduPoint bp = new BaiduPoint(); + bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); + logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); + mobilePosition.setGeodeticSystem("BD-09"); + mobilePosition.setCnLng(bp.getBdLng()); + mobilePosition.setCnLat(bp.getBdLat()); + if (!userSetup.getSavePositionHistory()) { + storager.clearMobilePositionsByDeviceId(deviceId); + } + storager.insertMobilePosition(mobilePosition); + } + } + // TODO: 需要实现存储报警信息、报警分类 + + if (offLineDetector.isOnline(deviceId)) { + publisher.deviceAlarmEventPublish(deviceAlarm); + } + } else if (rootElement.getName().equals("Response")) { // 处理报警查询响应 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_ALARM); + msg.setData(json); + deferredResultHolder.invokeResult(msg); + } + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + // } catch (DocumentException e) { + log.error("MessageRequestProcessor.processMessageAlarm error!", e); + } + } + + /*** + * 收到keepalive请求 处理 + * + * @param evt + */ + private void processMessageKeepAlive(RequestEvent evt) { + try { + Element rootElement = getRootElement(evt); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + // 检查设备是否存在, 不存在则不回复 + if (storager.exists(deviceId)) { + // 回复200 OK + responseAck(evt); + if (offLineDetector.isOnline(deviceId)) { + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); + } else { + } + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + log.error("MessageRequestProcessor.processMessageKeepAlive error!", e); + } + } + + /*** + * 处理RecordInfo设备录像列表Message请求 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致 + * + * @param evt + */ + private void processMessageRecordInfo(RequestEvent evt) { + try { + // 回复200 OK + responseAck(evt); + String uuid = UUID.randomUUID().toString().replace("-", ""); + RecordInfo recordInfo = new RecordInfo(); + Element rootElement = getRootElement(evt); + Element deviceIdElement = rootElement.element("DeviceID"); + String deviceId = deviceIdElement.getText().toString(); + recordInfo.setDeviceId(deviceId); + recordInfo.setName(XmlUtil.getText(rootElement, "Name")); + if (XmlUtil.getText(rootElement, "SumNum") == null || XmlUtil.getText(rootElement, "SumNum") == "") { + recordInfo.setSumNum(0); + } else { + recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement, "SumNum"))); + } + String sn = XmlUtil.getText(rootElement, "SN"); + Element recordListElement = rootElement.element("RecordList"); + if (recordListElement == null || recordInfo.getSumNum() == 0) { + logger.info("无录像数据"); + RequestMessage msg = new RequestMessage(); + msg.setDeviceId(deviceId); + msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); + msg.setData(recordInfo); + deferredResultHolder.invokeResult(msg); + } else { + Iterator recordListIterator = recordListElement.elementIterator(); + List recordList = new ArrayList(); + if (recordListIterator != null) { + RecordItem record = new RecordItem(); + logger.info("处理录像列表数据..."); + // 遍历DeviceList + while (recordListIterator.hasNext()) { + Element itemRecord = recordListIterator.next(); + Element recordElement = itemRecord.element("DeviceID"); + if (recordElement == null) { + logger.info("记录为空,下一个..."); + continue; + } + record = new RecordItem(); + record.setDeviceId(XmlUtil.getText(itemRecord, "DeviceID")); + record.setName(XmlUtil.getText(itemRecord, "Name")); + record.setFilePath(XmlUtil.getText(itemRecord, "FilePath")); + record.setAddress(XmlUtil.getText(itemRecord, "Address")); + record.setStartTime( + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "StartTime"))); + record.setEndTime( + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "EndTime"))); + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 + : Integer.parseInt(XmlUtil.getText(itemRecord, "Secrecy"))); + record.setType(XmlUtil.getText(itemRecord, "Type")); + record.setRecorderId(XmlUtil.getText(itemRecord, "RecorderID")); + recordList.add(record); + } + recordInfo.setRecordList(recordList); + } + + // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题 + String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn; + redis.set(cacheKey + "_" + uuid, recordList, 90); + if (!threadNameList.contains(cacheKey)) { + threadNameList.add(cacheKey); + CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo); + chk.setName(cacheKey); + chk.setDeferredResultHolder(deferredResultHolder); + chk.setRedis(redis); + chk.setLogger(logger); + chk.start(); + if (logger.isDebugEnabled()) { + logger.debug("Start Thread " + cacheKey + "."); + } + } else { + if (logger.isDebugEnabled()) { + logger.debug("Thread " + cacheKey + " already started."); + } + } + + // 存在录像且如果当前录像明细个数小于总条数,说明拆包返回,需要组装,暂不返回 + // if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) { + // // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分 + // String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn; + + // redis.set(cacheKey + "_" + uuid, recordList, 90); + // List cacheKeys = redis.scan(cacheKey + "_*"); + // List totalRecordList = new ArrayList(); + // for (int i = 0; i < cacheKeys.size(); i++) { + // totalRecordList.addAll((List) redis.get(cacheKeys.get(i).toString())); + // } + // if (totalRecordList.size() < recordInfo.getSumNum()) { + // logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项"); + // return; + // } + // logger.info("录像数据已全部获取,共" + recordInfo.getSumNum() + "项"); + // recordInfo.setRecordList(totalRecordList); + // for (int i = 0; i < cacheKeys.size(); i++) { + // redis.del(cacheKeys.get(i).toString()); + // } + // } + // // 自然顺序排序, 元素进行升序排列 + // recordInfo.getRecordList().sort(Comparator.naturalOrder()); + } + // 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作 + // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作 + // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据 + + // RequestMessage msg = new RequestMessage(); + // msg.setDeviceId(deviceId); + // msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); + // msg.setData(recordInfo); + // deferredResultHolder.invokeResult(msg); + // logger.info("处理完成,返回结果"); + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + log.error("MessageRequestProcessor.processMessageRecordInfo error!", e); + } + } + + /** + * 收到MediaStatus消息处理 + * + * @param evt + */ + private void processMessageMediaStatus(RequestEvent evt) { + try { + // 回复200 OK + responseAck(evt); + Element rootElement = getRootElement(evt); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + String NotifyType = XmlUtil.getText(rootElement, "NotifyType"); + if (NotifyType.equals("121")) { + logger.info("媒体播放完毕,通知关流"); + List streamInfos = redisCatchStorage.queryPlayBackByDeviceId(deviceId); + if (streamInfos != null && !streamInfos.isEmpty()) { + for (StreamInfo streamInfo : streamInfos) { + cmder.stopStreamByeCmd(streamInfo, null); + } + } + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { + log.error("MessageRequestProcessor.processMessageMediaStatus error!", e); + } + } + + + /*** + * 回复200 OK + * @param evt + * @throws SipException + * @throws InvalidArgumentException + * @throws ParseException + */ + private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { + Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); + getServerTransaction(evt).sendResponse(response); + } + + private Element getRootElement(RequestEvent evt) throws DocumentException { + Request request = evt.getRequest(); + SAXReader reader = new SAXReader(); + reader.setEncoding("gbk"); + Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); + return xml.getRootElement(); + } + + public void setCmder(SIPCommander cmder) { + this.cmder = cmder; + } + + public void setStorager(IVideoManagerStorager storager) { + this.storager = storager; + } + + public void setPublisher(EventPublisher publisher) { + this.publisher = publisher; + } + + public void setRedis(RedisUtil redis) { + this.redis = redis; + } + + public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { + this.deferredResultHolder = deferredResultHolder; + } + + public void setOffLineDetector(DeviceOffLineDetector offLineDetector) { + this.offLineDetector = offLineDetector; + } + + public IRedisCatchStorage getRedisCatchStorage() { + return redisCatchStorage; + } + + public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { + this.redisCatchStorage = redisCatchStorage; + } } \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java index d67267af..d78d6906 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java @@ -1,68 +1,62 @@ -package com.genersoft.iot.vmp.gb28181.transmit.response.impl; - -import java.text.ParseException; - -import javax.sip.Dialog; -import javax.sip.InvalidArgumentException; -import javax.sip.ResponseEvent; -import javax.sip.SipException; -import javax.sip.address.SipURI; -import javax.sip.header.CSeqHeader; -import javax.sip.header.ViaHeader; -import javax.sip.message.Request; -import javax.sip.message.Response; - -// import org.slf4j.Logger; -// import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.SipLayer; -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; - - -/** - * @Description:处理INVITE响应 - * @author: swwheihei - * @date: 2020年5月3日 下午4:43:52 - */ -@Component -public class InviteResponseProcessor implements ISIPResponseProcessor { - - // private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); - - /** - * 处理invite响应 - * - * @param evt 响应消息 - * @throws ParseException - */ - @Override - public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException { - try { - Response response = evt.getResponse(); - int statusCode = response.getStatusCode(); - // trying不会回复 - if (statusCode == Response.TRYING) { - } - // 成功响应 - // 下发ack - if (statusCode == Response.OK) { - Dialog dialog = evt.getDialog(); - CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); - Request reqAck = dialog.createAck(cseq.getSeqNumber()); - - SipURI requestURI = (SipURI) reqAck.getRequestURI(); - ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); - requestURI.setHost(viaHeader.getHost()); - requestURI.setPort(viaHeader.getPort()); - reqAck.setRequestURI(requestURI); - - dialog.sendAck(reqAck); - } - } catch (InvalidArgumentException | SipException e) { - e.printStackTrace(); - } - } - -} +package com.genersoft.iot.vmp.gb28181.transmit.response.impl; + +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.gb28181.SipLayer; +import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; +import org.springframework.stereotype.Component; + +import javax.sip.Dialog; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.address.SipURI; +import javax.sip.header.CSeqHeader; +import javax.sip.header.ViaHeader; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.text.ParseException; + + +/** + * @Description:处理INVITE响应 + * @author: swwheihei + * @date: 2020年5月3日 下午4:43:52 + */ +@Component +public class InviteResponseProcessor implements ISIPResponseProcessor { + + /** + * 处理invite响应 + * + * @param evt 响应消息 + * @throws ParseException + */ + @Override + public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException { + try { + Response response = evt.getResponse(); + int statusCode = response.getStatusCode(); + // trying不会回复 + if (statusCode == Response.TRYING) { + } + // 成功响应 + // 下发ack + if (statusCode == Response.OK) { + Dialog dialog = evt.getDialog(); + CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); + Request reqAck = dialog.createAck(cseq.getSeqNumber()); + + SipURI requestURI = (SipURI) reqAck.getRequestURI(); + ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); + requestURI.setHost(viaHeader.getHost()); + requestURI.setPort(viaHeader.getPort()); + reqAck.setRequestURI(requestURI); + + dialog.sendAck(reqAck); + } + } catch (InvalidArgumentException | SipException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java index d5c218cd..da659485 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java @@ -1,13 +1,13 @@ package com.genersoft.iot.vmp.media.zlm; import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -// import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -// import org.slf4j.Logger; -// import org.slf4j.LoggerFactory; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @@ -18,30 +18,34 @@ import javax.servlet.http.HttpServletResponse; @RequestMapping("/zlm") public class ZLMHTTPProxyController { - - // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class); - - // @Autowired - // private IVideoManagerStorager storager; - @Autowired private IRedisCatchStorage redisCatchStorage; - @Value("${media.port}") - private int mediaHttpPort; + @Autowired + private VideoStreamSessionManager streamSession; @ResponseBody @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8") - public Object proxy(HttpServletRequest request, HttpServletResponse response){ + public Object proxy(HttpServletRequest request, HttpServletResponse response) { if (redisCatchStorage.getMediaInfo() == null) { return "未接入流媒体"; } + String mediaServerIp = request.getParameter("mediaServerIp"); + if (StringUtils.isBlank(mediaServerIp)) { + String channelId = request.getParameter("channelId"); + String streamId = request.getParameter("stream"); + mediaServerIp = streamSession.getMediaServerIp(channelId, streamId); + } + if (StringUtils.isBlank(mediaServerIp)) { + // TODO 前端要提供zlm选择列表,传递回来 + mediaServerIp = "127.0.0.1"; + } MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); String requestURI = String.format("http://%s:%s%s?%s&%s", - mediaInfo.getLocalIP(), - mediaHttpPort, - request.getRequestURI().replace("/zlm",""), + mediaServerIp, + mediaInfo.getHttpPort(), + request.getRequestURI().replace("/zlm", ""), mediaInfo.getHookAdminParams(), request.getQueryString() ); @@ -50,9 +54,8 @@ public class ZLMHTTPProxyController { //将指定的url返回的参数自动封装到自定义好的对应类对象中 Object result = null; try { - result = restTemplate.getForObject(requestURI,Object.class); - - }catch (HttpClientErrorException httpClientErrorException) { + result = restTemplate.getForObject(requestURI, Object.class); + } catch (HttpClientErrorException httpClientErrorException) { response.setStatus(httpClientErrorException.getStatusCode().value()); } return result; diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index ba1a6f15..46bb495b 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -1,347 +1,342 @@ -package com.genersoft.iot.vmp.media.zlm; - -import java.util.UUID; - -import com.alibaba.fastjson.JSON; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.service.IPlayService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; - -import javax.servlet.http.HttpServletRequest; - -/** - * @Description:针对 ZLMediaServer的hook事件监听 - * @author: swwheihei - * @date: 2020年5月8日 上午10:46:48 - */ -@RestController -@RequestMapping("/index/hook") -public class ZLMHttpHookListener { - - private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); - - - @Autowired - private SIPCommander cmder; - - @Autowired - private IPlayService playService; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - // @Autowired - // private ZLMRESTfulUtils zlmresTfulUtils; - - @Autowired - private ZLMHttpHookSubscribe subscribe; - - @Value("${media.autoApplyPlay}") - private boolean autoApplyPlay; - - @Value("${media.ip}") - private String mediaIp; - - @Value("${media.wanIp}") - private String mediaWanIp; - - @Value("${media.port}") - private int mediaPort; - - /** - * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8") - public ResponseEntity onFlowReport(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString()); - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 访问http文件服务器上hls之外的文件时触发。 - * - */ - @ResponseBody - @PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8") - public ResponseEntity onHttpAccess(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("err", ""); - ret.put("path", ""); - ret.put("second", 600); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 - * - */ - @ResponseBody - @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") - public ResponseEntity onPlay(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString()); - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * rtsp/rtmp/rtp推流鉴权事件。 - * - */ - @ResponseBody - @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") - public ResponseEntity onPublish(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); - } - // String app = json.getString("app"); - // String streamId = json.getString("id"); - - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); - if (subscribe != null) subscribe.response(json); - - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - ret.put("enableHls", true); - ret.put("enableMP4", false); - ret.put("enableRtxp", true); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 录制mp4完成后通知事件;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8") - public ResponseEntity onRecordMp4(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString()); - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 - * - */ - @ResponseBody - @PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8") - public ResponseEntity onRtspRealm(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString()); - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("realm", ""); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - - /** - * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 - * - */ - @ResponseBody - @PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8") - public ResponseEntity onRtspAuth(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString()); - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("encrypted", false); - ret.put("passwd", "test"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。 - * - */ - @ResponseBody - @PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8") - public ResponseEntity onShellLogin(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString()); - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") - public ResponseEntity onStreamChanged(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); - } - // 流消失移除redis play - String app = json.getString("app"); - String streamId = json.getString("stream"); - boolean regist = json.getBoolean("regist"); - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if ("rtp".equals(app) && !regist ) { - if (streamInfo!=null){ - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - }else{ - streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); - redisCatchStorage.stopPlayback(streamInfo); - } - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 - * - */ - @ResponseBody - @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") - public ResponseEntity onStreamNoneReader(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString()); - } - - String streamId = json.getString("stream"); - - cmder.streamByeCmd(streamId); - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo!=null){ - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - }else{ - streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); - redisCatchStorage.stopPlayback(streamInfo); - } - - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("close", true); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") - public ResponseEntity onStreamNotFound(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); - } - if (autoApplyPlay) { - String app = json.getString("app"); - String streamId = json.getString("stream"); - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if ("rtp".equals(app) && streamId.indexOf("gb_play") > -1 && streamInfo == null) { - String[] s = streamId.split("_"); - if (s.length == 4) { - String deviceId = s[2]; - String channelId = s[3]; - Device device = storager.queryVideoDevice(deviceId); - if (device != null) { - UUID uuid = UUID.randomUUID(); - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); - }, null); - } - - } - - } - - } - - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") - public ResponseEntity onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); - } - -// String data = json.getString("data"); -// List mediaServerConfigs = JSON.parseArray(JSON.toJSONString(json), MediaServerConfig.class); -// MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0); - MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); - mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp); - mediaServerConfig.setLocalIP(mediaIp); - redisCatchStorage.updateMediaInfo(mediaServerConfig); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } -} +package com.genersoft.iot.vmp.media.zlm; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.MediaConfig; +import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.vmanager.service.IPlayService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +/** + * @Description:针对 ZLMediaServer的hook事件监听 + * @author: swwheihei + * @date: 2020年5月8日 上午10:46:48 + */ +@RestController +@RequestMapping("/index/hook") +public class ZLMHttpHookListener { + + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); + + + @Autowired + private SIPCommander cmder; + + @Autowired + private IPlayService playService; + + @Autowired + private IVideoManagerStorager storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private ZLMHttpHookSubscribe subscribe; + + @Autowired + MediaConfig mediaConfig; + + @Autowired + private VideoStreamSessionManager streamSession; + + /** + * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8") + public ResponseEntity onFlowReport(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString()); + } + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + /** + * 访问http文件服务器上hls之外的文件时触发。 + */ + @ResponseBody + @PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8") + public ResponseEntity onHttpAccess(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); + } + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("err", ""); + ret.put("path", ""); + ret.put("second", 600); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + /** + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 + */ + @ResponseBody + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") + public ResponseEntity onPlay(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString()); + } + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + /** + * rtsp/rtmp/rtp推流鉴权事件。 + */ + @ResponseBody + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") + public ResponseEntity onPublish(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); + } + // String app = json.getString("app"); + // String streamId = json.getString("id"); + + ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); + if (subscribe != null) { + subscribe.response(json); + } + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + ret.put("enableHls", true); + ret.put("enableMP4", false); + ret.put("enableRtxp", true); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + /** + * 录制mp4完成后通知事件;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8") + public ResponseEntity onRecordMp4(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString()); + } + // TODO Auto-generated method stub + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + /** + * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 + */ + @ResponseBody + @PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8") + public ResponseEntity onRtspRealm(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString()); + } + // TODO Auto-generated method stub + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("realm", ""); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + + /** + * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 + */ + @ResponseBody + @PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8") + public ResponseEntity onRtspAuth(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString()); + } + // TODO Auto-generated method stub + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("encrypted", false); + ret.put("passwd", "test"); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + /** + * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。 + */ + @ResponseBody + @PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8") + public ResponseEntity onShellLogin(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString()); + } + // TODO Auto-generated method stub + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } + + /** + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") + public ResponseEntity onStreamChanged(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); + } + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + + // 流消失移除redis play + String app = json.getString("app"); + String streamId = json.getString("stream"); + boolean regist = json.getBoolean("regist"); + if (!"rtp".equals(app) || regist) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + + String[] s = streamId.split("_"); + if (s.length != 4) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + String channelId = s[3]; + // TODO channelId + StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); + if (null != streamInfo) { + cmder.stopStreamByeCmd(streamInfo, null); + } + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + + /** + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") + public ResponseEntity onStreamNoneReader(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString()); + } + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("close", true); + + String app = json.getString("app"); + String streamId = json.getString("stream"); + if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + String[] s = streamId.split("_"); + if (s.length != 4) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + String channelId = s[3]; + // TODO channelId + StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); + if (null != streamInfo) { + cmder.stopStreamByeCmd(streamInfo, null); + } + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + + /** + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") + public ResponseEntity onStreamNotFound(@RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); + } + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + if (!mediaConfig.getAutoApplyPlay()) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + String app = json.getString("app"); + String streamId = json.getString("stream"); + + if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + String[] s = streamId.split("_"); + if (s.length != 4) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + String deviceId = s[2]; + String channelId = s[3]; + StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); + + if (streamInfo != null) { + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + Device device = storager.queryVideoDevice(deviceId); + if (device != null) { + RequestMessage msg = playService.createCallbackPlayMsg(); + cmder.playStreamCmd(device, channelId, (JSONObject response) -> { + logger.info("收到订阅消息: " + response.toJSONString()); + playService.onPublishHandlerForPlay(response, deviceId, channelId, msg); + }, null); + } + return new ResponseEntity<>(ret.toString(), HttpStatus.OK); + } + + /** + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") + public ResponseEntity onServerStarted(HttpServletRequest request, @RequestBody JSONObject json) { + + if (logger.isDebugEnabled()) { + logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); + } + + MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); + redisCatchStorage.updateMediaInfo(mediaServerConfig); + // TODO Auto-generated method stub + + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("msg", "success"); + return new ResponseEntity(ret.toString(), HttpStatus.OK); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java index 58de847e..9a7db24f 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java @@ -2,10 +2,14 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import okhttp3.*; +import com.genersoft.iot.vmp.conf.MediaConfig; +import okhttp3.FormBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; 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.stereotype.Component; import java.io.IOException; @@ -18,25 +22,19 @@ public class ZLMRESTfulUtils { private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); - @Value("${media.ip}") - private String mediaIp; + @Autowired + MediaConfig mediaConfig; - @Value("${media.port}") - private int mediaPort; - - @Value("${media.secret}") - private String mediaSecret; - - public JSONObject sendPost(String api, Map param) { + public JSONObject sendPost(String mediaServerIp, String api, Map param) { OkHttpClient client = new OkHttpClient(); - String url = String.format("http://%s:%s/index/api/%s", mediaIp, mediaPort, api); + String url = String.format("http://%s:%s/index/api/%s", mediaServerIp, mediaConfig.getMediaPort(), api); JSONObject responseJSON = null; logger.debug(url); FormBody.Builder builder = new FormBody.Builder(); - builder.add("secret",mediaSecret); + builder.add("secret", mediaConfig.getMediaSecret()); if (param != null) { - for (String key : param.keySet()){ + for (String key : param.keySet()) { builder.add(key, param.get(key).toString()); } } @@ -58,65 +56,111 @@ public class ZLMRESTfulUtils { } catch (ConnectException e) { logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); logger.info("请检查media配置并确认ZLM已启动..."); - }catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } return responseJSON; } - public JSONObject getMediaList(String app, String schema){ - Map param = new HashMap<>(); - param.put("app",app); - param.put("schema",schema); - param.put("vhost","__defaultVhost__"); - return sendPost("getMediaList",param); - } - - public JSONObject getMediaInfo(String app, String schema, String stream){ +// public JSONObject getMediaList(String app, String schema) { +// Map param = new HashMap<>(); +// param.put("app", app); +// param.put("schema", schema); +// param.put("vhost", "__defaultVhost__"); +// return sendPost("getMediaList", param); +// } +// +// public JSONObject getMediaInfo(String app, String schema, String stream) { +// Map param = new HashMap<>(); +// param.put("app", app); +// param.put("schema", schema); +// param.put("stream", stream); +// param.put("vhost", "__defaultVhost__"); +// return sendPost("getMediaInfo", param); +// } + + public JSONObject getRtpInfo(String mediaServerIp, String stream_id) { Map param = new HashMap<>(); - param.put("app",app); - param.put("schema",schema); - param.put("stream",stream); - param.put("vhost","__defaultVhost__"); - return sendPost("getMediaInfo",param); + param.put("stream_id", stream_id); + return sendPost(mediaServerIp, "getRtpInfo", param); } - public JSONObject getRtpInfo(String stream_id){ - Map param = new HashMap<>(); - param.put("stream_id",stream_id); - return sendPost("getRtpInfo",param); - } - - public JSONObject addFFmpegSource(String src_url, String dst_url, String timeout_ms){ + public JSONObject addFFmpegSource(String mediaServerIp, String src_url, String dst_url, String timeout_ms) { System.out.println(src_url); System.out.println(dst_url); Map param = new HashMap<>(); param.put("src_url", src_url); param.put("dst_url", dst_url); param.put("timeout_ms", timeout_ms); - return sendPost("addFFmpegSource",param); + return sendPost(mediaServerIp, "addFFmpegSource", param); } - public JSONObject delFFmpegSource(String key){ + public JSONObject delFFmpegSource(String mediaServerIp, String key) { Map param = new HashMap<>(); param.put("key", key); - return sendPost("delFFmpegSource",param); + return sendPost(mediaServerIp, "delFFmpegSource", param); } - public JSONObject getMediaServerConfig(){ - return sendPost("getServerConfig",null); + public JSONObject getMediaServerConfig(String mediaServerIp) { + return sendPost(mediaServerIp, "getServerConfig", null); } - public JSONObject setServerConfig(Map param){ - return sendPost("setServerConfig",param); + public JSONObject setServerConfig(String mediaServerIp, Map param) { + return sendPost(mediaServerIp, "setServerConfig", param); } - public JSONObject openRtpServer(Map param){ - return sendPost("openRtpServer",param); + public JSONObject openRtpServer(String mediaServerIp, Map param) { + return sendPost(mediaServerIp, "openRtpServer", param); } - public JSONObject closeRtpServer(Map param) { - return sendPost("closeRtpServer",param); + public JSONObject closeRtpServer(String mediaServerIp, Map param) { + return sendPost(mediaServerIp, "closeRtpServer", param); + } + + /** + * 截图 + * + * @param mediaServerIp + * @param url + * @return + */ + public JSONObject getSnap(String mediaServerIp, String url) { + Map param = new HashMap<>(); + param.put("url", url); + param.put("timeout_sec", 10); + param.put("expire_sec", 1); + param.put("url", url); + return sendPost(mediaServerIp, "getSnap", param); + } + + /** + * 开始录制 + * + * @param mediaServerIp + * @param stream + * @return + */ + public JSONObject startRecord(String mediaServerIp, String stream) { + Map param = new HashMap<>(); + param.put("type", 1); + param.put("app", "rtp"); + param.put("stream", stream); + return sendPost(mediaServerIp, "startRecord", param); + } + + /** + * 结束录制 + * + * @param mediaServerIp + * @param stream + * @return + */ + public JSONObject stopRecord(String mediaServerIp, String stream) { + Map param = new HashMap<>(); + param.put("type", 1); + param.put("app", "rtp"); + param.put("stream", stream); + return sendPost(mediaServerIp, "stopRecord", param); } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java index f69ff0f0..7df87427 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java @@ -1,89 +1,102 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.conf.MediaConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; @Component public class ZLMRTPServerFactory { private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory"); - @Value("${media.rtp.udpPortRange}") - private String udpPortRange; + @Autowired + MediaConfig mediaConfig; @Autowired private ZLMRESTfulUtils zlmresTfulUtils; private int[] udpPortRangeArray = new int[2]; - private int currentPort = 0; + private ConcurrentHashMap currentPortMap = new ConcurrentHashMap<>(); - public int createRTPServer(String streamId) { + public int createRTPServer(String mediaServerIp, String streamId) { Map param = new HashMap<>(); int result = -1; - int newPort = getPortFromUdpPortRange(); + int newPort = getPortFromUdpPortRange(mediaServerIp); param.put("port", newPort); param.put("enable_tcp", 1); param.put("stream_id", streamId); - JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); + JSONObject jsonObject = zlmresTfulUtils.openRtpServer(mediaServerIp, param); System.out.println(jsonObject); if (jsonObject != null) { - switch (jsonObject.getInteger("code")){ + switch (jsonObject.getInteger("code")) { case 0: - result= newPort; + result = newPort; break; case -300: // id已经存在 result = newPort; break; case -400: // 端口占用 - result= createRTPServer(streamId); + result = createRTPServer(mediaServerIp, streamId); break; default: logger.error("创建RTP Server 失败: " + jsonObject.getString("msg")); break; } - }else { + } else { // 检查ZLM状态 logger.error("创建RTP Server 失败: 请检查ZLM服务"); } return result; } - public boolean closeRTPServer(String streamId) { + public boolean closeRTPServer(String mediaServerIp, String streamId) { boolean result = false; Map param = new HashMap<>(); param.put("stream_id", streamId); - JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(param); - if (jsonObject != null ) { + + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(mediaServerIp, param); + if (jsonObject != null) { if (jsonObject.getInteger("code") == 0) { result = jsonObject.getInteger("hit") == 1; - }else { + } else { logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); } - }else { + } else { // 检查ZLM状态 logger.error("关闭RTP Server 失败: 请检查ZLM服务"); } return result; } - private int getPortFromUdpPortRange() { +// private int getPortFromUdpPortRange() { +// currentPort = getPortFromUdpPortRange(currentPort); +// return currentPort; +// } + + private int getPortFromUdpPortRange(String mediaServerIp) { + Integer currentPort = currentPortMap.get(mediaServerIp); + currentPort = getPortFromUdpPortRange(null == currentPort ? 0 : currentPort); + currentPortMap.put(mediaServerIp, currentPort); + return currentPort; + } + + private int getPortFromUdpPortRange(Integer currentPort) { if (currentPort == 0) { - String[] udpPortRangeStrArray = udpPortRange.split(","); + String[] udpPortRangeStrArray = mediaConfig.getUdpPortRange().split(","); udpPortRangeArray[0] = Integer.parseInt(udpPortRangeStrArray[0]); udpPortRangeArray[1] = Integer.parseInt(udpPortRangeStrArray[1]); } if (currentPort == 0 || currentPort++ > udpPortRangeArray[1]) { - currentPort = udpPortRangeArray[0]; return udpPortRangeArray[0]; } else { if (currentPort % 2 == 1) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index 490bfd8e..3f9611b7 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -3,11 +3,11 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +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.storager.IRedisCatchStorage; -//import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; @@ -18,116 +18,98 @@ import org.springframework.util.StringUtils; import java.util.HashMap; import java.util.Map; +@Slf4j @Component -@Order(value=1) +@Order(value = 1) public class ZLMRunner implements CommandLineRunner { - private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); - - // @Autowired - // private IVideoManagerStorager storager; - @Autowired private IRedisCatchStorage redisCatchStorage; - @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 + MediaConfig mediaConfig; + @Autowired + SipConfig sipConfig; @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @Override - public void run(String... strings) throws Exception { - // 获取zlm信息 - logger.info("等待zlm接入..."); - MediaServerConfig mediaServerConfig = getMediaServerConfig(); - if (mediaServerConfig != null) { - logger.info("zlm接入成功..."); - if (autoConfig) saveZLMConfig(); - mediaServerConfig = getMediaServerConfig(); - redisCatchStorage.updateMediaInfo(mediaServerConfig); + public void run(String... strings) { + String[] mediaIpArr = mediaConfig.getMediaIpArr(); + for (String mediaIp : mediaIpArr) { + // 获取zlm信息 + log.info("等待zlm {} 接入...", mediaIp); + MediaServerConfig mediaServerConfig = getMediaServerConfig(mediaIp); + if (mediaServerConfig != null) { + log.info("zlm {} 接入成功...", mediaIp); + if (mediaConfig.getAutoConfig()) { + // 自动配置zlm + saveZLMConfig(mediaIp); + // 配置后,从zlm重新获取流媒体服务器信息 + mediaServerConfig = getMediaServerConfig(mediaIp); + } + // TODO 这里需要把多台zlm服务器的配置,分别存储到redis,不能用同一个key,否则会覆盖 + redisCatchStorage.updateMediaInfo(mediaServerConfig); + } } } - public MediaServerConfig getMediaServerConfig() { - JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(); + public MediaServerConfig getMediaServerConfig(String mediaIp) { + JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaIp); MediaServerConfig mediaServerConfig = null; if (responseJSON != null) { JSONArray data = responseJSON.getJSONArray("data"); if (data != null && data.size() > 0) { mediaServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), MediaServerConfig.class); - mediaServerConfig.setLocalIP(mediaIp); - mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp); } } else { - logger.error("getMediaServerConfig失败, 1s后重试"); + log.error("getMediaServerConfig失败, 1s后重试"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } - mediaServerConfig = getMediaServerConfig(); + mediaServerConfig = getMediaServerConfig(mediaIp); } return mediaServerConfig; } - private void saveZLMConfig() { - logger.info("设置zlm..."); + private void saveZLMConfig(String mediaIp) { + log.info("设置zlm {} ...", mediaIp); + String mediaHookIp = mediaConfig.getMediaHookIp(); if (StringUtils.isEmpty(mediaHookIp)) { - mediaHookIp = sipIP; + mediaHookIp = sipConfig.getSipIp(); } - String hookPrex = String.format("http://%s:%s/index/hook", mediaHookIp, serverPort); Map param = new HashMap<>(); - param.put("api.secret",mediaSecret); // -profile:v Baseline - param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); - param.put("hook.enable","1"); - param.put("hook.on_flow_report",""); - param.put("hook.on_play",""); - param.put("hook.on_http_access",""); - param.put("hook.on_publish",String.format("%s/on_publish", hookPrex)); - param.put("hook.on_record_mp4",""); - param.put("hook.on_record_ts",""); - param.put("hook.on_rtsp_auth",""); - param.put("hook.on_rtsp_realm",""); - param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); - param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex)); - param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex)); - param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); - param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); - param.put("hook.timeoutSec","20"); - param.put("general.streamNoneReaderDelayMS",streamNoneReaderDelayMS); - - JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param); + param.put("api.secret", mediaConfig.getMediaSecret()); // -profile:v Baseline + param.put("ffmpeg.cmd", "%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); + param.put("hook.enable", "1"); + param.put("hook.on_flow_report", ""); + param.put("hook.on_play", ""); + param.put("hook.on_http_access", ""); + param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); + param.put("hook.on_record_mp4", ""); + param.put("hook.on_record_ts", ""); + param.put("hook.on_rtsp_auth", ""); + param.put("hook.on_rtsp_realm", ""); + param.put("hook.on_server_started", String.format("%s/on_server_started", hookPrex)); + param.put("hook.on_shell_login", String.format("%s/on_shell_login", hookPrex)); + param.put("hook.on_stream_changed", String.format("%s/on_stream_changed", hookPrex)); + param.put("hook.on_stream_none_reader", String.format("%s/on_stream_none_reader", hookPrex)); + param.put("hook.on_stream_not_found", String.format("%s/on_stream_not_found", hookPrex)); + param.put("hook.timeoutSec", "20"); + param.put("general.streamNoneReaderDelayMS", mediaConfig.getStreamNoneReaderDelayMS()); + + JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaIp, param); if (responseJSON != null && responseJSON.getInteger("code") == 0) { - logger.info("设置zlm成功"); - }else { - logger.info("设置zlm失败: " + responseJSON.getString("msg")); + log.info("设置zlm {} 成功", mediaIp); + } else { + log.info("设置zlm {} 失败: {}", mediaIp, responseJSON); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java index 8bc78b93..0ae29216 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -3,56 +3,52 @@ package com.genersoft.iot.vmp.storager; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; -import java.util.Map; +import java.util.List; public interface IRedisCatchStorage { /** - * 开始播放时将流存入 + * 更新流媒体信息 * - * @param stream 流信息 + * @param mediaServerConfig * @return */ - boolean startPlay(StreamInfo stream); - + boolean updateMediaInfo(MediaServerConfig mediaServerConfig); /** - * 停止播放时删除 + * 获取流媒体信息 * * @return */ - boolean stopPlay(StreamInfo streamInfo); + MediaServerConfig getMediaInfo(); /** - * 查询播放列表 + * 开始播放时将流存入 + * + * @param stream 流信息 * @return */ - StreamInfo queryPlay(StreamInfo streamInfo); - - StreamInfo queryPlayByStreamId(String steamId); - - StreamInfo queryPlaybackByStreamId(String steamId); + boolean startPlay(StreamInfo stream); - StreamInfo queryPlayByDevice(String deviceId, String code); /** - * 更新流媒体信息 - * @param mediaServerConfig + * 停止播放时删除 + * * @return */ - boolean updateMediaInfo(MediaServerConfig mediaServerConfig); + boolean stopPlay(StreamInfo streamInfo); - /** - * 获取流媒体信息 - * @return - */ - MediaServerConfig getMediaInfo(); + StreamInfo queryPlayByStreamId(String channelId, String steamId); - Map queryPlayByDeviceId(String deviceId); + StreamInfo queryPlayByChannel(String channelId); boolean startPlayback(StreamInfo stream); boolean stopPlayback(StreamInfo streamInfo); - StreamInfo queryPlaybackByDevice(String deviceId, String code); + StreamInfo queryPlaybackByStreamId(String channelId, String steamId); + + StreamInfo queryPlaybackByChannel(String channelId); + + List queryPlayBackByDeviceId(String deviceId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java index 37f3eb18..f25cb087 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java @@ -1,182 +1,190 @@ -package com.genersoft.iot.vmp.storager; - -import java.util.List; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.github.pagehelper.PageInfo; - -/** - * @Description:视频设备数据存储接口 - * @author: swwheihei - * @date: 2020年5月6日 下午2:14:31 - */ -@SuppressWarnings("rawtypes") -public interface IVideoManagerStorager { - - /** - * 根据设备ID判断设备是否存在 - * - * @param deviceId 设备ID - * @return true:存在 false:不存在 - */ - public boolean exists(String deviceId); - - /** - * 视频设备创建 - * - * @param device 设备对象 - * @return true:创建成功 false:创建失败 - */ - public boolean create(Device device); - - /** - * 视频设备更新 - * - * @param device 设备对象 - * @return true:创建成功 false:创建失败 - */ - public boolean updateDevice(Device device); - - /** - * 添加设备通道 - * - * @param deviceId 设备id - * @param channel 通道 - */ - public void updateChannel(String deviceId, DeviceChannel channel); - - /** - * 开始播放 - * @param deviceId 设备id - * @param channelId 通道ID - * @param streamId 流地址 - */ - public void startPlay(String deviceId, String channelId, String streamId); - - /** - * 停止播放 - * @param deviceId 设备id - * @param channelId 通道ID - */ - public void stopPlay(String deviceId, String channelId); - - /** - * 获取设备 - * - * @param deviceId 设备ID - * @return DShadow 设备对象 - */ - public Device queryVideoDevice(String deviceId); - - /** - * 获取某个设备的通道列表 - * - * @param deviceId 设备ID - * @param page 分页 当前页 - * @param count 每页数量 - * @return - */ - public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count); - - /** - * 获取某个设备的通道列表 - * - * @param deviceId 设备ID - * @return - */ - public List queryChannelsByDeviceId(String deviceId); - - /** - * 获取某个设备的通道 - * @param deviceId 设备ID - * @param channelId 通道ID - */ - public DeviceChannel queryChannel(String deviceId, String channelId); - - /** - * 获取多个设备 - * @param page 当前页数 - * @param count 每页数量 - * @return List 设备对象数组 - */ - public PageInfo queryVideoDeviceList(int page, int count); - - /** - * 获取多个设备 - * - * @return List 设备对象数组 - */ - public List queryVideoDeviceList(); - - /** - * 删除设备 - * - * @param deviceId 设备ID - * @return true:删除成功 false:删除失败 - */ - public boolean delete(String deviceId); - - /** - * 更新设备在线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - public boolean online(String deviceId); - - /** - * 更新设备离线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - public boolean outline(String deviceId); - - - /** - * 查询子设备 - * - * @param deviceId - * @param channelId - * @param page - * @param count - * @return - */ - PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count); - - - /** - * 清空通道 - * @param deviceId - */ - void cleanChannelsForDevice(String deviceId); - - /** - * 添加Mobile Position设备移动位置 - * @param MobilePosition - * @return - */ - public boolean insertMobilePosition(MobilePosition mobilePosition); - - /** - * 查询移动位置轨迹 - * @param deviceId - * @param startTime - * @param endTime - */ - public List queryMobilePositions(String deviceId, String startTime, String endTime); - - /** - * 查询最新移动位置 - * @param deviceId - */ - public MobilePosition queryLatestPosition(String deviceId); - - /** - * 删除指定设备的所有移动位置 - * @param deviceId - */ - public int clearMobilePositionsByDeviceId(String deviceId); -} +package com.genersoft.iot.vmp.storager; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * @Description:视频设备数据存储接口 + * @author: swwheihei + * @date: 2020年5月6日 下午2:14:31 + */ +public interface IVideoManagerStorager { + + /** + * 根据设备ID判断设备是否存在 + * + * @param deviceId 设备ID + * @return true:存在 false:不存在 + */ + public boolean exists(String deviceId); + + /** + * 视频设备创建 + * + * @param device 设备对象 + * @return true:创建成功 false:创建失败 + */ + public boolean create(Device device); + + /** + * 视频设备更新 + * + * @param device 设备对象 + * @return true:创建成功 false:创建失败 + */ + public boolean updateDevice(Device device); + + /** + * 添加设备通道 + * + * @param deviceId 设备id + * @param channel 通道 + */ + public void updateChannel(String deviceId, DeviceChannel channel); + + /** + * 开始播放 + * + * @param deviceId 设备id + * @param channelId 通道ID + * @param streamId 流地址 + */ + public void startPlay(String deviceId, String channelId, String streamId); + + /** + * 停止播放 + * + * @param deviceId 设备id + * @param channelId 通道ID + */ + public void stopPlay(String deviceId, String channelId); + + /** + * 获取设备 + * + * @param deviceId 设备ID + * @return DShadow 设备对象 + */ + public Device queryVideoDevice(String deviceId); + + /** + * 获取某个设备的通道列表 + * + * @param deviceId 设备ID + * @param page 分页 当前页 + * @param count 每页数量 + * @return + */ + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count); + + /** + * 获取某个设备的通道列表 + * + * @param deviceId 设备ID + * @return + */ + public List queryChannelsByDeviceId(String deviceId); + + /** + * 获取某个设备的通道 + * + * @param deviceId 设备ID + * @param channelId 通道ID + */ + public DeviceChannel queryChannel(String deviceId, String channelId); + + /** + * 获取多个设备 + * + * @param page 当前页数 + * @param count 每页数量 + * @return List 设备对象数组 + */ + public PageInfo queryVideoDeviceList(int page, int count); + + /** + * 获取多个设备 + * + * @return List 设备对象数组 + */ + public List queryVideoDeviceList(); + + /** + * 删除设备 + * + * @param deviceId 设备ID + * @return true:删除成功 false:删除失败 + */ + public boolean delete(String deviceId); + + /** + * 更新设备在线 + * + * @param deviceId 设备ID + * @return true:更新成功 false:更新失败 + */ + public boolean online(String deviceId); + + /** + * 更新设备离线 + * + * @param deviceId 设备ID + * @return true:更新成功 false:更新失败 + */ + public boolean outline(String deviceId); + + + /** + * 查询子设备 + * + * @param deviceId + * @param channelId + * @param page + * @param count + * @return + */ + PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count); + + + /** + * 清空通道 + * + * @param deviceId + */ + void cleanChannelsForDevice(String deviceId); + + /** + * 添加Mobile Position设备移动位置 + * + * @param MobilePosition + * @return + */ + public boolean insertMobilePosition(MobilePosition mobilePosition); + + /** + * 查询移动位置轨迹 + * + * @param deviceId + * @param startTime + * @param endTime + */ + public List queryMobilePositions(String deviceId, String startTime, String endTime); + + /** + * 查询最新移动位置 + * + * @param deviceId + */ + public MobilePosition queryLatestPosition(String deviceId); + + /** + * 删除指定设备的所有移动位置 + * + * @param deviceId + */ + public int clearMobilePositionsByDeviceId(String deviceId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java index 11b728a8..260a9eef 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java @@ -43,7 +43,7 @@ public interface DeviceChannelMapper { ", PTZType=${PTZType}" + ", status='${status}'" + ", streamId='${streamId}'" + - ", hasAudio='${hasAudio}'" + + ", hasAudio='#{hasAudio}'" + "WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+ " "}) int update(DeviceChannel channel); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java index 958b38a1..f421f5e5 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java @@ -7,153 +7,201 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; -import java.util.Map; @Component public class RedisCatchStorageImpl implements IRedisCatchStorage { @Autowired - private RedisUtil redis; + private RedisUtil redis; @Autowired private DeviceChannelMapper deviceChannelMapper; - /** - * 开始播放时将流存入redis + * 更新流媒体信息 * + * @param mediaServerConfig * @return */ @Override - public boolean startPlay(StreamInfo stream) { - return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()), - stream); + public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { + return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX, mediaServerConfig); } /** - * 停止播放时从redis删除 + * 获取流媒体信息 * * @return */ @Override - public boolean stopPlay(StreamInfo streamInfo) { - if (streamInfo == null) return false; - return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - streamInfo.getStreamId(), - streamInfo.getDeviceID(), - streamInfo.getChannelId())); + public MediaServerConfig getMediaInfo() { + return (MediaServerConfig) redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); } + /** - * 查询播放列表 + * 开始播放时将流存入redis + * * @return */ @Override - public StreamInfo queryPlay(StreamInfo streamInfo) { - return (StreamInfo)redis.get(String.format("%S_%s_%s_%s", - VideoManagerConstants.PLAYER_PREFIX, - streamInfo.getStreamId(), - streamInfo.getDeviceID(), - streamInfo.getChannelId())); + public boolean startPlay(StreamInfo stream) { + String key = getKey(VideoManagerConstants.PLAYER_PREFIX, + stream.getStreamId(), + stream.getChannelId(), + stream.getDeviceID() + ); + return redis.set(key, stream); } + + /** + * 停止播放时从redis删除 + * + * @return + */ @Override - public StreamInfo queryPlayByStreamId(String steamId) { - List playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, steamId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + public boolean stopPlay(StreamInfo streamInfo) { + if (streamInfo == null) { + return false; + } + String key = getKey(VideoManagerConstants.PLAYER_PREFIX, + streamInfo.getStreamId(), + streamInfo.getChannelId(), + streamInfo.getDeviceID() + ); + return redis.del(key); } @Override - public StreamInfo queryPlaybackByStreamId(String steamId) { - List playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, steamId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + public StreamInfo queryPlayByStreamId(String channelId, String steamId) { + String key = getKey(VideoManagerConstants.PLAYER_PREFIX, + steamId, + channelId, + null + ); + return scanOne(key); } @Override - public StreamInfo queryPlayByDevice(String deviceId, String code) { -// List playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - List playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - deviceId, - code)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + public StreamInfo queryPlayByChannel(String channelId) { + String key = getKey(VideoManagerConstants.PLAYER_PREFIX, + null, + channelId, + null + ); + return scanOne(key); } /** - * 更新流媒体信息 - * @param mediaServerConfig + * zlm流媒体服务器播流成功后回调,播放状态缓存到redis + * + * @param stream * @return */ @Override - public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { - return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX,mediaServerConfig); + public boolean startPlayback(StreamInfo stream) { + String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX, + stream.getStreamId(), + stream.getChannelId(), + stream.getDeviceID() + ); + return redis.set(key, stream); } /** - * 获取流媒体信息 + * zlm流媒体服务器成功停止拉流后回调,从redis缓存移除播放状态 + * + * @param streamInfo * @return */ @Override - public MediaServerConfig getMediaInfo() { - return (MediaServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); + public boolean stopPlayback(StreamInfo streamInfo) { + if (streamInfo == null) { + return false; + } + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(streamInfo.getDeviceID(), streamInfo.getChannelId()); + if (deviceChannel != null) { + deviceChannel.setStreamId(null); + deviceChannel.setDeviceId(streamInfo.getDeviceID()); + deviceChannelMapper.update(deviceChannel); + } + String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX, + streamInfo.getStreamId(), + streamInfo.getChannelId(), + streamInfo.getDeviceID() + ); + return redis.del(key); } @Override - public Map queryPlayByDeviceId(String deviceId) { - Map streamInfos = new HashMap<>(); -// List playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); - List players = redis.scan(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); - if (players.size() == 0) return streamInfos; - for (int i = 0; i < players.size(); i++) { - String key = (String) players.get(i); - StreamInfo streamInfo = (StreamInfo)redis.get(key); - streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo); - } - return streamInfos; + public StreamInfo queryPlaybackByStreamId(String channelId, String steamId) { + String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX, + steamId, + channelId, + null + ); + return scanOne(key); } - @Override - public boolean startPlayback(StreamInfo stream) { - return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()), - stream); + public StreamInfo queryPlaybackByChannel(String channelId) { + + String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX, + null, + channelId, + null + ); + return scanOne(key); } - @Override - public boolean stopPlayback(StreamInfo streamInfo) { - if (streamInfo == null) return false; - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(streamInfo.getDeviceID(), streamInfo.getChannelId()); - if (deviceChannel != null) { - deviceChannel.setStreamId(null); - deviceChannel.setDeviceId(streamInfo.getDeviceID()); - deviceChannelMapper.update(deviceChannel); + public List queryPlayBackByDeviceId(String deviceId) { + String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX, + null, + null, + deviceId + ); + List players = redis.scan(key); + if (players.size() == 0) { + return new ArrayList<>(); } - return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - streamInfo.getStreamId(), - streamInfo.getDeviceID(), - streamInfo.getChannelId())); + List streamInfos = new ArrayList<>(players.size()); + for (int i = 0; i < players.size(); i++) { + String redisKey = (String) players.get(i); + StreamInfo streamInfo = (StreamInfo) redis.get(redisKey); + streamInfos.add(streamInfo); + } + return streamInfos; } - @Override - public StreamInfo queryPlaybackByDevice(String deviceId, String code) { - // String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - // deviceId, - // code); - List playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - deviceId, - code)); + private StreamInfo scanOne(String key) { + List playLeys = redis.scan(key); if (playLeys == null || playLeys.size() == 0) { - playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - deviceId)); + return null; + } + return (StreamInfo) redis.get(playLeys.get(0).toString()); + } + + public static String getKey(String prefix, String streamId, String channelId, String deviceId) { + if (StringUtils.isBlank(streamId)) { + streamId = "*"; + } + if (StringUtils.isBlank(channelId)) { + channelId = "*"; + } + if (StringUtils.isBlank(deviceId)) { + deviceId = "*"; } - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + return String.format("%S%s_%s_%s", + prefix, + streamId, + channelId, + deviceId); } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java index 22292ef9..7f4ac6f8 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java @@ -1,9 +1,9 @@ package com.genersoft.iot.vmp.storager.impl; -import java.util.*; - +import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; import com.genersoft.iot.vmp.storager.dao.DeviceMapper; import com.genersoft.iot.vmp.storager.dao.DeviceMobilePositionMapper; @@ -12,241 +12,243 @@ import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import java.util.List; -/** +/** * @Description:视频设备数据存储-jdbc实现 * @author: swwheihei - * @date: 2020年5月6日 下午2:31:42 + * @date: 2020年5月6日 下午2:31:42 */ -@SuppressWarnings("rawtypes") @Component public class VideoManagerStoragerImpl implements IVideoManagerStorager { - @Autowired + @Autowired private DeviceMapper deviceMapper; - @Autowired - private DeviceChannelMapper deviceChannelMapper; - - @Autowired - private DeviceMobilePositionMapper deviceMobilePositionMapper; - - - /** - * 根据设备ID判断设备是否存在 - * - * @param deviceId 设备ID - * @return true:存在 false:不存在 - */ - @Override - public boolean exists(String deviceId) { - return deviceMapper.getDeviceByDeviceId(deviceId) != null; - } - - /** - * 视频设备创建 - * - * @param device 设备对象 - * @return true:创建成功 false:创建失败 - */ - @Override - public synchronized boolean create(Device device) { - return deviceMapper.add(device) > 0; - } - - - - /** - * 视频设备更新 - * - * @param device 设备对象 - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean updateDevice(Device device) { - Device deviceByDeviceId = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); - if (deviceByDeviceId == null) { - return deviceMapper.add(device) > 0; - }else { - return deviceMapper.update(device) > 0; - } - - } - - @Override - public synchronized void updateChannel(String deviceId, DeviceChannel channel) { - String channelId = channel.getChannelId(); - channel.setDeviceId(deviceId); - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); - if (deviceChannel == null) { - deviceChannelMapper.add(channel); - }else { - deviceChannelMapper.update(channel); - } - } - - @Override - public void startPlay(String deviceId, String channelId, String streamId) { - deviceChannelMapper.startPlay(deviceId, channelId, streamId); - } - - @Override - public void stopPlay(String deviceId, String channelId) { - deviceChannelMapper.stopPlay(deviceId, channelId); - } - - /** - * 获取设备 - * - * @param deviceId 设备ID - * @return Device 设备对象 - */ - @Override - public Device queryVideoDevice(String deviceId) { - return deviceMapper.getDeviceByDeviceId(deviceId); - } - - @Override - public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count) { - // 获取到所有正在播放的流 - PageHelper.startPage(page, count); - List all = deviceChannelMapper.queryChannelsByDeviceId(deviceId, null, query, hasSubChannel, online); - return new PageInfo<>(all); - } - - @Override - public List queryChannelsByDeviceId(String deviceId) { - return deviceChannelMapper.queryChannelsByDeviceId(deviceId, null,null, null, null); - } - - @Override - public PageInfo querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, String online, int page, int count) { - PageHelper.startPage(page, count); - List all = deviceChannelMapper.queryChannelsByDeviceId(deviceId, parentChannelId, null, null, null); - return new PageInfo<>(all); - } - - @Override - public DeviceChannel queryChannel(String deviceId, String channelId) { - return deviceChannelMapper.queryChannel(deviceId, channelId); - } - - - /** - * 获取多个设备 - * - * @param page 当前页数 - * @param count 每页数量 - * @return PageInfo 分页设备对象数组 - */ - @Override - public PageInfo queryVideoDeviceList(int page, int count) { - PageHelper.startPage(page, count); - List all = deviceMapper.getDevices(); - return new PageInfo<>(all); - } - - /** - * 获取多个设备 - * - * @return List 设备对象数组 - */ - @Override - public List queryVideoDeviceList() { - - List deviceList = deviceMapper.getDevices(); - return deviceList; - } - - /** - * 删除设备 - * - * @param deviceId 设备ID - * @return true:删除成功 false:删除失败 - */ - @Override - public boolean delete(String deviceId) { - int result = deviceMapper.del(deviceId); - - return result > 0; - } - - /** - * 更新设备在线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean online(String deviceId) { - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - if (device == null) { - return false; - } - device.setOnline(1); - System.out.println("更新设备在线"); - return deviceMapper.update(device) > 0; - } - - /** - * 更新设备离线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean outline(String deviceId) { - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - device.setOnline(0); - System.out.println("更新设备离线"); - return deviceMapper.update(device) > 0; - } - - /** - * 清空通道 - * @param deviceId - */ - @Override - public void cleanChannelsForDevice(String deviceId) { - deviceChannelMapper.cleanChannelsByDeviceId(deviceId); - } - - /** - * 添加Mobile Position设备移动位置 - * @param MobilePosition - */ - @Override - public synchronized boolean insertMobilePosition(MobilePosition mobilePosition) { - return deviceMobilePositionMapper.insertNewPosition(mobilePosition) > 0; - } - - /** - * 查询移动位置轨迹 - * @param deviceId - * @param startTime - * @param endTime - */ - @Override - public synchronized List queryMobilePositions(String deviceId, String startTime, String endTime) { - return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, startTime, endTime); - } - - /** - * 查询最新移动位置 - * @param deviceId - */ - @Override - public MobilePosition queryLatestPosition(String deviceId) { - return deviceMobilePositionMapper.queryLatestPositionByDevice(deviceId); - } - - /** - * 删除指定设备的所有移动位置 - * @param deviceId - */ - public int clearMobilePositionsByDeviceId(String deviceId) { - return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId); - } + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private DeviceMobilePositionMapper deviceMobilePositionMapper; + + + /** + * 根据设备ID判断设备是否存在 + * + * @param deviceId 设备ID + * @return true:存在 false:不存在 + */ + @Override + public boolean exists(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId) != null; + } + + /** + * 视频设备创建 + * + * @param device 设备对象 + * @return true:创建成功 false:创建失败 + */ + @Override + public synchronized boolean create(Device device) { + return deviceMapper.add(device) > 0; + } + + + /** + * 视频设备更新 + * + * @param device 设备对象 + * @return true:更新成功 false:更新失败 + */ + @Override + public synchronized boolean updateDevice(Device device) { + Device deviceByDeviceId = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); + if (deviceByDeviceId == null) { + return deviceMapper.add(device) > 0; + } else { + return deviceMapper.update(device) > 0; + } + + } + + @Override + public synchronized void updateChannel(String deviceId, DeviceChannel channel) { + String channelId = channel.getChannelId(); + channel.setDeviceId(deviceId); + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); + if (deviceChannel == null) { + deviceChannelMapper.add(channel); + } else { + deviceChannelMapper.update(channel); + } + } + + @Override + public void startPlay(String deviceId, String channelId, String streamId) { + deviceChannelMapper.startPlay(deviceId, channelId, streamId); + } + + @Override + public void stopPlay(String deviceId, String channelId) { + deviceChannelMapper.stopPlay(deviceId, channelId); + } + + /** + * 获取设备 + * + * @param deviceId 设备ID + * @return Device 设备对象 + */ + @Override + public Device queryVideoDevice(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId); + } + + @Override + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count) { + // 获取到所有正在播放的流 + PageHelper.startPage(page, count); + List all = deviceChannelMapper.queryChannelsByDeviceId(deviceId, null, query, hasSubChannel, online); + return new PageInfo<>(all); + } + + @Override + public List queryChannelsByDeviceId(String deviceId) { + return deviceChannelMapper.queryChannelsByDeviceId(deviceId, null, null, null, null); + } + + @Override + public PageInfo querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, String online, int page, int count) { + PageHelper.startPage(page, count); + List all = deviceChannelMapper.queryChannelsByDeviceId(deviceId, parentChannelId, null, null, null); + return new PageInfo<>(all); + } + + @Override + public DeviceChannel queryChannel(String deviceId, String channelId) { + return deviceChannelMapper.queryChannel(deviceId, channelId); + } + + + /** + * 获取多个设备 + * + * @param page 当前页数 + * @param count 每页数量 + * @return PageInfo 分页设备对象数组 + */ + @Override + public PageInfo queryVideoDeviceList(int page, int count) { + PageHelper.startPage(page, count); + List all = deviceMapper.getDevices(); + return new PageInfo<>(all); + } + + /** + * 获取多个设备 + * + * @return List 设备对象数组 + */ + @Override + public List queryVideoDeviceList() { + + List deviceList = deviceMapper.getDevices(); + return deviceList; + } + + /** + * 删除设备 + * + * @param deviceId 设备ID + * @return true:删除成功 false:删除失败 + */ + @Override + public boolean delete(String deviceId) { + int result = deviceMapper.del(deviceId); + + return result > 0; + } + + /** + * 更新设备在线 + * + * @param deviceId 设备ID + * @return true:更新成功 false:更新失败 + */ + @Override + public synchronized boolean online(String deviceId) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return false; + } + device.setOnline(1); + System.out.println("更新设备在线"); + return deviceMapper.update(device) > 0; + } + + /** + * 更新设备离线 + * + * @param deviceId 设备ID + * @return true:更新成功 false:更新失败 + */ + @Override + public synchronized boolean outline(String deviceId) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + device.setOnline(0); + System.out.println("更新设备离线"); + return deviceMapper.update(device) > 0; + } + + /** + * 清空通道 + * + * @param deviceId + */ + @Override + public void cleanChannelsForDevice(String deviceId) { + deviceChannelMapper.cleanChannelsByDeviceId(deviceId); + } + + /** + * 添加Mobile Position设备移动位置 + * + * @param MobilePosition + */ + @Override + public synchronized boolean insertMobilePosition(MobilePosition mobilePosition) { + return deviceMobilePositionMapper.insertNewPosition(mobilePosition) > 0; + } + + /** + * 查询移动位置轨迹 + * + * @param deviceId + * @param startTime + * @param endTime + */ + @Override + public synchronized List queryMobilePositions(String deviceId, String startTime, String endTime) { + return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, startTime, endTime); + } + + /** + * 查询最新移动位置 + * + * @param deviceId + */ + @Override + public MobilePosition queryLatestPosition(String deviceId) { + return deviceMobilePositionMapper.queryLatestPositionByDevice(deviceId); + } + + /** + * 删除指定设备的所有移动位置 + * + * @param deviceId + */ + public int clearMobilePositionsByDeviceId(String deviceId) { + return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java b/src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java new file mode 100644 index 00000000..125d8180 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java @@ -0,0 +1,8 @@ +package com.genersoft.iot.vmp.utils; + +public class ConfigConst { + /** + * 播流最大并发个数 + */ + public static final Integer MAX_STRTEAM_COUNT = 10000; +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java new file mode 100644 index 00000000..8db30537 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java @@ -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 smembers(String key) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + Set 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(); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java index f83d5f3e..c4a8af4c 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java @@ -1,732 +1,770 @@ -package com.genersoft.iot.vmp.utils.redis; - -import java.util.*; -import java.util.concurrent.TimeUnit; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.*; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -/** - * @Description:Redis工具类 - * @author: swwheihei - * @date: 2020年5月6日 下午8:27:29 - */ -@Component -@SuppressWarnings(value = {"rawtypes", "unchecked"}) -public class RedisUtil { - - @Autowired - private RedisTemplate redisTemplate; - - /** - * 指定缓存失效时间 - * @param key 键 - * @param time 时间(秒) - * @return true / false - */ - public boolean expire(String key, long time) { - try { - if (time > 0) { - redisTemplate.expire(key, time, TimeUnit.SECONDS); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 根据 key 获取过期时间 - * @param key 键 - * @return - */ - public long getExpire(String key) { - return redisTemplate.getExpire(key, TimeUnit.SECONDS); - } - - /** - * 判断 key 是否存在 - * @param key 键 - * @return true / false - */ - public boolean hasKey(String key) { - try { - return redisTemplate.hasKey(key); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 删除缓存 - * @SuppressWarnings("unchecked") 忽略类型转换警告 - * @param key 键(一个或者多个) - */ - public boolean del(String... key) { - try { - if (key != null && key.length > 0) { - if (key.length == 1) { - redisTemplate.delete(key[0]); - } else { -// 传入一个 Collection 集合 - redisTemplate.delete(CollectionUtils.arrayToList(key)); - } - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - -// ============================== String ============================== - - /** - * 普通缓存获取 - * @param key 键 - * @return 值 - */ - public Object get(String key) { - return key == null ? null : redisTemplate.opsForValue().get(key); - } - - /** - * 普通缓存放入 - * @param key 键 - * @param value 值 - * @return true / false - */ - public boolean set(String key, Object value) { - try { - redisTemplate.opsForValue().set(key, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 普通缓存放入并设置时间 - * @param key 键 - * @param value 值 - * @param time 时间(秒),如果 time < 0 则设置无限时间 - * @return true / false - */ - public boolean set(String key, Object value, long time) { - try { - if (time > 0) { - redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); - } else { - set(key, value); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 递增 - * @param key 键 - * @param delta 递增大小 - * @return - */ - public long incr(String key, long delta) { - if (delta < 0) { - throw new RuntimeException("递增因子必须大于 0"); - } - return redisTemplate.opsForValue().increment(key, delta); - } - - /** - * 递减 - * @param key 键 - * @param delta 递减大小 - * @return - */ - public long decr(String key, long delta) { - if (delta < 0) { - throw new RuntimeException("递减因子必须大于 0"); - } - return redisTemplate.opsForValue().increment(key, delta); - } - -// ============================== Map ============================== - - /** - * HashGet - * @param key 键(no null) - * @param item 项(no null) - * @return 值 - */ - public Object hget(String key, String item) { - return redisTemplate.opsForHash().get(key, item); - } - - /** - * 获取 key 对应的 map - * @param key 键(no null) - * @return 对应的多个键值 - */ - public Map hmget(String key) { - return redisTemplate.opsForHash().entries(key); - } - - /** - * HashSet - * @param key 键 - * @param map 值 - * @return true / false - */ - public boolean hmset(String key, Map map) { - try { - redisTemplate.opsForHash().putAll(key, map); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * HashSet 并设置时间 - * @param key 键 - * @param map 值 - * @param time 时间 - * @return true / false - */ - public boolean hmset(String key, Map map, long time) { - try { - redisTemplate.opsForHash().putAll(key, map); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 向一张 Hash表 中放入数据,如不存在则创建 - * @param key 键 - * @param item 项 - * @param value 值 - * @return true / false - */ - public boolean hset(String key, String item, Object value) { - try { - redisTemplate.opsForHash().put(key, item, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 向一张 Hash表 中放入数据,并设置时间,如不存在则创建 - * @param key 键 - * @param item 项 - * @param value 值 - * @param time 时间(如果原来的 Hash表 设置了时间,这里会覆盖) - * @return true / false - */ - public boolean hset(String key, String item, Object value, long time) { - try { - redisTemplate.opsForHash().put(key, item, value); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 删除 Hash表 中的值 - * @param key 键 - * @param item 项(可以多个,no null) - */ - public void hdel(String key, Object... item) { - redisTemplate.opsForHash().delete(key, item); - } - - /** - * 判断 Hash表 中是否有该键的值 - * @param key 键(no null) - * @param item 值(no null) - * @return true / false - */ - public boolean hHasKey(String key, String item) { - return redisTemplate.opsForHash().hasKey(key, item); - } - - /** - * Hash递增,如果不存在则创建一个,并把新增的值返回 - * @param key 键 - * @param item 项 - * @param by 递增大小 > 0 - * @return - */ - public Double hincr(String key, String item, Double by) { - return redisTemplate.opsForHash().increment(key, item, by); - } - - /** - * Hash递减 - * @param key 键 - * @param item 项 - * @param by 递减大小 - * @return - */ - public Double hdecr(String key, String item, Double by) { - return redisTemplate.opsForHash().increment(key, item, -by); - } - -// ============================== Set ============================== - - /** - * 根据 key 获取 set 中的所有值 - * @param key 键 - * @return 值 - */ - public Set sGet(String key) { - try { - return redisTemplate.opsForSet().members(key); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * 从键为 key 的 set 中,根据 value 查询是否存在 - * @param key 键 - * @param value 值 - * @return true / false - */ - public boolean sHasKey(String key, Object value) { - try { - return redisTemplate.opsForSet().isMember(key, value); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将数据放入 set缓存 - * @param key 键值 - * @param values 值(可以多个) - * @return 成功个数 - */ - public long sSet(String key, Object... values) { - try { - return redisTemplate.opsForSet().add(key, values); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 将数据放入 set缓存,并设置时间 - * @param key 键 - * @param time 时间 - * @param values 值(可以多个) - * @return 成功放入个数 - */ - public long sSet(String key, long time, Object... values) { - try { - long count = redisTemplate.opsForSet().add(key, values); - if (time > 0) { - expire(key, time); - } - return count; - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 获取 set缓存的长度 - * @param key 键 - * @return 长度 - */ - public long sGetSetSize(String key) { - try { - return redisTemplate.opsForSet().size(key); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 移除 set缓存中,值为 value 的 - * @param key 键 - * @param values 值 - * @return 成功移除个数 - */ - public long setRemove(String key, Object... values) { - try { - return redisTemplate.opsForSet().remove(key, values); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } -// ============================== ZSet ============================== - - /** - * 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能; zadd - * - * @param key - * @param value - * @param score - */ - public void zAdd(String key, String value, double score) { - redisTemplate.opsForZSet().add(key, value, score); - } - - /** - * 删除元素 zrem - * - * @param key - * @param value - */ - public void zRemove(String key, String value) { - redisTemplate.opsForZSet().remove(key, value); - } - - /** - * score的增加or减少 zincrby - * - * @param key - * @param value - * @param score - */ - public Double zIncrScore(String key, String value, double score) { - return redisTemplate.opsForZSet().incrementScore(key, value, score); - } - - /** - * 查询value对应的score zscore - * - * @param key - * @param value - * @return - */ - public Double zScore(String key, String value) { - return redisTemplate.opsForZSet().score(key, value); - } - - /** - * 判断value在zset中的排名 zrank - * - * @param key - * @param value - * @return - */ - public Long zRank(String key, String value) { - return redisTemplate.opsForZSet().rank(key, value); - } - - /** - * 返回集合的长度 - * - * @param key - * @return - */ - public Long zSize(String key) { - return redisTemplate.opsForZSet().zCard(key); - } - - /** - * 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容 zrange - * - * 返回有序的集合,score小的在前面 - * - * @param key - * @param start - * @param end - * @return - */ - public Set ZRange(String key, int start, int end) { - return redisTemplate.opsForZSet().range(key, start, end); - } - /** - * 查询集合中指定顺序的值和score,0, -1 表示获取全部的集合内容 - * - * @param key - * @param start - * @param end - * @return - */ - public Set> zRangeWithScore(String key, int start, int end) { - return redisTemplate.opsForZSet().rangeWithScores(key, start, end); - } - /** - * 查询集合中指定顺序的值 zrevrange - * - * 返回有序的集合中,score大的在前面 - * - * @param key - * @param start - * @param end - * @return - */ - public Set zRevRange(String key, int start, int end) { - return redisTemplate.opsForZSet().reverseRange(key, start, end); - } - /** - * 根据score的值,来获取满足条件的集合 zrangebyscore - * - * @param key - * @param min - * @param max - * @return - */ - public Set zSortRange(String key, int min, int max) { - return redisTemplate.opsForZSet().rangeByScore(key, min, max); - } - - -// ============================== List ============================== - - /** - * 获取 list缓存的内容 - * @param key 键 - * @param start 开始 - * @param end 结束(0 到 -1 代表所有值) - * @return - */ - public List lGet(String key, long start, long end) { - try { - return redisTemplate.opsForList().range(key, start, end); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * 获取 list缓存的长度 - * @param key 键 - * @return 长度 - */ - public long lGetListSize(String key) { - try { - return redisTemplate.opsForList().size(key); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 根据索引 index 获取键为 key 的 list 中的元素 - * @param key 键 - * @param index 索引 - * 当 index >= 0 时 {0:表头, 1:第二个元素} - * 当 index < 0 时 {-1:表尾, -2:倒数第二个元素} - * @return 值 - */ - public Object lGetIndex(String key, long index) { - try { - return redisTemplate.opsForList().index(key, index); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * 将值 value 插入键为 key 的 list 中,如果 list 不存在则创建空 list - * @param key 键 - * @param value 值 - * @return true / false - */ - public boolean lSet(String key, Object value) { - try { - redisTemplate.opsForList().rightPush(key, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将值 value 插入键为 key 的 list 中,并设置时间 - * @param key 键 - * @param value 值 - * @param time 时间 - * @return true / false - */ - public boolean lSet(String key, Object value, long time) { - try { - redisTemplate.opsForList().rightPush(key, value); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将 values 插入键为 key 的 list 中 - * @param key 键 - * @param values 值 - * @return true / false - */ - public boolean lSetList(String key, List values) { - try { - redisTemplate.opsForList().rightPushAll(key, values); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 将 values 插入键为 key 的 list 中,并设置时间 - * @param key 键 - * @param values 值 - * @param time 时间 - * @return true / false - */ - public boolean lSetList(String key, List values, long time) { - try { - redisTemplate.opsForList().rightPushAll(key, values); - if (time > 0) { - expire(key, time); - } - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 根据索引 index 修改键为 key 的值 - * @param key 键 - * @param index 索引 - * @param value 值 - * @return true / false - */ - public boolean lUpdateIndex(String key, long index, Object value) { - try { - redisTemplate.opsForList().set(key, index, value); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - /** - * 在键为 key 的 list 中删除值为 value 的元素 - * @param key 键 - * @param count 如果 count == 0 则删除 list 中所有值为 value 的元素 - * 如果 count > 0 则删除 list 中最左边那个值为 value 的元素 - * 如果 count < 0 则删除 list 中最右边那个值为 value 的元素 - * @param value - * @return - */ - public long lRemove(String key, long count, Object value) { - try { - return redisTemplate.opsForList().remove(key, count, value); - } catch (Exception e) { - e.printStackTrace(); - return 0; - } - } - - /** - * 模糊查询 - * @param key 键 - * @return true / false - */ - public List keys(String key) { - try { - Set set = redisTemplate.keys(key); - return new ArrayList<>(set); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - - /** - * 模糊查询 - * @param query 查询参数 - * @return - */ -// public List scan(String query) { -// List result = new ArrayList<>(); -// try { -// Cursor> cursor = redisTemplate.opsForHash().scan("field", -// ScanOptions.scanOptions().match(query).count(1000).build()); -// while (cursor.hasNext()) { -// Map.Entry entry = cursor.next(); -// result.add(entry.getKey()); -// Object key = entry.getKey(); -// Object valueSet = entry.getValue(); -// } -// //关闭cursor -// cursor.close(); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// return result; -// } - - /** - * 模糊查询 - * @param query 查询参数 - * @return - */ - public List scan(String query) { - Set keys = (Set) redisTemplate.execute((RedisCallback>) connection -> { - Set keysTmp = new HashSet<>(); - Cursor cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(query).count(1000).build()); - while (cursor.hasNext()) { - keysTmp.add(new String(cursor.next())); - } - return keysTmp; - }); -// Set keys = (Set) redisTemplate.execute(new RedisCallback>(){ -// -// @Override -// public Set doInRedis(RedisConnection connection) throws DataAccessException { -// Set keysTmp = new HashSet<>(); -// Cursor cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(query).count(1000).build()); -// while (cursor.hasNext()) { -// keysTmp.add(new String(cursor.next())); -// } -// return keysTmp; -// } -// }); - - return new ArrayList<>(keys); - } - -} +package com.genersoft.iot.vmp.utils.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.*; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @Description:Redis工具类 + * @author: swwheihei + * @date: 2020年5月6日 下午8:27:29 + */ +@Component +public class RedisUtil { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + * @return true / false + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据 key 获取过期时间 + * + * @param key 键 + * @return + */ + public long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断 key 是否存在 + * + * @param key 键 + * @return true / false + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 键(一个或者多个) + * @SuppressWarnings("unchecked") 忽略类型转换警告 + */ + public boolean del(String... key) { + try { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { +// 传入一个 Collection 集合 + redisTemplate.delete(CollectionUtils.arrayToList(key)); + } + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + +// ============================== String ============================== + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true / false + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒),如果 time < 0 则设置无限时间 + * @return true / false + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 递增 + * + * @param key 键 + * @param delta 递增大小 + * @return + */ + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于 0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @param delta 递减大小 + * @return + */ + public long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于 0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + +// ============================== Map ============================== + + /** + * HashGet + * + * @param key 键(no null) + * @param item 项(no null) + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取 key 对应的 map + * + * @param key 键(no null) + * @return 对应的多个键值 + */ + public Map hmget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * HashSet + * + * @param key 键 + * @param map 值 + * @return true / false + */ + public boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 值 + * @param time 时间 + * @return true / false + */ + public boolean hmset(String key, Map map, long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张 Hash表 中放入数据,如不存在则创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true / false + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张 Hash表 中放入数据,并设置时间,如不存在则创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(如果原来的 Hash表 设置了时间,这里会覆盖) + * @return true / false + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除 Hash表 中的值 + * + * @param key 键 + * @param item 项(可以多个,no null) + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断 Hash表 中是否有该键的值 + * + * @param key 键(no null) + * @param item 值(no null) + * @return true / false + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * Hash递增,如果不存在则创建一个,并把新增的值返回 + * + * @param key 键 + * @param item 项 + * @param by 递增大小 > 0 + * @return + */ + public Double hincr(String key, String item, Double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * Hash递减 + * + * @param key 键 + * @param item 项 + * @param by 递减大小 + * @return + */ + public Double hdecr(String key, String item, Double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + +// ============================== Set ============================== + + /** + * 根据 key 获取 set 中的所有值 + * + * @param key 键 + * @return 值 + */ + public Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 从键为 key 的 set 中,根据 value 查询是否存在 + * + * @param key 键 + * @param value 值 + * @return true / false + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将数据放入 set缓存 + * + * @param key 键值 + * @param values 值(可以多个) + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 将数据放入 set缓存,并设置时间 + * + * @param key 键 + * @param time 时间 + * @param values 值(可以多个) + * @return 成功放入个数 + */ + public long sSet(String key, long time, Object... values) { + try { + long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 获取 set缓存的长度 + * + * @param key 键 + * @return 长度 + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 移除 set缓存中,值为 value 的 + * + * @param key 键 + * @param values 值 + * @return 成功移除个数 + */ + public long setRemove(String key, Object... values) { + try { + return redisTemplate.opsForSet().remove(key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } +// ============================== ZSet ============================== + + /** + * 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能; zadd + * + * @param key + * @param value + * @param score + */ + public void zAdd(String key, String value, double score) { + redisTemplate.opsForZSet().add(key, value, score); + } + + /** + * 删除元素 zrem + * + * @param key + * @param value + */ + public void zRemove(String key, String value) { + redisTemplate.opsForZSet().remove(key, value); + } + + /** + * score的增加or减少 zincrby + * + * @param key + * @param value + * @param score + */ + public Double zIncrScore(String key, String value, double score) { + return redisTemplate.opsForZSet().incrementScore(key, value, score); + } + + /** + * 查询value对应的score zscore + * + * @param key + * @param value + * @return + */ + public Double zScore(String key, String value) { + return redisTemplate.opsForZSet().score(key, value); + } + + /** + * 判断value在zset中的排名 zrank + * + * @param key + * @param value + * @return + */ + public Long zRank(String key, String value) { + return redisTemplate.opsForZSet().rank(key, value); + } + + /** + * 返回集合的长度 + * + * @param key + * @return + */ + public Long zSize(String key) { + return redisTemplate.opsForZSet().zCard(key); + } + + /** + * 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容 zrange + *

+ * 返回有序的集合,score小的在前面 + * + * @param key + * @param start + * @param end + * @return + */ + public Set ZRange(String key, int start, int end) { + return redisTemplate.opsForZSet().range(key, start, end); + } + + /** + * 查询集合中指定顺序的值和score,0, -1 表示获取全部的集合内容 + * + * @param key + * @param start + * @param end + * @return + */ + public Set> zRangeWithScore(String key, int start, int end) { + return redisTemplate.opsForZSet().rangeWithScores(key, start, end); + } + + /** + * 查询集合中指定顺序的值 zrevrange + *

+ * 返回有序的集合中,score大的在前面 + * + * @param key + * @param start + * @param end + * @return + */ + public Set zRevRange(String key, int start, int end) { + return redisTemplate.opsForZSet().reverseRange(key, start, end); + } + + /** + * 根据score的值,来获取满足条件的集合 zrangebyscore + * + * @param key + * @param min + * @param max + * @return + */ + public Set zSortRange(String key, int min, int max) { + return redisTemplate.opsForZSet().rangeByScore(key, min, max); + } + + +// ============================== List ============================== + + /** + * 获取 list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束(0 到 -1 代表所有值) + * @return + */ + public List lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取 list缓存的长度 + * + * @param key 键 + * @return 长度 + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 根据索引 index 获取键为 key 的 list 中的元素 + * + * @param key 键 + * @param index 索引 + * 当 index >= 0 时 {0:表头, 1:第二个元素} + * 当 index < 0 时 {-1:表尾, -2:倒数第二个元素} + * @return 值 + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将值 value 插入键为 key 的 list 中,如果 list 不存在则创建空 list + * + * @param key 键 + * @param value 值 + * @return true / false + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将值 value 插入键为 key 的 list 中,并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间 + * @return true / false + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将 values 插入键为 key 的 list 中 + * + * @param key 键 + * @param values 值 + * @return true / false + */ + public boolean lSetList(String key, List values) { + try { + redisTemplate.opsForList().rightPushAll(key, values); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将 values 插入键为 key 的 list 中,并设置时间 + * + * @param key 键 + * @param values 值 + * @param time 时间 + * @return true / false + */ + public boolean lSetList(String key, List values, long time) { + try { + redisTemplate.opsForList().rightPushAll(key, values); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据索引 index 修改键为 key 的值 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return true / false + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 在键为 key 的 list 中删除值为 value 的元素 + * + * @param key 键 + * @param count 如果 count == 0 则删除 list 中所有值为 value 的元素 + * 如果 count > 0 则删除 list 中最左边那个值为 value 的元素 + * 如果 count < 0 则删除 list 中最右边那个值为 value 的元素 + * @param value + * @return + */ + public long lRemove(String key, long count, Object value) { + try { + return redisTemplate.opsForList().remove(key, count, value); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 模糊查询 + * + * @param key 键 + * @return true / false + */ + public List keys(String key) { + try { + Set set = redisTemplate.keys(key); + return new ArrayList<>(set); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + /** + * 模糊查询 + * @param query 查询参数 + * @return + */ +// public List scan(String query) { +// List result = new ArrayList<>(); +// try { +// Cursor> cursor = redisTemplate.opsForHash().scan("field", +// ScanOptions.scanOptions().match(query).count(1000).build()); +// while (cursor.hasNext()) { +// Map.Entry entry = cursor.next(); +// result.add(entry.getKey()); +// Object key = entry.getKey(); +// Object valueSet = entry.getValue(); +// } +// //关闭cursor +// cursor.close(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return result; +// } + + /** + * 模糊查询 + * + * @param query 查询参数 + * @return + */ + public List scan(String query) { + Set keys = (Set) redisTemplate.execute((RedisCallback>) connection -> { + Set keysTmp = new HashSet<>(); + Cursor cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(query).count(1000).build()); + while (cursor.hasNext()) { + keysTmp.add(new String(cursor.next())); + } + return keysTmp; + }); +// Set keys = (Set) redisTemplate.execute(new RedisCallback>(){ +// +// @Override +// public Set doInRedis(RedisConnection connection) throws DataAccessException { +// Set keysTmp = new HashSet<>(); +// Cursor cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(query).count(1000).build()); +// while (cursor.hasNext()) { +// keysTmp.add(new String(cursor.next())); +// } +// return keysTmp; +// } +// }); + + return new ArrayList<>(keys); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java deleted file mode 100644 index d046c0df..00000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java +++ /dev/null @@ -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> 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> result = new DeferredResult>(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> 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> result = new DeferredResult> (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; - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfigController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfigController.java new file mode 100644 index 00000000..46ecfcd2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfigController.java @@ -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> 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> result = new DeferredResult>(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> 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> result = new DeferredResult>(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; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java deleted file mode 100644 index 72213594..00000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java +++ /dev/null @@ -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 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("设备远程启动API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - /** - * 录像控制命令API接口 - * - * @param deviceId - * @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 - * @param channelId 通道编码(可选) - */ - @GetMapping("/control/{deviceId}/record/{recordCmdStr}") - public DeferredResult> 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> result = new DeferredResult>(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> 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> result = new DeferredResult>(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> 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> result = new DeferredResult>(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 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("强制关键帧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> 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> result = new DeferredResult>(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; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControlController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControlController.java new file mode 100644 index 00000000..a9d168dc --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControlController.java @@ -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 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("设备远程启动API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + /** + * 录像控制命令API接口 + * + * @param deviceId + * @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 + * @param channelId 通道编码(可选) + */ + @GetMapping("/control/{deviceId}/record/{recordCmdStr}") + public DeferredResult> 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> result = new DeferredResult>(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> 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> result = new DeferredResult>(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> 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> result = new DeferredResult>(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 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("强制关键帧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> 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> result = new DeferredResult>(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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java new file mode 100644 index 00000000..f85d0b8b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java @@ -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 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 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 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> 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> result = new DeferredResult>(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 delete(@PathVariable String deviceId) { + + if (logger.isDebugEnabled()) { + logger.debug("设备信息删除API调用,deviceId:" + deviceId); + } + + if (offLineDetector.isOnline(deviceId)) { + return new ResponseEntity("不允许删除在线设备!", 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 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 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 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 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> 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> result = new DeferredResult>(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> 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> result = new DeferredResult>(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; + } + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java deleted file mode 100644 index 997a43b2..00000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java +++ /dev/null @@ -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 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 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 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> 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> result = new DeferredResult>(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 delete(@PathVariable String deviceId){ - - if (logger.isDebugEnabled()) { - logger.debug("设备信息删除API调用,deviceId:" + deviceId); - } - - if (offLineDetector.isOnline(deviceId)) { - return new ResponseEntity("不允许删除在线设备!", 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 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 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 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 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> 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> result = new DeferredResult>(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> 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> result = new DeferredResult> (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; - } - - -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java index 08e5cc43..d7eb16c8 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java @@ -1,253 +1,230 @@ package com.genersoft.iot.vmp.vmanager.play; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.vmanager.service.IPlayService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; import javax.sip.message.Response; -import java.util.UUID; @CrossOrigin @RestController @RequestMapping("/api") public class PlayController { - private final static Logger logger = LoggerFactory.getLogger(PlayController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private ZLMRESTfulUtils zlmresTfulUtils; - - @Autowired - private DeferredResultHolder resultHolder; - - @Autowired - private IPlayService playService; - - @GetMapping("/play/{deviceId}/{channelId}") - public DeferredResult> play(@PathVariable String deviceId, - @PathVariable String channelId) { - - - Device device = storager.queryVideoDevice(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); - - UUID uuid = UUID.randomUUID(); - DeferredResult> result = new DeferredResult>(); - - // 录像查询以channelId作为deviceId查询 - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); - - if (streamInfo == null) { - // 发送点播消息 - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); - }, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - Response response = event.getResponse(); - msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - } else { - String streamId = streamInfo.getStreamId(); - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - if (rtpInfo.getBoolean("exist")) { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData(JSON.toJSONString(streamInfo)); - resultHolder.invokeResult(msg); - } else { - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - cmder.playStreamCmd(device, channelId, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); - }, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - Response response = event.getResponse(); - msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - } - } - - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - // 释放rtpserver - cmder.closeRTPServer(device, channelId); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - return result; - } - - @PostMapping("/play/{streamId}/stop") - public DeferredResult> playStop(@PathVariable String streamId) { - - logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId)); - - UUID uuid = UUID.randomUUID(); - DeferredResult> result = new DeferredResult>(); - - // 录像查询以channelId作为deviceId查询 - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result); - - cmder.streamByeCmd(streamId, event -> { - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo == null) { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("streamId not found"); - resultHolder.invokeResult(msg); - }else { - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); - //Response response = event.getResponse(); - msg.setData(String.format("success")); - resultHolder.invokeResult(msg); - } - }); - - if (streamId != null) { - JSONObject json = new JSONObject(); - json.put("streamId", streamId); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData(json.toString()); - resultHolder.invokeResult(msg); - } else { - logger.warn("设备预览/回放停止API调用失败!"); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("streamId null"); - resultHolder.invokeResult(msg); - } - - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备预览/回放停止超时,streamId:%s ", streamId)); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - return result; - } - - /** - * 将不是h264的视频通过ffmpeg 转码为h264 + aac - * @param streamId 流ID - * @return - */ - @PostMapping("/play/{streamId}/convert") - public ResponseEntity playConvert(@PathVariable String streamId) { - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo == null) { - logger.warn("视频转码API调用失败!, 视频流已经停止!"); - return new ResponseEntity("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); - } - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); - if (!rtpInfo.getBoolean("exist")) { - logger.warn("视频转码API调用失败!, 视频流已停止推流!"); - return new ResponseEntity("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); - } else { - MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); - String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), - streamId ); - String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); - JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(srcUrl, dstUrl, "1000000"); - System.out.println(jsonObject); - JSONObject result = new JSONObject(); - if (jsonObject != null && jsonObject.getInteger("code") == 0) { - result.put("code", 0); - JSONObject data = jsonObject.getJSONObject("data"); - if (data != null) { - result.put("key", data.getString("key")); - StreamInfo streamInfoResult = new StreamInfo(); - streamInfoResult.setRtmp(dstUrl); - streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); - streamInfoResult.setStreamId(streamId); - streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); - result.put("data", streamInfoResult); - } - }else { - result.put("code", 1); - result.put("msg", "cover fail"); - } - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); - } - } - - /** - * 结束转码 - * @param key - * @return - */ - @PostMapping("/play/convert/stop/{key}") - public ResponseEntity playConvertStop(@PathVariable String key) { - - JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(key); - System.out.println(jsonObject); - JSONObject result = new JSONObject(); - if (jsonObject != null && jsonObject.getInteger("code") == 0) { - result.put("code", 0); - JSONObject data = jsonObject.getJSONObject("data"); - if (data != null && data.getBoolean("flag")) { - result.put("code", "0"); - result.put("msg", "success"); - }else { - - } - }else { - result.put("code", 1); - result.put("msg", "delFFmpegSource fail"); - } - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); - } + private final static Logger logger = LoggerFactory.getLogger(PlayController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorager storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IPlayService playService; + + @Autowired + private VideoStreamSessionManager streamSession; + + @GetMapping("/play/{deviceId}/{channelId}") + public DeferredResult> play(@PathVariable String deviceId, + @PathVariable String channelId) { + Device device = storager.queryVideoDevice(deviceId); + + RequestMessage msg = playService.createCallbackPlayMsg(); + DeferredResult> result = new DeferredResult<>(); + // 超时处理 + result.onTimeout(() -> { + logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + // 释放rtpserver + cmder.closeRTPServer(device, channelId); + StreamInfo streamInfo = streamSession.getPlayStreamInfo(channelId); + streamSession.remove(streamInfo); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(msg.getId(), result); + + // 判断是否已经存在点播 + StreamInfo oldStreamInfo = streamSession.getPlayStreamInfo(channelId); + if (oldStreamInfo == null) { + // 发送点播消息 + playStreamCmd(device, channelId, msg); + return result; + } + + // 若已有人点播,直接播放 + String streamId = oldStreamInfo.getStreamId(); + String mediaServerIp = oldStreamInfo.getMediaServerIp(); + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerIp, streamId); + if (rtpInfo.getBoolean("exist")) { + msg.setData(JSON.toJSONString(oldStreamInfo)); + resultHolder.invokeResult(msg); + return result; + } + + // 若已有人点播,但已超时自动断开,则重新发起点播 + storager.stopPlay(oldStreamInfo.getDeviceID(), oldStreamInfo.getChannelId()); + streamSession.remove(oldStreamInfo); + + playStreamCmd(device, channelId, msg); + return result; + } + + private void playStreamCmd(Device device, String channelId, RequestMessage msg) { + cmder.playStreamCmd(device, channelId, (JSONObject response) -> { + logger.info("收到点播回调消息: " + response.toJSONString()); + playService.onPublishHandlerForPlay(response, device.getDeviceId(), channelId, msg); + }, event -> { + StreamInfo streamInfo = streamSession.getPlayStreamInfo(channelId); + streamSession.remove(streamInfo); + Response response = event.getResponse(); + int statusCode = response.getStatusCode(); + String errMsg; + if (503 == statusCode) { + errMsg = "点播失败,请检查在NVR上是否可以正常打开监控,并检查NVR和SIP是否连通, 错误码: %s, %s"; + } else { + errMsg = "点播失败,错误码: %s, %s"; + } + msg.setData(String.format(errMsg, statusCode, response.getReasonPhrase())); + resultHolder.invokeResult(msg); + }); + } + + @PostMapping("/play/{channelId}/{streamId}/stop") + public DeferredResult> playStop(@PathVariable String channelId, @PathVariable String streamId) { + + logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId)); + + RequestMessage msg = playService.createCallbackPlayMsg(); + DeferredResult> result = new DeferredResult<>(); + // 超时处理 + result.onTimeout(() -> { + logger.warn(String.format("设备预览/回放停止超时,streamId:%s ", streamId)); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + }); + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(msg.getId(), result); + + StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); + if (streamInfo == null) { + msg.setData("streamId not found"); + resultHolder.invokeResult(msg); + } else { + cmder.stopStreamByeCmd(streamInfo, event -> { + msg.setData(String.format("success")); + resultHolder.invokeResult(msg); + }); + } + return result; + } + + /** + * 将不是h264的视频通过ffmpeg 转码为h264 + aac + * + * @param streamId 流ID + */ + @PostMapping("/play/{channelId}/{streamId}/convert") + public ResponseEntity playConvert(@PathVariable String channelId, @PathVariable String streamId) { + StreamInfo streamInfo = streamSession.getPlayStreamInfo(channelId); + if (streamInfo == null) { + logger.warn("视频转码API调用失败!, 视频流已经停止!"); + return new ResponseEntity("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); + } + String mediaServerIp = streamInfo.getMediaServerIp(); + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerIp, streamId); + if (!rtpInfo.getBoolean("exist")) { + logger.warn("视频转码API调用失败!, 视频流已停止推流!"); + return new ResponseEntity("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); + } else { + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); + String dstUrl = String.format("rtmp://%s:%s/convert/%s", mediaServerIp, mediaInfo.getRtmpPort(), + streamId); + String srcUrl = String.format("rtsp://%s:%s/rtp/%s", mediaServerIp, mediaInfo.getRtspPort(), streamId); + JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaServerIp, srcUrl, dstUrl, "1000000"); + System.out.println(jsonObject); + JSONObject result = new JSONObject(); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + result.put("code", 0); + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null) { + result.put("key", data.getString("key")); + StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setRtmp(dstUrl); + streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaServerIp, mediaInfo.getRtspPort(), streamId)); + streamInfoResult.setStreamId(streamId); + streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaServerIp, mediaInfo.getHttpPort(), streamId)); + result.put("data", streamInfoResult); + } + } else { + result.put("code", 1); + result.put("msg", "cover fail"); + } + return new ResponseEntity<>(result.toJSONString(), HttpStatus.OK); + } + } + + /** + * 结束转码 + * + * @param key + * @return + */ + @PostMapping("/play/convert/stop/{channelId}/{streamId}/{key}") + public ResponseEntity playConvertStop(@PathVariable String channelId, @PathVariable String streamId, @PathVariable String key) { + String mediaServerIp = streamSession.getMediaServerIp(channelId, streamId); + JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaServerIp, key); + System.out.println(jsonObject); + JSONObject result = new JSONObject(); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + result.put("code", 0); + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null && data.getBoolean("flag")) { + result.put("code", "0"); + result.put("msg", "success"); + } else { + + } + } else { + result.put("code", 1); + result.put("msg", "delFFmpegSource fail"); + } + return new ResponseEntity(result.toJSONString(), HttpStatus.OK); + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java index 020a26c2..4b027c92 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java @@ -1,110 +1,101 @@ package com.genersoft.iot.vmp.vmanager.playback; +import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.vmanager.service.IPlayService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; import javax.sip.message.Response; -import java.util.UUID; @CrossOrigin @RestController @RequestMapping("/api") public class PlaybackController { - private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - // @Autowired - // private ZLMRESTfulUtils zlmresTfulUtils; - - @Autowired - private IPlayService playService; - - @Autowired - private DeferredResultHolder resultHolder; - - @GetMapping("/playback/{deviceId}/{channelId}") - public DeferredResult> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, - String endTime) { - - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); - } - UUID uuid = UUID.randomUUID(); - DeferredResult> result = new DeferredResult>(); - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); - Device device = storager.queryVideoDevice(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); - if (streamInfo != null) { - // 停止之前的回放 - cmder.streamByeCmd(streamInfo.getStreamId()); - } - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); - cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); - }, event -> { - Response response = event.getResponse(); - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); - resultHolder.invokeResult(msg); - }); - - return result; - } - - @RequestMapping("/playback/{ssrc}/stop") - public ResponseEntity playStop(@PathVariable String ssrc) { - - cmder.streamByeCmd(ssrc); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); - } - - if (ssrc != null) { - JSONObject json = new JSONObject(); - json.put("ssrc", ssrc); - return new ResponseEntity(json.toString(), HttpStatus.OK); - } else { - logger.warn("设备录像回放停止API调用失败!"); - return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); - } - } + private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorager storager; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private IPlayService playService; + + @Autowired + private DeferredResultHolder resultHolder; + + @GetMapping("/playback/{deviceId}/{channelId}") + public DeferredResult> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, + String endTime) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); + } + RequestMessage msg = playService.createCallbackPlayMsg(); + DeferredResult> result = new DeferredResult>(); + // 超时处理 + result.onTimeout(() -> { + logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId); + streamSession.remove(streamInfo); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + }); + Device device = storager.queryVideoDevice(deviceId); + StreamInfo oldStreamInfo = streamSession.getPlayBackStreamInfo(channelId); + if (oldStreamInfo != null) { + // TODO 只能停止自己之前的回放,不能停止别人的回放,否则会导致别人无法播放 + cmder.stopStreamByeCmd(oldStreamInfo, null); + } + resultHolder.put(msg.getId(), result); + cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { + logger.info("收到订阅消息: " + response.toJSONString()); + playService.onPublishHandlerForPlayBack(response, deviceId, channelId, msg); + }, event -> { + StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId); + streamSession.remove(streamInfo); + Response response = event.getResponse(); + msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); + resultHolder.invokeResult(msg); + }); + + return result; + } + + @RequestMapping("/playback/{channelId}/{ssrc}/stop") + public ResponseEntity playStop(@PathVariable String channelId, @PathVariable String ssrc) { + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); + } + if (ssrc == null) { + logger.warn("设备录像回放停止API调用失败!"); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + StreamInfo streamInfo = streamSession.getStreamInfo(channelId, ssrc); + if (streamInfo == null) { + logger.warn("回放播流不存在!"); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + cmder.stopStreamByeCmd(streamInfo, null); + JSONObject json = new JSONObject(); + json.put("ssrc", ssrc); + return new ResponseEntity<>(json.toString(), HttpStatus.OK); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java index d81bdedd..51dacffb 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java @@ -1,12 +1,16 @@ package com.genersoft.iot.vmp.vmanager.service; import com.alibaba.fastjson.JSONObject; +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; /** * 点播处理 */ public interface IPlayService { + RequestMessage createCallbackPlayMsg(); - void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid); - void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid); + void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, RequestMessage msg); + + + void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, RequestMessage msg); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java index f0879761..fd06b3f0 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java @@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.vmanager.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; @@ -15,6 +15,8 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.UUID; + @Service public class PlayServiceImpl implements IPlayService { @@ -29,11 +31,21 @@ public class PlayServiceImpl implements IPlayService { @Autowired private DeferredResultHolder resultHolder; + @Autowired + private VideoStreamSessionManager streamSession; + @Override - public void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, String uuid) { + public RequestMessage createCallbackPlayMsg() { + String msgId = DeferredResultHolder.CALLBACK_CMD_PlAY + UUID.randomUUID(); RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); + msg.setId(msgId); + return msg; + } + + @Override + public void onPublishHandlerForPlay(JSONObject response, String deviceId, String channelId, RequestMessage msg) { + String streamId = response.getString("id"); + StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); if (streamInfo != null) { DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); if (deviceChannel != null) { @@ -41,56 +53,29 @@ public class PlayServiceImpl implements IPlayService { storager.startPlay(deviceId, channelId, streamInfo.getStreamId()); } - redisCatchStorage.startPlay(streamInfo); +// redisCatchStorage.startPlay(streamInfo); msg.setData(JSON.toJSONString(streamInfo)); resultHolder.invokeResult(msg); } else { - logger.warn("设备预览API调用失败!"); - msg.setData("设备预览API调用失败!"); + logger.warn("设备点播API调用失败!"); + msg.setData("设备点播API调用失败!"); resultHolder.invokeResult(msg); } } @Override - public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid) { - RequestMessage msg = new RequestMessage(); - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); - StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); + public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, RequestMessage msg) { + String streamId = resonse.getString("id"); + StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId); if (streamInfo != null) { - redisCatchStorage.startPlayback(streamInfo); +// redisCatchStorage.startPlayback(streamInfo); msg.setData(JSON.toJSONString(streamInfo)); resultHolder.invokeResult(msg); } else { - logger.warn("设备预览API调用失败!"); - msg.setData("设备预览API调用失败!"); + logger.warn("设备回放API调用失败!"); + msg.setData("设备回放API调用失败!"); resultHolder.invokeResult(msg); } } - public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) { - String streamId = resonse.getString("id"); - StreamInfo streamInfo = new StreamInfo(); - streamInfo.setStreamId(streamId); - streamInfo.setDeviceID(deviceId); - streamInfo.setChannelId(channelId); - MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo(); - - streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); - - streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtmpPort(), streamId)); - streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtspPort(), streamId)); - - return streamInfo; - } - } diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java index 5a56bef7..2bd6b73d 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java @@ -5,9 +5,8 @@ import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -// import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.vmanager.play.PlayController; import org.slf4j.Logger; @@ -20,7 +19,6 @@ import org.springframework.web.context.request.async.DeferredResult; /** * 兼容LiveGBS的API:实时直播 */ -@SuppressWarnings(value = {"rawtypes", "unchecked"}) @CrossOrigin @RestController @RequestMapping(value = "/api/v1/stream") @@ -30,80 +28,73 @@ public class ApiStreamController { @Autowired private SIPCommander cmder; - @Autowired private IVideoManagerStorager storager; - @Autowired - private IRedisCatchStorage redisCatchStorage; - - - // @Autowired - // private ZLMRESTfulUtils zlmresTfulUtils; - - + private VideoStreamSessionManager streamSession; @Autowired private PlayController playController; /** * 实时直播 - 开始直播 - * @param serial 设备编号 - * @param channel 通道序号 默认值: 1 - * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 - * @param cdn TODO 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent - * @param audio TODO 是否开启音频, 默认 开启 - * @param transport 流传输模式, 默认 UDP + * + * @param serial 设备编号 + * @param channel 通道序号 默认值: 1 + * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 + * @param cdn TODO 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent + * @param audio TODO 是否开启音频, 默认 开启 + * @param transport 流传输模式, 默认 UDP * @param checkchannelstatus TODO 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线 - * @param transportmode TODO 当 transport=TCP 时有效, 指示流传输主被动模式, 默认被动 - * @param timeout TODO 拉流超时(秒), + * @param transportmode TODO 当 transport=TCP 时有效, 指示流传输主被动模式, 默认被动 + * @param timeout TODO 拉流超时(秒), * @return */ @RequestMapping(value = "/start") - private DeferredResult start(String serial , - @RequestParam(required = false)Integer channel , - @RequestParam(required = false)String code, - @RequestParam(required = false)String cdn, - @RequestParam(required = false)String audio, - @RequestParam(required = false)String transport, - @RequestParam(required = false)String checkchannelstatus , - @RequestParam(required = false)String transportmode, - @RequestParam(required = false)String timeout - - ){ + private DeferredResult start(String serial, + @RequestParam(required = false) Integer channel, + @RequestParam(required = false) String code, + @RequestParam(required = false) String cdn, + @RequestParam(required = false) String audio, + @RequestParam(required = false) String transport, + @RequestParam(required = false) String checkchannelstatus, + @RequestParam(required = false) String transportmode, + @RequestParam(required = false) String timeout + + ) { DeferredResult resultDeferredResult = new DeferredResult(); Device device = storager.queryVideoDevice(serial); - if (device == null ) { + if (device == null) { JSONObject result = new JSONObject(); - result.put("error","device[ " + serial + " ]未找到"); + result.put("error", "device[ " + serial + " ]未找到"); resultDeferredResult.setResult(result); - }else if (device.getOnline() == 0) { + } else if (device.getOnline() == 0) { JSONObject result = new JSONObject(); - result.put("error","device[ " + code + " ]offline"); + result.put("error", "device[ " + code + " ]offline"); resultDeferredResult.setResult(result); } - resultDeferredResult.onTimeout(()->{ + resultDeferredResult.onTimeout(() -> { logger.info("播放等待超时"); JSONObject result = new JSONObject(); - result.put("error","timeout"); + result.put("error", "timeout"); resultDeferredResult.setResult(result); - // 清理RTP server + // 清理RTP server }); DeviceChannel deviceChannel = storager.queryChannel(serial, code); if (deviceChannel == null) { JSONObject result = new JSONObject(); - result.put("error","channel[ " + code + " ]未找到"); + result.put("error", "channel[ " + code + " ]未找到"); resultDeferredResult.setResult(result); - }else if (deviceChannel.getStatus() == 0) { + } else if (deviceChannel.getStatus() == 0) { JSONObject result = new JSONObject(); - result.put("error","channel[ " + code + " ]offline"); + result.put("error", "channel[ " + code + " ]offline"); resultDeferredResult.setResult(result); } DeferredResult> play = playController.play(serial, code); - play.setResultHandler((Object o)->{ - ResponseEntity responseEntity = (ResponseEntity)o; + play.setResultHandler((Object o) -> { + ResponseEntity responseEntity = (ResponseEntity) o; StreamInfo streamInfo = JSON.parseObject(responseEntity.getBody(), StreamInfo.class); JSONObject result = new JSONObject(); result.put("StreamID", streamInfo.getStreamId()); @@ -143,49 +134,50 @@ public class ApiStreamController { /** * 实时直播 - 直播流停止 - * @param serial 设备编号 - * @param channel 通道序号 - * @param code 通道国标编号 + * + * @param serial 设备编号 + * @param channel 通道序号 + * @param code 通道国标编号 * @param check_outputs * @return */ @RequestMapping(value = "/stop") @ResponseBody - private JSONObject stop(String serial , - @RequestParam(required = false)Integer channel , - @RequestParam(required = false)String code, - @RequestParam(required = false)String check_outputs + private JSONObject stop(String serial, + @RequestParam(required = false) Integer channel, + @RequestParam(required = false) String code, + @RequestParam(required = false) String check_outputs - ){ + ) { + JSONObject result = new JSONObject(); - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code); + StreamInfo streamInfo = streamSession.getPlayStreamInfo(code); if (streamInfo == null) { - JSONObject result = new JSONObject(); - result.put("error","未找到流信息"); + result.put("error", "未找到流信息"); return result; } - cmder.streamByeCmd(streamInfo.getStreamId()); - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - return null; + cmder.stopStreamByeCmd(streamInfo, null); + result.put("success", "成功"); + return result; } /** * 实时直播 - 直播流保活 - * @param serial 设备编号 + * + * @param serial 设备编号 * @param channel 通道序号 - * @param code 通道国标编号 + * @param code 通道国标编号 * @return */ @RequestMapping(value = "/touch") @ResponseBody - private JSONObject touch(String serial ,String t, - @RequestParam(required = false)Integer channel , - @RequestParam(required = false)String code, - @RequestParam(required = false)String autorestart, - @RequestParam(required = false)String audio, - @RequestParam(required = false)String cdn - ){ + private JSONObject touch(String serial, String t, + @RequestParam(required = false) Integer channel, + @RequestParam(required = false) String code, + @RequestParam(required = false) String autorestart, + @RequestParam(required = false) String audio, + @RequestParam(required = false) String cdn + ) { return null; } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 26f5d084..611289d0 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -11,6 +11,9 @@ spring: password: # [可选] 超时时间 timeout: 10000 + poolMaxTotal: 1000 + poolMaxIdle: 50 + poolMaxWait: 500 # [不可用] jdbc数据库配置, 暂不支持 datasource: #name: eiot @@ -34,7 +37,7 @@ server: # 作为28181服务器的配置 sip: - # [必须修改] 本机的IP, 必须是网卡上的IP + # [必须修改] 本机的内网IP, 必须是网卡上的IP ip: 192.168.0.100 # [可选] 28181服务监听的端口 port: 5060 @@ -57,8 +60,8 @@ auth: #zlm服务器配置 media: - # [必须修改] zlm服务器的内网IP - ip: 192.168.0.100 + # [必须修改] zlm服务器的IP(内网公网IP均可),配置多台时IP用逗号隔开 + ip: 172.16.22.232 # [可选] zlm服务器的公网IP, 内网部署置空即可 wanIp: # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue index 4ee2b7c1..0816d087 100644 --- a/web_src/src/components/channelList.vue +++ b/web_src/src/components/channelList.vue @@ -214,7 +214,7 @@ export default { var that = this; this.$axios({ method: 'post', - url: '/api/play/' + itemData.streamId + '/stop' + url: '/api/play/' + itemData.channelId + "/" + itemData.streamId + '/stop' }).then(function (res) { console.log(JSON.stringify(res)); that.initData(); diff --git a/web_src/src/components/gb28181/devicePlayer.vue b/web_src/src/components/gb28181/devicePlayer.vue index 23418319..7da33733 100644 --- a/web_src/src/components/gb28181/devicePlayer.vue +++ b/web_src/src/components/gb28181/devicePlayer.vue @@ -196,7 +196,9 @@ export default { if (tab.name == "codec") { this.$axios({ method: 'get', - url: '/zlm/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app=rtp&stream='+ this.streamId + url: '/zlm/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app=rtp' + +'&channelId='+ this.channelId + +'&stream='+ this.streamId }).then(function (res) { that.tracksLoading = false; if (res.data.code == 0 && res.data.online) { @@ -251,7 +253,7 @@ export default { this.$refs.videoPlayer.pause() that.$axios({ method: 'post', - url: '/api/play/' + that.streamId + '/convert' + url: '/api/play/' + that.channelId + "/" + that.streamId + '/convert' }).then(function (res) { if (res.data.code == 0) { that.convertKey = res.data.key; @@ -288,7 +290,7 @@ export default { that.$refs.videoPlayer.pause() this.$axios({ method: 'post', - url: '/api/play/convert/stop/' + this.convertKey + url: '/api/play/convert/stop/' + this.channelId + "/" + + this.streamId + "/" + this.convertKey }).then(function (res) { if (res.data.code == 0) { console.log(res.data.msg) @@ -398,7 +400,7 @@ export default { this.videoUrl = ''; this.$axios({ method: 'get', - url: '/api/playback/' + this.streamId + '/stop' + url: '/api/playback/' + this.channelId + '/' + this.streamId + '/stop' }).then(function (res) { if (callback) callback() });