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) # Mobile Tools for Java (J2ME)
.mtj.tmp/ .mtj.tmp/
src/main/resources/application-*.yml src/main/resources/application-*.yml
src/main/resources/static/
web_src/package-lock.json
# Package Files # # Package Files #
#*.jar #*.jar
*.war *.war

2
README.md

@ -53,7 +53,7 @@ https://gitee.com/18010473990/wvp-GB28181.git
8. 支持udp/tcp国标流传输模式; 8. 支持udp/tcp国标流传输模式;
9. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址 9. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址
10. 支持国标网络校时 10. 支持国标网络校时
11. 支持公网部署, 支持wvp与zlm分开部署 11. 支持公网部署, 支持wvp与zlm分开部署,支持配置多台zlm
12. 支持播放h265, g.711格式的流 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)) 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. 报警信息处理,支持向前端推送报警信息 14. 报警信息处理,支持向前端推送报警信息

90
pom.xml

@ -13,6 +13,34 @@
<artifactId>wvp</artifactId> <artifactId>wvp</artifactId>
<name>web video platform</name> <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> <repositories>
<repository> <repository>
<id>nexus-aliyun</id> <id>nexus-aliyun</id>
@ -41,17 +69,6 @@
</pluginRepository> </pluginRepository>
</pluginRepositories> </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> <dependencies>
<dependency> <dependency>
@ -65,54 +82,54 @@
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version> <version>${mybatis-spring-boot-starter-version}</version>
</dependency> </dependency>
<!-- druid数据库连接池 --> <!-- druid数据库连接池 -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid</artifactId> <artifactId>druid</artifactId>
<version>1.2.3</version> <version>${druid-version}</version>
</dependency> </dependency>
<!-- mysql数据库 --> <!-- mysql数据库 -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version> <version>${mysql-connector-java-version}</version>
</dependency> </dependency>
<!-- 添加sqlite-jdbc数据库驱动 --> <!-- 添加sqlite-jdbc数据库驱动 -->
<dependency> <dependency>
<groupId>org.xerial</groupId> <groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId> <artifactId>sqlite-jdbc</artifactId>
<version>3.32.3.2</version> <version>${sqlite-jdbc-version}</version>
</dependency> </dependency>
<!--Mybatis分页插件 --> <!--Mybatis分页插件 -->
<dependency> <dependency>
<groupId>com.github.pagehelper</groupId> <groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId> <artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version> <version>${pagehelper-spring-boot-starter-version}</version>
</dependency> </dependency>
<!-- <dependency>--> <dependency>
<!-- <groupId>org.apache.commons</groupId>--> <groupId>redis.clients</groupId>
<!-- <artifactId>commons-lang3</artifactId>--> <artifactId>jedis</artifactId>
<!-- <version>3.11</version>--> <version>${jedis-version}</version>
<!-- </dependency>--> </dependency>
<!--Swagger2 --> <!--Swagger2 -->
<!--在线文档 --> <!--在线文档 -->
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId> <artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version> <version>${springfox-swagger2-version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId> <artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version> <version>${springfox-swagger-ui-version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.validation</groupId> <groupId>javax.validation</groupId>
@ -129,41 +146,56 @@
<dependency> <dependency>
<groupId>javax.sip</groupId> <groupId>javax.sip</groupId>
<artifactId>jain-sip-ri</artifactId> <artifactId>jain-sip-ri</artifactId>
<version>1.3.0-91</version> <version>${jain-sip-ri-version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
<version>1.2.17</version> <version>${log4j-version}</version>
</dependency> </dependency>
<!-- xml解析库 --> <!-- xml解析库 -->
<dependency> <dependency>
<groupId>org.dom4j</groupId> <groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId> <artifactId>dom4j</artifactId>
<version>2.1.3</version> <version>${dom4j-version}</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId> <artifactId>fastjson</artifactId>
<version>1.2.73</version> <version>${fastjson-version}</version>
</dependency> </dependency>
<!--Guava是一种基于开源的Java库--> <!--Guava是一种基于开源的Java库-->
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <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> </dependency>
<!-- okhttp --> <!-- okhttp -->
<dependency> <dependency>
<groupId>com.squareup.okhttp3</groupId> <groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId> <artifactId>okhttp</artifactId>
<version>4.9.0</version> <version>${okhttp-version}</version>
</dependency> </dependency>
</dependencies> </dependencies>

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

@ -1,23 +1,21 @@
package com.genersoft.iot.vmp; package com.genersoft.iot.vmp;
import java.util.logging.LogManager; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication
public class VManageBootstrap extends LogManager {
@SpringBootApplication private static String[] args;
public class VManageBootstrap extends LogManager { private static ConfigurableApplicationContext context;
private static String[] args; public static void main(String[] args) {
private static ConfigurableApplicationContext context; VManageBootstrap.args = args;
public static void main(String[] args) { VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
VManageBootstrap.args = args; }
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args); // 项目重启
} public static void restart() {
// 项目重启 context.close();
public static void restart() { VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
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; package com.genersoft.iot.vmp.common;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import lombok.Data;
@Data
public class StreamInfo { public class StreamInfo {
/**
* zlm流媒体服务器IP
*/
private String mediaServerIp;
private String ssrc;
private String streamId; private String streamId;
private String deviceID; private String deviceID;
private String channelId; private String channelId;
@ -18,116 +25,4 @@ public class StreamInfo {
private String rtmp; private String rtmp;
private String rtsp; private String rtsp;
private JSONArray tracks; 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; package com.genersoft.iot.vmp.common;
/** /**
* @Description: 定义常量 * @Description: 定义常量
* @author: swwheihei * @author: swwheihei
* @date: 2019年5月30日 下午3:04:04 * @date: 2019年5月30日 下午3:04:04
* *
*/ */
public class VideoManagerConstants { public class VideoManagerConstants {
public static final String MEDIA_SERVER_PREFIX = "VMP_media_server"; public static final String MEDIA_SERVER_PREFIX = "VMP_media_server";
public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream"; public static final String MEDIA_STREAM_PREFIX = "VMP_media_stream";
public static final String DEVICE_PREFIX = "VMP_device_"; public static final String DEVICE_PREFIX = "VMP_device_";
public static final String CACHEKEY_PREFIX = "VMP_channel_"; public static final String CACHEKEY_PREFIX = "VMP_channel_";
public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_"; public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_";
public static final String PLAYER_PREFIX = "VMP_player_"; public static final String PLAYER_PREFIX = "VMP_player_";
public static final String PLAY_BLACK_PREFIX = "VMP_playback_"; public static final String PLAY_BLACK_PREFIX = "VMP_playback_";
public static final String PLATFORM_PREFIX = "VMP_platform"; public static final String PLATFORM_PREFIX = "VMP_platform";
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_platform_keeplive_"; 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_CATCH_PREFIX = "VMP_platform_catch_";
public static final String PLATFORM_REGISTER_PREFIX = "VMP_platform_register_"; 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_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 PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_platform_send_rtp_info_";
public static final String Pattern_Topic = "VMP_keeplive_platform_"; public static final String Pattern_Topic = "VMP_keeplive_platform_";
public static final String EVENT_ONLINE_REGISTER = "1"; public static final String EVENT_ONLINE_REGISTER = "1";
public static final String EVENT_ONLINE_KEEPLIVE = "2"; public static final String EVENT_ONLINE_KEEPLIVE = "2";
public static final String EVENT_OUTLINE_UNREGISTER = "1"; public static final String EVENT_OUTLINE_UNREGISTER = "1";
public static final String EVENT_OUTLINE_TIMEOUT = "2"; 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; package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.utils.redis.JedisUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Set;
/** /**
* 对配置文件进行校验 * 对配置文件进行校验
*/ */
@Component @Component
@Order(value=2) @Order(value = 0)
public class ApplicationCheckRunner implements CommandLineRunner { public class ApplicationCheckRunner implements CommandLineRunner {
private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner");
@Autowired
@Value("${sip.ip}") SipConfig sipConfig;
private String sipIp; @Autowired
MediaConfig mediaConfig;
@Value("${media.ip}") @Autowired
private String mediaIp; JedisUtil jedisUtil;
@Value("${media.wanIp}")
private String mediaWanIp;
@Value("${media.hookIp}")
private String mediaHookIp;
@Value("${media.port}")
private int mediaPort;
@Value("${media.secret}")
private String mediaSecret;
@Value("${media.streamNoneReaderDelayMS}")
private String streamNoneReaderDelayMS;
@Value("${sip.ip}")
private String sipIP;
@Value("${server.port}")
private String serverPort;
@Value("${media.autoConfig}")
private boolean autoConfig;
@Override @Override
public void run(String... args) throws Exception { public void run(String... args) {
if (sipIP.equals("localhost") || sipIP.equals("127.0.0.1")) { String sipIp = sipConfig.getSipIp();
logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIP ); if (sipIp.equals("localhost") || sipIp.equals("127.0.0.1")) {
logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIp);
System.exit(1); System.exit(1);
} }
String mediaIp = mediaConfig.getMediaIp();
if (mediaIp.equals("localhost") || mediaIp.equals("127.0.0.1")) { String[] mediaIpArr = mediaIp.split(",");
logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); mediaConfig.setMediaIpArr(mediaIpArr);
for (String mId : mediaIpArr) {
if (mId.equals("localhost") || mId.equals("127.0.0.1")) {
logger.warn("mediaIp.ip使用localhost或127.0.0.1,将无法收到网络内其他设备的推流!!!");
}
} }
HashMap mediaServerSsrcMap = new HashMap<>(mediaIpArr.length);
for (int i = 0; i < mediaIpArr.length; i++) {
String mIp = mediaIpArr[i];
SsrcConfig ssrcConfig = new SsrcConfig();
Set<String> usedSet = jedisUtil.smembers(VideoManagerConstants.MEDIA_SSRC_USED_PREFIX + mIp);
ssrcConfig.init(mIp, usedSet);
mediaServerSsrcMap.put(mIp, ssrcConfig);
}
mediaConfig.setMediaServerSsrcMap(mediaServerSsrcMap);
} }
} }

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; package com.genersoft.iot.vmp.conf;
import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
@Data
public class MediaServerConfig { public class MediaServerConfig {
@JSONField(name = "api.apiDebug") @JSONField(name = "api.apiDebug")
@ -31,10 +33,6 @@ public class MediaServerConfig {
@JSONField(name = "general.streamNoneReaderDelayMS") @JSONField(name = "general.streamNoneReaderDelayMS")
private String generalStreamNoneReaderDelayMS; private String generalStreamNoneReaderDelayMS;
private String localIP;
private String wanIp;
@JSONField(name = "hls.fileBufSize") @JSONField(name = "hls.fileBufSize")
private String hlsFileBufSize; private String hlsFileBufSize;
@ -199,533 +197,4 @@ public class MediaServerConfig {
@JSONField(name = "shell.shell") @JSONField(name = "shell.shell")
private String shellPhell; 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; package com.genersoft.iot.vmp.conf;
import org.springframework.cache.annotation.CachingConfigurerSupport; import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.context.annotation.Bean; import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
import org.springframework.context.annotation.Configuration; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import com.alibaba.fastjson.parser.ParserConfig; import org.springframework.data.redis.core.RedisTemplate;
import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/** import redis.clients.jedis.JedisPool;
* @Description:Redis中间件配置类使用spring-data-redis集成自动从application.yml中加载redis配置 import redis.clients.jedis.JedisPoolConfig;
* @author: swwheihei
* @date: 2019年5月30日 上午10:58:25 /**
* * @Description:Redis中间件配置类使用spring-data-redis集成自动从application.yml中加载redis配置
*/ * @author: swwheihei
@Configuration * @date: 2019年5月30日 上午10:58:25
public class RedisConfig extends CachingConfigurerSupport { */
@Configuration
@Bean("redisTemplate") public class RedisConfig extends CachingConfigurerSupport {
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>(); @Value("${spring.redis.host}")
template.setConnectionFactory(redisConnectionFactory); private String host;
// 使用fastjson进行序列化处理,提高解析效率 @Value("${spring.redis.port}")
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class); private int port;
// value值的序列化采用fastJsonRedisSerializer @Value("${spring.redis.database}")
template.setValueSerializer(serializer); private int database;
template.setHashValueSerializer(serializer); @Value("${spring.redis.password}")
// key的序列化采用StringRedisSerializer private String password;
template.setKeySerializer(new StringRedisSerializer()); @Value("${spring.redis.timeout}")
template.setHashKeySerializer(new StringRedisSerializer()); private int timeout;
template.setConnectionFactory(redisConnectionFactory); @Value("${spring.redis.poolMaxTotal}")
// 使用fastjson时需设置此项,否则会报异常not support type private int poolMaxTotal;
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); @Value("${spring.redis.poolMaxIdle}")
return template; private int poolMaxIdle;
} @Value("${spring.redis.poolMaxWait}")
private int poolMaxWait;
/**
* redis消息监听器容器 可以添加多个监听不同话题的redis监听器只需要把消息监听器和相应的消息订阅处理器绑定该消息监听器 @Bean
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 public JedisPool jedisPool() {
* if (StringUtils.isBlank(password)) {
* @param connectionFactory password = null;
* @param listenerAdapter }
* @return JedisPoolConfig poolConfig = new JedisPoolConfig();
*/ poolConfig.setMaxIdle(poolMaxIdle);
@Bean poolConfig.setMaxTotal(poolMaxTotal);
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { // 秒转毫秒
poolConfig.setMaxWaitMillis(poolMaxWait * 1000);
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database);
container.setConnectionFactory(connectionFactory); return jp;
return container; }
}
@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 { public class SipConfig {
@Value("${sip.ip}") @Value("${sip.ip}")
private String sipIp; String sipIp;
@Value("${sip.port}") @Value("${sip.port}")
private Integer sipPort; Integer sipPort;
@Value("${sip.domain}") @Value("${sip.domain}")
private String sipDomain; String sipDomain;
@Value("${sip.id}") @Value("${sip.id}")
private String sipId; String sipId;
@Value("${sip.password}") @Value("${sip.password}")
private String sipPassword; String sipPassword;
@Value("${sip.ptz.speed:50}") @Value("${sip.ptz.speed:50}")
Integer speed; Integer speed;
@ -25,28 +25,47 @@ public class SipConfig {
return sipIp; return sipIp;
} }
public void setSipIp(String sipIp) {
this.sipIp = sipIp;
}
public Integer getSipPort() { public Integer getSipPort() {
return sipPort; return sipPort;
} }
public void setSipPort(Integer sipPort) {
this.sipPort = sipPort;
}
public String getSipDomain() { public String getSipDomain() {
return sipDomain; return sipDomain;
} }
public void setSipDomain(String sipDomain) {
this.sipDomain = sipDomain;
}
public String getSipId() { public String getSipId() {
return sipId; return sipId;
} }
public void setSipId(String sipId) {
this.sipId = sipId;
}
public String getSipPassword() { public String getSipPassword() {
return sipPassword; return sipPassword;
} }
public void setSipPassword(String sipPassword) {
this.sipPassword = sipPassword;
}
public Integer getSpeed() { public Integer getSpeed() {
return speed; 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; package com.genersoft.iot.vmp.gb28181;
import java.text.ParseException; import com.genersoft.iot.vmp.conf.SipConfig;
import java.util.Properties; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import java.util.TooManyListenersException; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;
import java.util.concurrent.LinkedBlockingQueue; import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
import java.util.concurrent.ThreadPoolExecutor; import gov.nist.javax.sip.SipStackImpl;
import java.util.concurrent.TimeUnit; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sip.*; import org.springframework.beans.factory.annotation.Autowired;
import javax.sip.header.CallIdHeader; import org.springframework.context.annotation.Bean;
import javax.sip.message.Response; import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import org.slf4j.Logger; import javax.sip.*;
import org.slf4j.LoggerFactory; import javax.sip.header.CallIdHeader;
import org.springframework.beans.factory.annotation.Autowired; import javax.sip.message.Response;
import org.springframework.context.annotation.Bean; import java.text.ParseException;
// import org.springframework.context.annotation.ComponentScan; import java.util.Properties;
import org.springframework.context.annotation.DependsOn; import java.util.TooManyListenersException;
import org.springframework.stereotype.Component; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import com.genersoft.iot.vmp.conf.SipConfig; import java.util.concurrent.TimeUnit;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;
import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; @Component
public class SipLayer implements SipListener {
import gov.nist.javax.sip.SipStackImpl;
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
@Component
public class SipLayer implements SipListener { @Autowired
private SipConfig sipConfig;
private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
@Autowired
@Autowired private SIPProcessorFactory processorFactory;
private SipConfig sipConfig;
@Autowired
@Autowired private SipSubscribe sipSubscribe;
private SIPProcessorFactory processorFactory;
private SipStack sipStack;
@Autowired
private SipSubscribe sipSubscribe; private SipFactory sipFactory;
private SipStack sipStack; /**
* 消息处理器线程池
private SipFactory sipFactory; */
private ThreadPoolExecutor processThreadPool;
/**
* 消息处理器线程池 @Bean("initSipServer")
*/ private ThreadPoolExecutor initSipServer() {
private ThreadPoolExecutor processThreadPool;
int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;
@Bean("initSipServer") LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);
private ThreadPoolExecutor initSipServer() { processThreadPool = new ThreadPoolExecutor(processThreadNum, processThreadNum,
0L, TimeUnit.MILLISECONDS, processQueue,
int processThreadNum = Runtime.getRuntime().availableProcessors() * 10; new ThreadPoolExecutor.CallerRunsPolicy());
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<Runnable>(10000); return processThreadPool;
processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum, }
0L,TimeUnit.MILLISECONDS,processQueue,
new ThreadPoolExecutor.CallerRunsPolicy()); @Bean("sipFactory")
return processThreadPool; @DependsOn("initSipServer")
} private SipFactory createSipFactory() {
sipFactory = SipFactory.getInstance();
@Bean("sipFactory") sipFactory.setPathName("gov.nist");
@DependsOn("initSipServer") return sipFactory;
private SipFactory createSipFactory() { }
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist"); @Bean("sipStack")
return sipFactory; @DependsOn({"initSipServer", "sipFactory"})
} private SipStack createSipStack() throws PeerUnavailableException {
Properties properties = new Properties();
@Bean("sipStack") properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
@DependsOn({"initSipServer", "sipFactory"}) properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getSipIp());
private SipStack createSipStack() throws PeerUnavailableException { properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false");
Properties properties = new Properties(); /**
properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); * sip_server_log.log sip_debug_log.log public static final int TRACE_NONE =
properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getSipIp()); * 0; public static final int TRACE_MESSAGES = 16; public static final int
properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false"); * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32;
/** */
* sip_server_log.log sip_debug_log.log public static final int TRACE_NONE = properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0");
* 0; public static final int TRACE_MESSAGES = 16; public static final int properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log");
* TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32; properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log");
*/ sipStack = (SipStackImpl) sipFactory.createSipStack(properties);
properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0"); return sipStack;
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); @Bean("tcpSipProvider")
return sipStack; @DependsOn("sipStack")
} private SipProvider startTcpListener() {
ListeningPoint tcpListeningPoint = null;
@Bean("tcpSipProvider") SipProvider tcpSipProvider = null;
@DependsOn("sipStack") try {
private SipProvider startTcpListener() { tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP");
ListeningPoint tcpListeningPoint = null; } catch (TransportNotSupportedException e) {
SipProvider tcpSipProvider = null; logger.error("创建SIP服务失败!", e);
try { System.exit(1);
tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP"); } catch (InvalidArgumentException e) {
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint); if ("Cannot assign requested address: JVM_Bind".equals(e.getMessage())) {
tcpSipProvider.addSipListener(this); logger.error("创建SIP服务失败,请检查sip.ip必须是本地网卡上的IP!", e);
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}"); } else {
} catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) { logger.error("创建SIP服务失败!", e);
logger.error(String.format("创建SIP服务失败: %s", e.getMessage())); }
} System.exit(1);
return tcpSipProvider; }
} try {
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
@Bean("udpSipProvider") } catch (ObjectInUseException e) {
@DependsOn("sipStack") logger.error("创建SIP服务失败!", e);
private SipProvider startUdpListener() throws Exception { System.exit(1);
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP"); }
SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint); try {
udpSipProvider.addSipListener(this); tcpSipProvider.addSipListener(this);
logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}"); } catch (TooManyListenersException e) {
return udpSipProvider; logger.error("创建SIP服务失败!", e);
} System.exit(1);
}
/** logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}");
* SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a return tcpSipProvider;
* new request arrives.
*/
@Override // try {
public void processRequest(RequestEvent evt) { // tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "TCP");
logger.debug(evt.getRequest().toString()); // tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
// 由于jainsip是单线程程序,为提高性能并发处理 // tcpSipProvider.addSipListener(this);
processThreadPool.execute(() -> { // logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getSipPort() + "}");
if (processorFactory != null) { // } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
processorFactory.createRequestProcessor(evt).process(); // logger.error(String.format("创建SIP服务失败: %s", e.getMessage()));
} // }
}); // return tcpSipProvider;
} }
@Override @Bean("udpSipProvider")
public void processResponse(ResponseEvent evt) { @DependsOn("sipStack")
Response response = evt.getResponse(); private SipProvider startUdpListener() throws Exception {
logger.debug(evt.getResponse().toString()); ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getSipIp(), sipConfig.getSipPort(), "UDP");
int status = response.getStatusCode(); SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
if (((status >= 200) && (status < 300)) || status == 401) { // Success! udpSipProvider.addSipListener(this);
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); logger.info("Sip Server UDP 启动成功 port {" + sipConfig.getSipPort() + "}");
try { return udpSipProvider;
processor.process(evt, this, sipConfig); }
} catch (ParseException e) {
// TODO Auto-generated catch block /**
e.printStackTrace(); * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a
} * new request arrives.
if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { */
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); @Override
if (callIdHeader != null) { public void processRequest(RequestEvent evt) {
SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); logger.debug(evt.getRequest().toString());
if (subscribe != null) { // 由于jainsip是单线程程序,为提高性能并发处理
subscribe.response(evt); processThreadPool.execute(() -> {
} if (processorFactory != null) {
} processorFactory.createRequestProcessor(evt).process();
} }
} else if ((status >= 100) && (status < 200)) { });
// 增加其它无需回复的响应,如101、180等 }
} else {
logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); @Override
if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { public void processResponse(ResponseEvent evt) {
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); Response response = evt.getResponse();
if (callIdHeader != null) { logger.debug(evt.getResponse().toString());
SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); int status = response.getStatusCode();
if (subscribe != null) { if (((status >= 200) && (status < 300)) || status == 401) { // Success!
subscribe.response(evt); 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());
* <p> if (subscribe != null) {
* Title: processTimeout subscribe.response(evt);
* </p> }
* <p> }
* Description: }
* </p> } else if ((status >= 100) && (status < 200)) {
* // 增加其它无需回复的响应,如101、180等
* @param timeoutEvent } else {
*/ logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);
@Override if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) {
public void processTimeout(TimeoutEvent timeoutEvent) { CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
// TODO Auto-generated method stub if (callIdHeader != null) {
SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
} if (subscribe != null) {
subscribe.response(evt);
/** }
* <p> }
* Title: processIOException }
* </p> }
* <p>
* Description:
* </p> }
*
* @param exceptionEvent /**
*/ * <p>
@Override * Title: processTimeout
public void processIOException(IOExceptionEvent exceptionEvent) { * </p>
// TODO Auto-generated method stub * <p>
* Description:
} * </p>
*
/** * @param timeoutEvent
* <p> */
* Title: processTransactionTerminated @Override
* </p> public void processTimeout(TimeoutEvent timeoutEvent) {
* <p> // TODO Auto-generated method stub
* Description:
* </p> }
*
* @param transactionTerminatedEvent /**
*/ * <p>
@Override * Title: processIOException
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) { * </p>
// TODO Auto-generated method stub * <p>
* Description:
} * </p>
*
/** * @param exceptionEvent
* <p> */
* Title: processDialogTerminated @Override
* </p> public void processIOException(IOExceptionEvent exceptionEvent) {
* <p> // TODO Auto-generated method stub
* Description:
* </p> }
*
* @param dialogTerminatedEvent /**
*/ * <p>
@Override * Title: processTransactionTerminated
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { * </p>
// TODO Auto-generated method stub * <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 name;
/** /**
* 生产厂商 * 生产厂商
*/ */
private String manufacturer; private String manufacturer;
/** /**
* 型号 * 型号
*/ */
private String model; private String model;
/** /**
* 固件版本 * 固件版本
*/ */
@ -45,7 +45,7 @@ public class Device {
/** /**
* wan地址_ip * wan地址_ip
*/ */
private String ip; private String ip;
/** /**
* wan地址_port * wan地址_port
@ -55,8 +55,8 @@ public class Device {
/** /**
* wan地址 * 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() { public String getDeviceId() {
return deviceId; return deviceId;
@ -388,11 +388,11 @@ public class DeviceChannel {
this.subCount = subCount; this.subCount = subCount;
} }
public boolean isHasAudio() { public Boolean isHasAudio() {
return hasAudio; return hasAudio;
} }
public void setHasAudio(boolean hasAudio) { public void setHasAudio(Boolean hasAudio) {
this.hasAudio = 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; package com.genersoft.iot.vmp.gb28181.bean;
//import gov.nist.javax.sip.header.SIPDate; import java.util.List;
import java.util.List; /**
* @Description:设备录像信息bean
/** * @author: swwheihei
* @Description:设备录像信息bean * @date: 2020年5月8日 下午2:05:56
* @author: swwheihei */
* @date: 2020年5月8日 下午2:05:56 public class RecordInfo {
*/
public class RecordInfo { private String deviceId;
private String deviceId; private String name;
private String name; private int sumNum;
private int sumNum; private List<RecordItem> recordList;
private List<RecordItem> recordList; public String getDeviceId() {
return deviceId;
public String getDeviceId() { }
return deviceId;
} public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
public void setDeviceId(String deviceId) { }
this.deviceId = deviceId;
} public String getName() {
return name;
public String getName() { }
return name;
} public void setName(String name) {
this.name = name;
public void setName(String name) { }
this.name = name;
} public int getSumNum() {
return sumNum;
public int getSumNum() { }
return sumNum;
} public void setSumNum(int sumNum) {
this.sumNum = sumNum;
public void setSumNum(int sumNum) { }
this.sumNum = sumNum;
} public List<RecordItem> getRecordList() {
return recordList;
public List<RecordItem> getRecordList() { }
return recordList;
} public void setRecordList(List<RecordItem> recordList) {
this.recordList = 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; package com.genersoft.iot.vmp.gb28181.event;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/** /**
* @Description:设备离在线状态检测器用于检测设备状态 * @Description:设备离在线状态检测器用于检测设备状态
* @author: swwheihei * @author: swwheihei
* @date: 2020年5月13日 下午2:40:29 * @date: 2020年5月13日 下午2:40:29
*/ */
@Component @Component
public class DeviceOffLineDetector { public class DeviceOffLineDetector {
@Autowired @Autowired
private RedisUtil redis; private RedisUtil redis;
public boolean isOnline(String deviceId) { public Boolean isOnline(String deviceId) {
String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + deviceId; String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + deviceId;
return redis.hasKey(key); 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; package com.genersoft.iot.vmp.gb28181.session;
import java.util.concurrent.ConcurrentHashMap; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import javax.sip.ClientTransaction; import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import org.springframework.stereotype.Component; import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.SsrcConfig;
/** import com.genersoft.iot.vmp.gb28181.bean.Device;
* @Description:视频流session管理器管理视频预览预览回放的通信句柄 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
* @author: swwheihei import com.genersoft.iot.vmp.utils.redis.JedisUtil;
* @date: 2020年5月13日 下午4:03:02 import lombok.extern.slf4j.Slf4j;
*/ import org.apache.commons.lang3.StringUtils;
@Component import org.springframework.beans.factory.annotation.Autowired;
public class VideoStreamSessionManager { import org.springframework.stereotype.Component;
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>(); import javax.annotation.PostConstruct;
private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>(); import javax.sip.ClientTransaction;
import java.util.List;
public String createPlaySsrc(){ import java.util.Map;
return SsrcUtil.getPlaySsrc(); import java.util.Random;
} import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public String createPlayBackSsrc(){
return SsrcUtil.getPlayBackSsrc(); /**
} * @Description:视频流session管理器管理视频预览预览回放的通信句柄
* @author: swwheihei
public void put(String streamId,String ssrc,ClientTransaction transaction){ * @date: 2020年5月13日 下午4:03:02
sessionMap.put(streamId, transaction); */
ssrcMap.put(streamId, ssrc); @Slf4j
} @Component
public class VideoStreamSessionManager {
public ClientTransaction get(String streamId){ /**
return sessionMap.get(streamId); * key: ssrc 播流会话句柄(streamId)和同步信源(SSRC)的对应关系
} * value: 流媒体服务器
*/
public void remove(String streamId) { private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
sessionMap.remove(streamId); private String ssrcPrefix;
SsrcUtil.releaseSsrc(ssrcMap.get(streamId));
ssrcMap.remove(streamId); @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; package com.genersoft.iot.vmp.gb28181.transmit;
import javax.sip.RequestEvent; import javax.sip.RequestEvent;
import javax.sip.ResponseEvent; import javax.sip.ResponseEvent;
import javax.sip.SipProvider; import javax.sip.SipProvider;
import javax.sip.header.CSeqHeader; import javax.sip.header.CSeqHeader;
import javax.sip.message.Request; import javax.sip.message.Request;
import javax.sip.message.Response; import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*; import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*;
import com.genersoft.iot.vmp.vmanager.service.IPlayService; import com.genersoft.iot.vmp.vmanager.service.IPlayService;
// import org.slf4j.Logger; // import org.slf4j.Logger;
// import org.slf4j.LoggerFactory; // import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler; import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;
import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 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.ISIPRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor; 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.ByeRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor; 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.InviteRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor; 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.NotifyRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor; 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.RegisterRequestProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor; 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.ISIPResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor; 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.CancelResponseProcessor;
import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor; 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.gb28181.transmit.response.impl.OtherResponseProcessor;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.utils.SpringBeanFactory; import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.utils.redis.RedisUtil;
/** /**
* @Description: SIP信令处理分配 * @Description: SIP信令处理分配
* @author: swwheihei * @author: swwheihei
* @date: 2020年5月3日 下午4:24:37 * @date: 2020年5月3日 下午4:24:37
*/ */
@Component @Component
public class SIPProcessorFactory { public class SIPProcessorFactory {
// private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class); // private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
@Autowired @Autowired
private SipConfig sipConfig; private SipConfig sipConfig;
@Autowired @Autowired
private RegisterLogicHandler handler; private RegisterLogicHandler handler;
@Autowired @Autowired
private IVideoManagerStorager storager; private IVideoManagerStorager storager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired @Autowired
private EventPublisher publisher; private EventPublisher publisher;
@Autowired @Autowired
private SIPCommander cmder; private SIPCommander cmder;
@Autowired @Autowired
private SIPCommanderFroPlatform cmderFroPlatform; private SIPCommanderFroPlatform cmderFroPlatform;
@Autowired @Autowired
private RedisUtil redis; private RedisUtil redis;
@Autowired @Autowired
private DeferredResultHolder deferredResultHolder; private DeferredResultHolder deferredResultHolder;
@Autowired @Autowired
private DeviceOffLineDetector offLineDetector; private DeviceOffLineDetector offLineDetector;
@Autowired @Autowired
private InviteResponseProcessor inviteResponseProcessor; private InviteResponseProcessor inviteResponseProcessor;
@Autowired @Autowired
private ByeResponseProcessor byeResponseProcessor; private ByeResponseProcessor byeResponseProcessor;
@Autowired @Autowired
private CancelResponseProcessor cancelResponseProcessor; private CancelResponseProcessor cancelResponseProcessor;
@Autowired @Autowired
@Lazy @Lazy
private RegisterResponseProcessor registerResponseProcessor; private RegisterResponseProcessor registerResponseProcessor;
@Autowired @Autowired
private OtherResponseProcessor otherResponseProcessor; private OtherResponseProcessor otherResponseProcessor;
@Autowired @Autowired
private IPlayService playService; private IPlayService playService;
@Autowired @Autowired
private ZLMRTPServerFactory zlmrtpServerFactory; private ZLMRTPServerFactory zlmrtpServerFactory;
// 注:这里使用注解会导致循环依赖注入,暂用springBean // 注:这里使用注解会导致循环依赖注入,暂用springBean
private SipProvider tcpSipProvider; private SipProvider tcpSipProvider;
// 注:这里使用注解会导致循环依赖注入,暂用springBean // 注:这里使用注解会导致循环依赖注入,暂用springBean
private SipProvider udpSipProvider; private SipProvider udpSipProvider;
public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) { public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) {
Request request = evt.getRequest(); Request request = evt.getRequest();
String method = request.getMethod(); String method = request.getMethod();
// logger.info("接收到消息:"+request.getMethod()); // logger.info("接收到消息:"+request.getMethod());
// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt); // sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
if (Request.INVITE.equals(method)) { if (Request.INVITE.equals(method)) {
InviteRequestProcessor processor = new InviteRequestProcessor(); InviteRequestProcessor processor = new InviteRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider()); processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider()); processor.setUdpSipProvider(getUdpSipProvider());
processor.setCmder(cmder); processor.setCmder(cmder);
processor.setCmderFroPlatform(cmderFroPlatform); processor.setCmderFroPlatform(cmderFroPlatform);
processor.setPlayService(playService); processor.setPlayService(playService);
processor.setStorager(storager); processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory); processor.setZlmrtpServerFactory(zlmrtpServerFactory);
return processor; return processor;
} else if (Request.REGISTER.equals(method)) { } else if (Request.REGISTER.equals(method)) {
RegisterRequestProcessor processor = new RegisterRequestProcessor(); RegisterRequestProcessor processor = new RegisterRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider()); processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider()); processor.setUdpSipProvider(getUdpSipProvider());
processor.setHandler(handler); processor.setHandler(handler);
processor.setPublisher(publisher); processor.setPublisher(publisher);
processor.setSipConfig(sipConfig); processor.setSipConfig(sipConfig);
processor.setVideoManagerStorager(storager); processor.setVideoManagerStorager(storager);
return processor; return processor;
} else if (Request.SUBSCRIBE.equals(method)) { } else if (Request.SUBSCRIBE.equals(method)) {
SubscribeRequestProcessor processor = new SubscribeRequestProcessor(); SubscribeRequestProcessor processor = new SubscribeRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
return processor; return processor;
} else if (Request.ACK.equals(method)) { } else if (Request.ACK.equals(method)) {
AckRequestProcessor processor = new AckRequestProcessor(); AckRequestProcessor processor = new AckRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory); processor.setZlmrtpServerFactory(zlmrtpServerFactory);
return processor; return processor;
} else if (Request.BYE.equals(method)) { } else if (Request.BYE.equals(method)) {
ByeRequestProcessor processor = new ByeRequestProcessor(); ByeRequestProcessor processor = new ByeRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
processor.setZlmrtpServerFactory(zlmrtpServerFactory); processor.setZlmrtpServerFactory(zlmrtpServerFactory);
processor.setSIPCommander(cmder); processor.setSIPCommander(cmder);
return processor; return processor;
} else if (Request.CANCEL.equals(method)) { } else if (Request.CANCEL.equals(method)) {
CancelRequestProcessor processor = new CancelRequestProcessor(); CancelRequestProcessor processor = new CancelRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
return processor; return processor;
} else if (Request.MESSAGE.equals(method)) { } else if (Request.MESSAGE.equals(method)) {
MessageRequestProcessor processor = new MessageRequestProcessor(); MessageRequestProcessor processor = new MessageRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider()); processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider()); processor.setUdpSipProvider(getUdpSipProvider());
processor.setPublisher(publisher); processor.setPublisher(publisher);
processor.setRedis(redis); processor.setRedis(redis);
processor.setDeferredResultHolder(deferredResultHolder); processor.setDeferredResultHolder(deferredResultHolder);
processor.setOffLineDetector(offLineDetector); processor.setOffLineDetector(offLineDetector);
processor.setCmder(cmder); processor.setCmder(cmder);
processor.setCmderFroPlatform(cmderFroPlatform); processor.setCmderFroPlatform(cmderFroPlatform);
processor.setStorager(storager); processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
return processor; return processor;
} else if (Request.NOTIFY.equalsIgnoreCase(method)) { } else if (Request.NOTIFY.equalsIgnoreCase(method)) {
NotifyRequestProcessor processor = new NotifyRequestProcessor(); NotifyRequestProcessor processor = new NotifyRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
processor.setTcpSipProvider(getTcpSipProvider()); processor.setTcpSipProvider(getTcpSipProvider());
processor.setUdpSipProvider(getUdpSipProvider()); processor.setUdpSipProvider(getUdpSipProvider());
processor.setPublisher(publisher); processor.setPublisher(publisher);
processor.setRedis(redis); processor.setRedis(redis);
processor.setDeferredResultHolder(deferredResultHolder); processor.setDeferredResultHolder(deferredResultHolder);
processor.setOffLineDetector(offLineDetector); processor.setOffLineDetector(offLineDetector);
processor.setCmder(cmder); processor.setCmder(cmder);
processor.setStorager(storager); processor.setStorager(storager);
processor.setRedisCatchStorage(redisCatchStorage); processor.setRedisCatchStorage(redisCatchStorage);
return processor; return processor;
} else { } else {
OtherRequestProcessor processor = new OtherRequestProcessor(); OtherRequestProcessor processor = new OtherRequestProcessor();
processor.setRequestEvent(evt); processor.setRequestEvent(evt);
return processor; return processor;
} }
} }
public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) { public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {
Response response = evt.getResponse(); Response response = evt.getResponse();
CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
String method = cseqHeader.getMethod(); String method = cseqHeader.getMethod();
if(Request.INVITE.equals(method)){ if(Request.INVITE.equals(method)){
return inviteResponseProcessor; return inviteResponseProcessor;
} else if (Request.BYE.equals(method)) { } else if (Request.BYE.equals(method)) {
return byeResponseProcessor; return byeResponseProcessor;
} else if (Request.CANCEL.equals(method)) { } else if (Request.CANCEL.equals(method)) {
return cancelResponseProcessor; return cancelResponseProcessor;
}else if (Request.REGISTER.equals(method)) { }else if (Request.REGISTER.equals(method)) {
return registerResponseProcessor; return registerResponseProcessor;
} else { } else {
return otherResponseProcessor; return otherResponseProcessor;
} }
} }
private SipProvider getTcpSipProvider() { private SipProvider getTcpSipProvider() {
if (tcpSipProvider == null) { if (tcpSipProvider == null) {
tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider"); tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider");
} }
return tcpSipProvider; return tcpSipProvider;
} }
private SipProvider getUdpSipProvider() { private SipProvider getUdpSipProvider() {
if (udpSipProvider == null) { if (udpSipProvider == null) {
udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
} }
return 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; package com.genersoft.iot.vmp.gb28181.transmit.callback;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
/** /**
* @Description: 异步请求处理 * @Description: 异步请求处理
* @author: swwheihei * @author: swwheihei
* @date: 2020年5月8日 下午7:59:05 * @date: 2020年5月8日 下午7:59:05
*/ */
@SuppressWarnings(value = {"rawtypes", "unchecked"}) @Component
@Component public class DeferredResultHolder {
public class DeferredResultHolder {
public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY";
public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY";
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
public void put(String key, DeferredResult result) {
public void put(String key, DeferredResult result) { map.put(key, result);
map.put(key, result); }
}
public void invokeResult(RequestMessage msg) {
public DeferredResult get(String key) { // DeferredResult result = map.get(msg.getId());
return map.get(key); // 获取并移除
} DeferredResult result = map.remove(msg.getId());
if (result == null) {
public void invokeResult(RequestMessage msg) { return;
DeferredResult result = map.get(msg.getId()); }
if (result == null) { result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
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; package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
/**
* @Description:设备能力接口用于定义设备的控制查询能力 /**
* @author: swwheihei * @Description:设备能力接口用于定义设备的控制查询能力
* @date: 2020年5月3日 下午9:16:34 * @author: swwheihei
*/ * @date: 2020年5月3日 下午9:16:34
public interface ISIPCommander { */
public interface ISIPCommander {
/**
* 云台方向放控制使用配置文件中的默认镜头移动速度 /**
* * 云台方向放控制使用配置文件中的默认镜头移动速度
* @param device 控制设备 *
* @param channelId 预览通道 * @param device 控制设备
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param channelId 预览通道
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param moveSpeed 镜头移动速度 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
*/ * @param moveSpeed 镜头移动速度
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); */
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);
/**
* 云台方向放控制 /**
* * 云台方向放控制
* @param device 控制设备 *
* @param channelId 预览通道 * @param device 控制设备
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param channelId 预览通道
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param moveSpeed 镜头移动速度 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
*/ * @param moveSpeed 镜头移动速度
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); */
boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);
/**
* 云台缩放控制使用配置文件中的默认镜头缩放速度 /**
* * 云台缩放控制使用配置文件中的默认镜头缩放速度
* @param device 控制设备 *
* @param channelId 预览通道 * @param device 控制设备
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 * @param channelId 预览通道
*/ * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
boolean ptzZoomCmd(Device device,String channelId,int inOut); */
boolean ptzZoomCmd(Device device,String channelId,int inOut);
/**
* 云台缩放控制 /**
* * 云台缩放控制
* @param device 控制设备 *
* @param channelId 预览通道 * @param device 控制设备
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 * @param channelId 预览通道
* @param zoomSpeed 镜头缩放速度 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
*/ * @param zoomSpeed 镜头缩放速度
boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); */
boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);
/**
* 云台控制支持方向与缩放控制 /**
* * 云台控制支持方向与缩放控制
* @param device 控制设备 *
* @param channelId 预览通道 * @param device 控制设备
* @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param channelId 预览通道
* @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
* @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
* @param moveSpeed 镜头移动速度 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
* @param zoomSpeed 镜头缩放速度 * @param moveSpeed 镜头移动速度
*/ * @param zoomSpeed 镜头缩放速度
boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); */
boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
/**
* 前端控制包括PTZ指令FI指令预置位指令巡航指令扫描指令和辅助开关指令 /**
* * 前端控制包括PTZ指令FI指令预置位指令巡航指令扫描指令和辅助开关指令
* @param device 控制设备 *
* @param channelId 预览通道 * @param device 控制设备
* @param cmdCode 指令码 * @param channelId 预览通道
* @param parameter1 数据1 * @param cmdCode 指令码
* @param parameter2 数据2 * @param parameter1 数据1
* @param combineCode2 组合码2 * @param parameter2 数据2
*/ * @param combineCode2 组合码2
boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); */
boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
/**
* 前端控制指令用于转发上级指令 /**
* @param device 控制设备 * 前端控制指令用于转发上级指令
* @param channelId 预览通道 * @param device 控制设备
* @param cmdString 前端控制指令串 * @param channelId 预览通道
*/ * @param cmdString 前端控制指令串
boolean fronEndCmd(Device device, String channelId, String cmdString); */
boolean fronEndCmd(Device device, String channelId, String cmdString);
/**
* 请求预览视频流 /**
* * 请求预览视频流
* @param device 视频设备 *
* @param channelId 预览通道 * @param device 视频设备
*/ * @param channelId 预览通道
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); */
void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 请求回放视频流 /**
* * 请求回放视频流
* @param device 视频设备 *
* @param channelId 预览通道 * @param device 视频设备
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss * @param channelId 预览通道
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss * @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); */
void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 视频流停止 /**
* * 视频流停止
* @param ssrc ssrc *
*/ * @param streamInfo streamInfo
void streamByeCmd(String ssrc, SipSubscribe.Event okEvent); * @param okEvent okEvent
void streamByeCmd(String ssrc); */
void stopStreamByeCmd(StreamInfo streamInfo, SipSubscribe.Event okEvent);
/**
* 语音广播 /**
* * 语音广播
* @param device 视频设备 *
* @param channelId 预览通道 * @param device 视频设备
*/ * @param channelId 预览通道
boolean audioBroadcastCmd(Device device,String channelId); */
boolean audioBroadcastCmd(Device device,String channelId);
/**
* 语音广播 /**
* * 语音广播
* @param device 视频设备 *
*/ * @param device 视频设备
void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent); */
boolean audioBroadcastCmd(Device device); void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent);
boolean audioBroadcastCmd(Device device);
/**
* 音视频录像控制 /**
* * 音视频录像控制
* @param device 视频设备 *
* @param channelId 预览通道 * @param device 视频设备
* @param recordCmdStr 录像命令Record / StopRecord * @param channelId 预览通道
*/ * @param recordCmdStr 录像命令Record / StopRecord
boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent); */
boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
/**
* 远程启动控制命令 /**
* * 远程启动控制命令
* @param device 视频设备 *
*/ * @param device 视频设备
boolean teleBootCmd(Device device); */
boolean teleBootCmd(Device device);
/**
* 报警布防/撤防命令 /**
* * 报警布防/撤防命令
* @param device 视频设备 *
* @param setGuard true: SetGuard, false: ResetGuard * @param device 视频设备
*/ * @param setGuard true: SetGuard, false: ResetGuard
boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent); */
boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
/**
* 报警复位命令 /**
* * 报警复位命令
* @param device 视频设备 *
* @param alarmMethod 报警方式可选 * @param device 视频设备
* @param alarmType 报警类型可选 * @param alarmMethod 报警方式可选
*/ * @param alarmType 报警类型可选
boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent); */
boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
/**
* 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 /**
* * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
* @param device 视频设备 *
* @param channelId 预览通道 * @param device 视频设备
*/ * @param channelId 预览通道
boolean iFrameCmd(Device device, String channelId); */
boolean iFrameCmd(Device device, String channelId);
/**
* 看守位控制命令 /**
* * 看守位控制命令
* @param device 视频设备 *
* @param enabled 看守位使能1 = 开启0 = 关闭 * @param device 视频设备
* @param resetTime 自动归位时间间隔开启看守位时使用单位:(s) * @param enabled 看守位使能1 = 开启0 = 关闭
* @param presetIndex 调用预置位编号开启看守位时使用取值范围0~255 * @param resetTime 自动归位时间间隔开启看守位时使用单位:(s)
*/ * @param presetIndex 调用预置位编号开启看守位时使用取值范围0~255
boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent); */
boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
/**
* 设备配置命令 /**
* * 设备配置命令
* @param device 视频设备 *
*/ * @param device 视频设备
boolean deviceConfigCmd(Device device); */
boolean deviceConfigCmd(Device device);
/**
* 设备配置命令basicParam /**
* * 设备配置命令basicParam
* @param device 视频设备 *
* @param channelId 通道编码可选 * @param device 视频设备
* @param name 设备/通道名称可选 * @param channelId 通道编码可选
* @param expiration 注册过期时间可选 * @param name 设备/通道名称可选
* @param heartBeatInterval 心跳间隔时间可选 * @param expiration 注册过期时间可选
* @param heartBeatCount 心跳超时次数可选 * @param heartBeatInterval 心跳间隔时间可选
*/ * @param heartBeatCount 心跳超时次数可选
boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent); */
boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
/**
* 查询设备状态 /**
* * 查询设备状态
* @param device 视频设备 *
*/ * @param device 视频设备
boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent); */
boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 查询设备信息 /**
* * 查询设备信息
* @param device 视频设备 *
* @return * @param device 视频设备
*/ * @return
boolean deviceInfoQuery(Device device); */
boolean deviceInfoQuery(Device device);
/**
* 查询目录列表 /**
* * 查询目录列表
* @param device 视频设备 *
*/ * @param device 视频设备
boolean catalogQuery(Device device, SipSubscribe.Event errorEvent); */
boolean catalogQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 查询录像信息 /**
* * 查询录像信息
* @param device 视频设备 *
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss * @param device 视频设备
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss * @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); */
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
/**
* 查询报警信息 /**
* * 查询报警信息
* @param device 视频设备 *
* @param startPriority 报警起始级别可选 * @param device 视频设备
* @param endPriority 报警终止级别可选 * @param startPriority 报警起始级别可选
* @param alarmMethod 报警方式条件可选 * @param endPriority 报警终止级别可选
* @param alarmType 报警类型 * @param alarmMethod 报警方式条件可选
* @param startTime 报警发生起始时间可选 * @param alarmType 报警类型
* @param endTime 报警发生终止时间可选 * @param startTime 报警发生起始时间可选
* @return true = 命令发送成功 * @param endTime 报警发生终止时间可选
*/ * @return true = 命令发送成功
boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, */
String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent); boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);
/**
* 查询设备配置 /**
* * 查询设备配置
* @param device 视频设备 *
* @param channelId 通道编码可选 * @param device 视频设备
* @param configType 配置类型 * @param channelId 通道编码可选
*/ * @param configType 配置类型
boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent); */
boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent);
/**
* 查询设备预置位置 /**
* * 查询设备预置位置
* @param device 视频设备 *
*/ * @param device 视频设备
boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent); */
boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
/**
* 查询移动设备位置数据 /**
* * 查询移动设备位置数据
* @param device 视频设备 *
*/ * @param device 视频设备
boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent); */
boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);
/**
* 订阅取消订阅移动位置 /**
* * 订阅取消订阅移动位置
* @param device 视频设备 *
* @param expires 订阅超时时间=0时为取消订阅 * @param device 视频设备
* @param interval 上报时间间隔 * @param expires 订阅超时时间=0时为取消订阅
* @return true = 命令发送成功 * @param interval 上报时间间隔
*/ * @return true = 命令发送成功
boolean mobilePositionSubscribe(Device device, int expires, int interval); */
boolean mobilePositionSubscribe(Device device, int expires, int interval);
/**
* 订阅取消订阅报警信息 /**
* @param device 视频设备 * 订阅取消订阅报警信息
* @param expires 订阅过期时间0 = 取消订阅 * @param device 视频设备
* @param startPriority 报警起始级别可选 * @param expires 订阅过期时间0 = 取消订阅
* @param endPriority 报警终止级别可选 * @param startPriority 报警起始级别可选
* @param alarmMethods 报警方式条件可选 * @param endPriority 报警终止级别可选
* @param alarmType 报警类型 * @param alarmMethods 报警方式条件可选
* @param startTime 报警发生起始时间可选 * @param alarmType 报警类型
* @param endTime 报警发生终止时间可选 * @param startTime 报警发生起始时间可选
* @return true = 命令发送成功 * @param endTime 报警发生终止时间可选
*/ * @return true = 命令发送成功
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); */
boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
/**
* 释放rtpserver /**
* @param device * 释放rtpserver
* @param channelId * @param device
*/ * @param channelId
void closeRTPServer(Device device, String 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; package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException; import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory; import javax.sip.SipFactory;
// import javax.sip.SipProvider; // import javax.sip.SipProvider;
import javax.sip.address.Address; import javax.sip.address.Address;
import javax.sip.address.SipURI; import javax.sip.address.SipURI;
import javax.sip.header.*; import javax.sip.header.*;
import javax.sip.message.Request; import javax.sip.message.Request;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.beans.factory.annotation.Qualifier; // import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
/** /**
* @Description:摄像头命令request创造器 TODO 冗余代码太多待优化 * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化
* @author: swwheihei * @author: swwheihei
* @date: 2020年5月6日 上午9:29:02 * @date: 2020年5月6日 上午9:29:02
*/ */
@Component @Component
public class SIPRequestHeaderProvider { public class SIPRequestHeaderProvider {
@Autowired @Autowired
private SipConfig sipConfig; private SipConfig sipConfig;
@Autowired @Autowired
private SipFactory sipFactory; private SipFactory sipFactory;
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null; Request request = null;
// sipuri // sipuri
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via // via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
device.getTransport(), viaTag); device.getTransport(), viaTag);
viaHeader.setRPort(); viaHeader.setRPort();
viaHeaders.add(viaHeader); viaHeaders.add(viaHeader);
// from // from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),
sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
// to // to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
// Forwards // Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq // ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards); toHeader, viaHeaders, maxForwards);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
request.setContent(content, contentTypeHeader); request.setContent(content, contentTypeHeader);
return request; 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 { 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; Request request = null;
//请求行 //请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
//via //via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag);
viaHeader.setRPort(); viaHeader.setRPort();
viaHeaders.add(viaHeader); viaHeaders.add(viaHeader);
//from //from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//to //to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
//Forwards //Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
//ceq //ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); 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(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
// Subject // Subject
SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0)); SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0));
request.addHeader(subjectHeader); request.addHeader(subjectHeader);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
request.setContent(content, contentTypeHeader); request.setContent(content, contentTypeHeader);
return request; return request;
} }
public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null; Request request = null;
//请求行 //请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via // via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag);
viaHeader.setRPort(); viaHeader.setRPort();
viaHeaders.add(viaHeader); viaHeaders.add(viaHeader);
//from //from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
//to //to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
//Forwards //Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
//ceq //ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); 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(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
request.setContent(content, contentTypeHeader); request.setContent(content, contentTypeHeader);
return request; 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 { 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; Request request = null;
// sipuri // sipuri
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
// via // via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(),
device.getTransport(), viaTag); device.getTransport(), viaTag);
viaHeader.setRPort(); viaHeader.setRPort();
viaHeaders.add(viaHeader); viaHeaders.add(viaHeader);
// from // from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),
sipConfig.getSipIp() + ":" + sipConfig.getSipPort()); sipConfig.getSipIp() + ":" + sipConfig.getSipPort());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
// to // to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain()); SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getSipDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
// Forwards // Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq // ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE);
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards); 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(), sipConfig.getSipIp()+":"+sipConfig.getSipPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
// Expires // Expires
ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires); ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
request.addHeader(expireHeader); request.addHeader(expireHeader);
// Event // Event
EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event); EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event);
request.addHeader(eventHeader); request.addHeader(eventHeader);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
request.setContent(content, contentTypeHeader); request.setContent(content, contentTypeHeader);
return request; 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; package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
import java.text.ParseException; import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer;
import javax.sip.Dialog; import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
import javax.sip.InvalidArgumentException; import org.springframework.stereotype.Component;
import javax.sip.ResponseEvent;
import javax.sip.SipException; import javax.sip.Dialog;
import javax.sip.address.SipURI; import javax.sip.InvalidArgumentException;
import javax.sip.header.CSeqHeader; import javax.sip.ResponseEvent;
import javax.sip.header.ViaHeader; import javax.sip.SipException;
import javax.sip.message.Request; import javax.sip.address.SipURI;
import javax.sip.message.Response; import javax.sip.header.CSeqHeader;
import javax.sip.header.ViaHeader;
// import org.slf4j.Logger; import javax.sip.message.Request;
// import org.slf4j.LoggerFactory; import javax.sip.message.Response;
import org.springframework.stereotype.Component; import java.text.ParseException;
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
/** */
* @Description:处理INVITE响应 @Component
* @author: swwheihei public class InviteResponseProcessor implements ISIPResponseProcessor {
* @date: 2020年5月3日 下午4:43:52
*/ /**
@Component * 处理invite响应
public class InviteResponseProcessor implements ISIPResponseProcessor { *
* @param evt 响应消息
// private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); * @throws ParseException
*/
/** @Override
* 处理invite响应 public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException {
* try {
* @param evt 响应消息 Response response = evt.getResponse();
* @throws ParseException int statusCode = response.getStatusCode();
*/ // trying不会回复
@Override if (statusCode == Response.TRYING) {
public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException { }
try { // 成功响应
Response response = evt.getResponse(); // 下发ack
int statusCode = response.getStatusCode(); if (statusCode == Response.OK) {
// trying不会回复 Dialog dialog = evt.getDialog();
if (statusCode == Response.TRYING) { CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
} Request reqAck = dialog.createAck(cseq.getSeqNumber());
// 成功响应
// 下发ack SipURI requestURI = (SipURI) reqAck.getRequestURI();
if (statusCode == Response.OK) { ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
Dialog dialog = evt.getDialog(); requestURI.setHost(viaHeader.getHost());
CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); requestURI.setPort(viaHeader.getPort());
Request reqAck = dialog.createAck(cseq.getSeqNumber()); reqAck.setRequestURI(requestURI);
SipURI requestURI = (SipURI) reqAck.getRequestURI(); dialog.sendAck(reqAck);
ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); }
requestURI.setHost(viaHeader.getHost()); } catch (InvalidArgumentException | SipException e) {
requestURI.setPort(viaHeader.getPort()); e.printStackTrace();
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; package com.genersoft.iot.vmp.media.zlm;
import com.genersoft.iot.vmp.conf.MediaServerConfig; 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.IRedisCatchStorage;
// import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import org.apache.commons.lang3.StringUtils;
// import org.slf4j.Logger;
// import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.*; 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.HttpClientErrorException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -18,30 +18,34 @@ import javax.servlet.http.HttpServletResponse;
@RequestMapping("/zlm") @RequestMapping("/zlm")
public class ZLMHTTPProxyController { public class ZLMHTTPProxyController {
// private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class);
// @Autowired
// private IVideoManagerStorager storager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Value("${media.port}") @Autowired
private int mediaHttpPort; private VideoStreamSessionManager streamSession;
@ResponseBody @ResponseBody
@RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8") @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) { if (redisCatchStorage.getMediaInfo() == null) {
return "未接入流媒体"; 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(); MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
String requestURI = String.format("http://%s:%s%s?%s&%s", String requestURI = String.format("http://%s:%s%s?%s&%s",
mediaInfo.getLocalIP(), mediaServerIp,
mediaHttpPort, mediaInfo.getHttpPort(),
request.getRequestURI().replace("/zlm",""), request.getRequestURI().replace("/zlm", ""),
mediaInfo.getHookAdminParams(), mediaInfo.getHookAdminParams(),
request.getQueryString() request.getQueryString()
); );
@ -50,9 +54,8 @@ public class ZLMHTTPProxyController {
//将指定的url返回的参数自动封装到自定义好的对应类对象中 //将指定的url返回的参数自动封装到自定义好的对应类对象中
Object result = null; Object result = null;
try { try {
result = restTemplate.getForObject(requestURI,Object.class); result = restTemplate.getForObject(requestURI, Object.class);
} catch (HttpClientErrorException httpClientErrorException) {
}catch (HttpClientErrorException httpClientErrorException) {
response.setStatus(httpClientErrorException.getStatusCode().value()); response.setStatus(httpClientErrorException.getStatusCode().value());
} }
return result; 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; package com.genersoft.iot.vmp.media.zlm;
import java.util.UUID; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.vmanager.service.IPlayService; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import org.slf4j.Logger; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import org.slf4j.LoggerFactory; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.springframework.beans.factory.annotation.Autowired; import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.springframework.beans.factory.annotation.Value; import org.slf4j.Logger;
import org.springframework.http.HttpStatus; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RestController;
/**
import com.alibaba.fastjson.JSONObject; * @Description:针对 ZLMediaServer的hook事件监听
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; * @author: swwheihei
* @date: 2020年5月8日 上午10:46:48
import javax.servlet.http.HttpServletRequest; */
@RestController
/** @RequestMapping("/index/hook")
* @Description:针对 ZLMediaServer的hook事件监听 public class ZLMHttpHookListener {
* @author: swwheihei
* @date: 2020年5月8日 上午10:46:48 private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
*/
@RestController
@RequestMapping("/index/hook") @Autowired
public class ZLMHttpHookListener { private SIPCommander cmder;
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); @Autowired
private IPlayService playService;
@Autowired @Autowired
private SIPCommander cmder; private IVideoManagerStorager storager;
@Autowired @Autowired
private IPlayService playService; private IRedisCatchStorage redisCatchStorage;
@Autowired @Autowired
private IVideoManagerStorager storager; private ZLMMediaListManager zlmMediaListManager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private ZLMHttpHookSubscribe subscribe;
@Autowired
@Autowired private ZLMHttpHookSubscribe subscribe;
private ZLMMediaListManager zlmMediaListManager;
@Autowired
@Autowired MediaConfig mediaConfig;
private ZLMHttpHookSubscribe subscribe;
@Autowired
@Value("${media.autoApplyPlay}") private VideoStreamSessionManager streamSession;
private boolean autoApplyPlay;
/**
@Value("${media.ip}") * 流量统计事件播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件阈值通过配置文件general.flowThreshold配置此事件对回复不敏感
private String mediaIp; *
*/
@Value("${media.wanIp}") @ResponseBody
private String mediaWanIp; @PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){
@Value("${media.port}")
private int mediaPort; if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString());
/** }
* 流量统计事件播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件阈值通过配置文件general.flowThreshold配置此事件对回复不敏感 JSONObject ret = new JSONObject();
* ret.put("code", 0);
*/ ret.put("msg", "success");
@ResponseBody return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8") }
public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){
/**
if (logger.isDebugEnabled()) { * 访问http文件服务器上hls之外的文件时触发
logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString()); *
} */
JSONObject ret = new JSONObject(); @ResponseBody
ret.put("code", 0); @PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8")
ret.put("msg", "success"); public ResponseEntity<String> onHttpAccess(@RequestBody JSONObject json){
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
} if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString());
/** }
* 访问http文件服务器上hls之外的文件时触发 JSONObject ret = new JSONObject();
* ret.put("code", 0);
*/ ret.put("err", "");
@ResponseBody ret.put("path", "");
@PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8") ret.put("second", 600);
public ResponseEntity<String> onHttpAccess(@RequestBody JSONObject json){ return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); /**
} * 播放器鉴权事件rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件
JSONObject ret = new JSONObject(); *
ret.put("code", 0); */
ret.put("err", ""); @ResponseBody
ret.put("path", ""); @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
ret.put("second", 600); public ResponseEntity<String> onPlay(@RequestBody JSONObject json){
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
} if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString());
/** }
* 播放器鉴权事件rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件 JSONObject ret = new JSONObject();
* ret.put("code", 0);
*/ ret.put("msg", "success");
@ResponseBody return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") }
public ResponseEntity<String> onPlay(@RequestBody JSONObject json){
/**
if (logger.isDebugEnabled()) { * rtsp/rtmp/rtp推流鉴权事件
logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString()); *
} */
JSONObject ret = new JSONObject(); @ResponseBody
ret.put("code", 0); @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
ret.put("msg", "success"); public ResponseEntity<String> onPublish(@RequestBody JSONObject json){
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
} if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString());
/** }
* rtsp/rtmp/rtp推流鉴权事件
* ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
*/ if (subscribe != null) {
@ResponseBody subscribe.response(json);
@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") }
public ResponseEntity<String> onPublish(@RequestBody JSONObject json){
JSONObject ret = new JSONObject();
if (logger.isDebugEnabled()) { ret.put("code", 0);
logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); ret.put("msg", "success");
} ret.put("enableHls", true);
ret.put("enableMP4", false);
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); ret.put("enableRtxp", true);
if (subscribe != null) subscribe.response(json); return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
JSONObject ret = new JSONObject();
ret.put("code", 0); /**
ret.put("msg", "success"); * 录制mp4完成后通知事件此事件对回复不敏感
ret.put("enableHls", true); *
ret.put("enableMP4", false); */
ret.put("enableRtxp", true); @ResponseBody
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
} public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json){
/** if (logger.isDebugEnabled()) {
* 录制mp4完成后通知事件此事件对回复不敏感 logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString());
* }
*/ JSONObject ret = new JSONObject();
@ResponseBody ret.put("code", 0);
@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8") ret.put("msg", "success");
public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json){ return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString()); /**
} * rtsp专用的鉴权事件先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件
JSONObject ret = new JSONObject(); *
ret.put("code", 0); */
ret.put("msg", "success"); @ResponseBody
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); @PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8")
} public ResponseEntity<String> onRtspRealm(@RequestBody JSONObject json){
/** if (logger.isDebugEnabled()) {
* rtsp专用的鉴权事件先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件 logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString());
* }
*/ JSONObject ret = new JSONObject();
@ResponseBody ret.put("code", 0);
@PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8") ret.put("realm", "");
public ResponseEntity<String> onRtspRealm(@RequestBody JSONObject json){ return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString());
} /**
JSONObject ret = new JSONObject(); * 该rtsp流是否开启rtsp专用方式的鉴权事件开启后才会触发on_rtsp_auth事件需要指出的是rtsp也支持url参数鉴权它支持两种方式鉴权
ret.put("code", 0); *
ret.put("realm", ""); */
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); @ResponseBody
} @PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onRtspAuth(@RequestBody JSONObject json){
/** if (logger.isDebugEnabled()) {
* 该rtsp流是否开启rtsp专用方式的鉴权事件开启后才会触发on_rtsp_auth事件需要指出的是rtsp也支持url参数鉴权它支持两种方式鉴权 logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString());
* }
*/ JSONObject ret = new JSONObject();
@ResponseBody ret.put("code", 0);
@PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8") ret.put("encrypted", false);
public ResponseEntity<String> onRtspAuth(@RequestBody JSONObject json){ ret.put("passwd", "test");
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
if (logger.isDebugEnabled()) { }
logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString());
} /**
JSONObject ret = new JSONObject(); * shell登录鉴权ZLMediaKit提供简单的telnet调试方式使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面
ret.put("code", 0); *
ret.put("encrypted", false); */
ret.put("passwd", "test"); @ResponseBody
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); @PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8")
} public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){
/** if (logger.isDebugEnabled()) {
* shell登录鉴权ZLMediaKit提供简单的telnet调试方式使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面 logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString());
* }
*/ // TODO 如果是带有rtpstream则开启按需拉流
@ResponseBody // String app = json.getString("app");
@PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8") // String stream = json.getString("stream");
public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
if (logger.isDebugEnabled()) { if (subscribe != null) subscribe.response(json);
logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString());
} JSONObject ret = new JSONObject();
// TODO 如果是带有rtpstream则开启按需拉流 ret.put("code", 0);
// String app = json.getString("app"); ret.put("msg", "success");
// String stream = json.getString("stream"); return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
}
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
if (subscribe != null) subscribe.response(json); /**
* rtsp/rtmp流注册或注销时触发此事件此事件对回复不敏感
JSONObject ret = new JSONObject(); */
ret.put("code", 0); @ResponseBody
ret.put("msg", "success"); @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json) {
}
if (logger.isDebugEnabled()) {
/** logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString());
* rtsp/rtmp流注册或注销时触发此事件此事件对回复不敏感 }
*
*/ JSONObject ret = new JSONObject();
@ResponseBody ret.put("code", 0);
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") ret.put("msg", "success");
public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json){
// 流消失移除redis play
if (logger.isDebugEnabled()) { String app = json.getString("app");
logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); String streamId = json.getString("stream");
} boolean regist = json.getBoolean("regist");
// 流消失移除redis play if (!"rtp".equals(app) || regist) {
String app = json.getString("app"); if (!"rtp".equals(app) && "rtsp".equals(schema)){
String streamId = json.getString("stream"); zlmMediaListManager.updateMediaList();
String schema = json.getString("schema"); }
boolean regist = json.getBoolean("regist"); return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); }
if ("rtp".equals(app) && !regist ) {
if (streamInfo!=null){ String[] s = streamId.split("_");
redisCatchStorage.stopPlay(streamInfo); if (s.length != 4) {
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}else{ }
streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); String channelId = s[3];
redisCatchStorage.stopPlayback(streamInfo); // TODO channelId
} StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId);
}else { if (null != streamInfo) {
if (!"rtp".equals(app) && "rtsp".equals(schema)){ cmder.stopStreamByeCmd(streamInfo, null);
zlmMediaListManager.updateMediaList(); }
} return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
} }
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());
@ResponseBody }
@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onStreamNoneReader(@RequestBody JSONObject json){ JSONObject ret = new JSONObject();
ret.put("code", 0);
if (logger.isDebugEnabled()) { ret.put("close", true);
logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString());
} String app = json.getString("app");
String streamId = json.getString("stream");
String streamId = json.getString("stream"); if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) {
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
JSONObject ret = new JSONObject(); String[] s = streamId.split("_");
ret.put("code", 0); if (s.length != 4) {
ret.put("close", true); return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
if (streamInfo != null) { String channelId = s[3];
if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) { // TODO channelId
ret.put("close", false); StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId);
} else { if (null != streamInfo) {
cmder.streamByeCmd(streamId); if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) {
redisCatchStorage.stopPlay(streamInfo); ret.put("close", false);
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); } else {
} cmder.stopStreamByeCmd(streamInfo, null);
}else{ }
cmder.streamByeCmd(streamId); }
streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
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) {
*/
@ResponseBody if (logger.isDebugEnabled()) {
@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString());
public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json){ }
if (logger.isDebugEnabled()) { JSONObject ret = new JSONObject();
logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); ret.put("code", 0);
} ret.put("msg", "success");
if (autoApplyPlay) { if (!mediaConfig.getAutoApplyPlay()) {
String app = json.getString("app"); return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
String streamId = json.getString("stream"); }
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); String app = json.getString("app");
if ("rtp".equals(app) && streamId.indexOf("gb_play") > -1 && streamInfo == null) { String streamId = json.getString("stream");
String[] s = streamId.split("_");
if (s.length == 4) { if (!"rtp".equals(app) || streamId.indexOf("gb_play") < 0) {
String deviceId = s[2]; return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
String channelId = s[3]; }
Device device = storager.queryVideoDevice(deviceId); String[] s = streamId.split("_");
if (device != null) { if (s.length != 4) {
UUID uuid = UUID.randomUUID(); return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
cmder.playStreamCmd(device, channelId, (JSONObject response) -> { }
logger.info("收到订阅消息: " + response.toJSONString()); String deviceId = s[2];
playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); String channelId = s[3];
}, null); 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) -> {
JSONObject ret = new JSONObject(); logger.info("收到订阅消息: " + response.toJSONString());
ret.put("code", 0); playService.onPublishHandlerForPlay(response, deviceId, channelId, msg);
ret.put("msg", "success"); }, null);
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); }
} return new ResponseEntity<>(ret.toString(), HttpStatus.OK);
}
/**
* 服务器启动事件可以用于监听服务器崩溃重启此事件对回复不敏感 /**
* * 服务器启动事件可以用于监听服务器崩溃重启此事件对回复不敏感
*/ *
@ResponseBody */
@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") @ResponseBody
public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){ @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()); 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 = JSON.toJavaObject(json, MediaServerConfig.class);
// MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0); redisCatchStorage.updateMediaInfo(mediaServerConfig);
MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); // TODO Auto-generated method stub
mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp);
mediaServerConfig.setLocalIP(mediaIp); JSONObject ret = new JSONObject();
redisCatchStorage.updateMediaInfo(mediaServerConfig); ret.put("code", 0);
JSONObject ret = new JSONObject(); ret.put("msg", "success");
ret.put("code", 0); return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
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.JSON;
import com.alibaba.fastjson.JSONObject; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
@ -18,25 +22,19 @@ public class ZLMRESTfulUtils {
private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class);
@Value("${media.ip}") @Autowired
private String mediaIp; MediaConfig mediaConfig;
@Value("${media.port}") public JSONObject sendPost(String mediaServerIp, String api, Map<String, Object> param) {
private int mediaPort;
@Value("${media.secret}")
private String mediaSecret;
public JSONObject sendPost(String api, Map<String, Object> param) {
OkHttpClient client = new OkHttpClient(); 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; JSONObject responseJSON = null;
logger.debug(url); logger.debug(url);
FormBody.Builder builder = new FormBody.Builder(); FormBody.Builder builder = new FormBody.Builder();
builder.add("secret",mediaSecret); builder.add("secret", mediaConfig.getMediaSecret());
if (param != null) { if (param != null) {
for (String key : param.keySet()){ for (String key : param.keySet()) {
builder.add(key, param.get(key).toString()); builder.add(key, param.get(key).toString());
} }
} }
@ -58,20 +56,29 @@ public class ZLMRESTfulUtils {
} catch (ConnectException e) { } catch (ConnectException e) {
logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
logger.info("请检查media配置并确认ZLM已启动..."); logger.info("请检查media配置并确认ZLM已启动...");
}catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return responseJSON; return responseJSON;
} }
public JSONObject getMediaList(String app, String schema){ // public JSONObject getMediaList(String app, String schema) {
Map<String, Object> param = new HashMap<>(); // Map<String, Object> param = new HashMap<>();
param.put("app",app); // param.put("app", app);
param.put("schema",schema); // param.put("schema", schema);
param.put("vhost","__defaultVhost__"); // param.put("vhost", "__defaultVhost__");
return sendPost("getMediaList",param); // 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(){ public JSONObject getMediaList(){
return sendPost("getMediaList",null); return sendPost("getMediaList",null);
@ -86,42 +93,88 @@ public class ZLMRESTfulUtils {
return sendPost("getMediaInfo",param); return sendPost("getMediaInfo",param);
} }
public JSONObject getRtpInfo(String stream_id){ public JSONObject getRtpInfo(String mediaServerIp, String stream_id) {
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("stream_id",stream_id); param.put("stream_id", stream_id);
return sendPost("getRtpInfo",param); 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(src_url);
System.out.println(dst_url); System.out.println(dst_url);
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("src_url", src_url); param.put("src_url", src_url);
param.put("dst_url", dst_url); param.put("dst_url", dst_url);
param.put("timeout_ms", timeout_ms); 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<>(); Map<String, Object> param = new HashMap<>();
param.put("key", key); 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(){ public JSONObject closeRtpServer(String mediaServerIp, Map<String, Object> param) {
return sendPost("getServerConfig",null); 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) { 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.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SsrcUtil; import com.genersoft.iot.vmp.gb28181.session.SsrcUtil;
import com.genersoft.iot.vmp.conf.MediaConfig;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component @Component
public class ZLMRTPServerFactory { public class ZLMRTPServerFactory {
private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory"); private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory");
@Value("${media.rtp.udpPortRange}") @Autowired
private String udpPortRange; MediaConfig mediaConfig;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;
private int[] udpPortRangeArray = new int[2]; 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<>(); Map<String, Object> param = new HashMap<>();
int result = -1; int result = -1;
int newPort = getPortFromUdpPortRange(); int newPort = getPortFromUdpPortRange(mediaServerIp);
param.put("port", newPort); param.put("port", newPort);
param.put("enable_tcp", 1); param.put("enable_tcp", 1);
param.put("stream_id", streamId); param.put("stream_id", streamId);
JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); JSONObject jsonObject = zlmresTfulUtils.openRtpServer(mediaServerIp, param);
System.out.println(jsonObject); System.out.println(jsonObject);
if (jsonObject != null) { if (jsonObject != null) {
switch (jsonObject.getInteger("code")){ switch (jsonObject.getInteger("code")) {
case 0: case 0:
result= newPort; result = newPort;
break; break;
case -300: // id已经存在 case -300: // id已经存在
result = newPort; result = newPort;
break; break;
case -400: // 端口占用 case -400: // 端口占用
result= createRTPServer(streamId); result = createRTPServer(mediaServerIp, streamId);
break; break;
default: default:
logger.error("创建RTP Server 失败: " + jsonObject.getString("msg")); logger.error("创建RTP Server 失败: " + jsonObject.getString("msg"));
break; break;
} }
}else { } else {
// 检查ZLM状态 // 检查ZLM状态
logger.error("创建RTP Server 失败: 请检查ZLM服务"); logger.error("创建RTP Server 失败: 请检查ZLM服务");
} }
return result; return result;
} }
public boolean closeRTPServer(String streamId) { public boolean closeRTPServer(String mediaServerIp, String streamId) {
boolean result = false; boolean result = false;
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("stream_id", streamId); 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) { if (jsonObject.getInteger("code") == 0) {
result = jsonObject.getInteger("hit") == 1; result = jsonObject.getInteger("hit") == 1;
}else { } else {
logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
} }
}else { } else {
// 检查ZLM状态 // 检查ZLM状态
logger.error("关闭RTP Server 失败: 请检查ZLM服务"); logger.error("关闭RTP Server 失败: 请检查ZLM服务");
} }
return result; 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) { if (currentPort == 0) {
String[] udpPortRangeStrArray = udpPortRange.split(","); String[] udpPortRangeStrArray = mediaConfig.getUdpPortRange().split(",");
udpPortRangeArray[0] = Integer.parseInt(udpPortRangeStrArray[0]); udpPortRangeArray[0] = Integer.parseInt(udpPortRangeStrArray[0]);
udpPortRangeArray[1] = Integer.parseInt(udpPortRangeStrArray[1]); udpPortRangeArray[1] = Integer.parseInt(udpPortRangeStrArray[1]);
} }
if (currentPort == 0 || currentPort++ > udpPortRangeArray[1]) { if (currentPort == 0 || currentPort++ > udpPortRangeArray[1]) {
currentPort = udpPortRangeArray[0];
return udpPortRangeArray[0]; return udpPortRangeArray[0];
} else { } else {
if (currentPort % 2 == 1) { 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.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; 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.MediaServerConfig;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
//import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
@ -18,44 +18,20 @@ import org.springframework.util.StringUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@Slf4j
@Component @Component
@Order(value=1) @Order(value = 1)
public class ZLMRunner implements CommandLineRunner { public class ZLMRunner implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
// @Autowired
// private IVideoManagerStorager storager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; 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}") @Value("${server.port}")
private String serverPort; private String serverPort;
@Autowired
@Value("${media.autoConfig}") MediaConfig mediaConfig;
private boolean autoConfig; @Autowired
SipConfig sipConfig;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;
@ -64,76 +40,81 @@ public class ZLMRunner implements CommandLineRunner {
private ZLMMediaListManager zlmMediaListManager; private ZLMMediaListManager zlmMediaListManager;
@Override @Override
public void run(String... strings) throws Exception { public void run(String... strings) {
// 获取zlm信息 String[] mediaIpArr = mediaConfig.getMediaIpArr();
logger.info("等待zlm接入..."); for (String mediaIp : mediaIpArr) {
MediaServerConfig mediaServerConfig = getMediaServerConfig(); // 获取zlm信息
if (mediaServerConfig != null) { log.info("等待zlm {} 接入...", mediaIp);
logger.info("zlm接入成功..."); MediaServerConfig mediaServerConfig = getMediaServerConfig(mediaIp);
if (autoConfig) saveZLMConfig(); if (mediaServerConfig != null) {
mediaServerConfig = getMediaServerConfig(); log.info("zlm {} 接入成功...", mediaIp);
redisCatchStorage.updateMediaInfo(mediaServerConfig); if (mediaConfig.getAutoConfig()) {
// 更新流列表 // 自动配置zlm
zlmMediaListManager.updateMediaList(); saveZLMConfig(mediaIp);
// 配置后,从zlm重新获取流媒体服务器信息
mediaServerConfig = getMediaServerConfig(mediaIp);
}
// TODO 这里需要把多台zlm服务器的配置,分别存储到redis,不能用同一个key,否则会覆盖
redisCatchStorage.updateMediaInfo(mediaServerConfig);
// 更新流列表
zlmMediaListManager.updateMediaList();
}
} }
} }
public MediaServerConfig getMediaServerConfig() { public MediaServerConfig getMediaServerConfig(String mediaIp) {
JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(); JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaIp);
MediaServerConfig mediaServerConfig = null; MediaServerConfig mediaServerConfig = null;
if (responseJSON != null) { if (responseJSON != null) {
JSONArray data = responseJSON.getJSONArray("data"); JSONArray data = responseJSON.getJSONArray("data");
if (data != null && data.size() > 0) { if (data != null && data.size() > 0) {
mediaServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), MediaServerConfig.class); mediaServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), MediaServerConfig.class);
mediaServerConfig.setLocalIP(mediaIp);
mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp);
} }
} else { } else {
logger.error("getMediaServerConfig失败, 1s后重试"); log.error("getMediaServerConfig失败, 1s后重试");
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
mediaServerConfig = getMediaServerConfig(); mediaServerConfig = getMediaServerConfig(mediaIp);
} }
return mediaServerConfig; return mediaServerConfig;
} }
private void saveZLMConfig() { private void saveZLMConfig(String mediaIp) {
logger.info("设置zlm..."); log.info("设置zlm {} ...", mediaIp);
String mediaHookIp = mediaConfig.getMediaHookIp();
if (StringUtils.isEmpty(mediaHookIp)) { if (StringUtils.isEmpty(mediaHookIp)) {
mediaHookIp = sipIP; mediaHookIp = sipConfig.getSipIp();
} }
String hookPrex = String.format("http://%s:%s/index/hook", mediaHookIp, serverPort); String hookPrex = String.format("http://%s:%s/index/hook", mediaHookIp, serverPort);
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("api.secret",mediaSecret); // -profile:v Baseline 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("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.enable", "1");
param.put("hook.on_flow_report",""); param.put("hook.on_flow_report", "");
param.put("hook.on_play",""); param.put("hook.on_play", "");
param.put("hook.on_http_access",""); param.put("hook.on_http_access", "");
param.put("hook.on_publish",String.format("%s/on_publish", hookPrex)); param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
param.put("hook.on_record_mp4",""); param.put("hook.on_record_mp4", "");
param.put("hook.on_record_ts",""); param.put("hook.on_record_ts", "");
param.put("hook.on_rtsp_auth",""); param.put("hook.on_rtsp_auth", "");
param.put("hook.on_rtsp_realm",""); param.put("hook.on_rtsp_realm", "");
param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); 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_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_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_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.on_stream_not_found", String.format("%s/on_stream_not_found", hookPrex));
param.put("hook.timeoutSec","20"); param.put("hook.timeoutSec", "20");
param.put("general.streamNoneReaderDelayMS",streamNoneReaderDelayMS); param.put("general.streamNoneReaderDelayMS", mediaConfig.getStreamNoneReaderDelayMS());
JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param); JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaIp, param);
if (responseJSON != null && responseJSON.getInteger("code") == 0) { if (responseJSON != null && responseJSON.getInteger("code") == 0) {
logger.info("设置zlm成功"); log.info("设置zlm {} 成功", mediaIp);
}else { } else {
logger.info("设置zlm失败: " + responseJSON.getString("msg")); 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 { public interface IRedisCatchStorage {
/** /**
* 开始播放时将流存入 * 更新流媒体信息
* *
* @param stream 流信息 * @param mediaServerConfig
* @return * @return
*/ */
boolean startPlay(StreamInfo stream); boolean updateMediaInfo(MediaServerConfig mediaServerConfig);
/** /**
* 停止播放时删除 * 获取流媒体信息
* *
* @return * @return
*/ */
boolean stopPlay(StreamInfo streamInfo); MediaServerConfig getMediaInfo();
/** /**
* 查询播放列表 * 开始播放时将流存入
*
* @param stream 流信息
* @return * @return
*/ */
StreamInfo queryPlay(StreamInfo streamInfo); boolean startPlay(StreamInfo stream);
StreamInfo queryPlayByStreamId(String steamId);
StreamInfo queryPlaybackByStreamId(String steamId);
StreamInfo queryPlayByDevice(String deviceId, String code);
/** /**
* 更新流媒体信息 * 停止播放时删除
* @param mediaServerConfig *
* @return * @return
*/ */
boolean updateMediaInfo(MediaServerConfig mediaServerConfig); boolean stopPlay(StreamInfo streamInfo);
/** StreamInfo queryPlayByStreamId(String channelId, String steamId);
* 获取流媒体信息
* @return
*/
MediaServerConfig getMediaInfo();
Map<String, StreamInfo> queryPlayByDeviceId(String deviceId); StreamInfo queryPlayByChannel(String channelId);
boolean startPlayback(StreamInfo stream); boolean startPlayback(StreamInfo stream);
boolean stopPlayback(StreamInfo streamInfo); 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); StreamInfo queryPlaybackByDevice(String deviceId, String code);
void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch); 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; 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.Device; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce; import com.github.pagehelper.PageInfo;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.github.pagehelper.PageInfo; import java.util.List;
/** /**
* @Description:视频设备数据存储接口 * @Description:视频设备数据存储接口
* @author: swwheihei * @author: swwheihei
* @date: 2020年5月6日 下午2:14:31 * @date: 2020年5月6日 下午2:14:31
*/ */
@SuppressWarnings("rawtypes") public interface IVideoManagerStorager {
public interface IVideoManagerStorager {
/**
/** * 根据设备ID判断设备是否存在
* 根据设备ID判断设备是否存在 *
* * @param deviceId 设备ID
* @param deviceId 设备ID * @return true:存在 false不存在
* @return true:存在 false不存在 */
*/ public boolean exists(String deviceId);
public boolean exists(String deviceId);
/**
/** * 视频设备创建
* 视频设备创建 *
* * @param device 设备对象
* @param device 设备对象 * @return true创建成功 false创建失败
* @return true创建成功 false创建失败 */
*/ public boolean create(Device device);
public boolean create(Device device);
/**
/** * 视频设备更新
* 视频设备更新 *
* * @param device 设备对象
* @param device 设备对象 * @return true创建成功 false创建失败
* @return true创建成功 false创建失败 */
*/ public boolean updateDevice(Device device);
public boolean updateDevice(Device device);
/**
/** * 添加设备通道
* 添加设备通道 *
* * @param deviceId 设备id
* @param deviceId 设备id * @param channel 通道
* @param channel 通道 */
*/ public void updateChannel(String deviceId, DeviceChannel channel);
public void updateChannel(String deviceId, DeviceChannel channel);
/**
/** * 开始播放
* 开始播放 *
* @param deviceId 设备id * @param deviceId 设备id
* @param channelId 通道ID * @param channelId 通道ID
* @param streamId 流地址 * @param streamId 流地址
*/ */
public void startPlay(String deviceId, String channelId, String streamId); public void startPlay(String deviceId, String channelId, String streamId);
/** /**
* 停止播放 * 停止播放
* @param deviceId 设备id *
* @param channelId 通道ID * @param deviceId 设备id
*/ * @param channelId 通道ID
public void stopPlay(String deviceId, String channelId); */
public void stopPlay(String deviceId, String channelId);
/**
* 获取设备 /**
* * 获取设备
* @param deviceId 设备ID *
* @return DShadow 设备对象 * @param deviceId 设备ID
*/ * @return DShadow 设备对象
public Device queryVideoDevice(String deviceId); */
public Device queryVideoDevice(String deviceId);
/**
* 获取某个设备的通道列表 /**
* * 获取某个设备的通道列表
* @param deviceId 设备ID *
* @param page 分页 当前页 * @param deviceId 设备ID
* @param count 每页数量 * @param page 分页 当前页
* @return * @param count 每页数量
*/ * @return
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count); */
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count);
/**
* 获取某个设备的通道列表 /**
* * 获取某个设备的通道列表
* @param deviceId 设备ID *
* @return * @param deviceId 设备ID
*/ * @return
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId); */
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId);
/**
* 获取某个设备的通道 /**
* @param deviceId 设备ID * 获取某个设备的通道
* @param channelId 通道ID *
*/ * @param deviceId 设备ID
public DeviceChannel queryChannel(String deviceId, String channelId); * @param channelId 通道ID
*/
/** public DeviceChannel queryChannel(String deviceId, String channelId);
* 获取多个设备
* @param page 当前页数 /**
* @param count 每页数量 * 获取多个设备
* @return List<Device> 设备对象数组 *
*/ * @param page 当前页数
public PageInfo<Device> queryVideoDeviceList(int page, int count); * @param count 每页数量
* @return List<Device> 设备对象数组
/** */
* 获取多个设备 public PageInfo<Device> queryVideoDeviceList(int page, int count);
*
* @return List<Device> 设备对象数组 /**
*/ * 获取多个设备
public List<Device> queryVideoDeviceList(); *
* @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 delete(String deviceId);
*
* @param deviceId 设备ID /**
* @return true更新成功 false更新失败 * 更新设备在线
*/ *
public boolean online(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 设备ID
* @return true更新成功 false更新失败
*/
/** public boolean outline(String deviceId);
* 查询子设备
*
* @param deviceId /**
* @param channelId * 查询子设备
* @param page *
* @param count * @param deviceId
* @return * @param channelId
*/ * @param page
PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count); * @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 deviceId
/** */
* 更新上级平台 void cleanChannelsForDevice(String deviceId);
* @param parentPlatform
*/ /**
boolean updateParentPlatform(ParentPlatform parentPlatform); * 添加Mobile Position设备移动位置
*
* @param MobilePosition
/** * @return
* 添加上级平台 */
* @param parentPlatform public boolean insertMobilePosition(MobilePosition mobilePosition);
*/
boolean addParentPlatform(ParentPlatform parentPlatform); /**
* 更新上级平台
/** * @param parentPlatform
* 删除上级平台 */
* @param parentPlatform boolean updateParentPlatform(ParentPlatform parentPlatform);
*/
boolean deleteParentPlatform(ParentPlatform parentPlatform);
/**
* 添加上级平台
/** * @param parentPlatform
* 分页获取上级平台 */
* @param page boolean addParentPlatform(ParentPlatform parentPlatform);
* @param count
* @return /**
*/ * 删除上级平台
PageInfo<ParentPlatform> queryParentPlatformList(int page, int count); * @param parentPlatform
*/
/** boolean deleteParentPlatform(ParentPlatform parentPlatform);
* 获取所有已启用的平台
* @return
*/ /**
List<ParentPlatform> queryEnableParentPlatformList(boolean enable); * 分页获取上级平台
* @param page
/** * @param count
* 获取上级平台 * @return
* @param platformGbId */
* @return PageInfo<ParentPlatform> queryParentPlatformList(int page, int count);
*/
ParentPlatform queryParentPlatById(String platformGbId); /**
* 获取所有已启用的平台
/** * @return
* 所有平台离线 */
*/ List<ParentPlatform> queryEnableParentPlatformList(boolean enable);
void outlineForAllParentPlatform();
/**
/** * 获取上级平台
* 查询通道信息不区分设备(已关联平台或全部) * @param platformGbId
*/ * @return
PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, Boolean inPlatform); */
ParentPlatform queryParentPlatById(String platformGbId);
/**
* 查询设备的通道信息 /**
*/ * 所有平台离线
List<ChannelReduce> queryChannelListInParentPlatform(String platformId); */
void outlineForAllParentPlatform();
/** /**
* 更新上级平台的通道信息 * 查询通道信息不区分设备(已关联平台或全部)
* @param platformId */
* @param channelReduces PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, Boolean inPlatform);
* @return
*/ /**
int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces); * 查询设备的通道信息
*/
/** List<ChannelReduce> queryChannelListInParentPlatform(String platformId);
* 移除上级平台的通道信息
* @param platformId
* @param channelReduces /**
* @return * 更新上级平台的通道信息
*/ * @param platformId
int delChannelForGB(String platformId, List<ChannelReduce> channelReduces); * @param channelReduces
* @return
*/
DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces);
Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); /**
* 移除上级平台的通道信息
* @param platformId
/** * @param channelReduces
* 添加Mobile Position设备移动位置 * @return
* @param MobilePosition */
* @return int delChannelForGB(String platformId, List<ChannelReduce> channelReduces);
*/
public boolean insertMobilePosition(MobilePosition mobilePosition);
DeviceChannel queryChannelInParentPlatform(String platformId, String channelId);
/**
* 查询移动位置轨迹 Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId);
* @param deviceId
* @param startTime
* @param endTime /**
*/ * 添加Mobile Position设备移动位置
public List<MobilePosition> queryMobilePositions(String deviceId, String startTime, String endTime); * @param MobilePosition
* @return
/** */
* 查询最新移动位置 public boolean insertMobilePosition(MobilePosition mobilePosition);
* @param deviceId
*/ /**
public MobilePosition queryLatestPosition(String deviceId); * 查询移动位置轨迹
*
/** * @param deviceId
* 删除指定设备的所有移动位置 * @param startTime
* @param deviceId * @param endTime
*/ */
public int clearMobilePositionsByDeviceId(String deviceId); 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=\"PTZType != null\">, PTZType=${PTZType}</if>" +
"<if test=\"status != null\">, status='${status}'</if>" + "<if test=\"status != null\">, status='${status}'</if>" +
"<if test=\"streamId != null\">, streamId='${streamId}'</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}'"+ "WHERE deviceId='${deviceId}' AND channelId='${channelId}'"+
" </script>"}) " </script>"})
int update(DeviceChannel channel); 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.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.*; import java.util.*;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Component @Component
public class RedisCatchStorageImpl implements IRedisCatchStorage { public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Autowired @Autowired
private RedisUtil redis; private RedisUtil redis;
@Autowired @Autowired
private DeviceChannelMapper deviceChannelMapper; private DeviceChannelMapper deviceChannelMapper;
/** /**
* 开始播放时将流存入redis * 更新流媒体信息
* *
* @param mediaServerConfig
* @return * @return
*/ */
@Override @Override
public boolean startPlay(StreamInfo stream) { public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) {
return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()), return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX, mediaServerConfig);
stream);
} }
/** /**
* 停止播放时从redis删除 * 获取流媒体信息
* *
* @return * @return
*/ */
@Override @Override
public boolean stopPlay(StreamInfo streamInfo) { public MediaServerConfig getMediaInfo() {
if (streamInfo == null) return false; return (MediaServerConfig) redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX);
return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
streamInfo.getStreamId(),
streamInfo.getDeviceID(),
streamInfo.getChannelId()));
} }
/** /**
* 查询播放列表 * 开始播放时将流存入redis
*
* @return * @return
*/ */
@Override @Override
public StreamInfo queryPlay(StreamInfo streamInfo) { public boolean startPlay(StreamInfo stream) {
return (StreamInfo)redis.get(String.format("%S_%s_%s_%s", String key = getKey(VideoManagerConstants.PLAYER_PREFIX,
VideoManagerConstants.PLAYER_PREFIX, stream.getStreamId(),
streamInfo.getStreamId(), stream.getChannelId(),
streamInfo.getDeviceID(), stream.getDeviceID()
streamInfo.getChannelId())); );
return redis.set(key, stream);
} }
/**
* 停止播放时从redis删除
*
* @return
*/
@Override @Override
public StreamInfo queryPlayByStreamId(String steamId) { public boolean stopPlay(StreamInfo streamInfo) {
List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, steamId)); if (streamInfo == null) {
if (playLeys == null || playLeys.size() == 0) return null; return false;
return (StreamInfo)redis.get(playLeys.get(0).toString()); }
String key = getKey(VideoManagerConstants.PLAYER_PREFIX,
streamInfo.getStreamId(),
streamInfo.getChannelId(),
streamInfo.getDeviceID()
);
return redis.del(key);
} }
@Override @Override
public StreamInfo queryPlaybackByStreamId(String steamId) { public StreamInfo queryPlayByStreamId(String channelId, String steamId) {
List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, steamId)); String key = getKey(VideoManagerConstants.PLAYER_PREFIX,
if (playLeys == null || playLeys.size() == 0) return null; steamId,
return (StreamInfo)redis.get(playLeys.get(0).toString()); channelId,
null
);
return scanOne(key);
} }
@Override @Override
public StreamInfo queryPlayByDevice(String deviceId, String code) { public StreamInfo queryPlayByChannel(String channelId) {
// List<Object> playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, String key = getKey(VideoManagerConstants.PLAYER_PREFIX,
List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, null,
deviceId, channelId,
code)); null
if (playLeys == null || playLeys.size() == 0) return null; );
return (StreamInfo)redis.get(playLeys.get(0).toString()); return scanOne(key);
} }
/** /**
* 更新流媒体信息 * zlm流媒体服务器播流成功后回调播放状态缓存到redis
* @param mediaServerConfig *
* @param stream
* @return * @return
*/ */
@Override @Override
public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { public boolean startPlayback(StreamInfo stream) {
return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX,mediaServerConfig); String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX,
stream.getStreamId(),
stream.getChannelId(),
stream.getDeviceID()
);
return redis.set(key, stream);
} }
/** /**
* 获取流媒体信息 * zlm流媒体服务器成功停止拉流后回调从redis缓存移除播放状态
*
* @param streamInfo
* @return * @return
*/ */
@Override @Override
public MediaServerConfig getMediaInfo() { public boolean stopPlayback(StreamInfo streamInfo) {
return (MediaServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); 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 @Override
public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) { public StreamInfo queryPlaybackByStreamId(String channelId, String steamId) {
Map<String, StreamInfo> streamInfos = new HashMap<>(); String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX,
// List<Object> playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); steamId,
List<Object> players = redis.scan(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); channelId,
if (players.size() == 0) return streamInfos; null
for (int i = 0; i < players.size(); i++) { );
String key = (String) players.get(i); return scanOne(key);
StreamInfo streamInfo = (StreamInfo)redis.get(key);
streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo);
}
return streamInfos;
} }
@Override @Override
public boolean startPlayback(StreamInfo stream) { public StreamInfo queryPlaybackByChannel(String channelId) {
return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()),
stream); String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX,
null,
channelId,
null
);
return scanOne(key);
} }
@Override @Override
public boolean stopPlayback(StreamInfo streamInfo) { public List<StreamInfo> queryPlayBackByDeviceId(String deviceId) {
if (streamInfo == null) return false; String key = getKey(VideoManagerConstants.PLAY_BLACK_PREFIX,
DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(streamInfo.getDeviceID(), streamInfo.getChannelId()); null,
if (deviceChannel != null) { null,
deviceChannel.setStreamId(null); deviceId
deviceChannel.setDeviceId(streamInfo.getDeviceID()); );
deviceChannelMapper.update(deviceChannel); 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, List<StreamInfo> streamInfos = new ArrayList<>(players.size());
streamInfo.getStreamId(), for (int i = 0; i < players.size(); i++) {
streamInfo.getDeviceID(), String redisKey = (String) players.get(i);
streamInfo.getChannelId())); StreamInfo streamInfo = (StreamInfo) redis.get(redisKey);
streamInfos.add(streamInfo);
}
return streamInfos;
} }
@Override private StreamInfo scanOne(String key) {
public StreamInfo queryPlaybackByDevice(String deviceId, String code) { List<Object> playLeys = redis.scan(key);
// 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));
if (playLeys == null || playLeys.size() == 0) { if (playLeys == null || playLeys.size() == 0) {
playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, return null;
deviceId));
} }
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 @Override
@ -285,4 +319,21 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
Set<Object> realVideos = redis.ZRange(key, start, end); Set<Object> realVideos = redis.ZRange(key, start, end);
return new ArrayList(realVideos); 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; 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.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device; 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.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; 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.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 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.play.bean.PlayResult;
import com.genersoft.iot.vmp.vmanager.service.IPlayService; import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -14,6 +20,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -35,170 +42,195 @@ import javax.sip.message.Response;
@RequestMapping("/api") @RequestMapping("/api")
public class PlayController { public class PlayController {
private final static Logger logger = LoggerFactory.getLogger(PlayController.class); private final static Logger logger = LoggerFactory.getLogger(PlayController.class);
@Autowired @Autowired
private SIPCommander cmder; private SIPCommander cmder;
@Autowired @Autowired
private IVideoManagerStorager storager; private IVideoManagerStorager storager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired @Autowired
private DeferredResultHolder resultHolder; private DeferredResultHolder resultHolder;
@Autowired @Autowired
private IPlayService playService; private IPlayService playService;
@GetMapping("/play/{deviceId}/{channelId}") @Autowired
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, private VideoStreamSessionManager streamSession;
@PathVariable String channelId) {
@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(()->{ StreamInfo oldStreamInfo = streamSession.getPlayStreamInfo(channelId);
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); if (oldStreamInfo == null) {
// 释放rtpserver // 发送点播消息
cmder.closeRTPServer(playResult.getDevice(), channelId); playStreamCmd(device, channelId, msg);
RequestMessage msg = new RequestMessage(); return result;
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid()); }
msg.setData("Timeout");
resultHolder.invokeResult(msg);
});
return playResult.getResult();
}
@PostMapping("/play/{streamId}/stop") // 若已有人点播,直接播放
public DeferredResult<ResponseEntity<String>> playStop(@PathVariable String streamId) { String streamId = oldStreamInfo.getStreamId();
String mediaServerIp = oldStreamInfo.getMediaServerIp();
logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId)); JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerIp, streamId);
if (rtpInfo.getBoolean("exist")) {
UUID uuid = UUID.randomUUID(); msg.setData(JSON.toJSONString(oldStreamInfo));
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); resultHolder.invokeResult(msg);
return result;
// 录像查询以channelId作为deviceId查询 }
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result);
cmder.streamByeCmd(streamId, event -> {
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
if (streamInfo == null) {
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
msg.setData("streamId not found");
resultHolder.invokeResult(msg);
}else {
redisCatchStorage.stopPlay(streamInfo);
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid);
//Response response = event.getResponse();
msg.setData(String.format("success"));
resultHolder.invokeResult(msg);
}
});
if (streamId != null) { // 若已有人点播,但已超时自动断开,则重新发起点播
JSONObject json = new JSONObject(); storager.stopPlay(oldStreamInfo.getDeviceID(), oldStreamInfo.getChannelId());
json.put("streamId", streamId); streamSession.remove(oldStreamInfo);
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);
}
// 超时处理 playStreamCmd(device, channelId, msg);
result.onTimeout(()->{ return result;
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;
}
/** private void playStreamCmd(Device device, String channelId, RequestMessage msg) {
* 将不是h264的视频通过ffmpeg 转码为h264 + aac cmder.playStreamCmd(device, channelId, (JSONObject response) -> {
* @param streamId 流ID logger.info("收到点播回调消息: " + response.toJSONString());
* @return playService.onPublishHandlerForPlay(response, device.getDeviceId(), channelId, msg);
*/ }, event -> {
@PostMapping("/play/{streamId}/convert") StreamInfo streamInfo = streamSession.getPlayStreamInfo(channelId);
public ResponseEntity<String> playConvert(@PathVariable String streamId) { streamSession.remove(streamInfo);
StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); Response response = event.getResponse();
if (streamInfo == null) { int statusCode = response.getStatusCode();
logger.warn("视频转码API调用失败!, 视频流已经停止!"); String errMsg;
return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); if (503 == statusCode) {
} errMsg = "点播失败,请检查在NVR上是否可以正常打开监控,并检查NVR和SIP是否连通, 错误码: %s, %s";
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); } else {
if (!rtpInfo.getBoolean("exist")) { errMsg = "点播失败,错误码: %s, %s";
logger.warn("视频转码API调用失败!, 视频流已停止推流!"); }
return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); msg.setData(String.format(errMsg, statusCode, response.getReasonPhrase()));
} else { resultHolder.invokeResult(msg);
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);
}
}
/** @PostMapping("/play/{channelId}/{streamId}/stop")
* 结束转码 public DeferredResult<ResponseEntity<String>> playStop(@PathVariable String channelId, @PathVariable String streamId) {
* @param key
* @return logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId));
*/
@PostMapping("/play/convert/stop/{key}") RequestMessage msg = playService.createCallbackPlayMsg();
public ResponseEntity<String> playConvertStop(@PathVariable String key) { DeferredResult<ResponseEntity<String>> result = new DeferredResult<>();
// 超时处理
JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(key); result.onTimeout(() -> {
System.out.println(jsonObject); logger.warn(String.format("设备预览/回放停止超时,streamId:%s ", streamId));
JSONObject result = new JSONObject(); msg.setData("Timeout");
if (jsonObject != null && jsonObject.getInteger("code") == 0) { resultHolder.invokeResult(msg);
result.put("code", 0); });
JSONObject data = jsonObject.getJSONObject("data");
if (data != null && data.getBoolean("flag")) { // 录像查询以channelId作为deviceId查询
result.put("code", "0"); resultHolder.put(msg.getId(), result);
result.put("msg", "success");
}else { 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 { }else {
@ -210,7 +242,7 @@ public class PlayController {
/** /**
* 语音广播命令API接口 * 语音广播命令API接口
* *
* @param deviceId * @param deviceId
*/ */
@GetMapping("/broadcast/{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; package com.genersoft.iot.vmp.vmanager.playback;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.service.IPlayService; import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.util.UUID;
@CrossOrigin @CrossOrigin
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
public class PlaybackController { public class PlaybackController {
private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class);
@Autowired @Autowired
private SIPCommander cmder; private SIPCommander cmder;
@Autowired @Autowired
private IVideoManagerStorager storager; private IVideoManagerStorager storager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private VideoStreamSessionManager streamSession;
// @Autowired @Autowired
// private ZLMRESTfulUtils zlmresTfulUtils; private IPlayService playService;
@Autowired @Autowired
private IPlayService playService; private DeferredResultHolder resultHolder;
@Autowired @GetMapping("/playback/{deviceId}/{channelId}")
private DeferredResultHolder resultHolder; public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime,
String endTime) {
@GetMapping("/playback/{deviceId}/{channelId}")
public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId, String startTime, if (logger.isDebugEnabled()) {
String endTime) { logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
}
if (logger.isDebugEnabled()) { RequestMessage msg = playService.createCallbackPlayMsg();
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
} // 超时处理
UUID uuid = UUID.randomUUID(); result.onTimeout(() -> {
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
// 超时处理 StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId);
result.onTimeout(()->{ streamSession.remove(streamInfo);
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); msg.setData("Timeout");
RequestMessage msg = new RequestMessage(); resultHolder.invokeResult(msg);
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); });
msg.setData("Timeout"); Device device = storager.queryVideoDevice(deviceId);
resultHolder.invokeResult(msg); StreamInfo oldStreamInfo = streamSession.getPlayBackStreamInfo(channelId);
}); if (oldStreamInfo != null) {
Device device = storager.queryVideoDevice(deviceId); // TODO 只能停止自己之前的回放,不能停止别人的回放,否则会导致别人无法播放
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); cmder.stopStreamByeCmd(oldStreamInfo, null);
if (streamInfo != null) { }
// 停止之前的回放 resultHolder.put(msg.getId(), result);
cmder.streamByeCmd(streamInfo.getStreamId()); cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> {
} logger.info("收到订阅消息: " + response.toJSONString());
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); playService.onPublishHandlerForPlayBack(response, deviceId, channelId, msg);
cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { }, event -> {
logger.info("收到订阅消息: " + response.toJSONString()); StreamInfo streamInfo = streamSession.getPlayBackStreamInfo(channelId);
playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); streamSession.remove(streamInfo);
}, event -> { Response response = event.getResponse();
Response response = event.getResponse(); msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
RequestMessage msg = new RequestMessage(); resultHolder.invokeResult(msg);
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); });
msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
resultHolder.invokeResult(msg); return result;
}); }
return result; @RequestMapping("/playback/{channelId}/{ssrc}/stop")
} public ResponseEntity<String> playStop(@PathVariable String channelId, @PathVariable String ssrc) {
if (logger.isDebugEnabled()) {
@RequestMapping("/playback/{ssrc}/stop") logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc));
public ResponseEntity<String> playStop(@PathVariable String ssrc) { }
if (ssrc == null) {
cmder.streamByeCmd(ssrc); logger.warn("设备录像回放停止API调用失败!");
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
if (logger.isDebugEnabled()) { }
logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); StreamInfo streamInfo = streamSession.getStreamInfo(channelId, ssrc);
} if (streamInfo == null) {
logger.warn("回放播流不存在!");
if (ssrc != null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND);
JSONObject json = new JSONObject(); }
json.put("ssrc", ssrc); cmder.stopStreamByeCmd(streamInfo, null);
return new ResponseEntity<String>(json.toString(), HttpStatus.OK); JSONObject json = new JSONObject();
} else { json.put("ssrc", ssrc);
logger.warn("设备录像回放停止API调用失败!"); return new ResponseEntity<>(json.toString(), HttpStatus.OK);
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); }
}
}
} }

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

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.vmanager.service; package com.genersoft.iot.vmp.vmanager.service;
import com.alibaba.fastjson.JSONObject; 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.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult; 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 { public interface IPlayService {
RequestMessage createCallbackPlayMsg();
void onPublishHandlerForPlayBack(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, String uuid);
void onPublishHandlerForPlay(JSONObject resonse, String deviceId, String channelId, RequestMessage msg);
PlayResult play(String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); 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.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; 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.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
@ -26,6 +27,8 @@ import org.springframework.web.context.request.async.DeferredResult;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.util.UUID; import java.util.UUID;
import java.util.UUID;
@Service @Service
public class PlayServiceImpl implements IPlayService { public class PlayServiceImpl implements IPlayService {
@ -43,6 +46,9 @@ public class PlayServiceImpl implements IPlayService {
@Autowired @Autowired
private DeferredResultHolder resultHolder; private DeferredResultHolder resultHolder;
@Autowired
private VideoStreamSessionManager streamSession;
@Autowired @Autowired
private ZLMRESTfulUtils zlmresTfulUtils; private ZLMRESTfulUtils zlmresTfulUtils;
@ -109,10 +115,17 @@ public class PlayServiceImpl implements IPlayService {
} }
@Override @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(); RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); msg.setId(msgId);
StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); 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) { if (streamInfo != null) {
DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
if (deviceChannel != null) { if (deviceChannel != null) {
@ -120,56 +133,29 @@ public class PlayServiceImpl implements IPlayService {
storager.startPlay(deviceId, channelId, streamInfo.getStreamId()); storager.startPlay(deviceId, channelId, streamInfo.getStreamId());
} }
redisCatchStorage.startPlay(streamInfo); // redisCatchStorage.startPlay(streamInfo);
msg.setData(JSON.toJSONString(streamInfo)); msg.setData(JSON.toJSONString(streamInfo));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
} else { } else {
logger.warn("设备预览API调用失败!"); logger.warn("设备点播API调用失败!");
msg.setData("设备预览API调用失败!"); msg.setData("设备点播API调用失败!");
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
} }
} }
@Override @Override
public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, String uuid) { public void onPublishHandlerForPlayBack(JSONObject resonse, String deviceId, String channelId, RequestMessage msg) {
RequestMessage msg = new RequestMessage(); String streamId = resonse.getString("id");
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); StreamInfo streamInfo = streamSession.getStreamInfo(channelId, streamId);
StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid);
if (streamInfo != null) { if (streamInfo != null) {
redisCatchStorage.startPlayback(streamInfo); // redisCatchStorage.startPlayback(streamInfo);
msg.setData(JSON.toJSONString(streamInfo)); msg.setData(JSON.toJSONString(streamInfo));
resultHolder.invokeResult(msg); resultHolder.invokeResult(msg);
} else { } else {
logger.warn("设备预览API调用失败!"); logger.warn("设备回放API调用失败!");
msg.setData("设备预览API调用失败!"); msg.setData("设备回放API调用失败!");
resultHolder.invokeResult(msg); 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.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; 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.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.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.play.PlayController; import com.genersoft.iot.vmp.vmanager.play.PlayController;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -20,7 +19,6 @@ import org.springframework.web.context.request.async.DeferredResult;
/** /**
* 兼容LiveGBS的API实时直播 * 兼容LiveGBS的API实时直播
*/ */
@SuppressWarnings(value = {"rawtypes", "unchecked"})
@CrossOrigin @CrossOrigin
@RestController @RestController
@RequestMapping(value = "/api/v1/stream") @RequestMapping(value = "/api/v1/stream")
@ -30,80 +28,73 @@ public class ApiStreamController {
@Autowired @Autowired
private SIPCommander cmder; private SIPCommander cmder;
@Autowired @Autowired
private IVideoManagerStorager storager; private IVideoManagerStorager storager;
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private VideoStreamSessionManager streamSession;
// @Autowired
// private ZLMRESTfulUtils zlmresTfulUtils;
@Autowired @Autowired
private PlayController playController; private PlayController playController;
/** /**
* 实时直播 - 开始直播 * 实时直播 - 开始直播
* @param serial 设备编号 *
* @param channel 通道序号 默认值: 1 * @param serial 设备编号
* @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 * @param channel 通道序号 默认值: 1
* @param cdn TODO 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可
* @param audio TODO 是否开启音频, 默认 开启 * @param cdn TODO 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent
* @param transport 流传输模式 默认 UDP * @param audio TODO 是否开启音频, 默认 开启
* @param transport 流传输模式 默认 UDP
* @param checkchannelstatus TODO 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线 * @param checkchannelstatus TODO 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线
* @param transportmode TODO transport=TCP 时有效, 指示流传输主被动模式, 默认被动 * @param transportmode TODO transport=TCP 时有效, 指示流传输主被动模式, 默认被动
* @param timeout TODO 拉流超时(), * @param timeout TODO 拉流超时(),
* @return * @return
*/ */
@RequestMapping(value = "/start") @RequestMapping(value = "/start")
private DeferredResult<JSONObject> start(String serial , private DeferredResult<JSONObject> start(String serial,
@RequestParam(required = false)Integer channel , @RequestParam(required = false) Integer channel,
@RequestParam(required = false)String code, @RequestParam(required = false) String code,
@RequestParam(required = false)String cdn, @RequestParam(required = false) String cdn,
@RequestParam(required = false)String audio, @RequestParam(required = false) String audio,
@RequestParam(required = false)String transport, @RequestParam(required = false) String transport,
@RequestParam(required = false)String checkchannelstatus , @RequestParam(required = false) String checkchannelstatus,
@RequestParam(required = false)String transportmode, @RequestParam(required = false) String transportmode,
@RequestParam(required = false)String timeout @RequestParam(required = false) String timeout
){ ) {
DeferredResult<JSONObject> resultDeferredResult = new DeferredResult<JSONObject>(); DeferredResult<JSONObject> resultDeferredResult = new DeferredResult<JSONObject>();
Device device = storager.queryVideoDevice(serial); Device device = storager.queryVideoDevice(serial);
if (device == null ) { if (device == null) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("error","device[ " + serial + " ]未找到"); result.put("error", "device[ " + serial + " ]未找到");
resultDeferredResult.setResult(result); resultDeferredResult.setResult(result);
}else if (device.getOnline() == 0) { } else if (device.getOnline() == 0) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("error","device[ " + code + " ]offline"); result.put("error", "device[ " + code + " ]offline");
resultDeferredResult.setResult(result); resultDeferredResult.setResult(result);
} }
resultDeferredResult.onTimeout(()->{ resultDeferredResult.onTimeout(() -> {
logger.info("播放等待超时"); logger.info("播放等待超时");
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("error","timeout"); result.put("error", "timeout");
resultDeferredResult.setResult(result); resultDeferredResult.setResult(result);
// 清理RTP server // 清理RTP server
}); });
DeviceChannel deviceChannel = storager.queryChannel(serial, code); DeviceChannel deviceChannel = storager.queryChannel(serial, code);
if (deviceChannel == null) { if (deviceChannel == null) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("error","channel[ " + code + " ]未找到"); result.put("error", "channel[ " + code + " ]未找到");
resultDeferredResult.setResult(result); resultDeferredResult.setResult(result);
}else if (deviceChannel.getStatus() == 0) { } else if (deviceChannel.getStatus() == 0) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("error","channel[ " + code + " ]offline"); result.put("error", "channel[ " + code + " ]offline");
resultDeferredResult.setResult(result); resultDeferredResult.setResult(result);
} }
DeferredResult<ResponseEntity<String>> play = playController.play(serial, code); DeferredResult<ResponseEntity<String>> play = playController.play(serial, code);
play.setResultHandler((Object o)->{ play.setResultHandler((Object o) -> {
ResponseEntity<String> responseEntity = (ResponseEntity)o; ResponseEntity<String> responseEntity = (ResponseEntity) o;
StreamInfo streamInfo = JSON.parseObject(responseEntity.getBody(), StreamInfo.class); StreamInfo streamInfo = JSON.parseObject(responseEntity.getBody(), StreamInfo.class);
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put("StreamID", streamInfo.getStreamId()); result.put("StreamID", streamInfo.getStreamId());
@ -143,49 +134,50 @@ public class ApiStreamController {
/** /**
* 实时直播 - 直播流停止 * 实时直播 - 直播流停止
* @param serial 设备编号 *
* @param channel 通道序号 * @param serial 设备编号
* @param code 通道国标编号 * @param channel 通道序号
* @param code 通道国标编号
* @param check_outputs * @param check_outputs
* @return * @return
*/ */
@RequestMapping(value = "/stop") @RequestMapping(value = "/stop")
@ResponseBody @ResponseBody
private JSONObject stop(String serial , private JSONObject stop(String serial,
@RequestParam(required = false)Integer channel , @RequestParam(required = false) Integer channel,
@RequestParam(required = false)String code, @RequestParam(required = false) String code,
@RequestParam(required = false)String check_outputs @RequestParam(required = false) String check_outputs
){ ) {
JSONObject result = new JSONObject();
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code); StreamInfo streamInfo = streamSession.getPlayStreamInfo(code);
if (streamInfo == null) { if (streamInfo == null) {
JSONObject result = new JSONObject(); result.put("error", "未找到流信息");
result.put("error","未找到流信息");
return result; return result;
} }
cmder.streamByeCmd(streamInfo.getStreamId()); cmder.stopStreamByeCmd(streamInfo, null);
redisCatchStorage.stopPlay(streamInfo); result.put("success", "成功");
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); return result;
return null;
} }
/** /**
* 实时直播 - 直播流保活 * 实时直播 - 直播流保活
* @param serial 设备编号 *
* @param serial 设备编号
* @param channel 通道序号 * @param channel 通道序号
* @param code 通道国标编号 * @param code 通道国标编号
* @return * @return
*/ */
@RequestMapping(value = "/touch") @RequestMapping(value = "/touch")
@ResponseBody @ResponseBody
private JSONObject touch(String serial ,String t, private JSONObject touch(String serial, String t,
@RequestParam(required = false)Integer channel , @RequestParam(required = false) Integer channel,
@RequestParam(required = false)String code, @RequestParam(required = false) String code,
@RequestParam(required = false)String autorestart, @RequestParam(required = false) String autorestart,
@RequestParam(required = false)String audio, @RequestParam(required = false) String audio,
@RequestParam(required = false)String cdn @RequestParam(required = false) String cdn
){ ) {
return null; return null;
} }
} }

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

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

2
web_src/src/components/channelList.vue

@ -216,7 +216,7 @@ export default {
var that = this; var that = this;
this.$axios({ this.$axios({
method: 'post', method: 'post',
url: '/api/play/' + itemData.streamId + '/stop' url: '/api/play/' + itemData.channelId + "/" + itemData.streamId + '/stop'
}).then(function (res) { }).then(function (res) {
console.log(JSON.stringify(res)); console.log(JSON.stringify(res));
that.initData(); that.initData();

10
web_src/src/components/gb28181/devicePlayer.vue

@ -196,7 +196,9 @@ export default {
if (tab.name == "codec") { if (tab.name == "codec") {
this.$axios({ this.$axios({
method: 'get', 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) { }).then(function (res) {
that.tracksLoading = false; that.tracksLoading = false;
if (res.data.code == 0 && res.data.online) { if (res.data.code == 0 && res.data.online) {
@ -251,7 +253,7 @@ export default {
this.$refs.videoPlayer.pause() this.$refs.videoPlayer.pause()
that.$axios({ that.$axios({
method: 'post', method: 'post',
url: '/api/play/' + that.streamId + '/convert' url: '/api/play/' + that.channelId + "/" + that.streamId + '/convert'
}).then(function (res) { }).then(function (res) {
if (res.data.code == 0) { if (res.data.code == 0) {
that.convertKey = res.data.key; that.convertKey = res.data.key;
@ -288,7 +290,7 @@ export default {
that.$refs.videoPlayer.pause() that.$refs.videoPlayer.pause()
this.$axios({ this.$axios({
method: 'post', method: 'post',
url: '/api/play/convert/stop/' + this.convertKey url: '/api/play/convert/stop/' + this.channelId + "/" + + this.streamId + "/" + this.convertKey
}).then(function (res) { }).then(function (res) {
if (res.data.code == 0) { if (res.data.code == 0) {
console.log(res.data.msg) console.log(res.data.msg)
@ -398,7 +400,7 @@ export default {
this.videoUrl = ''; this.videoUrl = '';
this.$axios({ this.$axios({
method: 'get', method: 'get',
url: '/api/playback/' + this.streamId + '/stop' url: '/api/playback/' + this.channelId + '/' + this.streamId + '/stop'
}).then(function (res) { }).then(function (res) {
if (callback) callback() if (callback) callback()
}); });

Loading…
Cancel
Save