Browse Source

Merge branch 'master' into wvp-28181-2.0

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
#	src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
#	src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
#	src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
#	src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
#	src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
#	src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
#	src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
#	src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
#	src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java
#	src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
#	src/main/resources/application-dev.yml
pull/76/head
wangshaopeng@sunnybs.com 4 years ago
parent
commit
19e8325ede
  1. 2
      .gitignore
  2. 2
      README.md
  3. 90
      pom.xml
  4. 44
      src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
  5. 119
      src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
  6. 94
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  7. 75
      src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java
  8. 53
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  9. 535
      src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java
  10. 146
      src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
  11. 29
      src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
  12. 51
      src/main/java/com/genersoft/iot/vmp/conf/SsrcConfig.java
  13. 496
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  14. 12
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
  15. 6
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
  16. 108
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
  17. 48
      src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java
  18. 23
      src/main/java/com/genersoft/iot/vmp/gb28181/session/PlayTypeEnum.java
  19. 93
      src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java
  20. 327
      src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
  21. 462
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
  22. 123
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
  23. 601
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  24. 366
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
  25. 2815
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  26. 2020
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
  27. 130
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
  28. 43
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
  29. 722
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  30. 127
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  31. 51
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
  32. 137
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
  33. 41
      src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
  34. 542
      src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
  35. 2
      src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
  36. 213
      src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
  37. 8
      src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java
  38. 97
      src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java
  39. 1502
      src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
  40. 120
      src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java
  41. 119
      src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfigController.java
  42. 238
      src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java
  43. 237
      src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControlController.java
  44. 261
      src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java
  45. 255
      src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java
  46. 332
      src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
  47. 169
      src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
  48. 7
      src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java
  49. 64
      src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
  50. 126
      src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java
  51. 9
      src/main/resources/application-dev.yml
  52. 2
      web_src/src/components/channelList.vue
  53. 10
      web_src/src/components/gb28181/devicePlayer.vue

2
.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

2
README.md

@ -53,7 +53,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. 报警信息处理,支持向前端推送报警信息

90
pom.xml

@ -13,6 +13,34 @@
<artifactId>wvp</artifactId>
<name>web video platform</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 依赖版本 -->
<mybatis-spring-boot-starter-version>2.1.4</mybatis-spring-boot-starter-version>
<druid-version>1.2.3</druid-version>
<mysql-connector-java-version>8.0.22</mysql-connector-java-version>
<sqlite-jdbc-version>3.32.3.2</sqlite-jdbc-version>
<jedis-version>3.1.0</jedis-version>
<pagehelper-spring-boot-starter-version>1.2.10</pagehelper-spring-boot-starter-version>
<springfox-swagger2-version>2.9.2</springfox-swagger2-version>
<springfox-swagger-ui-version>2.6.1</springfox-swagger-ui-version>
<jain-sip-ri-version>1.3.0-91</jain-sip-ri-version>
<log4j-version>1.2.17</log4j-version>
<dom4j-version>2.1.3</dom4j-version>
<fastjson-version>1.2.73</fastjson-version>
<guava-version>30.0-jre</guava-version>
<lombok-version>1.18.12</lombok-version>
<commons-lang3-version>3.7</commons-lang3-version>
<commons-io-version>2.6</commons-io-version>
<okhttp-version>4.9.0</okhttp-version>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
</properties>
<repositories>
<repository>
<id>nexus-aliyun</id>
@ -41,17 +69,6 @@
</pluginRepository>
</pluginRepositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 依赖版本 -->
<pagehelper.version>5.2.0</pagehelper.version>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
</properties>
<dependencies>
<dependency>
@ -65,54 +82,54 @@
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
<version>${mybatis-spring-boot-starter-version}</version>
</dependency>
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
<version>${druid-version}</version>
</dependency>
<!-- mysql数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
<version>${mysql-connector-java-version}</version>
</dependency>
<!-- 添加sqlite-jdbc数据库驱动 -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.32.3.2</version>
<version>${sqlite-jdbc-version}</version>
</dependency>
<!--Mybatis分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
<version>${pagehelper-spring-boot-starter-version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.apache.commons</groupId>-->
<!-- <artifactId>commons-lang3</artifactId>-->
<!-- <version>3.11</version>-->
<!-- </dependency>-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis-version}</version>
</dependency>
<!--Swagger2 -->
<!--在线文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<version>${springfox-swagger2-version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
<version>${springfox-swagger-ui-version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
@ -129,41 +146,56 @@
<dependency>
<groupId>javax.sip</groupId>
<artifactId>jain-sip-ri</artifactId>
<version>1.3.0-91</version>
<version>${jain-sip-ri-version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<version>${log4j-version}</version>
</dependency>
<!-- xml解析库 -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
<version>${dom4j-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
<version>${fastjson-version}</version>
</dependency>
<!--Guava是一种基于开源的Java库-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.0-jre</version>
<version>${guava-version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3-version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io-version}</version>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
<version>${okhttp-version}</version>
</dependency>
</dependencies>

44
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java

@ -1,23 +1,21 @@
package com.genersoft.iot.vmp;
import java.util.logging.LogManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class VManageBootstrap extends LogManager {
private static String[] args;
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
VManageBootstrap.args = args;
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
}
// 项目重启
public static void restart() {
context.close();
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
}
}
package com.genersoft.iot.vmp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class VManageBootstrap extends LogManager {
private static String[] args;
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
VManageBootstrap.args = args;
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
}
// 项目重启
public static void restart() {
context.close();
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
}
}

119
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;
}
}

94
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@ -1,46 +1,48 @@
package com.genersoft.iot.vmp.common;
/**
* @Description: 定义常量
* @author: swwheihei
* @date: 2019年5月30日 下午3:04:04
*
*/
public class VideoManagerConstants {
public static final String MEDIA_SERVER_PREFIX = "VMP_media_server";
public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream";
public static final String DEVICE_PREFIX = "VMP_device_";
public static final String CACHEKEY_PREFIX = "VMP_channel_";
public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_";
public static final String PLAYER_PREFIX = "VMP_player_";
public static final String PLAY_BLACK_PREFIX = "VMP_playback_";
public static final String PLATFORM_PREFIX = "VMP_platform";
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_platform_keeplive_";
public static final String PLATFORM_CATCH_PREFIX = "VMP_platform_catch_";
public static final String PLATFORM_REGISTER_PREFIX = "VMP_platform_register_";
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_platform_register_info_";
public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_platform_send_rtp_info_";
public static final String Pattern_Topic = "VMP_keeplive_platform_";
public static final String EVENT_ONLINE_REGISTER = "1";
public static final String EVENT_ONLINE_KEEPLIVE = "2";
public static final String EVENT_OUTLINE_UNREGISTER = "1";
public static final String EVENT_OUTLINE_TIMEOUT = "2";
}
package com.genersoft.iot.vmp.common;
/**
* @Description: 定义常量
* @author: swwheihei
* @date: 2019年5月30日 下午3:04:04
*
*/
public class VideoManagerConstants {
public static final String MEDIA_SERVER_PREFIX = "VMP_media_server";
public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream";
public static final String DEVICE_PREFIX = "VMP_device_";
public static final String CACHEKEY_PREFIX = "VMP_channel_";
public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_";
public static final String PLAYER_PREFIX = "VMP_player_";
public static final String PLAY_BLACK_PREFIX = "VMP_playback_";
public static final String PLATFORM_PREFIX = "VMP_platform";
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_platform_keeplive_";
public static final String PLATFORM_CATCH_PREFIX = "VMP_platform_catch_";
public static final String PLATFORM_REGISTER_PREFIX = "VMP_platform_register_";
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_platform_register_info_";
public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_platform_send_rtp_info_";
public static final String Pattern_Topic = "VMP_keeplive_platform_";
public static final String EVENT_ONLINE_REGISTER = "1";
public static final String EVENT_ONLINE_KEEPLIVE = "2";
public static final String EVENT_OUTLINE_UNREGISTER = "1";
public static final String EVENT_OUTLINE_TIMEOUT = "2";
public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
}

75
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<String> usedSet = jedisUtil.smembers(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mIp);
ssrcConfig.init(mIp, usedSet);
mediaServerSsrcMap.put(mIp, ssrcConfig);
}
mediaConfig.setMediaServerSsrcMap(mediaServerSsrcMap);
}
}

53
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<String, SsrcConfig> mediaServerSsrcMap;
}

535
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;
}
}

146
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<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用fastjson进行序列化处理,提高解析效率
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
// 使用fastjson时需设置此项,否则会报异常not support type
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
return template;
}
/**
* redis消息监听器容器 可以添加多个监听不同话题的redis监听器只需要把消息监听器和相应的消息订阅处理器绑定该消息监听器
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
*
* @param connectionFactory
* @param listenerAdapter
* @return
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
package com.genersoft.iot.vmp.conf;
import com.alibaba.fastjson.parser.ParserConfig;
import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @Description:Redis中间件配置类使用spring-data-redis集成自动从application.yml中加载redis配置
* @author: swwheihei
* @date: 2019年5月30日 上午10:58:25
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.poolMaxTotal}")
private int poolMaxTotal;
@Value("${spring.redis.poolMaxIdle}")
private int poolMaxIdle;
@Value("${spring.redis.poolMaxWait}")
private int poolMaxWait;
@Bean
public JedisPool jedisPool() {
if (StringUtils.isBlank(password)) {
password = null;
}
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(poolMaxIdle);
poolConfig.setMaxTotal(poolMaxTotal);
// 秒转毫秒
poolConfig.setMaxWaitMillis(poolMaxWait * 1000);
JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database);
return jp;
}
@Bean("redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用fastjson进行序列化处理,提高解析效率
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
// 使用fastjson时需设置此项,否则会报异常not support type
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
return template;
}
/**
* redis消息监听器容器 可以添加多个监听不同话题的redis监听器只需要把消息监听器和相应的消息订阅处理器绑定该消息监听器
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
*
* @param connectionFactory
* @return
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}

29
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java

@ -8,15 +8,15 @@ import org.springframework.context.annotation.Configuration;
public class SipConfig {
@Value("${sip.ip}")
private String sipIp;
String sipIp;
@Value("${sip.port}")
private Integer sipPort;
Integer sipPort;
@Value("${sip.domain}")
private String sipDomain;
String sipDomain;
@Value("${sip.id}")
private String sipId;
String sipId;
@Value("${sip.password}")
private String sipPassword;
String sipPassword;
@Value("${sip.ptz.speed:50}")
Integer speed;
@ -25,28 +25,47 @@ public class SipConfig {
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;
}
}

51
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<String> isUsed;
/**
* zlm流媒体服务器可用会话句柄
*/
private List<String> notUsed;
public void init(String mediaServerIp, Set<String> usedSet) {
this.mediaServerIp = mediaServerIp;
this.isUsed = new ArrayList<>();
this.notUsed = new ArrayList<>();
for (int i = 1; i < ConfigConst.MAX_STRTEAM_COUNT; i++) {
String ssrc;
if (i < 10) {
ssrc = "000" + i;
} else if (i < 100) {
ssrc = "00" + i;
} else if (i < 1000) {
ssrc = "0" + i;
} else {
ssrc = String.valueOf(i);
}
if (null == usedSet || !usedSet.contains(ssrc)) {
this.notUsed.add(ssrc);
} else {
this.isUsed.add(ssrc);
}
}
}
}

496
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

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

12
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;
/**
* 在线
*/

6
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;
}

108
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<RecordItem> recordList;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSumNum() {
return sumNum;
}
public void setSumNum(int sumNum) {
this.sumNum = sumNum;
}
public List<RecordItem> getRecordList() {
return recordList;
}
public void setRecordList(List<RecordItem> recordList) {
this.recordList = recordList;
}
}
package com.genersoft.iot.vmp.gb28181.bean;
import java.util.List;
/**
* @Description:设备录像信息bean
* @author: swwheihei
* @date: 2020年5月8日 下午2:05:56
*/
public class RecordInfo {
private String deviceId;
private String name;
private int sumNum;
private List<RecordItem> recordList;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSumNum() {
return sumNum;
}
public void setSumNum(int sumNum) {
this.sumNum = sumNum;
}
public List<RecordItem> getRecordList() {
return recordList;
}
public void setRecordList(List<RecordItem> recordList) {
this.recordList = recordList;
}
}

48
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);
}
}

23
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;
}
}

93
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java

@ -1,93 +0,0 @@
package com.genersoft.iot.vmp.gb28181.session;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
/**
* @Description:SIP信令中的SSRC工具类SSRC值由10位十进制整数组成的字符串第一位为0代表实况为1则代表回放第二位至第六位由监控域ID的第4位到第8位组成最后4位为不重复的4个整数
* @author: swwheihei
* @date: 2020年5月10日 上午11:57:57
*/
public class SsrcUtil {
private static String ssrcPrefix;
private static List<String> isUsed;
private static List<String> notUsed;
private static void init() {
SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig");
ssrcPrefix = sipConfig.getSipDomain().substring(3, 8);
isUsed = new ArrayList<String>();
notUsed = new ArrayList<String>();
for (int i = 1; i < 10000; i++) {
if (i < 10) {
notUsed.add("000" + i);
} else if (i < 100) {
notUsed.add("00" + i);
} else if (i < 1000) {
notUsed.add("0" + i);
} else {
notUsed.add(String.valueOf(i));
}
}
}
/**
* 获取视频预览的SSRC值,第一位固定为0
*
*/
public static String getPlaySsrc() {
return "0" + getSsrcPrefix() + getSN();
}
/**
* 获取录像回放的SSRC值,第一位固定为1
*
*/
public static String getPlayBackSsrc() {
return "1" + getSsrcPrefix() + getSN();
}
/**
* 释放ssrc主要用完的ssrc一定要释放否则会耗尽
*
*/
public static void releaseSsrc(String ssrc) {
String sn = ssrc.substring(6);
isUsed.remove(sn);
notUsed.add(sn);
}
/**
* 获取后四位数SN,随机数
*
*/
private static String getSN() {
String sn = null;
int index = 0;
if (notUsed.size() == 0) {
throw new RuntimeException("ssrc已经用完");
} else if (notUsed.size() == 1) {
sn = notUsed.get(0);
} else {
index = new Random().nextInt(notUsed.size() - 1);
sn = notUsed.get(index);
}
notUsed.remove(index);
isUsed.add(sn);
return sn;
}
private static String getSsrcPrefix() {
if (ssrcPrefix == null) {
init();
}
return ssrcPrefix;
}
}

327
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<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>();
public String createPlaySsrc(){
return SsrcUtil.getPlaySsrc();
}
public String createPlayBackSsrc(){
return SsrcUtil.getPlayBackSsrc();
}
public void put(String streamId,String ssrc,ClientTransaction transaction){
sessionMap.put(streamId, transaction);
ssrcMap.put(streamId, ssrc);
}
public ClientTransaction get(String streamId){
return sessionMap.get(streamId);
}
public void remove(String streamId) {
sessionMap.remove(streamId);
SsrcUtil.releaseSsrc(ssrcMap.get(streamId));
ssrcMap.remove(streamId);
}
}
package com.genersoft.iot.vmp.gb28181.session;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.SsrcConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.redis.JedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.sip.ClientTransaction;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description:视频流session管理器管理视频预览预览回放的通信句柄
* @author: swwheihei
* @date: 2020年5月13日 下午4:03:02
*/
@Slf4j
@Component
public class VideoStreamSessionManager {
/**
* key: ssrc 播流会话句柄(streamId)和同步信源(SSRC)的对应关系
* value: 流媒体服务器
*/
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
private String ssrcPrefix;
@Autowired
private SipConfig sipConfig;
@Autowired
private MediaConfig mediaConfig;
@Autowired
private JedisUtil jedisUtil;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@PostConstruct
public void init() {
this.ssrcPrefix = sipConfig.getSipDomain().substring(3, 8);
}
/**
* 获取视频预览的会话信息
*/
public StreamInfo createPlayStreamInfo(Device device, String channelId) {
// SSRC值,第一位固定为0
StreamInfo streamInfo = createStreamInfo(device, channelId, PlayTypeEnum.PLAY);
// 会话句柄和ZLM服务器的对应关系,存到redis,防止服务器宕机数据丢失。
redisCatchStorage.startPlay(streamInfo);
return streamInfo;
}
/**
* 获取录像回放的会话信息
* 会话句柄和ZLM服务器的对应关系存到redis防止服务器宕机数据丢失
*/
public StreamInfo createPlayBackStreamInfo(Device device, String channelId) {
// SSRC值,第一位固定为1
StreamInfo streamInfo = createStreamInfo(device, channelId, PlayTypeEnum.PLAY_BACK);
// 会话句柄和ZLM服务器的对应关系,存到redis,防止服务器宕机数据丢失。
redisCatchStorage.startPlayback(streamInfo);
return streamInfo;
}
/**
* 1选举ZLM服务器
* 2分配SSRC
* 3生成streamId和播流RUL如果此时未连接ZLM会抛出运行时异常
* 4已分配SSRC存储到redis防止服务器宕机后数据丢失
*/
private StreamInfo createStreamInfo(Device device, String channelId, PlayTypeEnum playType) {
// 1、选举ZLM服务器
SsrcConfig ssrcConfig = elect();
List<String> isUsed = ssrcConfig.getIsUsed();
List<String> notUsed = ssrcConfig.getNotUsed();
// 2、分配SSRC
String sn;
int index = 0;
if (notUsed.size() == 0) {
throw new RuntimeException("ssrc已经用完");
} else if (notUsed.size() == 1) {
sn = notUsed.get(0);
} else {
index = new Random().nextInt(notUsed.size() - 1);
sn = notUsed.get(index);
}
String ssrc = playType.getValue() + ssrcPrefix + sn;
String mediaServerIp = ssrcConfig.getMediaServerIp();
// 3、生成streamId和播流RUL,如果此时未连接ZLM,会抛出运行时异常
StreamInfo streamInfo = initStreamInfo(device, channelId, ssrc, mediaServerIp);
// 4、已分配SSRC存储到redis,防止服务器宕机后数据丢失。
jedisUtil.sadd(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mediaServerIp, sn);
notUsed.remove(index);
isUsed.add(sn);
return streamInfo;
}
/**
* 流媒体服务器选举算法
*
* @return
*/
private SsrcConfig elect() {
Set<Map.Entry<String, SsrcConfig>> entries = mediaConfig.getMediaServerSsrcMap().entrySet();
SsrcConfig min = null;
for (Map.Entry<String, SsrcConfig> e : entries) {
SsrcConfig vo = e.getValue();
if (null == min) {
min = vo;
continue;
}
if (vo.getNotUsed().size() > min.getNotUsed().size()) {
min = vo;
}
}
return min;
}
/**
* 生成streamId和播流RUL如果此时未连接ZLM会抛出运行时异常
*
* @param device
* @param channelId
* @param ssrc
* @param mediaServerIp
* @return
*/
private StreamInfo initStreamInfo(Device device, String channelId, String ssrc, String mediaServerIp) {
String streamId;
if (ssrc.startsWith(PlayTypeEnum.PLAY.getValue()) && mediaConfig.getRtpEnable()) {
streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
} else {
streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
}
StreamInfo streamInfo = new StreamInfo();
streamInfo.setMediaServerIp(mediaServerIp);
streamInfo.setSsrc(ssrc);
streamInfo.setStreamId(streamId);
streamInfo.setDeviceID(device.getDeviceId());
streamInfo.setChannelId(channelId);
MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo();
if (null == mediaServerConfig) {
throw new RuntimeException("点播时发现ZLM尚未连接...");
}
streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerIp, mediaServerConfig.getHttpPort(), streamId));
streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerIp, mediaServerConfig.getRtmpPort(), streamId));
streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerIp, mediaServerConfig.getRtspPort(), streamId));
return streamInfo;
}
/**
* 查找IPC通道播流使用流媒体服务器的IP
*
* @param channelId
* @param streamId
* @return
*/
public String getMediaServerIp(String channelId, String streamId) {
StreamInfo streamInfo = this.getStreamInfo(channelId, streamId);
return null == streamInfo ? null : streamInfo.getMediaServerIp();
}
public StreamInfo getPlayStreamInfo(String channelId) {
if (StringUtils.isBlank(channelId)) {
log.error("getPlayStreamInfo channelId can not be null!!!");
return null;
}
return redisCatchStorage.queryPlayByChannel(channelId);
}
public StreamInfo getPlayBackStreamInfo(String channelId) {
if (StringUtils.isBlank(channelId)) {
log.error("getPlayBackStreamInfo channelId can not be null!!!");
return null;
}
return redisCatchStorage.queryPlaybackByChannel(channelId);
}
public StreamInfo getStreamInfo(String channelId, String streamId) {
if (StringUtils.isBlank(channelId) || StringUtils.isBlank(streamId)) {
log.error("getStreamInfo channelId and streamId can not be null!!!");
return null;
}
StreamInfo streamInfo = getStreamInfo(channelId, streamId, PlayTypeEnum.PLAY);
if (null == streamInfo) {
streamInfo = getStreamInfo(channelId, streamId, PlayTypeEnum.PLAY_BACK);
}
return streamInfo;
}
private StreamInfo getStreamInfo(String channelId, String streamId, PlayTypeEnum playType) {
if (StringUtils.isBlank(channelId) || StringUtils.isBlank(streamId)) {
log.error("getStreamInfo channelId and streamId can not be null!!!");
return null;
}
// TODO channelId
if (null == playType || PlayTypeEnum.PLAY.equals(playType)) {
return redisCatchStorage.queryPlayByStreamId(channelId, streamId);
} else {
return redisCatchStorage.queryPlaybackByStreamId(channelId, streamId);
}
}
/**
* 存储会话
*
* @param channelId
* @param streamId
* @param transaction
*/
public void putClientTransaction(String channelId, String streamId, ClientTransaction transaction) {
String streamKey = getStreamKey(channelId, streamId);
sessionMap.put(streamKey, transaction);
}
public ClientTransaction getClientTransaction(String channelId, String streamId) {
String streamKey = getStreamKey(channelId, streamId);
return sessionMap.get(streamKey);
}
public void remove(String channelId, String streamId) {
StreamInfo streamInfo = this.getStreamInfo(channelId, streamId);
if (null == streamId) {
return;
}
this.remove(streamInfo);
}
/**
* 移除会话并释放ssrc主要用完的ssrc一定要释放否则会耗尽
*/
public void remove(StreamInfo streamInfo) {
String streamKey = getStreamKey(streamInfo.getChannelId(), streamInfo.getStreamId());
// 移除会话
sessionMap.remove(streamKey);
String ssrc = streamInfo.getSsrc();
String sn = ssrc.substring(6);
String mediaServerIp = streamInfo.getMediaServerIp();
// 释放ssrc,并从redis移除
jedisUtil.srem(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mediaServerIp, sn);
SsrcConfig ssrcConfig = mediaConfig.getMediaServerSsrcMap().get(mediaServerIp);
ssrcConfig.getIsUsed().remove(sn);
ssrcConfig.getNotUsed().add(sn);
// 会话句柄和ZLM服务器的对应关系,从redis移除
if (ssrc.startsWith(PlayTypeEnum.PLAY.getValue())) {
redisCatchStorage.stopPlay(streamInfo);
} else {
redisCatchStorage.stopPlayback(streamInfo);
}
}
private static String getStreamKey(String channelId, String streamId) {
return channelId + "_" + streamId;
}
}

462
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java

@ -1,231 +1,231 @@
package com.genersoft.iot.vmp.gb28181.transmit;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.SipProvider;
import javax.sip.header.CSeqHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*;
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
// import org.slf4j.Logger;
// import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/**
* @Description: SIP信令处理分配
* @author: swwheihei
* @date: 2020年5月3日 下午4:24:37
*/
@Component
public class SIPProcessorFactory {
// private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
@Autowired
private SipConfig sipConfig;
@Autowired
private RegisterLogicHandler handler;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private EventPublisher publisher;
@Autowired
private SIPCommander cmder;
@Autowired
private SIPCommanderFroPlatform cmderFroPlatform;
@Autowired
private RedisUtil redis;
@Autowired
private DeferredResultHolder deferredResultHolder;
@Autowired
private DeviceOffLineDetector offLineDetector;
@Autowired
private InviteResponseProcessor inviteResponseProcessor;
@Autowired
private ByeResponseProcessor byeResponseProcessor;
@Autowired
private CancelResponseProcessor cancelResponseProcessor;
@Autowired
@Lazy
private RegisterResponseProcessor registerResponseProcessor;
@Autowired
private OtherResponseProcessor otherResponseProcessor;
@Autowired
private IPlayService playService;
@Autowired
private ZLMRTPServerFactory zlmrtpServerFactory;
// 注:这里使用注解会导致循环依赖注入,暂用springBean
private SipProvider tcpSipProvider;
// 注:这里使用注解会导致循环依赖注入,暂用springBean
private SipProvider udpSipProvider;
public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) {
Request request = evt.getRequest();
String method = request.getMethod();
// logger.info("接收到消息:"+request.getMethod());
// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
if (Request.INVITE.equals(method)) {
InviteRequestProcessor processor = new InviteRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setCmder(cmder);
processor.setCmderFroPlatform(cmderFroPlatform);
processor.setPlayService(playService);
processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
return processor;
} else if (Request.REGISTER.equals(method)) {
RegisterRequestProcessor processor = new RegisterRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setHandler(handler);
processor.setPublisher(publisher);
processor.setSipConfig(sipConfig);
processor.setVideoManagerStorager(storager);
return processor;
} else if (Request.SUBSCRIBE.equals(method)) {
SubscribeRequestProcessor processor = new SubscribeRequestProcessor();
processor.setRequestEvent(evt);
return processor;
} else if (Request.ACK.equals(method)) {
AckRequestProcessor processor = new AckRequestProcessor();
processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
return processor;
} else if (Request.BYE.equals(method)) {
ByeRequestProcessor processor = new ByeRequestProcessor();
processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setSIPCommander(cmder);
return processor;
} else if (Request.CANCEL.equals(method)) {
CancelRequestProcessor processor = new CancelRequestProcessor();
processor.setRequestEvent(evt);
return processor;
} else if (Request.MESSAGE.equals(method)) {
MessageRequestProcessor processor = new MessageRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setPublisher(publisher);
processor.setRedis(redis);
processor.setDeferredResultHolder(deferredResultHolder);
processor.setOffLineDetector(offLineDetector);
processor.setCmder(cmder);
processor.setCmderFroPlatform(cmderFroPlatform);
processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage);
return processor;
} else if (Request.NOTIFY.equalsIgnoreCase(method)) {
NotifyRequestProcessor processor = new NotifyRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setPublisher(publisher);
processor.setRedis(redis);
processor.setDeferredResultHolder(deferredResultHolder);
processor.setOffLineDetector(offLineDetector);
processor.setCmder(cmder);
processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage);
return processor;
} else {
OtherRequestProcessor processor = new OtherRequestProcessor();
processor.setRequestEvent(evt);
return processor;
}
}
public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {
Response response = evt.getResponse();
CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
String method = cseqHeader.getMethod();
if(Request.INVITE.equals(method)){
return inviteResponseProcessor;
} else if (Request.BYE.equals(method)) {
return byeResponseProcessor;
} else if (Request.CANCEL.equals(method)) {
return cancelResponseProcessor;
}else if (Request.REGISTER.equals(method)) {
return registerResponseProcessor;
} else {
return otherResponseProcessor;
}
}
private SipProvider getTcpSipProvider() {
if (tcpSipProvider == null) {
tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider");
}
return tcpSipProvider;
}
private SipProvider getUdpSipProvider() {
if (udpSipProvider == null) {
udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
}
return udpSipProvider;
}
}
package com.genersoft.iot.vmp.gb28181.transmit;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.SipProvider;
import javax.sip.header.CSeqHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*;
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
// import org.slf4j.Logger;
// import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/**
* @Description: SIP信令处理分配
* @author: swwheihei
* @date: 2020年5月3日 下午4:24:37
*/
@Component
public class SIPProcessorFactory {
// private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
@Autowired
private SipConfig sipConfig;
@Autowired
private RegisterLogicHandler handler;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private EventPublisher publisher;
@Autowired
private SIPCommander cmder;
@Autowired
private SIPCommanderFroPlatform cmderFroPlatform;
@Autowired
private RedisUtil redis;
@Autowired
private DeferredResultHolder deferredResultHolder;
@Autowired
private DeviceOffLineDetector offLineDetector;
@Autowired
private InviteResponseProcessor inviteResponseProcessor;
@Autowired
private ByeResponseProcessor byeResponseProcessor;
@Autowired
private CancelResponseProcessor cancelResponseProcessor;
@Autowired
@Lazy
private RegisterResponseProcessor registerResponseProcessor;
@Autowired
private OtherResponseProcessor otherResponseProcessor;
@Autowired
private IPlayService playService;
@Autowired
private ZLMRTPServerFactory zlmrtpServerFactory;
// 注:这里使用注解会导致循环依赖注入,暂用springBean
private SipProvider tcpSipProvider;
// 注:这里使用注解会导致循环依赖注入,暂用springBean
private SipProvider udpSipProvider;
public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) {
Request request = evt.getRequest();
String method = request.getMethod();
// logger.info("接收到消息:"+request.getMethod());
// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
if (Request.INVITE.equals(method)) {
InviteRequestProcessor processor = new InviteRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setCmder(cmder);
processor.setCmderFroPlatform(cmderFroPlatform);
processor.setPlayService(playService);
processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
return processor;
} else if (Request.REGISTER.equals(method)) {
RegisterRequestProcessor processor = new RegisterRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setHandler(handler);
processor.setPublisher(publisher);
processor.setSipConfig(sipConfig);
processor.setVideoManagerStorager(storager);
return processor;
} else if (Request.SUBSCRIBE.equals(method)) {
SubscribeRequestProcessor processor = new SubscribeRequestProcessor();
processor.setRequestEvent(evt);
return processor;
} else if (Request.ACK.equals(method)) {
AckRequestProcessor processor = new AckRequestProcessor();
processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
return processor;
} else if (Request.BYE.equals(method)) {
ByeRequestProcessor processor = new ByeRequestProcessor();
processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setSIPCommander(cmder);
return processor;
} else if (Request.CANCEL.equals(method)) {
CancelRequestProcessor processor = new CancelRequestProcessor();
processor.setRequestEvent(evt);
return processor;
} else if (Request.MESSAGE.equals(method)) {
MessageRequestProcessor processor = new MessageRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setPublisher(publisher);
processor.setRedis(redis);
processor.setDeferredResultHolder(deferredResultHolder);
processor.setOffLineDetector(offLineDetector);
processor.setCmder(cmder);
processor.setCmderFroPlatform(cmderFroPlatform);
processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage);
return processor;
} else if (Request.NOTIFY.equalsIgnoreCase(method)) {
NotifyRequestProcessor processor = new NotifyRequestProcessor();
processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider());
processor.setPublisher(publisher);
processor.setRedis(redis);
processor.setDeferredResultHolder(deferredResultHolder);
processor.setOffLineDetector(offLineDetector);
processor.setCmder(cmder);
processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage);
return processor;
} else {
OtherRequestProcessor processor = new OtherRequestProcessor();
processor.setRequestEvent(evt);
return processor;
}
}
public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {
Response response = evt.getResponse();
CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
String method = cseqHeader.getMethod();
if(Request.INVITE.equals(method)){
return inviteResponseProcessor;
} else if (Request.BYE.equals(method)) {
return byeResponseProcessor;
} else if (Request.CANCEL.equals(method)) {
return cancelResponseProcessor;
}else if (Request.REGISTER.equals(method)) {
return registerResponseProcessor;
} else {
return otherResponseProcessor;
}
}
private SipProvider getTcpSipProvider() {
if (tcpSipProvider == null) {
tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider");
}
return tcpSipProvider;
}
private SipProvider getUdpSipProvider() {
if (udpSipProvider == null) {
udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
}
return udpSipProvider;
}
}

123
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java

@ -1,63 +1,60 @@
package com.genersoft.iot.vmp.gb28181.transmit.callback;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult;
/**
* @Description: 异步请求处理
* @author: swwheihei
* @date: 2020年5月8日 下午7:59:05
*/
@SuppressWarnings(value = {"rawtypes", "unchecked"})
@Component
public class DeferredResultHolder {
public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY";
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
public void put(String key, DeferredResult result) {
map.put(key, result);
}
public DeferredResult get(String key) {
return map.get(key);
}
public void invokeResult(RequestMessage msg) {
DeferredResult result = map.get(msg.getId());
if (result == null) {
return;
}
result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
}
}
package com.genersoft.iot.vmp.gb28181.transmit.callback;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult;
/**
* @Description: 异步请求处理
* @author: swwheihei
* @date: 2020年5月8日 下午7:59:05
*/
@Component
public class DeferredResultHolder {
public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY";
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
public void put(String key, DeferredResult result) {
map.put(key, result);
}
public void invokeResult(RequestMessage msg) {
// DeferredResult result = map.get(msg.getId());
// 获取并移除
DeferredResult result = map.remove(msg.getId());
if (result == null) {
return;
}
result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
}
}

601
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@ -1,300 +1,301 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
* @author: swwheihei
* @date: 2020年5月3日 下午9:16:34
*/
public interface ISIPCommander {
/**
* 云台方向放控制使用配置文件中的默认镜头移动速度
*
* @param device 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);
/**
* 云台方向放控制
*
* @param device 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);
/**
* 云台缩放控制使用配置文件中的默认镜头缩放速度
*
* @param device 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
*/
boolean ptzZoomCmd(Device device,String channelId,int inOut);
/**
* 云台缩放控制
*
* @param device 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param zoomSpeed 镜头缩放速度
*/
boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);
/**
* 云台控制支持方向与缩放控制
*
* @param device 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param moveSpeed 镜头移动速度
* @param zoomSpeed 镜头缩放速度
*/
boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
/**
* 前端控制包括PTZ指令FI指令预置位指令巡航指令扫描指令和辅助开关指令
*
* @param device 控制设备
* @param channelId 预览通道
* @param cmdCode 指令码
* @param parameter1 数据1
* @param parameter2 数据2
* @param combineCode2 组合码2
*/
boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
/**
* 前端控制指令用于转发上级指令
* @param device 控制设备
* @param channelId 预览通道
* @param cmdString 前端控制指令串
*/
boolean fronEndCmd(Device device, String channelId, String cmdString);
/**
* 请求预览视频流
*
* @param device 视频设备
* @param channelId 预览通道
*/
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 请求回放视频流
*
* @param device 视频设备
* @param channelId 预览通道
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 视频流停止
*
* @param ssrc ssrc
*/
void streamByeCmd(String ssrc, SipSubscribe.Event okEvent);
void streamByeCmd(String ssrc);
/**
* 语音广播
*
* @param device 视频设备
* @param channelId 预览通道
*/
boolean audioBroadcastCmd(Device device,String channelId);
/**
* 语音广播
*
* @param device 视频设备
*/
void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent);
boolean audioBroadcastCmd(Device device);
/**
* 音视频录像控制
*
* @param device 视频设备
* @param channelId 预览通道
* @param recordCmdStr 录像命令Record / StopRecord
*/
boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
/**
* 远程启动控制命令
*
* @param device 视频设备
*/
boolean teleBootCmd(Device device);
/**
* 报警布防/撤防命令
*
* @param device 视频设备
* @param setGuard true: SetGuard, false: ResetGuard
*/
boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
/**
* 报警复位命令
*
* @param device 视频设备
* @param alarmMethod 报警方式可选
* @param alarmType 报警类型可选
*/
boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
/**
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
*
* @param device 视频设备
* @param channelId 预览通道
*/
boolean iFrameCmd(Device device, String channelId);
/**
* 看守位控制命令
*
* @param device 视频设备
* @param enabled 看守位使能1 = 开启0 = 关闭
* @param resetTime 自动归位时间间隔开启看守位时使用单位:(s)
* @param presetIndex 调用预置位编号开启看守位时使用取值范围0~255
*/
boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
/**
* 设备配置命令
*
* @param device 视频设备
*/
boolean deviceConfigCmd(Device device);
/**
* 设备配置命令basicParam
*
* @param device 视频设备
* @param channelId 通道编码可选
* @param name 设备/通道名称可选
* @param expiration 注册过期时间可选
* @param heartBeatInterval 心跳间隔时间可选
* @param heartBeatCount 心跳超时次数可选
*/
boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
/**
* 查询设备状态
*
* @param device 视频设备
*/
boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 查询设备信息
*
* @param device 视频设备
* @return
*/
boolean deviceInfoQuery(Device device);
/**
* 查询目录列表
*
* @param device 视频设备
*/
boolean catalogQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 查询录像信息
*
* @param device 视频设备
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
/**
* 查询报警信息
*
* @param device 视频设备
* @param startPriority 报警起始级别可选
* @param endPriority 报警终止级别可选
* @param alarmMethod 报警方式条件可选
* @param alarmType 报警类型
* @param startTime 报警发生起始时间可选
* @param endTime 报警发生终止时间可选
* @return true = 命令发送成功
*/
boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);
/**
* 查询设备配置
*
* @param device 视频设备
* @param channelId 通道编码可选
* @param configType 配置类型
*/
boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent);
/**
* 查询设备预置位置
*
* @param device 视频设备
*/
boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
/**
* 查询移动设备位置数据
*
* @param device 视频设备
*/
boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 订阅取消订阅移动位置
*
* @param device 视频设备
* @param expires 订阅超时时间=0时为取消订阅
* @param interval 上报时间间隔
* @return true = 命令发送成功
*/
boolean mobilePositionSubscribe(Device device, int expires, int interval);
/**
* 订阅取消订阅报警信息
* @param device 视频设备
* @param expires 订阅过期时间0 = 取消订阅
* @param startPriority 报警起始级别可选
* @param endPriority 报警终止级别可选
* @param alarmMethods 报警方式条件可选
* @param alarmType 报警类型
* @param startTime 报警发生起始时间可选
* @param endTime 报警发生终止时间可选
* @return true = 命令发送成功
*/
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
/**
* 释放rtpserver
* @param device
* @param channelId
*/
void closeRTPServer(Device device, String channelId);
}
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
* @author: swwheihei
* @date: 2020年5月3日 下午9:16:34
*/
public interface ISIPCommander {
/**
* 云台方向放控制使用配置文件中的默认镜头移动速度
*
* @param device 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);
/**
* 云台方向放控制
*
* @param device 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度
*/
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);
/**
* 云台缩放控制使用配置文件中的默认镜头缩放速度
*
* @param device 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
*/
boolean ptzZoomCmd(Device device,String channelId,int inOut);
/**
* 云台缩放控制
*
* @param device 控制设备
* @param channelId 预览通道
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param zoomSpeed 镜头缩放速度
*/
boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);
/**
* 云台控制支持方向与缩放控制
*
* @param device 控制设备
* @param channelId 预览通道
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param moveSpeed 镜头移动速度
* @param zoomSpeed 镜头缩放速度
*/
boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
/**
* 前端控制包括PTZ指令FI指令预置位指令巡航指令扫描指令和辅助开关指令
*
* @param device 控制设备
* @param channelId 预览通道
* @param cmdCode 指令码
* @param parameter1 数据1
* @param parameter2 数据2
* @param combineCode2 组合码2
*/
boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
/**
* 前端控制指令用于转发上级指令
* @param device 控制设备
* @param channelId 预览通道
* @param cmdString 前端控制指令串
*/
boolean fronEndCmd(Device device, String channelId, String cmdString);
/**
* 请求预览视频流
*
* @param device 视频设备
* @param channelId 预览通道
*/
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 请求回放视频流
*
* @param device 视频设备
* @param channelId 预览通道
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 视频流停止
*
* @param streamInfo streamInfo
* @param okEvent okEvent
*/
void stopStreamByeCmd(StreamInfo streamInfo, SipSubscribe.Event okEvent);
/**
* 语音广播
*
* @param device 视频设备
* @param channelId 预览通道
*/
boolean audioBroadcastCmd(Device device,String channelId);
/**
* 语音广播
*
* @param device 视频设备
*/
void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent);
boolean audioBroadcastCmd(Device device);
/**
* 音视频录像控制
*
* @param device 视频设备
* @param channelId 预览通道
* @param recordCmdStr 录像命令Record / StopRecord
*/
boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
/**
* 远程启动控制命令
*
* @param device 视频设备
*/
boolean teleBootCmd(Device device);
/**
* 报警布防/撤防命令
*
* @param device 视频设备
* @param setGuard true: SetGuard, false: ResetGuard
*/
boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
/**
* 报警复位命令
*
* @param device 视频设备
* @param alarmMethod 报警方式可选
* @param alarmType 报警类型可选
*/
boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
/**
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
*
* @param device 视频设备
* @param channelId 预览通道
*/
boolean iFrameCmd(Device device, String channelId);
/**
* 看守位控制命令
*
* @param device 视频设备
* @param enabled 看守位使能1 = 开启0 = 关闭
* @param resetTime 自动归位时间间隔开启看守位时使用单位:(s)
* @param presetIndex 调用预置位编号开启看守位时使用取值范围0~255
*/
boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
/**
* 设备配置命令
*
* @param device 视频设备
*/
boolean deviceConfigCmd(Device device);
/**
* 设备配置命令basicParam
*
* @param device 视频设备
* @param channelId 通道编码可选
* @param name 设备/通道名称可选
* @param expiration 注册过期时间可选
* @param heartBeatInterval 心跳间隔时间可选
* @param heartBeatCount 心跳超时次数可选
*/
boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
/**
* 查询设备状态
*
* @param device 视频设备
*/
boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 查询设备信息
*
* @param device 视频设备
* @return
*/
boolean deviceInfoQuery(Device device);
/**
* 查询目录列表
*
* @param device 视频设备
*/
boolean catalogQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 查询录像信息
*
* @param device 视频设备
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
/**
* 查询报警信息
*
* @param device 视频设备
* @param startPriority 报警起始级别可选
* @param endPriority 报警终止级别可选
* @param alarmMethod 报警方式条件可选
* @param alarmType 报警类型
* @param startTime 报警发生起始时间可选
* @param endTime 报警发生终止时间可选
* @return true = 命令发送成功
*/
boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);
/**
* 查询设备配置
*
* @param device 视频设备
* @param channelId 通道编码可选
* @param configType 配置类型
*/
boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent);
/**
* 查询设备预置位置
*
* @param device 视频设备
*/
boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
/**
* 查询移动设备位置数据
*
* @param device 视频设备
*/
boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 订阅取消订阅移动位置
*
* @param device 视频设备
* @param expires 订阅超时时间=0时为取消订阅
* @param interval 上报时间间隔
* @return true = 命令发送成功
*/
boolean mobilePositionSubscribe(Device device, int expires, int interval);
/**
* 订阅取消订阅报警信息
* @param device 视频设备
* @param expires 订阅过期时间0 = 取消订阅
* @param startPriority 报警起始级别可选
* @param endPriority 报警终止级别可选
* @param alarmMethods 报警方式条件可选
* @param alarmType 报警类型
* @param startTime 报警发生起始时间可选
* @param endTime 报警发生终止时间可选
* @return true = 命令发送成功
*/
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
/**
* 释放rtpserver
* @param device
* @param channelId
*/
void closeRTPServer(Device device, String channelId);
}

366
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java

@ -1,183 +1,183 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import java.text.ParseException;
import java.util.ArrayList;
import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory;
// import javax.sip.SipProvider;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
/**
* @Description:摄像头命令request创造器 TODO 冗余代码太多待优化
* @author: swwheihei
* @date: 2020年5月6日 上午9:29:02
*/
@Component
public class SIPRequestHeaderProvider {
@Autowired
private SipConfig sipConfig;
@Autowired
private SipFactory sipFactory;
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
// sipuri
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),
sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
//请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
//via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
//Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
// Subject
SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0));
request.addHeader(subjectHeader);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
//请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
//Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
// sipuri
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),
sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE);
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
// Expires
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
request.addHeader(expireHeader);
// Event
EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event);
request.addHeader(eventHeader);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
request.setContent(content, contentTypeHeader);
return request;
}
}
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import java.text.ParseException;
import java.util.ArrayList;
import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory;
// import javax.sip.SipProvider;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
/**
* @Description:摄像头命令request创造器 TODO 冗余代码太多待优化
* @author: swwheihei
* @date: 2020年5月6日 上午9:29:02
*/
@Component
public class SIPRequestHeaderProvider {
@Autowired
private SipConfig sipConfig;
@Autowired
private SipFactory sipFactory;
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
// sipuri
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),
sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
//请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
//via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
//Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
// Subject
SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0));
request.addHeader(subjectHeader);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
//请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
//Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
// sipuri
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),
sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE);
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
// Expires
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
request.addHeader(expireHeader);
// Event
EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event);
request.addHeader(eventHeader);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
request.setContent(content, contentTypeHeader);
return request;
}
}

2815
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

File diff suppressed because it is too large

2020
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java

File diff suppressed because it is too large

130
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();
}
}
}

43
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;

722
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@ -1,362 +1,360 @@
package com.genersoft.iot.vmp.media.zlm;
import java.util.UUID;
import com.alibaba.fastjson.JSON;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import javax.servlet.http.HttpServletRequest;
/**
* @Description:针对 ZLMediaServer的hook事件监听
* @author: swwheihei
* @date: 2020年5月8日 上午10:46:48
*/
@RestController
@RequestMapping("/index/hook")
public class ZLMHttpHookListener {
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
@Autowired
private SIPCommander cmder;
@Autowired
private IPlayService playService;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private ZLMMediaListManager zlmMediaListManager;
@Autowired
private ZLMHttpHookSubscribe subscribe;
@Value("${media.autoApplyPlay}")
private boolean autoApplyPlay;
@Value("${media.ip}")
private String mediaIp;
@Value("${media.wanIp}")
private String mediaWanIp;
@Value("${media.port}")
private int mediaPort;
/**
* 流量统计事件播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件阈值通过配置文件general.flowThreshold配置此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 访问http文件服务器上hls之外的文件时触发
*
*/
@ResponseBody
@PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onHttpAccess(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("err", "");
ret.put("path", "");
ret.put("second", 600);
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 播放器鉴权事件rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件
*
*/
@ResponseBody
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onPlay(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* rtsp/rtmp/rtp推流鉴权事件
*
*/
@ResponseBody
@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onPublish(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString());
}
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
if (subscribe != null) subscribe.response(json);
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
ret.put("enableHls", true);
ret.put("enableMP4", false);
ret.put("enableRtxp", true);
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 录制mp4完成后通知事件此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* rtsp专用的鉴权事件先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件
*
*/
@ResponseBody
@PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onRtspRealm(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("realm", "");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 该rtsp流是否开启rtsp专用方式的鉴权事件开启后才会触发on_rtsp_auth事件需要指出的是rtsp也支持url参数鉴权它支持两种方式鉴权
*
*/
@ResponseBody
@PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onRtspAuth(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("encrypted", false);
ret.put("passwd", "test");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* shell登录鉴权ZLMediaKit提供简单的telnet调试方式使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面
*
*/
@ResponseBody
@PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString());
}
// TODO 如果是带有rtpstream则开启按需拉流
// String app = json.getString("app");
// String stream = json.getString("stream");
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
if (subscribe != null) subscribe.response(json);
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* rtsp/rtmp流注册或注销时触发此事件此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString());
}
// 流消失移除redis play
String app = json.getString("app");
String streamId = json.getString("stream");
String schema = json.getString("schema");
boolean regist = json.getBoolean("regist");
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
if ("rtp".equals(app) && !regist ) {
if (streamInfo!=null){
redisCatchStorage.stopPlay(streamInfo);
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
}else{
streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
redisCatchStorage.stopPlayback(streamInfo);
}
}else {
if (!"rtp".equals(app) && "rtsp".equals(schema)){
zlmMediaListManager.updateMediaList();
}
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 流无人观看时事件用户可以通过此事件选择是否关闭无人看的流
*
*/
@ResponseBody
@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamNoneReader(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString());
}
String streamId = json.getString("stream");
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("close", true);
if (streamInfo != null) {
if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) {
ret.put("close", false);
} else {
cmder.streamByeCmd(streamId);
redisCatchStorage.stopPlay(streamInfo);
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
}
}else{
cmder.streamByeCmd(streamId);
streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
redisCatchStorage.stopPlayback(streamInfo);
}
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 流未找到事件用户可以在此事件触发时立即去拉流这样可以实现按需拉流此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString());
}
if (autoApplyPlay) {
String app = json.getString("app");
String streamId = json.getString("stream");
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
if ("rtp".equals(app) && streamId.indexOf("gb_play") > -1 && streamInfo == null) {
String[] s = streamId.split("_");
if (s.length == 4) {
String deviceId = s[2];
String channelId = s[3];
Device device = storager.queryVideoDevice(deviceId);
if (device != null) {
UUID uuid = UUID.randomUUID();
cmder.playStreamCmd(device, channelId, (JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString());
}, null);
}
}
}
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 服务器启动事件可以用于监听服务器崩溃重启此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString());
}
// String data = json.getString("data");
// List<MediaServerConfig> mediaServerConfigs = JSON.parseArray(JSON.toJSONString(json), MediaServerConfig.class);
// MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0);
MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class);
mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp);
mediaServerConfig.setLocalIP(mediaIp);
redisCatchStorage.updateMediaInfo(mediaServerConfig);
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
}
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
/**
* @Description:针对 ZLMediaServer的hook事件监听
* @author: swwheihei
* @date: 2020年5月8日 上午10:46:48
*/
@RestController
@RequestMapping("/index/hook")
public class ZLMHttpHookListener {
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
@Autowired
private SIPCommander cmder;
@Autowired
private IPlayService playService;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private ZLMMediaListManager zlmMediaListManager;
@Autowired
private ZLMHttpHookSubscribe subscribe;
@Autowired
private ZLMHttpHookSubscribe subscribe;
@Autowired
MediaConfig mediaConfig;
@Autowired
private VideoStreamSessionManager streamSession;
/**
* 流量统计事件播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件阈值通过配置文件general.flowThreshold配置此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 访问http文件服务器上hls之外的文件时触发
*
*/
@ResponseBody
@PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onHttpAccess(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("err", "");
ret.put("path", "");
ret.put("second", 600);
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 播放器鉴权事件rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件
*
*/
@ResponseBody
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onPlay(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* rtsp/rtmp/rtp推流鉴权事件
*
*/
@ResponseBody
@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onPublish(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString());
}
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
if (subscribe != null) {
subscribe.response(json);
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
ret.put("enableHls", true);
ret.put("enableMP4", false);
ret.put("enableRtxp", true);
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 录制mp4完成后通知事件此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* rtsp专用的鉴权事件先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件
*
*/
@ResponseBody
@PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onRtspRealm(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("realm", "");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* 该rtsp流是否开启rtsp专用方式的鉴权事件开启后才会触发on_rtsp_auth事件需要指出的是rtsp也支持url参数鉴权它支持两种方式鉴权
*
*/
@ResponseBody
@PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onRtspAuth(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("encrypted", false);
ret.put("passwd", "test");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* shell登录鉴权ZLMediaKit提供简单的telnet调试方式使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面
*
*/
@ResponseBody
@PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString());
}
// TODO 如果是带有rtpstream则开启按需拉流
// String app = json.getString("app");
// String stream = json.getString("stream");
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
if (subscribe != null) subscribe.response(json);
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
/**
* rtsp/rtmp流注册或注销时触发此事件此事件对回复不敏感
*/
@ResponseBody
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json) {
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
// 流消失移除redis play
String app = json.getString("app");
String streamId = json.getString("stream");
boolean regist = json.getBoolean("regist");
if (!"rtp".equals(app) || regist) {
if (!"rtp".equals(app) && "rtsp".equals(schema)){
zlmMediaListManager.updateMediaList();
}
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
String[] s = streamId.split("_");
if (s.length != 4) {
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
String channelId = s[3];
// TODO channelId
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId);
if (null != streamInfo) {
cmder.stopStreamByeCmd(streamInfo, null);
}
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
/**
* 流无人观看时事件用户可以通过此事件选择是否关闭无人看的流
*/
@ResponseBody
@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamNoneReader(@RequestBody JSONObject json) {
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("close", true);
String app = json.getString("app");
String streamId = json.getString("stream");
if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) {
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
String[] s = streamId.split("_");
if (s.length != 4) {
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
String channelId = s[3];
// TODO channelId
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId);
if (null != streamInfo) {
if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) {
ret.put("close", false);
} else {
cmder.stopStreamByeCmd(streamInfo, null);
}
}
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
/**
* 流未找到事件用户可以在此事件触发时立即去拉流这样可以实现按需拉流此事件对回复不敏感
*/
@ResponseBody
@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json) {
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString());
}
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
if (!mediaConfig.getAutoApplyPlay()) {
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
String app = json.getString("app");
String streamId = json.getString("stream");
if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) {
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
String[] s = streamId.split("_");
if (s.length != 4) {
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
String deviceId = s[2];
String channelId = s[3];
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId);
if (streamInfo != null) {
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
Device device = storager.queryVideoDevice(deviceId);
if (device != null) {
RequestMessage msg = playService.createCallbackPlayMsg();
cmder.playStreamCmd(device, channelId, (JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlay(response, deviceId, channelId, msg);
}, null);
}
return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
/**
* 服务器启动事件可以用于监听服务器崩溃重启此事件对回复不敏感
*
*/
@ResponseBody
@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString());
}
MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class);
redisCatchStorage.updateMediaInfo(mediaServerConfig);
// TODO Auto-generated method stub
JSONObject ret = new JSONObject();
ret.put("code", 0);
ret.put("msg", "success");
return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
}
}

127
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<String, Object> param) {
public JSONObject sendPost(String mediaServerIp, String api, Map<String, Object> 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,20 +56,29 @@ 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<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("schema",schema);
param.put("vhost","__defaultVhost__");
return sendPost("getMediaList",param);
}
// public JSONObject getMediaList(String app, String schema) {
// Map<String, Object> 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<String, Object> 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 getMediaList(){
return sendPost("getMediaList",null);
@ -86,42 +93,88 @@ public class ZLMRESTfulUtils {
return sendPost("getMediaInfo",param);
}
public JSONObject getRtpInfo(String stream_id){
public JSONObject getRtpInfo(String mediaServerIp, String stream_id) {
Map<String, Object> param = new HashMap<>();
param.put("stream_id",stream_id);
return sendPost("getRtpInfo",param);
param.put("stream_id", stream_id);
return sendPost(mediaServerIp, "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<String, Object> 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<String, Object> param = new HashMap<>();
param.put("key", key);
return sendPost("delFFmpegSource",param);
return sendPost(mediaServerIp, "delFFmpegSource", param);
}
public JSONObject getMediaServerConfig(String mediaServerIp) {
return sendPost(mediaServerIp, "getServerConfig", null);
}
public JSONObject setServerConfig(String mediaServerIp, Map<String, Object> param) {
return sendPost(mediaServerIp, "setServerConfig", param);
}
public JSONObject openRtpServer(String mediaServerIp, Map<String, Object> param) {
return sendPost(mediaServerIp, "openRtpServer", param);
}
public JSONObject getMediaServerConfig(){
return sendPost("getServerConfig",null);
public JSONObject closeRtpServer(String mediaServerIp, Map<String, Object> param) {
return sendPost(mediaServerIp, "closeRtpServer", param);
}
public JSONObject setServerConfig(Map<String, Object> param){
return sendPost("setServerConfig",param);
/**
* 截图
*
* @param mediaServerIp
* @param url
* @return
*/
public JSONObject getSnap(String mediaServerIp, String url) {
Map<String, Object> 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);
}
public JSONObject openRtpServer(Map<String, Object> param){
return sendPost("openRtpServer",param);
/**
* 开始录制
*
* @param mediaServerIp
* @param stream
* @return
*/
public JSONObject startRecord(String mediaServerIp, String stream) {
Map<String, Object> param = new HashMap<>();
param.put("type", 1);
param.put("app", "rtp");
param.put("stream", stream);
return sendPost(mediaServerIp, "startRecord", param);
}
public JSONObject closeRtpServer(Map<String, Object> param) {
return sendPost("closeRtpServer",param);
/**
* 结束录制
*
* @param mediaServerIp
* @param stream
* @return
*/
public JSONObject stopRecord(String mediaServerIp, String stream) {
Map<String, Object> param = new HashMap<>();
param.put("type", 1);
param.put("app", "rtp");
param.put("stream", stream);
return sendPost(mediaServerIp, "stopRecord", param);
}
public JSONObject startSendRtp(Map<String, Object> param) {

51
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java

@ -3,89 +3,102 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SsrcUtil;
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<String, Integer> currentPortMap = new ConcurrentHashMap<>();
public int createRTPServer(String streamId) {
public int createRTPServer(String mediaServerIp, String streamId) {
Map<String, Object> 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<String, Object> 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) {

137
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,44 +18,20 @@ 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;
@ -64,76 +40,81 @@ public class ZLMRunner implements CommandLineRunner {
private ZLMMediaListManager zlmMediaListManager;
@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);
// 更新流列表
zlmMediaListManager.updateMediaList();
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);
// 更新流列表
zlmMediaListManager.updateMediaList();
}
}
}
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<String, Object> 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);
}
}
}

41
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java

@ -13,52 +13,49 @@ import java.util.Map;
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<String, StreamInfo> queryPlayByDeviceId(String deviceId);
StreamInfo queryPlayByChannel(String channelId);
boolean startPlayback(StreamInfo stream);
boolean stopPlayback(StreamInfo streamInfo);
StreamInfo queryPlaybackByStreamId(String channelId, String steamId);
StreamInfo queryPlaybackByChannel(String channelId);
List<StreamInfo> queryPlayBackByDeviceId(String deviceId);
StreamInfo queryPlaybackByDevice(String deviceId, String code);
void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch);

542
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java

@ -1,264 +1,278 @@
package com.genersoft.iot.vmp.storager;
import java.util.List;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.github.pagehelper.PageInfo;
/**
* @Description:视频设备数据存储接口
* @author: swwheihei
* @date: 2020年5月6日 下午2:14:31
*/
@SuppressWarnings("rawtypes")
public interface IVideoManagerStorager {
/**
* 根据设备ID判断设备是否存在
*
* @param deviceId 设备ID
* @return true:存在 false不存在
*/
public boolean exists(String deviceId);
/**
* 视频设备创建
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
public boolean create(Device device);
/**
* 视频设备更新
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
public boolean updateDevice(Device device);
/**
* 添加设备通道
*
* @param deviceId 设备id
* @param channel 通道
*/
public void updateChannel(String deviceId, DeviceChannel channel);
/**
* 开始播放
* @param deviceId 设备id
* @param channelId 通道ID
* @param streamId 流地址
*/
public void startPlay(String deviceId, String channelId, String streamId);
/**
* 停止播放
* @param deviceId 设备id
* @param channelId 通道ID
*/
public void stopPlay(String deviceId, String channelId);
/**
* 获取设备
*
* @param deviceId 设备ID
* @return DShadow 设备对象
*/
public Device queryVideoDevice(String deviceId);
/**
* 获取某个设备的通道列表
*
* @param deviceId 设备ID
* @param page 分页 当前页
* @param count 每页数量
* @return
*/
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count);
/**
* 获取某个设备的通道列表
*
* @param deviceId 设备ID
* @return
*/
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId);
/**
* 获取某个设备的通道
* @param deviceId 设备ID
* @param channelId 通道ID
*/
public DeviceChannel queryChannel(String deviceId, String channelId);
/**
* 获取多个设备
* @param page 当前页数
* @param count 每页数量
* @return List<Device> 设备对象数组
*/
public PageInfo<Device> queryVideoDeviceList(int page, int count);
/**
* 获取多个设备
*
* @return List<Device> 设备对象数组
*/
public List<Device> queryVideoDeviceList();
/**
* 删除设备
*
* @param deviceId 设备ID
* @return true删除成功 false删除失败
*/
public boolean delete(String deviceId);
/**
* 更新设备在线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
public boolean online(String deviceId);
/**
* 更新设备离线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
public boolean outline(String deviceId);
/**
* 查询子设备
*
* @param deviceId
* @param channelId
* @param page
* @param count
* @return
*/
PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count);
/**
* 清空通道
* @param deviceId
*/
void cleanChannelsForDevice(String deviceId);
/**
* 更新上级平台
* @param parentPlatform
*/
boolean updateParentPlatform(ParentPlatform parentPlatform);
/**
* 添加上级平台
* @param parentPlatform
*/
boolean addParentPlatform(ParentPlatform parentPlatform);
/**
* 删除上级平台
* @param parentPlatform
*/
boolean deleteParentPlatform(ParentPlatform parentPlatform);
/**
* 分页获取上级平台
* @param page
* @param count
* @return
*/
PageInfo<ParentPlatform> queryParentPlatformList(int page, int count);
/**
* 获取所有已启用的平台
* @return
*/
List<ParentPlatform> queryEnableParentPlatformList(boolean enable);
/**
* 获取上级平台
* @param platformGbId
* @return
*/
ParentPlatform queryParentPlatById(String platformGbId);
/**
* 所有平台离线
*/
void outlineForAllParentPlatform();
/**
* 查询通道信息不区分设备(已关联平台或全部)
*/
PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, Boolean inPlatform);
/**
* 查询设备的通道信息
*/
List<ChannelReduce> queryChannelListInParentPlatform(String platformId);
/**
* 更新上级平台的通道信息
* @param platformId
* @param channelReduces
* @return
*/
int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces);
/**
* 移除上级平台的通道信息
* @param platformId
* @param channelReduces
* @return
*/
int delChannelForGB(String platformId, List<ChannelReduce> channelReduces);
DeviceChannel queryChannelInParentPlatform(String platformId, String channelId);
Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId);
/**
* 添加Mobile Position设备移动位置
* @param MobilePosition
* @return
*/
public boolean insertMobilePosition(MobilePosition mobilePosition);
/**
* 查询移动位置轨迹
* @param deviceId
* @param startTime
* @param endTime
*/
public List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime);
/**
* 查询最新移动位置
* @param deviceId
*/
public MobilePosition queryLatestPosition(String deviceId);
/**
* 删除指定设备的所有移动位置
* @param deviceId
*/
public int clearMobilePositionsByDeviceId(String deviceId);
}
package com.genersoft.iot.vmp.storager;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.github.pagehelper.PageInfo;
import java.util.List;
/**
* @Description:视频设备数据存储接口
* @author: swwheihei
* @date: 2020年5月6日 下午2:14:31
*/
public interface IVideoManagerStorager {
/**
* 根据设备ID判断设备是否存在
*
* @param deviceId 设备ID
* @return true:存在 false不存在
*/
public boolean exists(String deviceId);
/**
* 视频设备创建
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
public boolean create(Device device);
/**
* 视频设备更新
*
* @param device 设备对象
* @return true创建成功 false创建失败
*/
public boolean updateDevice(Device device);
/**
* 添加设备通道
*
* @param deviceId 设备id
* @param channel 通道
*/
public void updateChannel(String deviceId, DeviceChannel channel);
/**
* 开始播放
*
* @param deviceId 设备id
* @param channelId 通道ID
* @param streamId 流地址
*/
public void startPlay(String deviceId, String channelId, String streamId);
/**
* 停止播放
*
* @param deviceId 设备id
* @param channelId 通道ID
*/
public void stopPlay(String deviceId, String channelId);
/**
* 获取设备
*
* @param deviceId 设备ID
* @return DShadow 设备对象
*/
public Device queryVideoDevice(String deviceId);
/**
* 获取某个设备的通道列表
*
* @param deviceId 设备ID
* @param page 分页 当前页
* @param count 每页数量
* @return
*/
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count);
/**
* 获取某个设备的通道列表
*
* @param deviceId 设备ID
* @return
*/
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId);
/**
* 获取某个设备的通道
*
* @param deviceId 设备ID
* @param channelId 通道ID
*/
public DeviceChannel queryChannel(String deviceId, String channelId);
/**
* 获取多个设备
*
* @param page 当前页数
* @param count 每页数量
* @return List<Device> 设备对象数组
*/
public PageInfo<Device> queryVideoDeviceList(int page, int count);
/**
* 获取多个设备
*
* @return List<Device> 设备对象数组
*/
public List<Device> queryVideoDeviceList();
/**
* 删除设备
*
* @param deviceId 设备ID
* @return true删除成功 false删除失败
*/
public boolean delete(String deviceId);
/**
* 更新设备在线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
public boolean online(String deviceId);
/**
* 更新设备离线
*
* @param deviceId 设备ID
* @return true更新成功 false更新失败
*/
public boolean outline(String deviceId);
/**
* 查询子设备
*
* @param deviceId
* @param channelId
* @param page
* @param count
* @return
*/
PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count);
/**
* 清空通道
*
* @param deviceId
*/
void cleanChannelsForDevice(String deviceId);
/**
* 添加Mobile Position设备移动位置
*
* @param MobilePosition
* @return
*/
public boolean insertMobilePosition(MobilePosition mobilePosition);
/**
* 更新上级平台
* @param parentPlatform
*/
boolean updateParentPlatform(ParentPlatform parentPlatform);
/**
* 添加上级平台
* @param parentPlatform
*/
boolean addParentPlatform(ParentPlatform parentPlatform);
/**
* 删除上级平台
* @param parentPlatform
*/
boolean deleteParentPlatform(ParentPlatform parentPlatform);
/**
* 分页获取上级平台
* @param page
* @param count
* @return
*/
PageInfo<ParentPlatform> queryParentPlatformList(int page, int count);
/**
* 获取所有已启用的平台
* @return
*/
List<ParentPlatform> queryEnableParentPlatformList(boolean enable);
/**
* 获取上级平台
* @param platformGbId
* @return
*/
ParentPlatform queryParentPlatById(String platformGbId);
/**
* 所有平台离线
*/
void outlineForAllParentPlatform();
/**
* 查询通道信息不区分设备(已关联平台或全部)
*/
PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, Boolean inPlatform);
/**
* 查询设备的通道信息
*/
List<ChannelReduce> queryChannelListInParentPlatform(String platformId);
/**
* 更新上级平台的通道信息
* @param platformId
* @param channelReduces
* @return
*/
int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces);
/**
* 移除上级平台的通道信息
* @param platformId
* @param channelReduces
* @return
*/
int delChannelForGB(String platformId, List<ChannelReduce> channelReduces);
DeviceChannel queryChannelInParentPlatform(String platformId, String channelId);
Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId);
/**
* 添加Mobile Position设备移动位置
* @param MobilePosition
* @return
*/
public boolean insertMobilePosition(MobilePosition mobilePosition);
/**
* 查询移动位置轨迹
*
* @param deviceId
* @param startTime
* @param endTime
*/
public List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime);
/**
* 查询最新移动位置
*
* @param deviceId
*/
public MobilePosition queryLatestPosition(String deviceId);
/**
* 删除指定设备的所有移动位置
*
* @param deviceId
*/
public int clearMobilePositionsByDeviceId(String deviceId);
}

2
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java

@ -46,7 +46,7 @@ public interface DeviceChannelMapper {
"<if test=\"PTZType != null\">, PTZType=${PTZType}</if>" +
"<if test=\"status != null\">, status='${status}'</if>" +
"<if test=\"streamId != null\">, streamId='${streamId}'</if>" +
"<if test=\"hasAudio != null\">, hasAudio='${hasAudio}'</if>" +
"<if test=\"hasAudio != null\">, hasAudio='#{hasAudio}'</if>" +
"WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+
" </script>"})
int update(DeviceChannel channel);

213
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java

@ -8,153 +8,187 @@ import com.genersoft.iot.vmp.gb28181.bean.*;
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.*;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("rawtypes")
@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<Object> 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<Object> 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<Object> playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
List<Object> 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<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
Map<String, StreamInfo> streamInfos = new HashMap<>();
// List<Object> playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId));
List<Object> 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<StreamInfo> queryPlayBackByDeviceId(String deviceId) {
String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX,
null,
null,
deviceId
);
List<Object> 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<StreamInfo> 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<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
deviceId,
code));
private StreamInfo scanOne(String key) {
List<Object> playLeys = redis.scan(key);
if (playLeys == null || playLeys.size() == 0) {
playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
deviceId));
return null;
}
if (playLeys == null || playLeys.size() == 0) return null;
return (StreamInfo)redis.get(playLeys.get(0).toString());
return (StreamInfo) redis.get(playLeys.get(0).toString());
}
@Override
@ -285,4 +319,21 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
Set<Object> realVideos = redis.ZRange(key, start, end);
return new ArrayList(realVideos);
}
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 = "*";
}
return String.format("%S%s_%s_%s",
prefix,
streamId,
channelId,
deviceId);
}
}

8
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;
}

97
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<String> smembers(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Set<String> smembers = jedis.smembers(key);
return smembers;
} finally {
returnToPool(jedis);
}
}
/**
* SREM key member1 [member2]
* 移除集合中一个或多个成员
*/
public Long srem(String key, String... member) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Long srem = jedis.srem(key, member);
return srem;
} finally {
returnToPool(jedis);
}
}
private void returnToPool(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
}

1502
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java

File diff suppressed because it is too large

120
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceConfig.java

@ -1,120 +0,0 @@
/**
* 设备设置命令API接口
*
* @author lawrencehj
* @date 2021年2月2日
*/
package com.genersoft.iot.vmp.vmanager.device;
import javax.sip.message.Response;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
@CrossOrigin
@RestController
@RequestMapping("/api")
public class DeviceConfig {
private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);
@Autowired
private IVideoManagerStorager storager;
@Autowired
private SIPCommander cmder;
@Autowired
private DeferredResultHolder resultHolder;
/**
* 看守位控制命令API接口
*
* @param deviceId
* @param enabled 看守位使能1:开启,0:关闭
* @param resetTime 自动归位时间间隔可选
* @param presetIndex 调用预置位编号可选
* @param channelId 通道编码可选
*/
@GetMapping("/config/{deviceId}/basicParam")
public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId,
@RequestParam(required = false) String channelId,
@RequestParam(required = false) String name,
@RequestParam(required = false) String expiration,
@RequestParam(required = false) String heartBeatInterval,
@RequestParam(required = false) String heartBeatCount) {
if (logger.isDebugEnabled()) {
logger.debug("报警复位API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("设备配置操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("Status", "Timeout");
json.put("Description", "设备配置操作超时, 设备未返回应答指令");
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
/**
* 设备配置查询请求API接口
*
* @param deviceId
*/
@GetMapping("/config/{deviceId}/query/{configType}")
public DeferredResult<ResponseEntity<String>> configDownloadApi(@PathVariable String deviceId,
@PathVariable String configType,
@RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("设备状态查询API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.deviceConfigQuery(device, channelId, configType, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);
result.onTimeout(()->{
logger.warn(String.format("获取设备配置超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
}

119
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<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId,
@RequestParam(required = false) String channelId,
@RequestParam(required = false) String name,
@RequestParam(required = false) String expiration,
@RequestParam(required = false) String heartBeatInterval,
@RequestParam(required = false) String heartBeatCount) {
if (logger.isDebugEnabled()) {
logger.debug("报警复位API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("设备配置操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("设备配置操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("Status", "Timeout");
json.put("Description", "设备配置操作超时, 设备未返回应答指令");
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
/**
* 设备配置查询请求API接口
*
* @param deviceId
*/
@GetMapping("/config/{deviceId}/query/{configType}")
public DeferredResult<ResponseEntity<String>> configDownloadApi(@PathVariable String deviceId,
@PathVariable String configType,
@RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("设备状态查询API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.deviceConfigQuery(device, channelId, configType, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("获取设备配置失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("获取设备配置超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
}

238
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceControl.java

@ -1,238 +0,0 @@
/**
* 设备控制命令API接口
*
* @author lawrencehj
* @date 2021年2月1日
*/
package com.genersoft.iot.vmp.vmanager.device;
import javax.sip.message.Response;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
@CrossOrigin
@RestController
@RequestMapping("/api")
public class DeviceControl {
private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);
@Autowired
private IVideoManagerStorager storager;
@Autowired
private SIPCommander cmder;
@Autowired
private DeferredResultHolder resultHolder;
/**
* 远程启动控制命令API接口
*
* @param deviceId
*/
@GetMapping("/control/{deviceId}/teleboot")
@PostMapping("/control/{deviceId}/teleboot")
public ResponseEntity<String> teleBootApi(@PathVariable String deviceId) {
if (logger.isDebugEnabled()) {
logger.debug("设备远程启动API调用");
}
Device device = storager.queryVideoDevice(deviceId);
boolean sucsess = cmder.teleBootCmd(device);
if (sucsess) {
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("Result", "OK");
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK);
} else {
logger.warn("设备远程启动API调用失败!");
return new ResponseEntity<String>("设备远程启动API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 录像控制命令API接口
*
* @param deviceId
* @param recordCmdStr Record手动录像StopRecord停止手动录像
* @param channelId 通道编码可选
*/
@GetMapping("/control/{deviceId}/record/{recordCmdStr}")
public DeferredResult<ResponseEntity<String>> recordApi(@PathVariable String deviceId,
@PathVariable String recordCmdStr, @RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("开始/停止录像API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.recordCmd(device, channelId, recordCmdStr, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
/**
* 报警布防/撤防命令API接口
*
* @param deviceId
* @param guardCmdStr SetGuard布防ResetGuard撤防
*/
@GetMapping("/control/{deviceId}/guard/{guardCmdStr}")
public DeferredResult<ResponseEntity<String>> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) {
if (logger.isDebugEnabled()) {
logger.debug("布防/撤防API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.guardCmd(device, guardCmdStr, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result);
return result;
}
/**
* 报警复位API接口
*
* @param deviceId
* @param alarmMethod 报警方式可选
* @param alarmType 报警类型可选
*/
@GetMapping("/control/{deviceId}/resetAlarm")
public DeferredResult<ResponseEntity<String>> resetAlarmApi(@PathVariable String deviceId,
@RequestParam(required = false) String alarmMethod,
@RequestParam(required = false) String alarmType) {
if (logger.isDebugEnabled()) {
logger.debug("报警复位API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.alarmCmd(device, alarmMethod, alarmType, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData(String.format("报警复位操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("报警复位操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result);
return result;
}
/**
* 强制关键帧API接口
*
* @param deviceId
* @param channelId
*/
@GetMapping("/control/{deviceId}/iFrame")
@PostMapping("/control/{deviceId}/iFrame")
public ResponseEntity<String> iFrame(@PathVariable String deviceId,
@RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("强制关键帧API调用");
}
Device device = storager.queryVideoDevice(deviceId);
boolean sucsess = cmder.iFrameCmd(device, channelId);
if (sucsess) {
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("ChannelID", channelId);
json.put("Result", "OK");
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK);
} else {
logger.warn("强制关键帧API调用失败!");
return new ResponseEntity<String>("强制关键帧API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 看守位控制命令API接口
*
* @param deviceId
* @param enabled 看守位使能1:开启,0:关闭
* @param resetTime 自动归位时间间隔可选
* @param presetIndex 调用预置位编号可选
* @param channelId 通道编码可选
*/
@GetMapping("/control/{deviceId}/homePosition/{enabled}")
public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId,
@PathVariable String enabled,
@RequestParam(required = false) String resetTime,
@RequestParam(required = false) String presetIndex,
@RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("报警复位API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("Status", "Timeout");
json.put("Description", "看守位控制操作超时, 设备未返回应答指令");
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
}

237
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<String> teleBootApi(@PathVariable String deviceId) {
if (logger.isDebugEnabled()) {
logger.debug("设备远程启动API调用");
}
Device device = storager.queryVideoDevice(deviceId);
boolean sucsess = cmder.teleBootCmd(device);
if (sucsess) {
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("Result", "OK");
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK);
} else {
logger.warn("设备远程启动API调用失败!");
return new ResponseEntity<String>("设备远程启动API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 录像控制命令API接口
*
* @param deviceId
* @param recordCmdStr Record手动录像StopRecord停止手动录像
* @param channelId 通道编码可选
*/
@GetMapping("/control/{deviceId}/record/{recordCmdStr}")
public DeferredResult<ResponseEntity<String>> recordApi(@PathVariable String deviceId,
@PathVariable String recordCmdStr, @RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("开始/停止录像API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.recordCmd(device, channelId, recordCmdStr, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
/**
* 报警布防/撤防命令API接口
*
* @param deviceId
* @param guardCmdStr SetGuard布防ResetGuard撤防
*/
@GetMapping("/control/{deviceId}/guard/{guardCmdStr}")
public DeferredResult<ResponseEntity<String>> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) {
if (logger.isDebugEnabled()) {
logger.debug("布防/撤防API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.guardCmd(device, guardCmdStr, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result);
return result;
}
/**
* 报警复位API接口
*
* @param deviceId
* @param alarmMethod 报警方式可选
* @param alarmType 报警类型可选
*/
@GetMapping("/control/{deviceId}/resetAlarm")
public DeferredResult<ResponseEntity<String>> resetAlarmApi(@PathVariable String deviceId,
@RequestParam(required = false) String alarmMethod,
@RequestParam(required = false) String alarmType) {
if (logger.isDebugEnabled()) {
logger.debug("报警复位API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.alarmCmd(device, alarmMethod, alarmType, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData(String.format("报警复位操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("报警复位操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId);
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId, result);
return result;
}
/**
* 强制关键帧API接口
*
* @param deviceId
* @param channelId
*/
@GetMapping("/control/{deviceId}/iFrame")
@PostMapping("/control/{deviceId}/iFrame")
public ResponseEntity<String> iFrame(@PathVariable String deviceId,
@RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("强制关键帧API调用");
}
Device device = storager.queryVideoDevice(deviceId);
boolean sucsess = cmder.iFrameCmd(device, channelId);
if (sucsess) {
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("ChannelID", channelId);
json.put("Result", "OK");
return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK);
} else {
logger.warn("强制关键帧API调用失败!");
return new ResponseEntity<String>("强制关键帧API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 看守位控制命令API接口
*
* @param deviceId
* @param enabled 看守位使能1:开启,0:关闭
* @param resetTime 自动归位时间间隔可选
* @param presetIndex 调用预置位编号可选
* @param channelId 通道编码可选
*/
@GetMapping("/control/{deviceId}/homePosition/{enabled}")
public DeferredResult<ResponseEntity<String>> homePositionApi(@PathVariable String deviceId,
@PathVariable String enabled,
@RequestParam(required = false) String resetTime,
@RequestParam(required = false) String presetIndex,
@RequestParam(required = false) String channelId) {
if (logger.isDebugEnabled()) {
logger.debug("报警复位API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId));
JSONObject json = new JSONObject();
json.put("DeviceID", deviceId);
json.put("Status", "Timeout");
json.put("Description", "看守位控制操作超时, 设备未返回应答指令");
msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (XmlUtil.isEmpty(channelId) ? deviceId : channelId), result);
return result;
}
}

261
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<Device> devices(@PathVariable String deviceId) {
if (logger.isDebugEnabled()) {
logger.debug("查询视频设备API调用,deviceId:" + deviceId);
}
Device device = storager.queryVideoDevice(deviceId);
return new ResponseEntity<>(device, HttpStatus.OK);
}
@GetMapping("/devices")
public PageInfo<Device> devices(Integer page, Integer count) {
if (logger.isDebugEnabled()) {
logger.debug("查询所有视频设备API调用");
}
if (null == page) {
page = 1;
}
if (null == count) {
count = 10;
}
return storager.queryVideoDeviceList(page, count);
}
/**
* 分页查询通道数
*
* @param deviceId 设备id
* @param page 当前页
* @param count 每页条数
* @param query 查询内容
* @param online 是否在线 在线 true / 离线 false
* @param channelType 设备 false/子目录 true
* @return 通道列表
*/
@GetMapping("/devices/{deviceId}/channels")
public ResponseEntity<PageInfo> channels(@PathVariable String deviceId,
int page, int count,
@RequestParam(required = false) String query,
@RequestParam(required = false) Boolean online,
@RequestParam(required = false) Boolean channelType
) {
if (logger.isDebugEnabled()) {
logger.debug("查询所有视频设备API调用");
}
if (StringUtils.isEmpty(query)) {
query = null;
}
PageInfo pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count);
return new ResponseEntity<>(pageResult, HttpStatus.OK);
}
@PostMapping("/devices/{deviceId}/sync")
public DeferredResult<ResponseEntity<Device>> devicesSync(@PathVariable String deviceId) {
if (logger.isDebugEnabled()) {
}
logger.debug("设备通道信息同步API调用,deviceId:" + deviceId);
Device device = storager.queryVideoDevice(deviceId);
cmder.catalogQuery(device, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId);
msg.setData(String.format("同步通道失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>(2 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("设备通道信息同步超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId);
msg.setData("Timeout");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId, result);
return result;
}
@PostMapping("/devices/{deviceId}/delete")
public ResponseEntity<String> delete(@PathVariable String deviceId) {
if (logger.isDebugEnabled()) {
logger.debug("设备信息删除API调用,deviceId:" + deviceId);
}
if (offLineDetector.isOnline(deviceId)) {
return new ResponseEntity<String>("不允许删除在线设备!", HttpStatus.NOT_ACCEPTABLE);
}
boolean isSuccess = storager.delete(deviceId);
if (isSuccess) {
JSONObject json = new JSONObject();
json.put("deviceId", deviceId);
return new ResponseEntity<>(json.toString(), HttpStatus.OK);
} else {
logger.warn("设备信息删除API调用失败!");
return new ResponseEntity<>("设备信息删除API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 分页查询通道数
*
* @param channelId 通道id
* @param page 当前页
* @param count 每页条数
* @return 子通道列表
*/
@GetMapping("/subChannels/{deviceId}/{channelId}/channels")
public ResponseEntity<PageInfo> subChannels(@PathVariable String deviceId,
@PathVariable String channelId,
int page,
int count,
@RequestParam(required = false) String query,
@RequestParam(required = false) String online,
@RequestParam(required = false) Boolean channelType) {
if (logger.isDebugEnabled()) {
logger.debug("查询所有视频通道API调用");
}
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
if (deviceChannel == null) {
PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>();
return new ResponseEntity<>(deviceChannelPageResult, HttpStatus.OK);
}
PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count);
return new ResponseEntity<>(pageResult, HttpStatus.OK);
}
@PostMapping("/channel/update/{deviceId}")
public ResponseEntity<PageInfo> updateChannel(@PathVariable String deviceId, DeviceChannel channel) {
storager.updateChannel(deviceId, channel);
return new ResponseEntity<>(null, HttpStatus.OK);
}
@GetMapping("/devices/{deviceId}/transport/{streamMode}")
@PostMapping("/devices/{deviceId}/transport/{streamMode}")
public ResponseEntity<PageInfo> updateTransport(@PathVariable String deviceId, @PathVariable String streamMode) {
Device device = storager.queryVideoDevice(deviceId);
device.setStreamMode(streamMode);
storager.updateDevice(device);
return new ResponseEntity<>(null, HttpStatus.OK);
}
/**
* 设备状态查询请求API接口
*
* @param deviceId
*/
@GetMapping("/devices/{deviceId}/status")
public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) {
if (logger.isDebugEnabled()) {
logger.debug("设备状态查询API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.deviceStatusQuery(device, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId);
msg.setData(String.format("获取设备状态失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("获取设备状态超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId);
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, result);
return result;
}
/**
* 设备报警查询请求API接口
*
* @param deviceId
*/
@GetMapping("/alarm/{deviceId}")
public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId,
@RequestParam(required = false) String startPriority,
@RequestParam(required = false) String endPriority,
@RequestParam(required = false) String alarmMethod,
@RequestParam(required = false) String alarmType,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime) {
if (logger.isDebugEnabled()) {
logger.debug("设备报警查询API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId);
msg.setData(String.format("设备报警查询失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
result.onTimeout(() -> {
logger.warn(String.format("设备报警查询超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId);
msg.setData("设备报警查询超时");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, result);
return result;
}
}

255
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceQuery.java

@ -1,255 +0,0 @@
package com.genersoft.iot.vmp.vmanager.device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import javax.sip.message.Response;
@SuppressWarnings("rawtypes")
@CrossOrigin
@RestController
@RequestMapping("/api")
public class DeviceQuery {
private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);
@Autowired
private IVideoManagerStorager storager;
@Autowired
private SIPCommander cmder;
@Autowired
private DeferredResultHolder resultHolder;
@Autowired
private DeviceOffLineDetector offLineDetector;
@GetMapping("/devices/{deviceId}")
public ResponseEntity<Device> devices(@PathVariable String deviceId){
if (logger.isDebugEnabled()) {
logger.debug("查询视频设备API调用,deviceId:" + deviceId);
}
Device device = storager.queryVideoDevice(deviceId);
return new ResponseEntity<>(device,HttpStatus.OK);
}
@GetMapping("/devices")
public PageInfo<Device> devices(int page, int count){
if (logger.isDebugEnabled()) {
logger.debug("查询所有视频设备API调用");
}
return storager.queryVideoDeviceList(page, count);
}
/**
* 分页查询通道数
*
* @param deviceId 设备id
* @param page 当前页
* @param count 每页条数
* @param query 查询内容
* @param online 是否在线 在线 true / 离线 false
* @param channelType 设备 false/子目录 true
* @return 通道列表
*/
@GetMapping("/devices/{deviceId}/channels")
public ResponseEntity<PageInfo> channels(@PathVariable String deviceId,
int page, int count,
@RequestParam(required = false) String query,
@RequestParam(required = false) Boolean online,
@RequestParam(required = false) Boolean channelType) {
if (logger.isDebugEnabled()) {
logger.debug("查询视频设备通道API调用");
}
if (StringUtils.isEmpty(query)) {
query = null;
}
PageInfo pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count);
return new ResponseEntity<>(pageResult,HttpStatus.OK);
}
@PostMapping("/devices/{deviceId}/sync")
public DeferredResult<ResponseEntity<Device>> devicesSync(@PathVariable String deviceId){
if (logger.isDebugEnabled()) {
}
logger.debug("设备通道信息同步API调用,deviceId:" + deviceId);
Device device = storager.queryVideoDevice(deviceId);
cmder.catalogQuery(device, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId);
msg.setData(String.format("同步通道失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>(2*1000L);
result.onTimeout(()->{
logger.warn(String.format("设备通道信息同步超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId);
msg.setData("Timeout");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result);
return result;
}
@PostMapping("/devices/{deviceId}/delete")
public ResponseEntity<String> delete(@PathVariable String deviceId){
if (logger.isDebugEnabled()) {
logger.debug("设备信息删除API调用,deviceId:" + deviceId);
}
if (offLineDetector.isOnline(deviceId)) {
return new ResponseEntity<String>("不允许删除在线设备!", HttpStatus.NOT_ACCEPTABLE);
}
boolean isSuccess = storager.delete(deviceId);
if (isSuccess) {
JSONObject json = new JSONObject();
json.put("deviceId", deviceId);
return new ResponseEntity<>(json.toString(),HttpStatus.OK);
} else {
logger.warn("设备信息删除API调用失败!");
return new ResponseEntity<String>("设备信息删除API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 分页查询通道数
* @param channelId 通道id
* @param page 当前页
* @param count 每页条数
* @return 子通道列表
*/
@GetMapping("/subChannels/{deviceId}/{channelId}/channels")
public ResponseEntity<PageInfo> subChannels(@PathVariable String deviceId,
@PathVariable String channelId,
int page,
int count,
@RequestParam(required = false) String query,
@RequestParam(required = false) String online,
@RequestParam(required = false) Boolean channelType){
if (logger.isDebugEnabled()) {
logger.debug("查询所有视频通道API调用");
}
DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId);
if (deviceChannel == null) {
PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>();
return new ResponseEntity<>(deviceChannelPageResult,HttpStatus.OK);
}
PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count);
return new ResponseEntity<>(pageResult,HttpStatus.OK);
}
@PostMapping("/channel/update/{deviceId}")
public ResponseEntity<PageInfo> updateChannel(@PathVariable String deviceId,DeviceChannel channel){
storager.updateChannel(deviceId, channel);
return new ResponseEntity<>(null,HttpStatus.OK);
}
@GetMapping("/devices/{deviceId}/transport/{streamMode}")
@PostMapping("/devices/{deviceId}/transport/{streamMode}")
public ResponseEntity<PageInfo> updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){
Device device = storager.queryVideoDevice(deviceId);
device.setStreamMode(streamMode);
storager.updateDevice(device);
return new ResponseEntity<>(null,HttpStatus.OK);
}
/**
* 设备状态查询请求API接口
*
* @param deviceId
*/
@GetMapping("/devices/{deviceId}/status")
public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) {
if (logger.isDebugEnabled()) {
logger.debug("设备状态查询API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.deviceStatusQuery(device, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId);
msg.setData(String.format("获取设备状态失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);
result.onTimeout(()->{
logger.warn(String.format("获取设备状态超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId);
msg.setData("Timeout. Device did not response to this command.");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, result);
return result;
}
/**
* 设备报警查询请求API接口
*
* @param deviceId
*/
@GetMapping("/alarm/{deviceId}")
public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId,
@RequestParam(required = false) String startPriority,
@RequestParam(required = false) String endPriority,
@RequestParam(required = false) String alarmMethod,
@RequestParam(required = false) String alarmType,
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime) {
if (logger.isDebugEnabled()) {
logger.debug("设备报警查询API调用");
}
Device device = storager.queryVideoDevice(deviceId);
cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId);
msg.setData(String.format("设备报警查询失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);
result.onTimeout(()->{
logger.warn(String.format("设备报警查询超时"));
// 释放rtpserver
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId);
msg.setData("设备报警查询超时");
resultHolder.invokeResult(msg);
});
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, result);
return result;
}
}

332
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java

@ -1,12 +1,18 @@
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.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.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.slf4j.Logger;
@ -14,6 +20,7 @@ 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.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -35,170 +42,195 @@ import javax.sip.message.Response;
@RequestMapping("/api")
public class PlayController {
private final static Logger logger = LoggerFactory.getLogger(PlayController.class);
private final static Logger logger = LoggerFactory.getLogger(PlayController.class);
@Autowired
private SIPCommander cmder;
@Autowired
private SIPCommander cmder;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired
private DeferredResultHolder resultHolder;
@Autowired
private DeferredResultHolder resultHolder;
@Autowired
private IPlayService playService;
@Autowired
private IPlayService playService;
@GetMapping("/play/{deviceId}/{channelId}")
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
@PathVariable String channelId) {
@Autowired
private VideoStreamSessionManager streamSession;
@GetMapping("/play/{deviceId}/{channelId}")
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
@PathVariable String channelId) {
Device device = storager.queryVideoDevice(deviceId);
PlayResult playResult = playService.play(deviceId, channelId, null, null);
RequestMessage msg = playService.createCallbackPlayMsg();
DeferredResult<ResponseEntity<String>> 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);
// 超时处理
playResult.getResult().onTimeout(()->{
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId));
// 释放rtpserver
cmder.closeRTPServer(playResult.getDevice(), channelId);
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid());
msg.setData("Timeout");
resultHolder.invokeResult(msg);
});
return playResult.getResult();
}
// 判断是否已经存在点播
StreamInfo oldStreamInfo = streamSession.getPlayStreamInfo(channelId);
if (oldStreamInfo == null) {
// 发送点播消息
playStreamCmd(device, channelId, msg);
return result;
}
@PostMapping("/play/{streamId}/stop")
public DeferredResult<ResponseEntity<String>> playStop(@PathVariable String streamId) {
logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId));
UUID uuid = UUID.randomUUID();
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
// 录像查询以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);
}
});
// 若已有人点播,直接播放
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;
}
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);
}
// 若已有人点播,但已超时自动断开,则重新发起点播
storager.stopPlay(oldStreamInfo.getDeviceID(), oldStreamInfo.getChannelId());
streamSession.remove(oldStreamInfo);
// 超时处理
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;
}
playStreamCmd(device, channelId, msg);
return result;
}
/**
* 将不是h264的视频通过ffmpeg 转码为h264 + aac
* @param streamId 流ID
* @return
*/
@PostMapping("/play/{streamId}/convert")
public ResponseEntity<String> playConvert(@PathVariable String streamId) {
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
if (streamInfo == null) {
logger.warn("视频转码API调用失败!, 视频流已经停止!");
return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK);
}
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
if (!rtpInfo.getBoolean("exist")) {
logger.warn("视频转码API调用失败!, 视频流已停止推流!");
return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", 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<String>( result.toJSONString(), HttpStatus.OK);
}
}
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);
});
}
/**
* 结束转码
* @param key
* @return
*/
@PostMapping("/play/convert/stop/{key}")
public ResponseEntity<String> 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 {
@PostMapping("/play/{channelId}/{streamId}/stop")
public DeferredResult<ResponseEntity<String>> playStop(@PathVariable String channelId, @PathVariable String streamId) {
logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId));
RequestMessage msg = playService.createCallbackPlayMsg();
DeferredResult<ResponseEntity<String>> 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<String> playConvert(@PathVariable String channelId, @PathVariable String streamId) {
StreamInfo streamInfo = streamSession.getPlayStreamInfo(channelId);
if (streamInfo == null) {
logger.warn("视频转码API调用失败!, 视频流已经停止!");
return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK);
}
String mediaServerIp = streamInfo.getMediaServerIp();
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerIp, streamId);
if (!rtpInfo.getBoolean("exist")) {
logger.warn("视频转码API调用失败!, 视频流已停止推流!");
return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", 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<String> 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 {
@ -210,7 +242,7 @@ public class PlayController {
/**
* 语音广播命令API接口
*
*
* @param deviceId
*/
@GetMapping("/broadcast/{deviceId}")

169
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<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime,
String endTime) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
}
UUID uuid = UUID.randomUUID();
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
// 超时处理
result.onTimeout(()->{
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
msg.setData("Timeout");
resultHolder.invokeResult(msg);
});
Device device = storager.queryVideoDevice(deviceId);
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId);
if (streamInfo != null) {
// 停止之前的回放
cmder.streamByeCmd(streamInfo.getStreamId());
}
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString());
}, event -> {
Response response = event.getResponse();
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
return result;
}
@RequestMapping("/playback/{ssrc}/stop")
public ResponseEntity<String> playStop(@PathVariable String ssrc) {
cmder.streamByeCmd(ssrc);
if (logger.isDebugEnabled()) {
logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc));
}
if (ssrc != null) {
JSONObject json = new JSONObject();
json.put("ssrc", ssrc);
return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
} else {
logger.warn("设备录像回放停止API调用失败!");
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class);
@Autowired
private SIPCommander cmder;
@Autowired
private IVideoManagerStorager storager;
@Autowired
private VideoStreamSessionManager streamSession;
@Autowired
private IPlayService playService;
@Autowired
private DeferredResultHolder resultHolder;
@GetMapping("/playback/{deviceId}/{channelId}")
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime,
String endTime) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
}
RequestMessage msg = playService.createCallbackPlayMsg();
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
// 超时处理
result.onTimeout(() -> {
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId);
streamSession.remove(streamInfo);
msg.setData("Timeout");
resultHolder.invokeResult(msg);
});
Device device = storager.queryVideoDevice(deviceId);
StreamInfo oldStreamInfo = streamSession.getPlayBackStreamInfo(channelId);
if (oldStreamInfo != null) {
// TODO 只能停止自己之前的回放,不能停止别人的回放,否则会导致别人无法播放
cmder.stopStreamByeCmd(oldStreamInfo, null);
}
resultHolder.put(msg.getId(), result);
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, msg);
}, event -> {
StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId);
streamSession.remove(streamInfo);
Response response = event.getResponse();
msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg);
});
return result;
}
@RequestMapping("/playback/{channelId}/{ssrc}/stop")
public ResponseEntity<String> playStop(@PathVariable String channelId, @PathVariable String ssrc) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc));
}
if (ssrc == null) {
logger.warn("设备录像回放停止API调用失败!");
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
StreamInfo streamInfo = streamSession.getStreamInfo(channelId, ssrc);
if (streamInfo == null) {
logger.warn("回放播流不存在!");
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
cmder.stopStreamByeCmd(streamInfo, null);
JSONObject json = new JSONObject();
json.put("ssrc", ssrc);
return new ResponseEntity<>(json.toString(), HttpStatus.OK);
}
}

7
src/main/java/com/genersoft/iot/vmp/vmanager/service/IPlayService.java

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.vmanager.service;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
@ -9,9 +10,11 @@ import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
* 点播处理
*/
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);
PlayResult play(String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
}

64
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java

@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
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;
@ -26,6 +27,8 @@ import org.springframework.web.context.request.async.DeferredResult;
import javax.sip.message.Response;
import java.util.UUID;
import java.util.UUID;
@Service
public class PlayServiceImpl implements IPlayService {
@ -43,6 +46,9 @@ public class PlayServiceImpl implements IPlayService {
@Autowired
private DeferredResultHolder resultHolder;
@Autowired
private VideoStreamSessionManager streamSession;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@ -109,10 +115,17 @@ public class PlayServiceImpl implements IPlayService {
}
@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) {
@ -120,56 +133,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;
}
}

126
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<JSONObject> 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<JSONObject> 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<JSONObject> resultDeferredResult = new DeferredResult<JSONObject>();
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<ResponseEntity<String>> play = playController.play(serial, code);
play.setResultHandler((Object o)->{
ResponseEntity<String> responseEntity = (ResponseEntity)o;
play.setResultHandler((Object o) -> {
ResponseEntity<String> 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;
}
}

9
src/main/resources/application-dev.yml

@ -11,6 +11,9 @@ spring:
password:
# [可选] 超时时间
timeout: 10000
poolMaxTotal: 1000
poolMaxIdle: 50
poolMaxWait: 500
# [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置
datasource:
# name: eiot
@ -34,7 +37,7 @@ server:
# 作为28181服务器的配置
sip:
# [必须修改] 本机的IP, 必须是网卡上的IP
# [必须修改] 本机的内网IP, 必须是网卡上的IP
ip: 192.168.0.100
# [可选] 28181服务监听的端口
port: 5060
@ -57,10 +60,10 @@ auth:
#zlm服务器配置
media:
# [必须修改] zlm服务器的内网IP
# [必须修改] zlm服务器的IP(内网公网IP均可),配置多台时IP用逗号隔开
ip: 192.168.0.100
# [可选] zlm服务器的公网IP, 内网部署置空即可
wanIp:
wanIp:
# [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip
hookIp:
# [必须修改] zlm服务器的http.port

2
web_src/src/components/channelList.vue

@ -216,7 +216,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();

10
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()
});

Loading…
Cancel
Save