瀏覽代碼

初始化项目

DESKTOP-6LTVLN7\Liumouren 3 年之前
父節點
當前提交
0f0f840c88
共有 100 個文件被更改,包括 9654 次插入0 次删除
  1. 127 0
      DOCKERFILE
  2. 21 0
      LICENSE
  3. 49 0
      docker/docker-compose.yml
  4. 2 0
      docker/redis/redis.conf
  5. 81 0
      docker/wvp/Dockerfile
  6. 二進制
      otherUnit/FFmpeg-FFmpeg-n5.1-dev-537-ge85958e.tar.gz
  7. 二進制
      otherUnit/好例子网_GB28181-2016 IPC模拟设备 + 自动化测试工具.rar
  8. 305 0
      pom.xml
  9. 544 0
      sql/mysql.sql
  10. 36 0
      src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
  11. 172 0
      src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java
  12. 267 0
      src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
  13. 22 0
      src/main/java/com/genersoft/iot/vmp/common/SystemInfoDto.java
  14. 136 0
      src/main/java/com/genersoft/iot/vmp/common/VersionPo.java
  15. 79 0
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  16. 117 0
      src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
  17. 46 0
      src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
  18. 216 0
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  19. 16 0
      src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java
  20. 275 0
      src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
  21. 99 0
      src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
  22. 41 0
      src/main/java/com/genersoft/iot/vmp/conf/RedisKeyExpirationEventMessageListener.java
  23. 117 0
      src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
  24. 68 0
      src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
  25. 117 0
      src/main/java/com/genersoft/iot/vmp/conf/Swagger3Config.java
  26. 34 0
      src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java
  27. 59 0
      src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java
  28. 147 0
      src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java
  29. 37 0
      src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java
  30. 37 0
      src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java
  31. 33 0
      src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java
  32. 64 0
      src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java
  33. 24 0
      src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java
  34. 54 0
      src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java
  35. 43 0
      src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
  36. 47 0
      src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
  37. 24 0
      src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java
  38. 65 0
      src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java
  39. 24 0
      src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
  40. 27 0
      src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java
  41. 78 0
      src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
  42. 169 0
      src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
  43. 102 0
      src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java
  44. 110 0
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  45. 297 0
      src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
  46. 32 0
      src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java
  47. 24 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/BaiduPoint.java
  48. 43 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java
  49. 8 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdType.java
  50. 286 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
  51. 137 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java
  52. 444 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
  53. 21 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceNotFoundEvent.java
  54. 112 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java
  55. 35 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Host.java
  56. 179 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java
  57. 293 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
  58. 56 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java
  59. 71 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java
  60. 31 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformGbStream.java
  61. 15 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformRegister.java
  62. 74 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
  63. 133 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
  64. 14 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SDPInfo.java
  65. 203 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java
  66. 77 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
  67. 100 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java
  68. 149 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java
  69. 28 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java
  70. 153 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
  71. 151 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
  72. 31 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java
  73. 58 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java
  74. 83 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java
  75. 66 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java
  76. 40 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java
  77. 77 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java
  78. 42 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java
  79. 105 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java
  80. 28 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEvent.java
  81. 88 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java
  82. 24 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformCycleRegisterEvent.java
  83. 47 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformCycleRegisterEventLister.java
  84. 25 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEvent.java
  85. 105 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
  86. 32 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEvent.java
  87. 49 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
  88. 52 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/SubscribeListenerForPlatform.java
  89. 58 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java
  90. 176 0
      src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
  91. 74 0
      src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
  92. 139 0
      src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java
  93. 128 0
      src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
  94. 69 0
      src/main/java/com/genersoft/iot/vmp/gb28181/task/GPSSubscribeTask.java
  95. 6 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/ISIPProcessorObserver.java
  96. 179 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java
  97. 75 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java
  98. 122 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
  99. 39 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java
  100. 340 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

+ 127 - 0
DOCKERFILE

@@ -0,0 +1,127 @@
+FROM ubuntu:20.04 AS build
+
+ARG DEBIAN_FRONTEND=noninteractive
+ENV TZ=Asia/Shanghai
+
+EXPOSE 18080/tcp
+
+EXPOSE 5060/tcp
+EXPOSE 5060/udp
+
+EXPOSE 6379/tcp
+
+EXPOSE 18081/tcp
+
+EXPOSE 80/tcp
+EXPOSE 1935/tcp
+EXPOSE 554/tcp
+EXPOSE 554/udp
+EXPOSE 30000-30500/tcp
+EXPOSE 30000-30500/udp
+
+ENV LC_ALL zh_CN.UTF-8
+
+# 使用了自己的settings.xml作为maven的源,加快打包速度
+RUN apt-get update && \
+        DEBIAN_FRONTEND="noninteractive" && \
+         apt-get install -y --no-install-recommends openjdk-11-jre git maven nodejs npm build-essential tcl language-pack-zh-hans \
+         cmake curl  vim ca-certificates  tzdata libmysqlclient-dev  redis-server libssl-dev libx264-dev libfaac-dev ffmpeg
+WORKDIR /home
+
+RUN      git clone https://gitee.com/pan648540858/maven.git && \
+         cp maven/settings.xml /usr/share/maven/conf/ && \
+         git clone https://gitee.com/pan648540858/wvp-GB28181.git && \
+         git clone https://gitee.com/pan648540858/wvp-pro-assist.git
+         # 编译前端界面
+WORKDIR /home/wvp-GB28181/web_src
+
+RUN      npm install && \
+         npm run build && \
+         mkdir -p /opt/wvp/config && \
+         mkdir -p /opt/assist/config && \
+         cp /home/wvp-GB28181/src/main/resources/application-dev.yml /opt/wvp/config/application.yml && \
+         cp /home/wvp-pro-assist/src/main/resources/application-dev.yml /opt/assist/config/application.yml
+
+         # wvp打包
+WORKDIR /home/wvp-GB28181
+RUN      mvn compile && \
+         mvn package && \
+         cp /home/wvp-GB28181/target/wvp*.jar /opt/wvp/
+
+         # wvp 录像管理打包
+WORKDIR /home/wvp-pro-assist
+RUN      mvn compile && \
+         mvn package && \
+         cp /home/wvp-pro-assist/target/*.jar /opt/assist/
+
+         # zlm打包
+WORKDIR /home
+RUN     mkdir -p /opt/media && \
+        git clone --depth=1 https://gitee.com/xia-chu/ZLMediaKit && \
+        cd ZLMediaKit && git submodule update --init --recursive && \
+        mkdir -p build release/linux/Release/ &&\
+        cd build && \
+        cmake -DCMAKE_BUILD_TYPE=Release .. && \
+        make -j4 && \
+        rm -rf ../release/linux/Release/config.ini && \
+        cp -r ../release/linux/Release/* /opt/media && \
+        mkdir -p /opt/media/www/record
+
+
+         # 清理
+ RUN     rm -rf /home/wiki && \
+         rm -rf /home/wvp-GB28181 && \
+         apt-get autoremove -y git maven nodejs npm && \
+         apt-get clean -y && \
+         rm -rf /var/lib/apt/lists/*dic
+
+WORKDIR /opt/wvp
+RUN     echo '#!/bin/bash' > run.sh && \
+        echo 'echo ${WVP_IP}' >> run.sh && \
+        echo 'echo ${WVP_CONFIG}' >> run.sh && \
+        echo 'redis-server --daemonize yes --bind 0.0.0.0' >> run.sh && \
+        echo 'cd /opt/assist' >> run.sh && \
+        echo 'nohup java -jar *.jar --userSettings.record=/opt/media/www/record/ &' >> run.sh && \
+        echo 'nohup /opt/media/MediaServer -d -m 3 &' >> run.sh && \
+        echo 'cd /opt/wvp' >> run.sh && \
+        echo 'if [${WVP_CONFIG}]; then' >> run.sh && \
+        echo '        java -jar *.jar --spring.confi    g.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \
+        echo 'else' >> run.sh && \
+        echo '        java -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 --media.ip=127.0.0.1 --media.sdp-ip=${WVP_IP} --sip.ip=${WVP_IP} --media.stream-ip=${WVP_IP}' >> run.sh  && \
+        echo 'fi' >> run.sh
+RUN chmod +x run.sh
+
+FROM ubuntu:20.04
+
+ARG DEBIAN_FRONTEND=noninteractive
+ENV TZ=Asia/Shanghai
+
+EXPOSE 18080/tcp
+
+EXPOSE 5060/tcp
+EXPOSE 5060/udp
+
+EXPOSE 6379/tcp
+
+EXPOSE 18081/tcp
+
+EXPOSE 80/tcp
+EXPOSE 1935/tcp
+EXPOSE 554/tcp
+EXPOSE 554/udp
+EXPOSE 30000-30500/tcp
+EXPOSE 30000-30500/udp
+
+ENV LC_ALL zh_CN.UTF-8
+
+RUN apt-get update && \
+        DEBIAN_FRONTEND="noninteractive" && \
+        apt-get install -y --no-install-recommends openjdk-11-jre tcl language-pack-zh-hans \
+        ca-certificates  tzdata libmysqlclient21  redis-server libssl1.1 libx264-155 libfaac0 ffmpeg && \
+        apt-get autoremove -y && \
+        apt-get clean -y && \
+        rm -rf /var/lib/apt/lists/*dic
+
+WORKDIR /opt/wvp
+COPY --from=build /opt /opt
+CMD ["sh", "run.sh"]

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 swwhaha
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 49 - 0
docker/docker-compose.yml

@@ -0,0 +1,49 @@
+version: '3'
+services:
+  redis:
+    image: redis
+    restart: always
+    volumes:
+      - ./redis/redis.conf:/etc/redis/redis_default.conf
+      - ./redis/data/:/data
+    environment:
+      TZ: "Asia/Shanghai"
+    command: redis-server /etc/redis/redis_default.conf --appendonly yes
+  wvp:
+    build:
+      context: ./wvp
+      args:
+        gitUrl: "https://gitee.com/pan648540858"
+        zlmGitUrl: "https://gitee.com/xia-chu/ZLMediaKit"
+    restart: always
+    ports:
+      - "5060:5060"
+      - "5060:5060/udp"
+      - "18080:18080"
+      - "80:80"
+      - "10000:10000/tcp"
+      - "10000:10000/udp"
+      - "30000-30500:30000-30500/tcp"
+      - "30000-30500:30000-30500/udp"
+    volumes:
+      - ./video:/opt/media/www/record/
+      - ./logs/wvp:/opt/wvp/logs/
+      - ./logs/assist:/opt/assist/logs/
+      - ./logs/media:/opt/media/log/
+    environment:
+      TZ: "Asia/Shanghai"
+      # [必须修改] 本机的IP
+      WVP_HOST: 172.18.0.61
+      WVP_PWD: aseqw_+hiy123
+      WVP_DOMAIN: 6101130049
+      WVP_ID: 61011300490000000001
+      REDIS_HOST: redis
+      REDIS_PORT: 6379
+      REDIS_DB: 6
+      REDIS_PWD: root
+      ASSIST_JVM_CONFIG: -Xms128m -Xmx256m
+      WVP_JVM_CONFIG: -Xms128m -Xmx256m
+      ASSIST_CONFIG:
+      WVP_CONFIG:
+    depends_on:
+      - redis

+ 2 - 0
docker/redis/redis.conf

@@ -0,0 +1,2 @@
+requirepass root
+bind 0.0.0.0

+ 81 - 0
docker/wvp/Dockerfile

@@ -0,0 +1,81 @@
+FROM ubuntu:20.04   as   build
+
+ARG gitUrl="https://gitee.com/pan648540858"
+ARG zlmGitUrl="https://gitee.com/xia-chu/ZLMediaKit"
+
+RUN export DEBIAN_FRONTEND=noninteractive &&\
+        apt-get update && \
+        apt-get install -y --no-install-recommends openjdk-11-jre git maven nodejs npm build-essential \
+        cmake ca-certificates openssl ffmpeg &&\
+        mkdir -p /opt/wvp/config /opt/wvp/heapdump /opt/wvp/config /opt/assist/config /opt/assist/heapdump /opt/media/www/record
+
+RUN cd /home && \
+        git clone "${gitUrl}/maven.git" && \
+        cp maven/settings.xml /usr/share/maven/conf/
+
+RUN cd /home && \
+        git clone "${gitUrl}/wvp-GB28181-pro.git"
+RUN cd /home/wvp-GB28181-pro/web_src && \
+        npm install && \
+        npm run build
+RUN cd /home/wvp-GB28181-pro && \
+        mvn clean package -Dmaven.test.skip=true && \
+        cp /home/wvp-GB28181-pro/target/*.jar /opt/wvp/ && \
+        cp /home/wvp-GB28181-pro/src/main/resources/application-docker.yml /opt/wvp/config/application.yml
+
+RUN cd /home && \
+		git clone "${gitUrl}/wvp-pro-assist.git"
+RUN cd /home/wvp-pro-assist && \
+	git reset --hard 58f1a79136a55a7cd1593c95b56ddadcc2225b61 && \
+        mvn clean package -Dmaven.test.skip=true && \
+        cp /home/wvp-pro-assist/target/*.jar /opt/assist/ && \
+        cp /home/wvp-pro-assist/src/main/resources/application-dev.yml /opt/assist/config/application.yml
+
+RUN cd /home && \
+        git clone --depth=1 "${zlmGitUrl}"
+RUN cd /home/ZLMediaKit && \
+        git submodule update --init --recursive && \
+        mkdir -p build release/linux/Release/ &&\
+        cd build && \
+        cmake -DCMAKE_BUILD_TYPE=Release .. && \
+        make -j4 && \
+        rm -rf ../release/linux/Release/config.ini && \
+        cp -r ../release/linux/Release/* /opt/media
+
+RUN cd /opt/wvp && \
+        echo '#!/bin/bash' > run.sh && \
+        echo 'echo ${WVP_IP}' >> run.sh && \
+        echo 'echo ${WVP_CONFIG}' >> run.sh && \
+        echo 'cd /opt/assist' >> run.sh && \
+        echo 'nohup java ${ASSIST_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/assist/heapdump/ -jar *.jar --spring.config.location=/opt/assist/config/application.yml --userSettings.record=/opt/media/www/record/  --media.record-assist-port=18081 ${ASSIST_CONFIG} &' >> run.sh && \
+        echo 'nohup /opt/media/MediaServer -d -m 3 &' >> run.sh && \
+        echo 'cd /opt/wvp' >> run.sh && \
+        echo 'java ${WVP_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/wvp/heapdump/ -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \
+        chmod +x run.sh
+
+FROM ubuntu:20.04
+
+EXPOSE 18080/tcp
+EXPOSE 5060/tcp
+EXPOSE 5060/udp
+EXPOSE 6379/tcp
+EXPOSE 18081/tcp
+EXPOSE 80/tcp
+EXPOSE 1935/tcp
+EXPOSE 554/tcp
+EXPOSE 554/udp
+EXPOSE 30000-30500/tcp
+EXPOSE 30000-30500/udp
+
+ENV LC_ALL zh_CN.UTF-8
+
+RUN export DEBIAN_FRONTEND=noninteractive &&\
+        apt-get update && \
+        apt-get install -y --no-install-recommends openjdk-11-jre ca-certificates ffmpeg language-pack-zh-hans && \
+        apt-get autoremove -y && \
+        apt-get clean -y && \
+        rm -rf /var/lib/apt/lists/*dic
+
+COPY --from=build /opt /opt
+WORKDIR /opt/wvp
+CMD ["sh", "run.sh"]

二進制
otherUnit/FFmpeg-FFmpeg-n5.1-dev-537-ge85958e.tar.gz


二進制
otherUnit/好例子网_GB28181-2016 IPC模拟设备 + 自动化测试工具.rar


+ 305 - 0
pom.xml

@@ -0,0 +1,305 @@
+<?xml version="1.0"?>
+<project 
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.3.5.RELEASE</version>
+	</parent>
+
+	<groupId>com.genersoft</groupId>
+	<artifactId>wvp-pro</artifactId>
+	<version>2.0.2</version>
+	<name>web video platform</name>
+	<description>国标28181视频平台</description>
+
+	<repositories>
+		<repository>
+			<id>nexus-aliyun</id>
+			<name>Nexus aliyun</name>
+			<url>https://maven.aliyun.com/repository/public</url>
+			<layout>default</layout>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+		</repository>
+	</repositories>
+	<pluginRepositories>
+		<pluginRepository>
+			<id>nexus-aliyun</id>
+			<name>Nexus aliyun</name>
+			<url>https://maven.aliyun.com/repository/public</url>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+		</pluginRepository>
+	</pluginRepositories>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<maven.build.timestamp.format>MMddHHmm</maven.build.timestamp.format>
+		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
+		<jedis-version>3.1.0</jedis-version>
+
+		<!-- 依赖版本 -->
+		<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
+		<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
+		<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
+		<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
+		<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-configuration-processor</artifactId>
+			<optional>true</optional>
+		</dependency>
+		<dependency>
+			<groupId>org.mybatis.spring.boot</groupId>
+			<artifactId>mybatis-spring-boot-starter</artifactId>
+			<version>2.1.4</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>redis.clients</groupId>
+			<artifactId>jedis</artifactId>
+			<version>${jedis-version}</version>
+		</dependency>
+
+		<!-- druid数据库连接池 -->
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid-spring-boot-starter</artifactId>
+			<version>1.1.22</version>
+		</dependency>
+
+		<!-- mysql数据库 -->
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>8.0.22</version>
+		</dependency>
+
+		<!-- 添加sqlite-jdbc数据库驱动 -->
+		<dependency>
+			<groupId>org.xerial</groupId>
+			<artifactId>sqlite-jdbc</artifactId>
+			<version>3.32.3.2</version>
+		</dependency>
+
+		<!--Mybatis分页插件 -->
+		<dependency>
+			<groupId>com.github.pagehelper</groupId>
+			<artifactId>pagehelper-spring-boot-starter</artifactId>
+			<version>1.4.1</version>
+		</dependency>
+
+		<!--Swagger3 -->
+		<!--在线文档 -->
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-boot-starter</artifactId>
+			<version>3.0.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.github.xiaoymin</groupId>
+			<artifactId>knife4j-spring-boot-starter</artifactId>
+			<version>3.0.2</version>
+		</dependency>
+
+
+		<!--参数校验 -->
+		<dependency>
+			<groupId>javax.validation</groupId>
+			<artifactId>validation-api</artifactId>
+		</dependency>
+
+		<!-- 日志相关 -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-aop</artifactId>
+		</dependency>
+
+		<!-- sip协议栈 -->
+		<dependency>
+			<groupId>javax.sip</groupId>
+			<artifactId>jain-sip-ri</artifactId>
+			<version>1.3.0-91</version>
+		</dependency>
+
+		<!-- 取代log4j -->
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>log4j-over-slf4j</artifactId>
+			<version>1.7.35</version>
+		</dependency>
+
+		<!-- xml解析库 -->
+		<dependency>
+			<groupId>org.dom4j</groupId>
+			<artifactId>dom4j</artifactId>
+			<version>2.1.3</version>
+		</dependency>
+
+		<!-- json解析库fastjson -->
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<version>1.2.73</version>
+		</dependency>
+
+		<!--Guava是一种基于开源的Java库-->
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>30.0-jre</version>
+		</dependency>
+
+		<!-- okhttp -->
+		<dependency>
+			<groupId>com.squareup.okhttp3</groupId>
+			<artifactId>okhttp</artifactId>
+			<version>4.9.0</version>
+		</dependency>
+
+		<!-- okhttp 调试日志 -->
+		<dependency>
+			<groupId>com.squareup.okhttp3</groupId>
+			<artifactId>logging-interceptor</artifactId>
+			<version>4.9.0</version>
+		</dependency>
+
+
+
+		<!-- okhttp-digest -->
+		<dependency>
+			<groupId>com.burgstaller</groupId>
+			<artifactId>okhttp-digest</artifactId>
+			<version>2.1</version>
+		</dependency>
+
+		<!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
+		<dependency>
+			<groupId>net.sf.kxml</groupId>
+			<artifactId>kxml2</artifactId>
+			<version>2.3.0</version>
+		</dependency>
+
+		<!--反向代理-->
+		<dependency>
+			<groupId>org.mitre.dsmiley.httpproxy</groupId>
+			<artifactId>smiley-http-proxy-servlet</artifactId>
+			<version>1.12</version>
+		</dependency>
+
+		<!--excel解析库-->
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>easyexcel</artifactId>
+			<version>3.0.4</version>
+		</dependency>
+
+		<!-- 获取系统信息 -->
+		<dependency>
+			<groupId>com.github.oshi</groupId>
+			<artifactId>oshi-core</artifactId>
+			<version>6.1.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.session</groupId>
+			<artifactId>spring-session-core</artifactId>
+		</dependency>
+
+<!--		&lt;!&ndash; 检测文件编码 &ndash;&gt;-->
+<!--		&lt;!&ndash; https://mvnrepository.com/artifact/cpdetector/cpdetector &ndash;&gt;-->
+<!--		<dependency>-->
+<!--			<groupId>cpdetector</groupId>-->
+<!--			<artifactId>cpdetector</artifactId>-->
+<!--			<version>1.0.8</version>-->
+<!--		</dependency>-->
+
+		<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>31.0.1-jre</version>
+		</dependency>
+
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+<!--			<scope>test</scope>-->
+		</dependency>
+	</dependencies>
+
+
+
+	<build>
+		<finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+				<configuration>
+					<includeSystemScope>true</includeSystemScope>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>pl.project13.maven</groupId>
+				<artifactId>git-commit-id-plugin</artifactId>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<skipTests>true</skipTests>
+				</configuration>
+			</plugin>
+
+		</plugins>
+		<resources>
+			<resource>
+				<directory>src/main/resources</directory>
+			</resource>
+			<resource>
+				<directory>src/main/java</directory>
+				<includes>
+					<include>**/*.xml</include>
+				</includes>
+			</resource>
+		</resources>
+	</build>
+</project>

+ 544 - 0
sql/mysql.sql

@@ -0,0 +1,544 @@
+-- MySQL dump 10.13  Distrib 8.0.28, for Linux (x86_64)
+--
+-- Host: 127.0.0.1    Database: wvp
+-- ------------------------------------------------------
+-- Server version	8.0.28-0ubuntu0.20.04.3
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!50503 SET NAMES utf8mb4 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `device`
+--
+
+DROP TABLE IF EXISTS `device`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `device` (
+                          `id` int NOT NULL AUTO_INCREMENT,
+                          `deviceId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                          `name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `manufacturer` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `model` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `firmware` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `transport` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `streamMode` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `online` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `registerTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `keepaliveTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `ip` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                          `createTime` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                          `updateTime` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                          `port` int NOT NULL,
+                          `expires` int NOT NULL,
+                          `subscribeCycleForCatalog` int NOT NULL,
+                          `hostAddress` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                          `charset` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                          PRIMARY KEY (`id`),
+                          UNIQUE KEY `device_deviceId_uindex` (`deviceId`)
+) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `device`
+--
+
+LOCK TABLES `device` WRITE;
+/*!40000 ALTER TABLE `device` DISABLE KEYS */;
+/*!40000 ALTER TABLE `device` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `device_alarm`
+--
+
+DROP TABLE IF EXISTS `device_alarm`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `device_alarm` (
+                                `id` int NOT NULL AUTO_INCREMENT,
+                                `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `alarmPriority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `alarmMethod` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `alarmTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `alarmDescription` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `longitude` double DEFAULT NULL,
+                                `latitude` double DEFAULT NULL,
+                                `alarmType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `device_alarm`
+--
+
+LOCK TABLES `device_alarm` WRITE;
+/*!40000 ALTER TABLE `device_alarm` DISABLE KEYS */;
+/*!40000 ALTER TABLE `device_alarm` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `device_channel`
+--
+
+DROP TABLE IF EXISTS `device_channel`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `device_channel` (
+                                  `id` int NOT NULL AUTO_INCREMENT,
+                                  `channelId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                  `name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `manufacture` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `model` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `owner` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `civilCode` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `block` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `address` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `parentId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `safetyWay` int DEFAULT NULL,
+                                  `registerWay` int DEFAULT NULL,
+                                  `certNum` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `certifiable` int DEFAULT NULL,
+                                  `errCode` int DEFAULT NULL,
+                                  `endTime` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `secrecy` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `ipAddress` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `port` int DEFAULT NULL,
+                                  `password` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `PTZType` int DEFAULT NULL,
+                                  `status` int DEFAULT NULL,
+                                  `longitude` double DEFAULT NULL,
+                                  `latitude` double DEFAULT NULL,
+                                  `streamId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `deviceId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                  `parental` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                  `hasAudio` bit(1) DEFAULT NULL,
+                                  `createTime` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                  `updateTime` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                  `subCount` int DEFAULT '0',
+                                  PRIMARY KEY (`id`),
+                                  UNIQUE KEY `device_channel_id_uindex` (`id`),
+                                  UNIQUE KEY `device_channel_pk` (`channelId`,`deviceId`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `device_channel`
+--
+
+LOCK TABLES `device_channel` WRITE;
+/*!40000 ALTER TABLE `device_channel` DISABLE KEYS */;
+/*!40000 ALTER TABLE `device_channel` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `device_mobile_position`
+--
+
+DROP TABLE IF EXISTS `device_mobile_position`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `device_mobile_position` (
+                                          `id` int NOT NULL AUTO_INCREMENT,
+                                          `deviceId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                          `channelId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                          `deviceName` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                          `time` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                          `longitude` double NOT NULL,
+                                          `latitude` double NOT NULL,
+                                          `altitude` double DEFAULT NULL,
+                                          `speed` double DEFAULT NULL,
+                                          `direction` double DEFAULT NULL,
+                                          `reportSource` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                          `geodeticSystem` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                          `cnLng` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                          `cnLat` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                          PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `device_mobile_position`
+--
+
+LOCK TABLES `device_mobile_position` WRITE;
+/*!40000 ALTER TABLE `device_mobile_position` DISABLE KEYS */;
+/*!40000 ALTER TABLE `device_mobile_position` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `gb_stream`
+--
+
+DROP TABLE IF EXISTS `gb_stream`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `gb_stream` (
+                             `gbStreamId` int NOT NULL AUTO_INCREMENT,
+                             `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                             `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                             `gbId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                             `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                             `longitude` double DEFAULT NULL,
+                             `latitude` double DEFAULT NULL,
+                             `streamType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                             `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                             `status` int DEFAULT NULL,
+                             `createStamp` bigint DEFAULT NULL,
+                             PRIMARY KEY (`gbStreamId`) USING BTREE,
+                             UNIQUE KEY `app` (`app`,`stream`) USING BTREE,
+                             UNIQUE KEY `gbId` (`gbId`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=375 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `gb_stream`
+--
+
+LOCK TABLES `gb_stream` WRITE;
+/*!40000 ALTER TABLE `gb_stream` DISABLE KEYS */;
+/*!40000 ALTER TABLE `gb_stream` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `log`
+--
+
+DROP TABLE IF EXISTS `log`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `log` (
+                       `id` int NOT NULL AUTO_INCREMENT,
+                       `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                       `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                       `uri` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                       `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                       `result` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                       `timing` bigint NOT NULL,
+                       `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                       `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                       PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=313 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `log`
+--
+
+LOCK TABLES `log` WRITE;
+/*!40000 ALTER TABLE `log` DISABLE KEYS */;
+/*!40000 ALTER TABLE `log` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `media_server`
+--
+
+DROP TABLE IF EXISTS `media_server`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `media_server` (
+                                `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `hookIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `sdpIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `streamIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `httpPort` int NOT NULL,
+                                `httpSSlPort` int NOT NULL,
+                                `rtmpPort` int NOT NULL,
+                                `rtmpSSlPort` int NOT NULL,
+                                `rtpProxyPort` int NOT NULL,
+                                `rtspPort` int NOT NULL,
+                                `rtspSSLPort` int NOT NULL,
+                                `autoConfig` int NOT NULL,
+                                `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `streamNoneReaderDelayMS` int NOT NULL,
+                                `rtpEnable` int NOT NULL,
+                                `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `recordAssistPort` int NOT NULL,
+                                `defaultServer` int NOT NULL,
+                                `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                `hookAliveInterval` int NOT NULL,
+                                PRIMARY KEY (`id`) USING BTREE,
+                                UNIQUE KEY `media_server_i` (`ip`,`httpPort`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `media_server`
+--
+
+LOCK TABLES `media_server` WRITE;
+/*!40000 ALTER TABLE `media_server` DISABLE KEYS */;
+/*!40000 ALTER TABLE `media_server` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `parent_platform`
+--
+
+DROP TABLE IF EXISTS `parent_platform`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `parent_platform` (
+                                   `id` int NOT NULL AUTO_INCREMENT,
+                                   `enable` int DEFAULT NULL,
+                                   `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `serverGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                   `serverGBDomain` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `serverIP` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `serverPort` int DEFAULT NULL,
+                                   `deviceGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                   `deviceIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `devicePort` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `expires` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `keepTimeout` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `characterSet` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                   `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                   `ptz` int DEFAULT NULL,
+                                   `rtcp` int DEFAULT NULL,
+                                   `status` bit(1) DEFAULT NULL,
+                                   `shareAllLiveStream` int DEFAULT NULL,
+                                   PRIMARY KEY (`id`),
+                                   UNIQUE KEY `parent_platform_id_uindex` (`id`),
+                                   UNIQUE KEY `parent_platform_pk` (`serverGBId`)
+) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `parent_platform`
+--
+
+LOCK TABLES `parent_platform` WRITE;
+/*!40000 ALTER TABLE `parent_platform` DISABLE KEYS */;
+/*!40000 ALTER TABLE `parent_platform` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `platform_catalog`
+--
+
+DROP TABLE IF EXISTS `platform_catalog`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `platform_catalog` (
+                                    `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                    `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                    `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                    `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                    PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `platform_catalog`
+--
+
+LOCK TABLES `platform_catalog` WRITE;
+/*!40000 ALTER TABLE `platform_catalog` DISABLE KEYS */;
+/*!40000 ALTER TABLE `platform_catalog` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `platform_gb_channel`
+--
+
+DROP TABLE IF EXISTS `platform_gb_channel`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `platform_gb_channel` (
+                                       `id` int NOT NULL AUTO_INCREMENT,
+                                       `platformId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                       `catalogId` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                       `deviceChannelId` int NOT NULL,
+                                       PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `platform_gb_channel`
+--
+
+LOCK TABLES `platform_gb_channel` WRITE;
+/*!40000 ALTER TABLE `platform_gb_channel` DISABLE KEYS */;
+/*!40000 ALTER TABLE `platform_gb_channel` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `platform_gb_stream`
+--
+
+DROP TABLE IF EXISTS `platform_gb_stream`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `platform_gb_stream` (
+                                      `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                      `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                                      `gbStreamId` int NOT NULL,
+                                      `id` int NOT NULL AUTO_INCREMENT,
+                                      PRIMARY KEY (`id`),
+                                      UNIQUE KEY `platform_gb_stream_pk` (`platformId`,`catalogId`,`gbStreamId`)
+) ENGINE=InnoDB AUTO_INCREMENT=406 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `platform_gb_stream`
+--
+
+LOCK TABLES `platform_gb_stream` WRITE;
+/*!40000 ALTER TABLE `platform_gb_stream` DISABLE KEYS */;
+/*!40000 ALTER TABLE `platform_gb_stream` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `stream_proxy`
+--
+
+DROP TABLE IF EXISTS `stream_proxy`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `stream_proxy` (
+                                `id` int NOT NULL AUTO_INCREMENT,
+                                `type` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
+                                `app` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
+                                `stream` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
+                                `url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `src_url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `dst_url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `timeout_ms` int DEFAULT NULL,
+                                `ffmpeg_cmd_key` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `rtp_type` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `mediaServerId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `enable_hls` bit(1) DEFAULT NULL,
+                                `enable_mp4` bit(1) DEFAULT NULL,
+                                `enable` bit(1) NOT NULL,
+                                `status` bit(1) NOT NULL,
+                                `enable_remove_none_reader` bit(1) NOT NULL,
+                                `createTime` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
+                                PRIMARY KEY (`id`),
+                                UNIQUE KEY `stream_proxy_pk` (`app`,`stream`)
+) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `stream_proxy`
+--
+
+LOCK TABLES `stream_proxy` WRITE;
+/*!40000 ALTER TABLE `stream_proxy` DISABLE KEYS */;
+/*!40000 ALTER TABLE `stream_proxy` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `stream_push`
+--
+
+DROP TABLE IF EXISTS `stream_push`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `stream_push` (
+                               `id` int NOT NULL AUTO_INCREMENT,
+                               `app` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
+                               `stream` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
+                               `totalReaderCount` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                               `originType` int DEFAULT NULL,
+                               `originTypeStr` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                               `createStamp` bigint DEFAULT NULL,
+                               `aliveSecond` int DEFAULT NULL,
+                               `mediaServerId` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                               PRIMARY KEY (`id`),
+                               UNIQUE KEY `stream_push_pk` (`app`,`stream`)
+) ENGINE=InnoDB AUTO_INCREMENT=394 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `stream_push`
+--
+
+LOCK TABLES `stream_push` WRITE;
+/*!40000 ALTER TABLE `stream_push` DISABLE KEYS */;
+/*!40000 ALTER TABLE `stream_push` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `user`
+--
+
+DROP TABLE IF EXISTS `user`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `user` (
+                        `id` int NOT NULL AUTO_INCREMENT,
+                        `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                        `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                        `roleId` int NOT NULL,
+                        `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                        `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                        PRIMARY KEY (`id`) USING BTREE,
+                        UNIQUE KEY `user_username_uindex` (`username`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `user`
+--
+
+LOCK TABLES `user` WRITE;
+/*!40000 ALTER TABLE `user` DISABLE KEYS */;
+INSERT INTO `user` VALUES (1,'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57');
+/*!40000 ALTER TABLE `user` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `user_role`
+--
+
+DROP TABLE IF EXISTS `user_role`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `user_role` (
+                             `id` int NOT NULL AUTO_INCREMENT,
+                             `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                             `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                             `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                             `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                             PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `user_role`
+--
+
+LOCK TABLES `user_role` WRITE;
+/*!40000 ALTER TABLE `user_role` DISABLE KEYS */;
+INSERT INTO `user_role` VALUES (1,'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');
+/*!40000 ALTER TABLE `user_role` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2022-02-25 20:32:21

+ 36 - 0
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java

@@ -0,0 +1,36 @@
+package com.genersoft.iot.vmp;
+
+import java.util.logging.LogManager;
+
+import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import springfox.documentation.oas.annotations.EnableOpenApi;
+
+/**
+ *
+ */
+@ServletComponentScan("com.genersoft.iot.vmp.conf")
+@SpringBootApplication
+@EnableScheduling
+@EnableOpenApi
+@EnableDruidSupport
+public class VManageBootstrap extends LogManager {
+	private static String[] args;
+	private static ConfigurableApplicationContext context;
+	public static void main(String[] args) {
+		VManageBootstrap.args = args;
+		VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
+	}
+	// 项目重启
+	public static void restart() {
+		context.close();
+		VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
+	}
+	
+
+}

+ 172 - 0
src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java

@@ -0,0 +1,172 @@
+package com.genersoft.iot.vmp.common;
+
+public class ApiSaveConstant {
+
+    public static String getVal(String key) {
+        String[] keyItemArray = key.split("/");
+        if (keyItemArray.length <= 1 || !"api".equals(keyItemArray[1])) {
+            return null;
+        }
+        if (keyItemArray.length >= 4) {
+            switch (keyItemArray[2]) {
+                case "alarm":
+                    if ("delete".equals(keyItemArray[3])) {
+                        return "删除报警";
+                    }
+                    break;
+                case "device":
+                    switch (keyItemArray[3]) {
+                        case "config":
+                            if (keyItemArray.length >= 5 && "basicParam".equals(keyItemArray[4])) {
+                                return "[设备配置] 基本配置设置命令";
+                            }
+                            break;
+                        case "control":
+                            switch (keyItemArray[4]) {
+                                case "teleboot":
+                                    return "[设备控制] 远程启动";
+                                case "record":
+                                    return "[设备控制] 录像控制";
+                                case "guard":
+                                    return "[设备控制] 布防/撤防命令";
+                                case "reset_alarm":
+                                    return "[设备控制] 报警复位";
+                                case "i_frame":
+                                    return "[设备控制] 强制关键帧";
+                                case "home_position":
+                                    return "[设备控制] 看守位控制";
+                            }
+                            break;
+                            case "query":
+                                if (keyItemArray.length <= 5) return null;
+                                switch (keyItemArray[4]) {
+                                    case "devices":
+                                        if (keyItemArray.length < 7) return null;
+                                        switch (keyItemArray[6]) {
+                                            case "sync":
+                                                return "[设备查询] 同步设备通道";
+                                            case "delete":
+                                                return "[设备查询] 移除设备";
+                                        }
+                                        break;
+                                    case "channel":
+                                        return "[设备查询] 更新通道信息";
+                                    case "transport":
+                                        return "[设备查询] 修改数据流传输模式";
+                                }
+                                break;
+                            }
+                case "gbStream":
+                    switch (keyItemArray[3]) {
+                        case "del":
+                            return "移除通道与国标的关联";
+                        case "add":
+                            return "添加通道与国标的关联";
+                    }
+                    break;
+                case "media":
+                    break;
+                case "position":
+                    if ("subscribe".equals(keyItemArray[3])) {
+                        return "订阅位置信息";
+                    }
+                    break;
+                case "platform":
+                    switch (keyItemArray[3]) {
+                        case "save":
+                            return "添加上级平台";
+                        case "delete":
+                            return "移除上级平台";
+                        case "update_channel_for_gb":
+                            return "向上级平台添加国标通道";
+                        case "del_channel_for_gb":
+                            return "从上级平台移除国标通道";
+                    }
+                    break;
+                case "platform_gb_stream":
+                    break;
+                case "play":
+                    switch (keyItemArray[3]) {
+                        case "start":
+                            return "开始点播";
+                        case "stop":
+                            return "停止点播";
+                        case "convert":
+                            return "转码";
+                        case "convertStop":
+                            return "结束转码";
+                        case "broadcast":
+                            return "语音广播";
+                    }
+                    break;
+                case "download":
+                    switch (keyItemArray[3]) {
+                        case "start":
+                            return "开始历史媒体下载";
+                        case "stop":
+                            return "停止历史媒体下载";
+                    }
+                    break;
+                case "playback":
+                    switch (keyItemArray[3]) {
+                        case "start":
+                            return "开始视频回放";
+                        case "stop":
+                            return "停止视频回放";
+                    }
+                    break;
+                case "ptz":
+                    switch (keyItemArray[3]) {
+                        case "control":
+                            return "云台控制";
+                        case "front_end_command":
+                            return "通用前端控制命令";
+                    }
+                    break;
+                case "gb_record":
+                    break;
+                case "onvif":
+                    break;
+                case "server":
+                    if ("restart".equals(keyItemArray[3])) {
+                        return "重启流媒体服务";
+                    }
+                    break;
+                case "proxy":
+                    switch (keyItemArray[3]) {
+                        case "save":
+                            return "保存代理";
+                        case "del":
+                            return "移除代理";
+                        case "start":
+                            return "启用代理";
+                        case "stop":
+                            return "停用代理";
+                    }
+                    break;
+                case "push":
+                    switch (keyItemArray[3]) {
+                        case "save_to_gb":
+                            return "将推流添加到国标";
+                        case "remove_form_gb":
+                            return "将推流移出到国标";
+                    }
+                    break;
+                case "user":
+                    switch (keyItemArray[3]) {
+                        case "login":
+                            return "登录";
+                        case "changePassword":
+                            return "修改密码";
+                        case "add":
+                            return "添加用户";
+                        case "delete":
+                            return "删除用户";
+                    }
+                    break;
+            }
+        }
+        return null;
+    }
+}
+

+ 267 - 0
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java

@@ -0,0 +1,267 @@
+package com.genersoft.iot.vmp.common;
+
+import com.alibaba.fastjson.JSONArray;
+
+public class StreamInfo {
+
+    private String app;
+    private String stream;
+    private String deviceID;
+    private String channelId;
+    private String flv;
+    private String https_flv;
+    private String ws_flv;
+    private String wss_flv;
+    private String fmp4;
+    private String https_fmp4;
+    private String ws_fmp4;
+    private String wss_fmp4;
+    private String hls;
+    private String https_hls;
+    private String ws_hls;
+    private String wss_hls;
+    private String ts;
+    private String https_ts;
+    private String ws_ts;
+    private String wss_ts;
+    private String rtmp;
+    private String rtmps;
+    private String rtsp;
+    private String rtsps;
+    private String rtc;
+    private String mediaServerId;
+    private Object tracks;
+
+    public static class TransactionInfo{
+        public String callId;
+        public String localTag;
+        public String remoteTag;
+        public String branch;
+    }
+
+    private TransactionInfo transactionInfo;
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    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 Object getTracks() {
+        return tracks;
+    }
+
+    public void setTracks(Object 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 getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public String getRtc() {
+        return rtc;
+    }
+
+    public void setRtc(String rtc) {
+        this.rtc = rtc;
+    }
+
+    public TransactionInfo getTransactionInfo() {
+        return transactionInfo;
+    }
+
+    public void setTransactionInfo(TransactionInfo transactionInfo) {
+        this.transactionInfo = transactionInfo;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public String getHttps_flv() {
+        return https_flv;
+    }
+
+    public void setHttps_flv(String https_flv) {
+        this.https_flv = https_flv;
+    }
+
+    public String getWss_flv() {
+        return wss_flv;
+    }
+
+    public void setWss_flv(String wss_flv) {
+        this.wss_flv = wss_flv;
+    }
+
+    public String getWss_fmp4() {
+        return wss_fmp4;
+    }
+
+    public void setWss_fmp4(String wss_fmp4) {
+        this.wss_fmp4 = wss_fmp4;
+    }
+
+    public String getWss_hls() {
+        return wss_hls;
+    }
+
+    public void setWss_hls(String wss_hls) {
+        this.wss_hls = wss_hls;
+    }
+
+    public String getWss_ts() {
+        return wss_ts;
+    }
+
+    public void setWss_ts(String wss_ts) {
+        this.wss_ts = wss_ts;
+    }
+
+    public String getRtmps() {
+        return rtmps;
+    }
+
+    public void setRtmps(String rtmps) {
+        this.rtmps = rtmps;
+    }
+
+    public String getRtsps() {
+        return rtsps;
+    }
+
+    public void setRtsps(String rtsps) {
+        this.rtsps = rtsps;
+    }
+
+    public String getHttps_hls() {
+        return https_hls;
+    }
+
+    public void setHttps_hls(String https_hls) {
+        this.https_hls = https_hls;
+    }
+
+    public String getHttps_fmp4() {
+        return https_fmp4;
+    }
+
+    public void setHttps_fmp4(String https_fmp4) {
+        this.https_fmp4 = https_fmp4;
+    }
+
+    public String getHttps_ts() {
+        return https_ts;
+    }
+
+    public void setHttps_ts(String https_ts) {
+        this.https_ts = https_ts;
+    }
+}

+ 22 - 0
src/main/java/com/genersoft/iot/vmp/common/SystemInfoDto.java

@@ -0,0 +1,22 @@
+package com.genersoft.iot.vmp.common;
+
+public class SystemInfoDto<T> {
+    private String time;
+    private T data;
+
+    public String getTime() {
+        return time;
+    }
+
+    public void setTime(String time) {
+        this.time = time;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+}

+ 136 - 0
src/main/java/com/genersoft/iot/vmp/common/VersionPo.java

@@ -0,0 +1,136 @@
+package com.genersoft.iot.vmp.common;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+public class VersionPo {
+    /**
+     * git的全版本号
+     */
+    @JSONField(name="GIT-Revision")
+    private String GIT_Revision;
+    /**
+     * maven版本
+     */
+    @JSONField(name = "Create-By")
+    private String Create_By;
+    /**
+     * git的分支
+     */
+    @JSONField(name = "GIT-BRANCH")
+    private String GIT_BRANCH;
+    /**
+     * git的url
+     */
+    @JSONField(name = "GIT-URL")
+    private String GIT_URL;
+    /**
+     * 构建日期
+     */
+    @JSONField(name = "BUILD-DATE")
+    private String BUILD_DATE;
+    /**
+     * 项目名称 配合pom使用
+     */
+    @JSONField(name = "artifactId")
+    private String artifactId;
+    /**
+     * git局部版本号
+     */
+    @JSONField(name = "GIT-Revision-SHORT")
+    private String GIT_Revision_SHORT;
+    /**
+     * 项目的版本如2.0.1.0 配合pom使用
+     */
+    @JSONField(name = "version")
+    private String version;
+    /**
+     * 子系统名称
+     */
+    @JSONField(name = "project")
+    private String project;
+    /**
+     * jdk版本
+     */
+    @JSONField(name="Build_Jdk")
+    private String Build_Jdk;
+
+    public void setGIT_Revision(String GIT_Revision) {
+        this.GIT_Revision = GIT_Revision;
+    }
+
+    public void setCreate_By(String create_By) {
+        Create_By = create_By;
+    }
+
+    public void setGIT_BRANCH(String GIT_BRANCH) {
+        this.GIT_BRANCH = GIT_BRANCH;
+    }
+
+    public void setGIT_URL(String GIT_URL) {
+        this.GIT_URL = GIT_URL;
+    }
+
+    public void setBUILD_DATE(String BUILD_DATE) {
+        this.BUILD_DATE = BUILD_DATE;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public void setGIT_Revision_SHORT(String GIT_Revision_SHORT) {
+        this.GIT_Revision_SHORT = GIT_Revision_SHORT;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public void setProject(String project) {
+        this.project = project;
+    }
+
+    public void setBuild_Jdk(String build_Jdk) {
+        Build_Jdk = build_Jdk;
+    }
+
+    public String getGIT_Revision() {
+        return GIT_Revision;
+    }
+
+    public String getCreate_By() {
+        return Create_By;
+    }
+
+    public String getGIT_BRANCH() {
+        return GIT_BRANCH;
+    }
+
+    public String getGIT_URL() {
+        return GIT_URL;
+    }
+
+    public String getBUILD_DATE() {
+        return BUILD_DATE;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public String getGIT_Revision_SHORT() {
+        return GIT_Revision_SHORT;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getProject() {
+        return project;
+    }
+
+    public String getBuild_Jdk() {
+        return Build_Jdk;
+    }
+}

+ 79 - 0
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@@ -0,0 +1,79 @@
+package com.genersoft.iot.vmp.common;
+
+/**    
+ * @description: 定义常量   
+ * @author: swwheihei
+ * @date:   2019年5月30日 下午3:04:04   
+ *   
+ */
+public class VideoManagerConstants {
+	
+	public static final String WVP_SERVER_PREFIX = "VMP_SIGNALLING_SERVER_INFO_";
+
+	public static final String WVP_SERVER_STREAM_PREFIX = "VMP_SIGNALLING_STREAM_";
+
+	public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
+
+	public static final String MEDIA_SERVER_KEEPALIVE_PREFIX = "VMP_MEDIA_SERVER_KEEPALIVE_";
+
+	public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS_";
+
+	public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM";
+
+	public static final String DEVICE_PREFIX = "VMP_DEVICE_";
+
+	public static final String CACHEKEY_PREFIX = "VMP_CHANNEL_";
+
+	public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_";
+
+	// 此处多了一个_,暂不修改
+	public static final String PLAYER_PREFIX = "VMP_PLAYER_";
+	public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
+	public static final String PLAY_INFO_PREFIX = "VMP_PLAY_INFO_";
+
+	public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
+
+	public static final String PLATFORM_KEEPALIVE_PREFIX = "VMP_PLATFORM_KEEPALIVE_";
+
+	public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_";
+
+	public static final String PLATFORM_REGISTER_PREFIX = "VMP_PLATFORM_REGISTER_";
+
+	public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_";
+
+	public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_";
+
+	public static final String EVENT_ONLINE_REGISTER = "1";
+	
+	public static final String EVENT_ONLINE_KEEPLIVE = "2";
+
+	public static final String EVENT_ONLINE_MESSAGE = "3";
+
+	public static final String EVENT_OUTLINE_UNREGISTER = "1";
+	
+	public static final String EVENT_OUTLINE_TIMEOUT = "2";
+
+	public static final String MEDIA_SSRC_USED_PREFIX = "VMP_MEDIA_USED_SSRC_";
+
+	public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_";
+
+	public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_";
+
+	public static final String SIP_SN_PREFIX = "VMP_SIP_SN_";
+
+	public static final String SIP_SUBSCRIBE_PREFIX = "VMP_SIP_SUBSCRIBE_";
+
+	public static final String SYSTEM_INFO_CPU_PREFIX = "VMP_SYSTEM_INFO_CPU_";
+
+	public static final String SYSTEM_INFO_MEM_PREFIX = "VMP_SYSTEM_INFO_MEM_";
+
+	public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_";
+
+	//************************** redis 消息*********************************
+	public static final String WVP_MSG_STREAM_CHANGE_PREFIX = "WVP_MSG_STREAM_CHANGE_";
+	public static final String WVP_MSG_GPS_PREFIX = "VM_MSG_GPS";
+
+	//**************************    第三方  ****************************************
+	public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
+	public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
+}

+ 117 - 0
src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java

@@ -0,0 +1,117 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.genersoft.iot.vmp.common.ApiSaveConstant;
+import com.genersoft.iot.vmp.conf.security.SecurityUtils;
+import com.genersoft.iot.vmp.service.ILogService;
+import com.genersoft.iot.vmp.storager.dao.dto.LogDto;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+
+/**
+ * @author lin
+ */
+@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true)
+public class ApiAccessFilter extends OncePerRequestFilter {
+
+    private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class);
+
+    private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    @Autowired
+    private UserSetup userSetup;
+
+    @Autowired
+    private ILogService logService;
+
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
+        String username = null;
+        if (SecurityUtils.getUserInfo() == null) {
+            username = servletRequest.getParameter("username");
+        }else {
+            username = SecurityUtils.getUserInfo().getUsername();
+        }
+        long start = System.currentTimeMillis(); // 请求进入时间
+        String uriName = ApiSaveConstant.getVal(servletRequest.getRequestURI());
+
+        filterChain.doFilter(servletRequest, servletResponse);
+
+        if (uriName != null && userSetup.getLogInDatebase()) {
+
+            LogDto logDto = new LogDto();
+            logDto.setName(uriName);
+            logDto.setUsername(username);
+            logDto.setAddress(servletRequest.getRemoteAddr());
+            logDto.setResult(HttpStatus.valueOf(servletResponse.getStatus()).toString());
+            logDto.setTiming(System.currentTimeMillis() - start);
+            logDto.setType(servletRequest.getMethod());
+            logDto.setUri(servletRequest.getRequestURI());
+            logDto.setCreateTime(format.format(System.currentTimeMillis()));
+            logService.add(logDto);
+//            logger.warn("[Api Access]  [{}] [{}] [{}] [{}] [{}] {}ms",
+//                    uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()),
+//                    System.currentTimeMillis() - start);
+
+        }
+    }
+
+    /**
+     * 获取IP地址
+     *
+     * @param request 请求
+     * @return request发起客户端的IP地址
+     */
+    private String getIP(HttpServletRequest request) {
+        if (request == null) {
+            return "0.0.0.0";
+        }
+
+        String Xip = request.getHeader("X-Real-IP");
+        String XFor = request.getHeader("X-Forwarded-For");
+
+        String UNKNOWN_IP = "unknown";
+        if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
+            //多次反向代理后会有多个ip值,第一个ip才是真实ip
+            int index = XFor.indexOf(",");
+            if (index != -1) {
+                return XFor.substring(0, index);
+            } else {
+                return XFor;
+            }
+        }
+
+        XFor = Xip;
+        if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
+            return XFor;
+        }
+
+        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
+            XFor = request.getHeader("Proxy-Client-IP");
+        }
+        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
+            XFor = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
+            XFor = request.getHeader("HTTP_CLIENT_IP");
+        }
+        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
+            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
+        }
+        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
+            XFor = request.getRemoteAddr();
+        }
+        return XFor;
+    }
+}

+ 46 - 0
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java

@@ -0,0 +1,46 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
+
+/**
+ * 动态定时任务
+ */
+@Component
+public class DynamicTask {
+
+    @Autowired
+    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
+
+    private Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
+
+    @Bean
+    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
+        return new ThreadPoolTaskScheduler();
+    }
+
+    public String startCron(String key, Runnable task, int cycleForCatalog) {
+        stopCron(key);
+        // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔
+        ScheduledFuture future = threadPoolTaskScheduler.scheduleWithFixedDelay(task, cycleForCatalog * 1000L);
+        futureMap.put(key, future);
+        return "startCron";
+    }
+
+    public void stopCron(String key) {
+        if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
+            futureMap.get(key).cancel(true);
+        }
+    }
+
+    public boolean contains(String key) {
+        return futureMap.get(key) != null;
+    }
+
+}

+ 216 - 0
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java

@@ -0,0 +1,216 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+@Configuration("mediaConfig")
+public class MediaConfig{
+
+    // 修改必须配置,不再支持自动获取
+    @Value("${media.id}")
+    private String id;
+
+    @Value("${media.ip}")
+    private String ip;
+
+    @Value("${media.hook-ip:${sip.ip}}")
+    private String hookIp;
+
+    @Value("${sip.ip}")
+    private String sipIp;
+
+    @Value("${sip.domain}")
+    private String sipDomain;
+
+    @Value("${media.sdp-ip:${media.ip}}")
+    private String sdpIp;
+
+    @Value("${media.stream-ip:${media.ip}}")
+    private String streamIp;
+
+    @Value("${media.http-port}")
+    private Integer httpPort;
+
+    @Value("${media.http-ssl-port:0}")
+    private Integer httpSSlPort = 0;
+
+    @Value("${media.rtmp-port:0}")
+    private Integer rtmpPort = 0;
+
+    @Value("${media.rtmp-ssl-port:0}")
+    private Integer rtmpSSlPort = 0;
+
+    @Value("${media.rtp-proxy-port:0}")
+    private Integer rtpProxyPort = 0;
+
+    @Value("${media.rtsp-port:0}")
+    private Integer rtspPort = 0;
+
+    @Value("${media.rtsp-ssl-port:0}")
+    private Integer rtspSSLPort = 0;
+
+    @Value("${media.auto-config:true}")
+    private boolean autoConfig = true;
+
+    @Value("${media.secret}")
+    private String secret;
+
+    @Value("${media.stream-none-reader-delay-ms:18000}")
+    private int streamNoneReaderDelayMS = 18000;
+
+    @Value("${media.rtp.enable}")
+    private boolean rtpEnable;
+
+    @Value("${media.rtp.port-range}")
+    private String rtpPortRange;
+
+
+    @Value("${media.rtp.send-port-range}")
+    private String sendRtpPortRange;
+
+    @Value("${media.record-assist-port:0}")
+    private Integer recordAssistPort = 0;
+
+    public String getId() {
+        return id;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public String getHookIp() {
+        if (StringUtils.isEmpty(hookIp)){
+            return sipIp;
+        }else {
+            return hookIp;
+        }
+
+    }
+
+    public String getSipIp() {
+        if (sipIp == null) {
+            return this.ip;
+        }else {
+            return sipIp;
+        }
+    }
+
+    public int getHttpPort() {
+        return httpPort;
+    }
+
+    public int getHttpSSlPort() {
+        return httpSSlPort;
+    }
+
+    public int getRtmpPort() {
+        return rtmpPort;
+    }
+    
+    public int getRtmpSSlPort() {
+        return rtmpSSlPort;
+    }
+
+    public int getRtpProxyPort() {
+        if (rtpProxyPort == null) {
+            return 0;
+        }else {
+            return rtpProxyPort;
+        }
+
+    }
+
+    public int getRtspPort() {
+        return rtspPort;
+    }
+
+    public int getRtspSSLPort() {
+        return rtspSSLPort;
+    }
+
+    public boolean isAutoConfig() {
+        return autoConfig;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public int getStreamNoneReaderDelayMS() {
+        return streamNoneReaderDelayMS;
+    }
+
+    public boolean isRtpEnable() {
+        return rtpEnable;
+    }
+
+    public String getRtpPortRange() {
+        return rtpPortRange;
+    }
+    
+    public int getRecordAssistPort() {
+        return recordAssistPort;
+    }
+
+    public String getSdpIp() {
+        if (StringUtils.isEmpty(sdpIp)){
+            return ip;
+        }else {
+            return sdpIp;
+        }
+    }
+
+    public String getStreamIp() {
+        if (StringUtils.isEmpty(streamIp)){
+            return ip;
+        }else {
+            return streamIp;
+        }
+    }
+
+    public String getSipDomain() {
+        return sipDomain;
+    }
+
+    public String getSendRtpPortRange() {
+        return sendRtpPortRange;
+    }
+
+    public MediaServerItem getMediaSerItem(){
+        MediaServerItem mediaServerItem = new MediaServerItem();
+        mediaServerItem.setId(id);
+        mediaServerItem.setIp(ip);
+        mediaServerItem.setDefaultServer(true);
+        mediaServerItem.setHookIp(getHookIp());
+        mediaServerItem.setSdpIp(getSdpIp());
+        mediaServerItem.setStreamIp(getStreamIp());
+        mediaServerItem.setHttpPort(httpPort);
+        mediaServerItem.setHttpSSlPort(httpSSlPort);
+        mediaServerItem.setRtmpPort(rtmpPort);
+        mediaServerItem.setRtmpSSlPort(rtmpSSlPort);
+        mediaServerItem.setRtpProxyPort(getRtpProxyPort());
+        mediaServerItem.setRtspPort(rtspPort);
+        mediaServerItem.setRtspSSLPort(rtspSSLPort);
+        mediaServerItem.setAutoConfig(autoConfig);
+        mediaServerItem.setSecret(secret);
+        mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS);
+        mediaServerItem.setRtpEnable(rtpEnable);
+        mediaServerItem.setRtpPortRange(rtpPortRange);
+        mediaServerItem.setSendRtpPortRange(sendRtpPortRange);
+        mediaServerItem.setRecordAssistPort(recordAssistPort);
+        mediaServerItem.setHookAliveInterval(120);
+
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        mediaServerItem.setCreateTime(format.format(System.currentTimeMillis()));
+        mediaServerItem.setUpdateTime(format.format(System.currentTimeMillis()));
+
+        return mediaServerItem;
+    }
+
+}

+ 16 - 0
src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java

@@ -0,0 +1,16 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.scheduling.annotation.Scheduled;
+
+/**
+ * 定时向zlm同步媒体流状态
+ */
+public class MediaStatusTimerTask {
+
+
+    @Scheduled(fixedRate = 2 * 1000)   //每3秒执行一次
+    public void execute(){
+
+    }
+}

+ 275 - 0
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java

@@ -0,0 +1,275 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.mitre.dsmiley.httpproxy.ProxyServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.ConnectException;
+
+/**
+ * @author lin
+ */
+@SuppressWarnings(value = {"rawtypes", "unchecked"})
+@Configuration
+public class ProxyServletConfig {
+
+    private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Value("${server.port}")
+    private int serverPort;
+
+    @Bean
+    public ServletRegistrationBean zlmServletRegistrationBean(){
+        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZlmProxyServlet(),"/zlm/*");
+        servletRegistrationBean.setName("zlm_Proxy");
+        servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080");
+        servletRegistrationBean.addUrlMappings();
+        if (logger.isDebugEnabled()) {
+            servletRegistrationBean.addInitParameter("log", "true");
+        }
+        return servletRegistrationBean;
+    }
+
+    class ZlmProxyServlet extends ProxyServlet{
+        @Override
+        protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
+            String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
+            MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
+            if (mediaInfo != null) {
+                if (!StringUtils.isEmpty(queryStr)) {
+                    queryStr += "&secret=" + mediaInfo.getSecret();
+                }else {
+                    queryStr = "secret=" + mediaInfo.getSecret();
+                }
+            }
+            return queryStr;
+        }
+
+        /**
+         * 异常处理
+         */
+        @Override
+        protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
+            try {
+                super.handleRequestException(proxyRequest, proxyResonse, e);
+            } catch (ServletException servletException) {
+                logger.error("zlm 代理失败: ", e);
+            } catch (IOException ioException) {
+                if (ioException instanceof ConnectException) {
+                    logger.error("zlm 连接失败");
+                } else if (ioException instanceof ClientAbortException) {
+                    logger.error("zlm: 用户已中断连接,代理终止");
+                } else {
+                    logger.error("zlm 代理失败: ", e);
+                }
+            } catch (RuntimeException exception){
+                logger.error("zlm 代理失败: ", e);
+            }
+        }
+
+        /**
+         * 对于为按照格式请求的可以直接返回404
+         */
+        @Override
+        protected String getTargetUri(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+
+            String uri = null;
+            if (mediaInfo != null) {
+//                String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
+                uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
+            }else {
+                uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以
+            }
+            return uri;
+        }
+
+        /**
+         * 动态替换请求目标
+         */
+        @Override
+        protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            HttpHost host;
+            if (mediaInfo != null) {
+                host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
+            }else {
+                host = new HttpHost("127.0.0.1", serverPort);
+            }
+            return host;
+
+        }
+
+        /**
+         * 根据uri获取流媒体信息
+         */
+        MediaServerItem getMediaInfoByUri(String uri){
+            String[] split = uri.split("/");
+            String mediaServerId = split[2];
+            if ("default".equals(mediaServerId)) {
+                return mediaServerService.getDefaultMediaServer();
+            }else {
+                return mediaServerService.getOne(mediaServerId);
+            }
+        }
+
+        /**
+         * 去掉url中的标志信息
+         */
+        @Override
+        protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            String url = super.rewriteUrlFromRequest(servletRequest);
+            if (mediaInfo == null) {
+                logger.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI);
+                return  url;
+            }
+            if (!StringUtils.isEmpty(mediaInfo.getId())) {
+                url = url.replace(mediaInfo.getId() + "/", "");
+            }
+            return url.replace("default/", "");
+        }
+    }
+
+    @Bean
+    public ServletRegistrationBean recordServletRegistrationBean(){
+        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new RecordProxyServlet(),"/record_proxy/*");
+        servletRegistrationBean.setName("record_proxy");
+        servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:18081");
+        servletRegistrationBean.addUrlMappings();
+        if (logger.isDebugEnabled()) {
+            servletRegistrationBean.addInitParameter("log", "true");
+        }
+        return servletRegistrationBean;
+    }
+
+    class RecordProxyServlet extends ProxyServlet{
+
+        @Override
+        protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
+            String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
+            MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
+            String remoteHost = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
+            if (mediaInfo != null) {
+                if (!StringUtils.isEmpty(queryStr)) {
+                    queryStr += "&remoteHost=" + remoteHost;
+                }else {
+                    queryStr = "remoteHost=" + remoteHost;
+                }
+            }
+            return queryStr;
+        }
+
+        /**
+         * 异常处理
+         */
+        @Override
+        protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e){
+            try {
+                super.handleRequestException(proxyRequest, proxyResponse, e);
+            } catch (ServletException servletException) {
+                logger.error("录像服务 代理失败: ", e);
+            } catch (IOException ioException) {
+                if (ioException instanceof ConnectException) {
+                    logger.error("录像服务 连接失败");
+                } else if (ioException instanceof ClientAbortException) {
+                    logger.error("录像服务:用户已中断连接,代理终止");
+                } else {
+                    logger.error("录像服务 代理失败: ", e);
+                }
+            } catch (RuntimeException exception){
+                logger.error("录像服务 代理失败: ", e);
+            }
+        }
+
+        /**
+         * 对于为按照格式请求的可以直接返回404
+         */
+        @Override
+        protected String getTargetUri(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+
+            String uri = null;
+            if (mediaInfo != null) {
+//                String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
+                uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
+            }else {
+                uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以
+            }
+            return uri;
+        }
+
+        /**
+         * 动态替换请求目标
+         */
+        @Override
+        protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            HttpHost host;
+            if (mediaInfo != null) {
+                host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
+            }else {
+                host = new HttpHost("127.0.0.1", serverPort);
+            }
+            return host;
+
+        }
+
+        /**
+         * 根据uri获取流媒体信息
+         */
+        MediaServerItem getMediaInfoByUri(String uri){
+            String[] split = uri.split("/");
+            String mediaServerId = split[2];
+            if ("default".equals(mediaServerId)) {
+                return mediaServerService.getDefaultMediaServer();
+            }else {
+                return mediaServerService.getOne(mediaServerId);
+            }
+
+        }
+
+        /**
+         * 去掉url中的标志信息
+         */
+        @Override
+        protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            String url = super.rewriteUrlFromRequest(servletRequest);
+            if (mediaInfo == null) {
+                logger.error("[录像服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI);
+                return  url;
+            }
+            if (!StringUtils.isEmpty(mediaInfo.getId())) {
+                url = url.replace(mediaInfo.getId() + "/", "");
+            }
+            return url.replace("default/", "");
+        }
+    }
+
+}

+ 99 - 0
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java

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

+ 41 - 0
src/main/java/com/genersoft/iot/vmp/conf/RedisKeyExpirationEventMessageListener.java

@@ -0,0 +1,41 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.util.StringUtils;
+
+import java.util.Properties;
+
+public class RedisKeyExpirationEventMessageListener extends KeyExpirationEventMessageListener {
+
+    private UserSetup userSetup;
+    private RedisMessageListenerContainer listenerContainer;
+    private String keyspaceNotificationsConfigParameter = "EA";
+
+    public RedisKeyExpirationEventMessageListener(RedisMessageListenerContainer listenerContainer, UserSetup userSetup) {
+        super(listenerContainer);
+        this.listenerContainer = listenerContainer;
+        this.userSetup = userSetup;
+    }
+
+    @Override
+    public void init() {
+        if (!userSetup.getRedisConfig()) {
+            // 配置springboot默认Config为空,即不让应用去修改redis的默认配置,因为Redis服务出于安全会禁用CONFIG命令给远程用户使用
+            setKeyspaceNotificationsConfigParameter("");
+        }else {
+
+            RedisConnection connection = this.listenerContainer.getConnectionFactory().getConnection();
+            Properties config = connection.getConfig("notify-keyspace-events");
+            try {
+                if (!config.getProperty("notify-keyspace-events").equals(keyspaceNotificationsConfigParameter)) {
+                    connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);
+                }
+            } finally {
+                connection.close();
+            }
+        }
+        super.init();
+    }
+}

+ 117 - 0
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java

@@ -0,0 +1,117 @@
+package com.genersoft.iot.vmp.conf;
+
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
+public class SipConfig {
+
+	private String ip;
+
+	/**
+	 * 默认使用 0.0.0.0
+	 */
+	private String monitorIp = "0.0.0.0";
+
+	private Integer port;
+
+	private String domain;
+
+	private String id;
+
+	private String password;
+	
+	Integer ptzSpeed = 50;
+
+	Integer keepaliveTimeOut = 255;
+
+	Integer registerTimeInterval = 120;
+
+	private boolean alarm = false;
+
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
+
+	public void setMonitorIp(String monitorIp) {
+		this.monitorIp = monitorIp;
+	}
+
+	public void setPort(Integer port) {
+		this.port = port;
+	}
+
+	public void setDomain(String domain) {
+		this.domain = domain;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public void setPtzSpeed(Integer ptzSpeed) {
+		this.ptzSpeed = ptzSpeed;
+	}
+
+	public void setKeepaliveTimeOut(Integer keepaliveTimeOut) {
+		this.keepaliveTimeOut = keepaliveTimeOut;
+	}
+
+	public void setRegisterTimeInterval(Integer registerTimeInterval) {
+		this.registerTimeInterval = registerTimeInterval;
+	}
+
+	public String getMonitorIp() {
+		return monitorIp;
+	}
+
+	public String getIp() {
+		return ip;
+	}
+
+
+	public Integer getPort() {
+		return port;
+	}
+
+
+	public String getDomain() {
+		return domain;
+	}
+
+
+	public String getId() {
+		return id;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+
+	public Integer getPtzSpeed() {
+		return ptzSpeed;
+	}
+
+	public Integer getKeepaliveTimeOut() {
+		return keepaliveTimeOut;
+	}
+
+	public Integer getRegisterTimeInterval() {
+		return registerTimeInterval;
+	}
+
+	public boolean isAlarm() {
+		return alarm;
+	}
+
+	public void setAlarm(boolean alarm) {
+		this.alarm = alarm;
+	}
+}

+ 68 - 0
src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java

@@ -0,0 +1,68 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 系统启动时控制上级平台重新注册
+ */
+@Component
+@Order(value=3)
+public class SipPlatformRunner implements CommandLineRunner {
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Autowired
+    private ISIPCommanderForPlatform sipCommanderForPlatform;
+
+
+    @Override
+    public void run(String... args) throws Exception {
+        // 设置所有平台离线
+        storager.outlineForAllParentPlatform();
+
+        // 清理所有平台注册缓存
+        redisCatchStorage.cleanPlatformRegisterInfos();
+
+        // 停止所有推流
+//        zlmrtpServerFactory.closeAllSendRtpStream();
+
+        List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true);
+
+        for (ParentPlatform parentPlatform : parentPlatforms) {
+            redisCatchStorage.updatePlatformRegister(parentPlatform);
+
+            redisCatchStorage.updatePlatformKeepalive(parentPlatform);
+
+            ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
+
+            parentPlatformCatch.setParentPlatform(parentPlatform);
+            parentPlatformCatch.setId(parentPlatform.getServerGBId());
+            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+
+            // 取消订阅
+            sipCommanderForPlatform.unregister(parentPlatform, null, null);
+            Thread.sleep(500);
+            // 发送平台未注册消息
+            publisher.platformNotRegisterEventPublish(parentPlatform.getServerGBId());
+        }
+    }
+}

+ 117 - 0
src/main/java/com/genersoft/iot/vmp/conf/Swagger3Config.java

@@ -0,0 +1,117 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+@Configuration
+public class Swagger3Config {
+
+    @Value("${swagger-ui.enabled: true}")
+    private boolean enable;
+
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .groupName("1. 全部")
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager"))
+                .paths(PathSelectors.any())
+                .build()
+                .pathMapping("/")
+                .enable(enable);
+    }
+    @Bean
+    public Docket createRestGBApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .groupName("2. 国标28181")
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.gb28181"))
+                .paths(PathSelectors.any())
+                .build()
+                .pathMapping("/")
+                .enable(enable);
+    }
+
+    @Bean
+    public Docket createRestONVIFApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .groupName("3. ONVIF")
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.onvif"))
+                .paths(PathSelectors.any())
+                .build()
+                .pathMapping("/")
+                .enable(enable);
+    }
+
+    @Bean
+    public Docket createRestStreamProxyApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .groupName("4. 拉流转发")
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.streamProxy"))
+                .paths(PathSelectors.any())
+                .build()
+                .pathMapping("/")
+                .enable(enable);
+    }
+    @Bean
+    public Docket createRestStreamPushApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .groupName("5. 推流管理")
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.streamPush"))
+                .paths(PathSelectors.any())
+                .build()
+                .pathMapping("/")
+                .enable(enable);
+    }
+
+
+    @Bean
+    public Docket createServerApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .groupName("6. 服务管理")
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.server"))
+                .paths(PathSelectors.any())
+                .build()
+                .pathMapping("/")
+                .enable(enable);
+    }
+    @Bean
+    public Docket createUserApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .groupName("7. 用户管理")
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.user"))
+                .paths(PathSelectors.any())
+                .build()
+                .pathMapping("/")
+                .enable(enable);
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("WVP-PRO 接口文档")
+                .description("更多请咨询服务开发者(https://github.com/648540858/wvp-GB28181-pro)。")
+                .contact(new Contact("648540858", "648540858", "648540858@qq.com"))
+                .version("2.0")
+                .build();
+    }
+}

+ 34 - 0
src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java

@@ -0,0 +1,34 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.utils.SystemInfoUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 获取系统信息写入redis
+ */
+@Component
+public class SystemInfoTimerTask {
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Scheduled(fixedRate = 1000)   //每1秒执行一次
+    public void execute(){
+        try {
+            double cpuInfo = SystemInfoUtils.getCpuInfo();
+            redisCatchStorage.addCpuInfo(cpuInfo);
+            double memInfo = SystemInfoUtils.getMemInfo();
+            redisCatchStorage.addMemInfo(memInfo);
+            Map<String, String> networkInterfaces = SystemInfoUtils.getNetworkInterfaces();
+            redisCatchStorage.addNetInfo(networkInterfaces);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+    }
+}

+ 59 - 0
src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java

@@ -0,0 +1,59 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+@Configuration
+@EnableAsync(proxyTargetClass = true)
+public class ThreadPoolTaskConfig {
+
+    public static final int cpuNum = Runtime.getRuntime().availableProcessors();
+
+    /**
+     *   默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
+     *    当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
+     *  当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
+     */
+
+    /**
+     * 核心线程数(默认线程数)
+     */
+    private static final int corePoolSize = cpuNum;
+    /**
+     * 最大线程数
+     */
+    private static final int maxPoolSize = cpuNum*2;
+    /**
+     * 允许线程空闲时间(单位:默认为秒)
+     */
+    private static final int keepAliveTime = 30;
+    /**
+     * 缓冲队列大小
+     */
+    private static final int queueCapacity = 500;
+    /**
+     * 线程池名前缀
+     */
+    private static final String threadNamePrefix = "wvp-";
+
+    @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
+    public ThreadPoolTaskExecutor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(corePoolSize);
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveTime);
+        executor.setThreadNamePrefix(threadNamePrefix);
+
+        // 线程池对拒绝任务的处理策略
+        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        // 初始化
+        executor.initialize();
+        return executor;
+    }
+}

+ 147 - 0
src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java

@@ -0,0 +1,147 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Component
+@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true)
+public class UserSetup {
+
+    private Boolean savePositionHistory = Boolean.FALSE;
+
+    private Boolean autoApplyPlay = Boolean.FALSE;
+
+    private Boolean seniorSdp = Boolean.FALSE;
+
+    private Long playTimeout = 18000L;
+
+    private Boolean waitTrack = Boolean.FALSE;
+
+    private Boolean interfaceAuthentication = Boolean.TRUE;
+
+    private Boolean recordPushLive = Boolean.TRUE;
+
+    private Boolean recordSip = Boolean.TRUE;
+
+    private Boolean logInDatebase = Boolean.TRUE;
+
+    private Boolean redisConfig = Boolean.TRUE;
+
+    private String serverId = "000000";
+
+    private String thirdPartyGBIdReg = "[\\s\\S]*";
+
+    private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
+
+    public Boolean getSavePositionHistory() {
+        return savePositionHistory;
+    }
+
+    public Boolean isSavePositionHistory() {
+        return savePositionHistory;
+    }
+
+    public Boolean isAutoApplyPlay() {
+        return autoApplyPlay;
+    }
+
+    public Boolean isSeniorSdp() {
+        return seniorSdp;
+    }
+
+    public Long getPlayTimeout() {
+        return playTimeout;
+    }
+
+    public Boolean isWaitTrack() {
+        return waitTrack;
+    }
+
+    public Boolean isInterfaceAuthentication() {
+        return interfaceAuthentication;
+    }
+
+    public Boolean isRecordPushLive() {
+        return recordPushLive;
+    }
+
+    public List<String> getInterfaceAuthenticationExcludes() {
+        return interfaceAuthenticationExcludes;
+    }
+
+    public void setSavePositionHistory(Boolean savePositionHistory) {
+        this.savePositionHistory = savePositionHistory;
+    }
+
+    public void setAutoApplyPlay(Boolean autoApplyPlay) {
+        this.autoApplyPlay = autoApplyPlay;
+    }
+
+    public void setSeniorSdp(Boolean seniorSdp) {
+        this.seniorSdp = seniorSdp;
+    }
+
+    public void setPlayTimeout(Long playTimeout) {
+        this.playTimeout = playTimeout;
+    }
+
+    public void setWaitTrack(Boolean waitTrack) {
+        this.waitTrack = waitTrack;
+    }
+
+    public void setInterfaceAuthentication(boolean interfaceAuthentication) {
+        this.interfaceAuthentication = interfaceAuthentication;
+    }
+
+    public void setRecordPushLive(Boolean recordPushLive) {
+        this.recordPushLive = recordPushLive;
+    }
+
+    public void setInterfaceAuthenticationExcludes(List<String> interfaceAuthenticationExcludes) {
+        this.interfaceAuthenticationExcludes = interfaceAuthenticationExcludes;
+    }
+
+    public Boolean getLogInDatebase() {
+        return logInDatebase;
+    }
+
+    public void setLogInDatebase(Boolean logInDatebase) {
+        this.logInDatebase = logInDatebase;
+    }
+
+    public String getServerId() {
+        return serverId;
+    }
+
+    public void setServerId(String serverId) {
+        this.serverId = serverId;
+    }
+
+    public String getThirdPartyGBIdReg() {
+        return thirdPartyGBIdReg;
+    }
+
+    public void setThirdPartyGBIdReg(String thirdPartyGBIdReg) {
+        this.thirdPartyGBIdReg = thirdPartyGBIdReg;
+    }
+
+    public Boolean getRedisConfig() {
+        return redisConfig;
+    }
+
+    public void setRedisConfig(Boolean redisConfig) {
+        this.redisConfig = redisConfig;
+    }
+
+    public Boolean getRecordSip() {
+        return recordSip;
+    }
+
+    public void setRecordSip(Boolean recordSip) {
+        this.recordSip = recordSip;
+    }
+}

+ 37 - 0
src/main/java/com/genersoft/iot/vmp/conf/VersionConfig.java

@@ -0,0 +1,37 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "version")
+public class VersionConfig {
+
+    private String version;
+    private String artifactId;
+    private String description;
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 37 - 0
src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java

@@ -0,0 +1,37 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.genersoft.iot.vmp.common.VersionPo;
+import com.genersoft.iot.vmp.utils.GitUtil;
+import com.genersoft.iot.vmp.utils.JarFileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Component
+public class VersionInfo {
+
+    @Autowired
+    VersionConfig config;
+    @Autowired
+    GitUtil gitUtil;
+    @Autowired
+    JarFileUtils jarFileUtils;
+
+    public VersionPo getVersion() {
+        VersionPo versionPo = new VersionPo();
+        Map<String,String> map=jarFileUtils.readJarFile();
+        versionPo.setGIT_Revision(gitUtil.getGitCommitId());
+        versionPo.setCreate_By(map.get("Created-By"));
+        versionPo.setGIT_BRANCH(gitUtil.getBranch());
+        versionPo.setGIT_URL(gitUtil.getGitUrl());
+        versionPo.setBUILD_DATE(gitUtil.getBuildDate());
+        versionPo.setArtifactId(config.getArtifactId());
+        versionPo.setGIT_Revision_SHORT(gitUtil.getCommitIdShort());
+        versionPo.setVersion(config.getVersion());
+        versionPo.setProject(config.getDescription());
+        versionPo.setBuild_Jdk(map.get("Build-Jdk"));
+
+        return versionPo;
+    }
+}

+ 33 - 0
src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java

@@ -0,0 +1,33 @@
+package com.genersoft.iot.vmp.conf;
+
+import com.alibaba.fastjson.JSONObject;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+public class WVPTimerTask {
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Value("${server.port}")
+    private int serverPort;
+
+    @Autowired
+    private SipConfig sipConfig;
+
+    @Scheduled(fixedRate = 2 * 1000)   //每3秒执行一次
+    public void execute(){
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("ip", sipConfig.getIp());
+        jsonObject.put("port", serverPort);
+        redisCatchStorage.updateWVPInfo(jsonObject, 3);
+    }
+}

+ 64 - 0
src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java

@@ -0,0 +1,64 @@
+package com.genersoft.iot.vmp.conf.druid;
+
+import com.alibaba.druid.support.http.StatViewServlet;
+import com.alibaba.druid.support.http.WebStatFilter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+/**
+ * druid监控配置
+ * @author
+ */
+public class DruidConfiguration  {
+
+    @Value("${rj-druid-manage.allow:127.0.0.1}")
+    private String allow;
+
+    @Value("${rj-druid-manage.deny:}")
+    private String deny;
+
+    @Value("${rj-druid-manage.loginUsername:admin}")
+    private String loginUsername;
+
+    @Value("${rj-druid-manage.loginPassword:admin}")
+    private String loginPassword;
+
+    @Value("${rj-druid-manage.resetEnable:false}")
+    private String resetEnable;
+
+    /**
+     * druid监控页面开启
+     */
+    @Bean
+    public ServletRegistrationBean druidServlet() {
+        ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
+        // IP白名单
+        servletRegistrationBean.addInitParameter("allow", allow);
+        // IP黑名单(共同存在时,deny优先于allow)
+        servletRegistrationBean.addInitParameter("deny", deny);
+        //控制台管理用户
+        servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
+        servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
+        //是否能够重置数据 禁用HTML页面上的“Reset All”功能
+        servletRegistrationBean.addInitParameter("resetEnable", resetEnable);
+        return servletRegistrationBean;
+    }
+
+    /**
+     * druid url监控配置
+     */
+    @Bean
+    public FilterRegistrationBean filterRegistrationBean() {
+        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
+        filterRegistrationBean.addUrlPatterns("/*");
+        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
+        return filterRegistrationBean;
+    }
+
+
+}

+ 24 - 0
src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java

@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.conf.druid;
+
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+/**
+ * druid监控支持注解
+ *
+ * @author
+ * {@link DruidConfiguration} druid监控页面安全配置支持
+ * {@link ServletComponentScan} druid监控页面需要扫描servlet
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@Import({
+        DruidConfiguration.class,
+})
+@ServletComponentScan
+public @interface EnableDruidSupport {
+}

+ 54 - 0
src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java

@@ -0,0 +1,54 @@
+package com.genersoft.iot.vmp.conf.runner;
+
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.service.IDeviceService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+
+/**
+ * 系统启动时控制设备离线
+ */
+@Component
+@Order(value=4)
+public class SipDeviceRunner implements CommandLineRunner {
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private UserSetup userSetup;
+
+    @Autowired
+    private IDeviceService deviceService;
+
+    @Override
+    public void run(String... args) throws Exception {
+        // 读取redis没有心跳信息的则设置为离线,等收到下次心跳设置为在线
+        // 设置所有设备离线
+        storager.outlineForAll();
+        List<String> onlineForAll = redisCatchStorage.getOnlineForAll();
+        for (String deviceId : onlineForAll) {
+            storager.online(deviceId);
+            Device device = redisCatchStorage.getDevice(deviceId);
+            if (device != null && device.getSubscribeCycleForCatalog() > 0) {
+                // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
+                deviceService.addCatalogSubscribe(device);
+            }
+        }
+        // 重置cseq计数
+        redisCatchStorage.resetAllCSEQ();
+
+
+    }
+}

+ 43 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java

@@ -0,0 +1,43 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.alibaba.fastjson.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 处理匿名用户访问逻辑
+ */
+@Component
+public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+    private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
+
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
+//        logger.debug("用户需要登录,访问[{}]失败,AuthenticationException=[{}]", request.getRequestURI(), e.getMessage());
+        // 允许跨域
+        response.setHeader("Access-Control-Allow-Origin", "*");
+        // 允许自定义请求头token(允许head跨域)
+        response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");
+        response.setHeader("Content-type", "application/json;charset=UTF-8");
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("code", "-1");
+        jsonObject.put("msg", "请登录后重新请求");
+        if (request.getRequestURI().contains("api/user/login")){
+            jsonObject.put("msg", e.getMessage());
+        }
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        try {
+            response.getWriter().print(jsonObject.toJSONString());
+        } catch (IOException ioException) {
+            ioException.printStackTrace();
+        }
+    }
+}

+ 47 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java

@@ -0,0 +1,47 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
+import com.genersoft.iot.vmp.service.IUserService;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
+import com.github.xiaoymin.knife4j.core.util.StrUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+/**
+ * 用户登录认证逻辑
+ */
+@Component
+public class DefaultUserDetailsServiceImpl implements UserDetailsService {
+
+    private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
+
+    @Autowired
+    private IUserService userService;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        if (StrUtil.isBlank(username)) {
+            logger.info("登录用户:{} 不存在", username);
+            throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
+        }
+
+        // 查出密码
+        User user = userService.getUserByUsername(username);
+        if (user == null) {
+            logger.info("登录用户:{} 不存在", username);
+            throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
+        }
+        String password = SecurityUtils.encryptPassword(user.getPassword());
+        user.setPassword(password);
+        return new LoginUser(user, LocalDateTime.now());
+    }
+
+
+}

+ 24 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java

@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.web.session.InvalidSessionStrategy;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 登录超时的处理
+ */
+public class InvalidSessionHandler implements InvalidSessionStrategy {
+
+    private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class);
+
+    @Override
+    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException {
+        String username = request.getParameter("username");
+        logger.info("[登录超时] - [{}]", username);
+    }
+}

+ 65 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java

@@ -0,0 +1,65 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.*;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class LoginFailureHandler implements AuthenticationFailureHandler {
+
+    private final static Logger logger = LoggerFactory.getLogger(LoginFailureHandler.class);
+
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Override
+    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
+
+        String username = request.getParameter("username");
+        if (e instanceof AccountExpiredException) {
+            // 账号过期
+            logger.info("[登录失败] - 用户[{}]账号过期", username);
+
+        } else if (e instanceof BadCredentialsException) {
+            // 密码错误
+            logger.info("[登录失败] - 用户[{}]密码/SIP服务器ID 错误", username);
+
+        } else if (e instanceof CredentialsExpiredException) {
+            // 密码过期
+            logger.info("[登录失败] - 用户[{}]密码过期", username);
+
+        } else if (e instanceof DisabledException) {
+            // 用户被禁用
+            logger.info("[登录失败] - 用户[{}]被禁用", username);
+
+        } else if (e instanceof LockedException) {
+            // 用户被锁定
+            logger.info("[登录失败] - 用户[{}]被锁定", username);
+
+        } else if (e instanceof InternalAuthenticationServiceException) {
+            // 内部错误
+            logger.error(String.format("[登录失败] - [%s]内部错误", username), e);
+
+        } else {
+            // 其他错误
+            logger.error(String.format("[登录失败] - [%s]其他错误", username), e);
+        }
+        Map<String, Object> map = new HashMap<>();
+        map.put("code","0");
+        map.put("msg","登录失败");
+        response.setContentType("application/json;charset=UTF-8");
+        response.getWriter().write(objectMapper.writeValueAsString(map));
+    }
+}

+ 24 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java

@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class LoginSuccessHandler implements AuthenticationSuccessHandler {
+
+    private final static Logger logger = LoggerFactory.getLogger(LoginSuccessHandler.class);
+
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
+        String username = request.getParameter("username");
+        logger.info("[登录成功] - [{}]", username);
+    }
+}

+ 27 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java

@@ -0,0 +1,27 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 退出登录成功
+ */
+@Component
+public class LogoutHandler implements LogoutSuccessHandler {
+
+    private final static Logger logger = LoggerFactory.getLogger(LogoutHandler.class);
+
+    @Override
+    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
+        String username = request.getParameter("username");
+        logger.info("[退出登录成功] - [{}]", username);
+    }
+}

+ 78 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java

@@ -0,0 +1,78 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+import javax.security.sasl.AuthenticationException;
+
+public class SecurityUtils {
+
+    /**
+     * 描述根据账号密码进行调用security进行认证授权 主动调
+     * 用AuthenticationManager的authenticate方法实现
+     * 授权成功后将用户信息存入SecurityContext当中
+     * @param username 用户名
+     * @param password 密码
+     * @param authenticationManager 认证授权管理器,
+     * @see  AuthenticationManager
+     * @return UserInfo  用户信息
+     */
+    public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
+        //使用security框架自带的验证token生成器  也可以自定义。
+        UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
+        Authentication authenticate = authenticationManager.authenticate(token);
+        SecurityContextHolder.getContext().setAuthentication(authenticate);
+        LoginUser user = (LoginUser) authenticate.getPrincipal();
+        return user;
+    }
+
+    /**
+     * 获取当前登录的所有认证信息
+     * @return
+     */
+    public static Authentication getAuthentication(){
+        SecurityContext context = SecurityContextHolder.getContext();
+        return context.getAuthentication();
+    }
+
+    /**
+     * 获取当前登录用户信息
+     * @return
+     */
+    public static LoginUser getUserInfo(){
+        Authentication authentication = getAuthentication();
+        if(authentication!=null){
+            Object principal = authentication.getPrincipal();
+            if(principal!=null && !"anonymousUser".equals(principal)){
+                LoginUser user = (LoginUser) authentication.getPrincipal();
+                return user;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 获取当前登录用户ID
+     * @return
+     */
+    public static int getUserId(){
+        LoginUser user = getUserInfo();
+        return user.getId();
+    }
+
+    /**
+     * 生成BCryptPasswordEncoder密码
+     *
+     * @param password 密码
+     * @return 加密字符串
+     */
+    public static String encryptPassword(String password) {
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        return passwordEncoder.encode(password);
+    }
+}

+ 169 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java

@@ -0,0 +1,169 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.genersoft.iot.vmp.conf.UserSetup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+import java.util.List;
+
+/**
+ * 配置Spring Security
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
+
+    @Autowired
+    private UserSetup userSetup;
+
+    @Autowired
+    private DefaultUserDetailsServiceImpl userDetailsService;
+    /**
+     * 登出成功的处理
+     */
+    @Autowired
+    private LoginFailureHandler loginFailureHandler;
+    /**
+     * 登录成功的处理
+     */
+    @Autowired
+    private LoginSuccessHandler loginSuccessHandler;
+    /**
+     * 登出成功的处理
+     */
+    @Autowired
+    private LogoutHandler logoutHandler;
+    /**
+     * 未登录的处理
+     */
+    @Autowired
+    private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
+//    /**
+//     * 超时处理
+//     */
+//    @Autowired
+//    private InvalidSessionHandler invalidSessionHandler;
+
+//    /**
+//     * 顶号处理
+//     */
+//    @Autowired
+//    private SessionInformationExpiredHandler sessionInformationExpiredHandler;
+//    /**
+//     * 登录用户没有权限访问资源
+//     */
+//    @Autowired
+//    private LoginUserAccessDeniedHandler accessDeniedHandler;
+
+
+    /**
+     * 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链
+     **/
+    @Override
+    public void configure(WebSecurity web) {
+
+        if (!userSetup.isInterfaceAuthentication()) {
+            web.ignoring().antMatchers("**");
+        }else {
+            // 可以直接访问的静态数据
+            web.ignoring()
+                    .antMatchers("/")
+                    .antMatchers("/#/**")
+                    .antMatchers("/static/**")
+                    .antMatchers("/index.html")
+                    .antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**"
+                    .antMatchers("/webjars/**")
+                    .antMatchers("/swagger-resources/**")
+                    .antMatchers("/v3/api-docs/**")
+                    .antMatchers("/js/**");
+            List<String> interfaceAuthenticationExcludes = userSetup.getInterfaceAuthenticationExcludes();
+            for (String interfaceAuthenticationExclude : interfaceAuthenticationExcludes) {
+                if (interfaceAuthenticationExclude.split("/").length < 4 ) {
+                    logger.warn("{}不满足两级目录,已忽略", interfaceAuthenticationExclude);
+                }else {
+                    web.ignoring().antMatchers(interfaceAuthenticationExclude);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * 配置认证方式
+     * @param auth
+     * @throws Exception
+     */
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+        // 设置不隐藏 未找到用户异常
+        provider.setHideUserNotFoundExceptions(true);
+        // 用户认证service - 查询数据库的逻辑
+        provider.setUserDetailsService(userDetailsService);
+        // 设置密码加密算法
+        provider.setPasswordEncoder(passwordEncoder());
+        auth.authenticationProvider(provider);
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.cors().and().csrf().disable();
+        // 设置允许添加静态文件
+        http.headers().contentTypeOptions().disable();
+        http.authorizeRequests()
+                // 放行接口
+                .antMatchers("/api/user/login","/index/hook/**").permitAll()
+                // 除上面外的所有请求全部需要鉴权认证
+                .anyRequest().authenticated()
+                // 异常处理(权限拒绝、登录失效等)
+                .and().exceptionHandling()
+                .authenticationEntryPoint(anonymousAuthenticationEntryPoint)//匿名用户访问无权限资源时的异常处理
+//                .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
+                // 登入
+                .and().formLogin().permitAll()//允许所有用户
+                .successHandler(loginSuccessHandler)//登录成功处理逻辑
+                .failureHandler(loginFailureHandler)//登录失败处理逻辑
+                // 登出
+                .and().logout().logoutUrl("/api/user/logout").permitAll()//允许所有用户
+                .logoutSuccessHandler(logoutHandler)//登出成功处理逻辑
+                .deleteCookies("JSESSIONID")
+                // 会话管理
+//                .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
+//                .maximumSessions(1)//同一账号同时登录最大用户数
+//                .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理
+        ;
+
+    }
+
+    /**
+     * 描述: 密码加密算法 BCrypt 推荐使用
+     **/
+    @Bean
+    public BCryptPasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+    /**
+     * 描述: 注入AuthenticationManager管理器
+     **/
+    @Override
+    @Bean
+    public AuthenticationManager authenticationManager() throws Exception {
+        return super.authenticationManager();
+    }
+}

+ 102 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java

@@ -0,0 +1,102 @@
+package com.genersoft.iot.vmp.conf.security.dto;
+
+import com.genersoft.iot.vmp.storager.dao.dto.Role;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
+import org.springframework.security.core.CredentialsContainer;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.SpringSecurityCoreVersion;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+
+public class LoginUser implements UserDetails, CredentialsContainer {
+
+    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
+
+    /**
+     * 用户
+     */
+    private User user;
+
+
+    /**
+     * 登录时间
+     */
+    private LocalDateTime loginTime;
+
+    public LoginUser(User user, LocalDateTime loginTime) {
+        this.user = user;
+        this.loginTime = loginTime;
+    }
+
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return null;
+    }
+
+    @Override
+    public String getPassword() {
+        return user.getPassword();
+    }
+
+    @Override
+    public String getUsername() {
+        return user.getUsername();
+    }
+
+    /**
+     * 账户是否未过期,过期无法验证
+     */
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    /**
+     * 指定用户是否解锁,锁定的用户无法进行身份验证
+     * <p>
+     * 密码锁定
+     * </p>
+     */
+    @Override
+    public boolean isAccountNonLocked() {
+        return true;
+    }
+
+    /**
+     * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
+     */
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    /**
+     * 用户是否被启用或禁用。禁用的用户无法进行身份验证。
+     */
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    /**
+     * 认证完成后,擦除密码
+     */
+    @Override
+    public void eraseCredentials() {
+        user.setPassword(null);
+    }
+
+
+    public int getId() {
+        return user.getId();
+    }
+
+    public Role getRole() {
+        return user.getRole();
+    }
+
+
+}

+ 110 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@@ -0,0 +1,110 @@
+package com.genersoft.iot.vmp.gb28181;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
+import gov.nist.javax.sip.SipProviderImpl;
+import gov.nist.javax.sip.SipStackImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.stereotype.Component;
+
+import javax.sip.*;
+import java.util.Properties;
+import java.util.TooManyListenersException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class SipLayer{
+
+	private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
+
+	@Autowired
+	private SipConfig sipConfig;
+
+	@Autowired
+	private ISIPProcessorObserver sipProcessorObserver;
+
+	private SipStackImpl sipStack;
+
+	private SipFactory sipFactory;
+
+
+	@Bean("sipFactory")
+	private SipFactory createSipFactory() {
+		sipFactory = SipFactory.getInstance();
+		sipFactory.setPathName("gov.nist");
+		return sipFactory;
+	}
+	
+	@Bean("sipStack")
+	@DependsOn({"sipFactory"})
+	private SipStack createSipStack() throws PeerUnavailableException {
+		Properties properties = new Properties();
+		properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
+		properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getMonitorIp());
+		properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false");
+		/**
+		 * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE =
+		 * 0; public static final int TRACE_MESSAGES = 16; public static final int
+		 * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32;
+		 */
+		properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0");
+		properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log");
+		properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log");
+		sipStack = (SipStackImpl) sipFactory.createSipStack(properties);
+		return sipStack;
+	}
+
+	@Bean(name = "tcpSipProvider")
+	@DependsOn("sipStack")
+	private SipProviderImpl startTcpListener() {
+		ListeningPoint tcpListeningPoint = null;
+		SipProviderImpl tcpSipProvider  = null;
+		try {
+			tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP");
+			tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
+			tcpSipProvider.setDialogErrorsAutomaticallyHandled();
+			tcpSipProvider.addSipListener(sipProcessorObserver);
+			logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}");
+		} catch (TransportNotSupportedException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			logger.error("无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
+					, sipConfig.getMonitorIp(), sipConfig.getPort());
+		} catch (TooManyListenersException e) {
+			e.printStackTrace();
+		} catch (ObjectInUseException e) {
+			e.printStackTrace();
+		}
+		return tcpSipProvider;
+	}
+	
+	@Bean(name = "udpSipProvider")
+	@DependsOn("sipStack")
+	private SipProviderImpl startUdpListener() {
+		ListeningPoint udpListeningPoint = null;
+		SipProviderImpl udpSipProvider = null;
+		try {
+			udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
+			udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
+			udpSipProvider.addSipListener(sipProcessorObserver);
+		} catch (TransportNotSupportedException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			logger.error("无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
+					, sipConfig.getMonitorIp(), sipConfig.getPort());
+		} catch (TooManyListenersException e) {
+			e.printStackTrace();
+		} catch (ObjectInUseException e) {
+			e.printStackTrace();
+		}
+		logger.info("Sip Server UDP 启动成功 port [" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "]");
+		return udpSipProvider;
+	}
+
+}

+ 297 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java

@@ -0,0 +1,297 @@
+/*
+ * Conditions Of Use
+ *
+ * This software was developed by employees of the National Institute of
+ * Standards and Technology (NIST), an agency of the Federal Government.
+ * Pursuant to title 15 Untied States Code Section 105, works of NIST
+ * employees are not subject to copyright protection in the United States
+ * and are considered to be in the public domain.  As a result, a formal
+ * license is not needed to use the software.
+ *
+ * This software is provided by NIST as a service and is expressly
+ * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
+ * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
+ * AND DATA ACCURACY.  NIST does not warrant or make any representations
+ * regarding the use of the software or the results thereof, including but
+ * not limited to the correctness, accuracy, reliability or usefulness of
+ * the software.
+ *
+ * Permission to use this software is contingent upon your acceptance
+ * of the terms of this agreement
+ *
+ * .
+ *
+ */
+package com.genersoft.iot.vmp.gb28181.auth;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.DecimalFormat;
+import java.util.Date;
+import java.util.Random;
+
+import javax.sip.address.URI;
+import javax.sip.header.AuthorizationHeader;
+import javax.sip.header.HeaderFactory;
+import javax.sip.header.WWWAuthenticateHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+import gov.nist.core.InternalErrorHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implements the HTTP digest authentication method server side functionality.
+ *
+ * @author M. Ranganathan
+ * @author Marc Bednarek
+ */
+
+public class DigestServerAuthenticationHelper  {
+
+    private Logger logger = LoggerFactory.getLogger(DigestServerAuthenticationHelper.class);
+
+    private MessageDigest messageDigest;
+
+    public static final String DEFAULT_ALGORITHM = "MD5";
+    public static final String DEFAULT_SCHEME = "Digest";
+
+
+
+
+    /** to hex converter */
+    private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
+            '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+    /**
+     * Default constructor.
+     * @throws NoSuchAlgorithmException
+     */
+    public DigestServerAuthenticationHelper()
+            throws NoSuchAlgorithmException {
+        messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+    }
+
+    public static String toHexString(byte b[]) {
+        int pos = 0;
+        char[] c = new char[b.length * 2];
+        for (int i = 0; i < b.length; i++) {
+            c[pos++] = toHex[(b[i] >> 4) & 0x0F];
+            c[pos++] = toHex[b[i] & 0x0f];
+        }
+        return new String(c);
+    }
+
+    /**
+     * Generate the challenge string.
+     *
+     * @return a generated nonce.
+     */
+    private String generateNonce() {
+        // Get the time of day and run MD5 over it.
+        Date date = new Date();
+        long time = date.getTime();
+        Random rand = new Random();
+        long pad = rand.nextLong();
+        // String nonceString = (new Long(time)).toString()
+        //         + (new Long(pad)).toString();
+        String nonceString = Long.valueOf(time).toString()
+                + Long.valueOf(pad).toString();
+        byte mdbytes[] = messageDigest.digest(nonceString.getBytes());
+        // Convert the mdbytes array into a hex string.
+        return toHexString(mdbytes);
+    }
+
+    public Response generateChallenge(HeaderFactory headerFactory, Response response, String realm) {
+        try {
+            WWWAuthenticateHeader proxyAuthenticate = headerFactory
+                    .createWWWAuthenticateHeader(DEFAULT_SCHEME);
+            proxyAuthenticate.setParameter("realm", realm);
+            proxyAuthenticate.setParameter("qop", "auth");
+            proxyAuthenticate.setParameter("nonce", generateNonce());
+            proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM);
+
+            response.setHeader(proxyAuthenticate);
+        } catch (Exception ex) {
+            InternalErrorHandler.handleException(ex);
+        }
+        return response;
+    }
+    /**
+     * Authenticate the inbound request.
+     *
+     * @param request - the request to authenticate.
+     * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
+     *
+     * @return true if authentication succeded and false otherwise.
+     */
+    public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
+        AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
+        if ( authHeader == null ) return false;
+        String realm = authHeader.getRealm();
+        String username = authHeader.getUsername();
+
+        if ( username == null || realm == null ) {
+            return false;
+        }
+
+        String nonce = authHeader.getNonce();
+        URI uri = authHeader.getURI();
+        if (uri == null) {
+            return false;
+        }
+
+
+
+        String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
+        String HA1 = hashedPassword;
+
+
+        byte[] mdbytes = messageDigest.digest(A2.getBytes());
+        String HA2 = toHexString(mdbytes);
+
+        String cnonce = authHeader.getCNonce();
+        String KD = HA1 + ":" + nonce;
+        if (cnonce != null) {
+            KD += ":" + cnonce;
+        }
+        KD += ":" + HA2;
+        mdbytes = messageDigest.digest(KD.getBytes());
+        String mdString = toHexString(mdbytes);
+        String response = authHeader.getResponse();
+
+
+        return mdString.equals(response);
+    }
+
+    /**
+     * Authenticate the inbound request given plain text password.
+     *
+     * @param request - the request to authenticate.
+     * @param pass -- the plain text password.
+     *
+     * @return true if authentication succeded and false otherwise.
+     */
+    public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
+        AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
+        if ( authHeader == null ) return false;
+        String realm = authHeader.getRealm().trim();
+        String username = authHeader.getUsername().trim();
+
+        if ( username == null || realm == null ) {
+            return false;
+        }
+
+        String nonce = authHeader.getNonce();
+        URI uri = authHeader.getURI();
+        if (uri == null) {
+            return false;
+        }
+        // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
+        String qop = authHeader.getQop();
+
+        // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
+        // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
+        String cnonce = authHeader.getCNonce();
+
+        // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量
+        int nc = authHeader.getNonceCount();
+        String ncStr = String.format("%08x", nc).toUpperCase();
+        // String ncStr = new DecimalFormat("00000000").format(nc);
+        // String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16));
+
+        String A1 = username + ":" + realm + ":" + pass;
+        String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
+        byte mdbytes[] = messageDigest.digest(A1.getBytes());
+        String HA1 = toHexString(mdbytes);
+        logger.debug("A1: " + A1);
+        logger.debug("A2: " + A2);
+
+        mdbytes = messageDigest.digest(A2.getBytes());
+        String HA2 = toHexString(mdbytes);
+        logger.debug("HA1: " + HA1);
+        logger.debug("HA2: " + HA2);
+        // String cnonce = authHeader.getCNonce();
+        logger.debug("nonce: " + nonce);
+        logger.debug("nc: " + ncStr);
+        logger.debug("cnonce: " + cnonce);
+        logger.debug("qop: " + qop);
+        String KD = HA1 + ":" + nonce;
+
+        if (qop != null && qop.equals("auth") ) {
+            if (nc != -1) {
+                KD += ":" + ncStr;
+            }
+            if (cnonce != null) {
+                KD += ":" + cnonce;
+            }
+            KD += ":" + qop;
+        }
+        KD += ":" + HA2;
+        logger.debug("KD: " + KD);
+        mdbytes = messageDigest.digest(KD.getBytes());
+        String mdString = toHexString(mdbytes);
+        logger.debug("mdString: " + mdString);
+        String response = authHeader.getResponse();
+        logger.debug("response: " + response);
+        return mdString.equals(response);
+
+    }
+
+//     public static void main(String[] args) throws NoSuchAlgorithmException {
+//         String realm = "3402000000";
+//         String username = "44010000001180008012";
+
+
+//         String nonce = "07cab60999fbf643264ace27d3b7de8b";
+//         String uri = "sip:34020000002000000001@3402000000";
+//         // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
+//         String qop = "auth";
+
+//         // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
+//         // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
+//         //String cNonce = authHeader.getCNonce();
+
+//         // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量
+//         int nc = 1;
+//         String ncStr = new DecimalFormat("00000000").format(nc);
+// //        String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16));
+//         MessageDigest messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+//         String A1 = username + ":" + realm + ":" + "12345678";
+//         String A2 = "REGISTER" + ":" + uri;
+//         byte mdbytes[] = messageDigest.digest(A1.getBytes());
+//         String HA1 = toHexString(mdbytes);
+//         System.out.println("A1: " + A1);
+//         System.out.println("A2: " + A2);
+
+//         mdbytes = messageDigest.digest(A2.getBytes());
+//         String HA2 = toHexString(mdbytes);
+//         System.out.println("HA1: " + HA1);
+//         System.out.println("HA2: " + HA2);
+//         String cnonce = "0a4f113b";
+//         System.out.println("nonce: " + nonce);
+//         System.out.println("nc: " + ncStr);
+//         System.out.println("cnonce: " + cnonce);
+//         System.out.println("qop: " + qop);
+//         String KD = HA1 + ":" + nonce;
+
+//         if (qop != null && qop.equals("auth") ) {
+//             if (nc != -1) {
+//                 KD += ":" + ncStr;
+//             }
+//             if (cnonce != null) {
+//                 KD += ":" + cnonce;
+//             }
+//             KD += ":" + qop;
+//         }
+//         KD += ":" + HA2;
+//         System.out.println("KD: " + KD);
+//         mdbytes = messageDigest.digest(KD.getBytes());
+//         String mdString = toHexString(mdbytes);
+//         System.out.println("mdString: " + mdString);
+//         String response = "4f0507d4b87cdecff04bdaf4c96348f0";
+//         System.out.println("response: " + response);
+//     }
+}

+ 32 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java

@@ -0,0 +1,32 @@
+package com.genersoft.iot.vmp.gb28181.auth;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+
+/**    
+ * @description:注册逻辑处理,当设备注册后触发逻辑。
+ * @author: swwheihei
+ * @date:   2020年5月8日 下午9:41:46     
+ */
+@Component
+public class RegisterLogicHandler {
+
+	private Logger logger = LoggerFactory.getLogger(RegisterLogicHandler.class);
+
+	@Autowired
+	private SIPCommander cmder;
+	
+	public void onRegister(Device device) {
+		// 只有第一次注册时调用查询设备信息,如需更新调用更新API接口
+		if (device.isFirsRegister()) {
+			logger.info("[{}] 首次注册,查询设备信息以及通道信息", device.getDeviceId());
+			cmder.deviceInfoQuery(device);
+			cmder.catalogQuery(device, null);
+		}
+	}
+}

+ 24 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/BaiduPoint.java

@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class BaiduPoint {
+
+    String bdLng;
+
+    String bdLat;
+
+    public String getBdLng() {
+        return bdLng;
+    }
+
+    public void setBdLng(String bdLng) {
+        this.bdLng = bdLng;
+    }
+
+    public String getBdLat() {
+        return bdLat;
+    }
+
+    public void setBdLat(String bdLat) {
+        this.bdLat = bdLat;
+    }
+}

+ 43 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java

@@ -0,0 +1,43 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import java.util.Date;
+import java.util.List;
+
+public class CatalogData {
+    private int total;
+    private List<DeviceChannel> channelList;
+    private Date lastTime;
+    private Device device;
+
+    public int getTotal() {
+        return total;
+    }
+
+    public void setTotal(int total) {
+        this.total = total;
+    }
+
+    public List<DeviceChannel> getChannelList() {
+        return channelList;
+    }
+
+    public void setChannelList(List<DeviceChannel> channelList) {
+        this.channelList = channelList;
+    }
+
+    public Date getLastTime() {
+        return lastTime;
+    }
+
+    public void setLastTime(Date lastTime) {
+        this.lastTime = lastTime;
+    }
+
+    public Device getDevice() {
+        return device;
+    }
+
+    public void setDevice(Device device) {
+        this.device = device;
+    }
+}

+ 8 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdType.java

@@ -0,0 +1,8 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class CmdType {
+
+    public static final String CATALOG = "Catalog";
+    public static final String ALARM = "Alarm";
+    public static final String MOBILE_POSITION = "MobilePosition";
+}

+ 286 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java

@@ -0,0 +1,286 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+
+public class Device {
+
+	/**
+	 * 设备Id
+	 */
+	private String deviceId;
+
+	/**
+	 * 设备名
+	 */
+	private String name;
+	
+	/**
+	 * 生产厂商
+	 */
+	private String manufacturer;
+	
+	/**
+	 * 型号
+	 */
+	private String model;
+	
+	/**
+	 * 固件版本
+	 */
+	private String firmware;
+
+	/**
+	 * 传输协议
+	 * UDP/TCP
+	 */
+	private String transport;
+
+	/**
+	 * 数据流传输模式
+	 * UDP:udp传输
+	 * TCP-ACTIVE:tcp主动模式
+	 * TCP-PASSIVE:tcp被动模式
+	 */
+	private String streamMode;
+
+	/**
+	 * wan地址_ip
+	 */
+	private String  ip;
+
+	/**
+	 * wan地址_port
+	 */
+	private int port;
+
+	/**
+	 * wan地址
+	 */
+	private String  hostAddress;
+	
+	/**
+	 * 在线
+	 */
+	private int online;
+
+
+	/**
+	 * 注册时间
+	 */
+	private String registerTime;
+
+
+	/**
+	 * 心跳时间
+	 */
+	private String keepaliveTime;
+
+	/**
+	 * 通道个数
+	 */
+	private int channelCount;
+
+	/**
+	 * 注册有效期
+	 */
+	private int expires;
+
+	/**
+	 * 创建时间
+	 */
+	private String createTime;
+
+	/**
+	 * 更新时间
+	 */
+	private String updateTime;
+
+	/**
+	 * 设备使用的媒体id, 默认为null
+	 */
+	private String mediaServerId;
+
+	/**
+	 * 首次注册
+	 */
+	private boolean firsRegister;
+
+	/**
+	 * 字符集, 支持 utf-8 与 gb2312
+	 */
+	private String charset ;
+
+	/**
+	 * 目录订阅周期,0为不订阅
+	 */
+	private int subscribeCycleForCatalog ;
+
+
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getManufacturer() {
+		return manufacturer;
+	}
+
+	public void setManufacturer(String manufacturer) {
+		this.manufacturer = manufacturer;
+	}
+
+	public String getModel() {
+		return model;
+	}
+
+	public void setModel(String model) {
+		this.model = model;
+	}
+
+	public String getFirmware() {
+		return firmware;
+	}
+
+	public void setFirmware(String firmware) {
+		this.firmware = firmware;
+	}
+
+	public String getTransport() {
+		return transport;
+	}
+
+	public void setTransport(String transport) {
+		this.transport = transport;
+	}
+
+	public String getStreamMode() {
+		return streamMode;
+	}
+
+	public void setStreamMode(String streamMode) {
+		this.streamMode = streamMode;
+	}
+
+	public String getIp() {
+		return ip;
+	}
+
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
+
+	public int getPort() {
+		return port;
+	}
+
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	public String getHostAddress() {
+		return hostAddress;
+	}
+
+	public void setHostAddress(String hostAddress) {
+		this.hostAddress = hostAddress;
+	}
+
+	public int getOnline() {
+		return online;
+	}
+
+	public void setOnline(int online) {
+		this.online = online;
+	}
+
+	public int getChannelCount() {
+		return channelCount;
+	}
+
+	public void setChannelCount(int channelCount) {
+		this.channelCount = channelCount;
+	}
+
+	public String getRegisterTime() {
+		return registerTime;
+	}
+
+	public void setRegisterTime(String registerTime) {
+		this.registerTime = registerTime;
+	}
+
+	public String getKeepaliveTime() {
+		return keepaliveTime;
+	}
+
+	public void setKeepaliveTime(String keepaliveTime) {
+		this.keepaliveTime = keepaliveTime;
+	}
+
+	public int getExpires() {
+		return expires;
+	}
+
+	public void setExpires(int expires) {
+		this.expires = expires;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(String updateTime) {
+		this.updateTime = updateTime;
+	}
+
+	public String getMediaServerId() {
+		return mediaServerId;
+	}
+
+	public void setMediaServerId(String mediaServerId) {
+		this.mediaServerId = mediaServerId;
+	}
+
+	public boolean isFirsRegister() {
+		return firsRegister;
+	}
+
+	public void setFirsRegister(boolean firsRegister) {
+		this.firsRegister = firsRegister;
+	}
+
+	public String getCharset() {
+		return charset;
+	}
+
+	public void setCharset(String charset) {
+		this.charset = charset;
+	}
+
+	public int getSubscribeCycleForCatalog() {
+		return subscribeCycleForCatalog;
+	}
+
+	public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) {
+		this.subscribeCycleForCatalog = subscribeCycleForCatalog;
+	}
+}

+ 137 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java

@@ -0,0 +1,137 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+
+public class DeviceAlarm {
+
+	/**
+	 * 数据库id
+	 */
+	private String id;
+
+	/**
+	 * 设备Id
+	 */
+	private String deviceId;
+
+	/**
+	 * 通道Id
+	 */
+	private String channelId;
+
+	/**
+	 * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情-
+	 */
+	private String alarmPriority;
+
+	/**
+	 * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,
+	 * 7其他报警;可以为直接组合如12为电话报警或 设备报警-
+	 */
+	private String alarmMethod;
+
+	/**
+	 * 报警时间
+	 */
+	private String alarmTime;
+
+	/**
+	 * 报警内容描述
+	 */
+	private String alarmDescription;
+
+	/**
+	 * 经度
+	 */
+	private double longitude;
+
+	/**
+	 * 纬度
+	 */
+	private double latitude;
+
+	/**
+	 * 报警类型
+	 */
+	private String alarmType;
+
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getAlarmPriority() {
+		return alarmPriority;
+	}
+
+	public void setAlarmPriority(String alarmPriority) {
+		this.alarmPriority = alarmPriority;
+	}
+
+	public String getAlarmMethod() {
+		return alarmMethod;
+	}
+
+	public void setAlarmMethod(String alarmMethod) {
+		this.alarmMethod = alarmMethod;
+	}
+
+	public String getAlarmTime() {
+		return alarmTime;
+	}
+
+	public void setAlarmTime(String alarmTime) {
+		this.alarmTime = alarmTime;
+	}
+
+	public String getAlarmDescription() {
+		return alarmDescription;
+	}
+
+	public void setAlarmDescription(String alarmDescription) {
+		this.alarmDescription = alarmDescription;
+	}
+
+	public double getLongitude() {
+		return longitude;
+	}
+
+	public void setLongitude(double longitude) {
+		this.longitude = longitude;
+	}
+
+	public double getLatitude() {
+		return latitude;
+	}
+
+	public void setLatitude(double latitude) {
+		this.latitude = latitude;
+	}
+
+	public String getAlarmType() {
+		return alarmType;
+	}
+
+	public void setAlarmType(String alarmType) {
+		this.alarmType = alarmType;
+	}
+
+	public String getChannelId() {
+		return channelId;
+	}
+
+	public void setChannelId(String channelId) {
+		this.channelId = channelId;
+	}
+}

+ 444 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java

@@ -0,0 +1,444 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class DeviceChannel {
+
+
+	/**
+	 * 数据库自赠ID
+	 */
+	private int id;
+
+	/**
+	 * 通道id
+	 */
+	private String channelId;
+
+	/**
+	 * 设备id
+	 */
+	private String deviceId;
+	
+	/**
+	 * 通道名
+	 */
+	private String name;
+	
+	/**
+	 * 生产厂商
+	 */
+	private String manufacture;
+	
+	/**
+	 * 型号
+	 */
+	private String model;
+	
+	/**
+	 * 设备归属
+	 */
+	private String owner;
+	
+	/**
+	 * 行政区域
+	 */
+	private String civilCode;
+	
+	/**
+	 * 警区
+	 */
+	private String block;
+
+	/**
+	 * 安装地址
+	 */
+	private String address;
+	
+	/**
+	 * 是否有子设备 1有, 0没有
+	 */
+	private int parental;
+	
+	/**
+	 * 父级id
+	 */
+	private String parentId;
+	
+	/**
+	 * 信令安全模式  缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式
+	 */
+	private int safetyWay;
+	
+	/**
+	 * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式
+	 */
+	private int registerWay;
+	
+	/**
+	 * 证书序列号
+	 */
+	private String certNum;
+	
+	/**
+	 * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效
+	 */
+	private int certifiable;
+	
+	/**
+	 * 证书无效原因码
+	 */
+	private int errCode;
+	
+	/**
+	 * 证书终止有效期
+	 */
+	private String endTime;
+	
+	/**
+	 * 保密属性 缺省为0; 0:不涉密, 1:涉密
+	 */
+	private String secrecy;
+	
+	/**
+	 * IP地址
+	 */
+	private String ipAddress;
+	
+	/**
+	 * 端口号
+	 */
+	private int port;
+	
+	/**
+	 * 密码
+	 */
+	private String password;
+
+	/**
+	 * 云台类型
+	 */
+	private int PTZType;
+
+	/**
+	 * 云台类型描述字符串
+	 */
+	private String PTZTypeText;
+
+	/**
+	 * 创建时间
+	 */
+	private String createTime;
+
+	/**
+	 * 更新时间
+	 */
+	private String updateTime;
+	
+	/**
+	 * 在线/离线
+	 * 1在线,0离线
+	 * 默认在线
+	 * 信令:
+	 * <Status>ON</Status>
+	 * <Status>OFF</Status>
+	 * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF
+	 */
+	private int status;
+
+	/**
+	 * 经度
+	 */
+	private double longitude;
+	
+	/**
+	 * 纬度
+	 */
+	private double latitude;
+
+	/**
+	 * 子设备数
+	 */
+	private int subCount;
+
+	/**
+	 * 流唯一编号,存在表示正在直播
+	 */
+	private String  streamId;
+
+	/**
+	 *  是否含有音频
+	 */
+	private boolean hasAudio;
+
+	public int getId() {
+		return id;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public void setPTZType(int PTZType) {
+		this.PTZType = PTZType;
+		switch (PTZType) {
+			case 0:
+				this.PTZTypeText = "未知";
+				break;
+			case 1:
+				this.PTZTypeText = "球机";
+				break;
+			case 2:
+				this.PTZTypeText = "半球";
+				break;
+			case 3:
+				this.PTZTypeText = "固定枪机";
+				break;
+			case 4:
+				this.PTZTypeText = "遥控枪机";
+				break;
+		}
+	}
+
+	public String getChannelId() {
+		return channelId;
+	}
+
+	public void setChannelId(String channelId) {
+		this.channelId = channelId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getManufacture() {
+		return manufacture;
+	}
+
+	public void setManufacture(String manufacture) {
+		this.manufacture = manufacture;
+	}
+
+	public String getModel() {
+		return model;
+	}
+
+	public void setModel(String model) {
+		this.model = model;
+	}
+
+	public String getOwner() {
+		return owner;
+	}
+
+	public void setOwner(String owner) {
+		this.owner = owner;
+	}
+
+	public String getCivilCode() {
+		return civilCode;
+	}
+
+	public void setCivilCode(String civilCode) {
+		this.civilCode = civilCode;
+	}
+
+	public String getBlock() {
+		return block;
+	}
+
+	public void setBlock(String block) {
+		this.block = block;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+
+	public int getParental() {
+		return parental;
+	}
+
+	public void setParental(int parental) {
+		this.parental = parental;
+	}
+
+	public String getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(String parentId) {
+		this.parentId = parentId;
+	}
+
+	public int getSafetyWay() {
+		return safetyWay;
+	}
+
+	public void setSafetyWay(int safetyWay) {
+		this.safetyWay = safetyWay;
+	}
+
+	public int getRegisterWay() {
+		return registerWay;
+	}
+
+	public void setRegisterWay(int registerWay) {
+		this.registerWay = registerWay;
+	}
+
+	public String getCertNum() {
+		return certNum;
+	}
+
+	public void setCertNum(String certNum) {
+		this.certNum = certNum;
+	}
+
+	public int getCertifiable() {
+		return certifiable;
+	}
+
+	public void setCertifiable(int certifiable) {
+		this.certifiable = certifiable;
+	}
+
+	public int getErrCode() {
+		return errCode;
+	}
+
+	public void setErrCode(int errCode) {
+		this.errCode = errCode;
+	}
+
+	public String getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	public String getSecrecy() {
+		return secrecy;
+	}
+
+	public void setSecrecy(String secrecy) {
+		this.secrecy = secrecy;
+	}
+
+	public String getIpAddress() {
+		return ipAddress;
+	}
+
+	public void setIpAddress(String ipAddress) {
+		this.ipAddress = ipAddress;
+	}
+
+	public int getPort() {
+		return port;
+	}
+
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public int getPTZType() {
+		return PTZType;
+	}
+
+	public String getPTZTypeText() {
+		return PTZTypeText;
+	}
+
+	public void setPTZTypeText(String PTZTypeText) {
+		this.PTZTypeText = PTZTypeText;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public double getLongitude() {
+		return longitude;
+	}
+
+	public void setLongitude(double longitude) {
+		this.longitude = longitude;
+	}
+
+	public double getLatitude() {
+		return latitude;
+	}
+
+	public void setLatitude(double latitude) {
+		this.latitude = latitude;
+	}
+
+	public int getSubCount() {
+		return subCount;
+	}
+
+	public void setSubCount(int subCount) {
+		this.subCount = subCount;
+	}
+
+	public boolean isHasAudio() {
+		return hasAudio;
+	}
+
+	public void setHasAudio(boolean hasAudio) {
+		this.hasAudio = hasAudio;
+	}
+
+	public String getStreamId() {
+		return streamId;
+	}
+
+	public void setStreamId(String streamId) {
+		this.streamId = streamId;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(String updateTime) {
+		this.updateTime = updateTime;
+	}
+}

+ 21 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceNotFoundEvent.java

@@ -0,0 +1,21 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import javax.sip.Dialog;
+import java.util.EventObject;
+
+public class DeviceNotFoundEvent extends EventObject {
+    /**
+     * Constructs a prototypical Event.
+     *
+     * @param dialog
+     * @throws IllegalArgumentException if source is null.
+     */
+    public DeviceNotFoundEvent(Dialog dialog) {
+        super(dialog);
+    }
+
+
+    public Dialog getDialog() {
+        return (Dialog)super.getSource();
+    }
+}

+ 112 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java

@@ -0,0 +1,112 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+/**
+ * 直播流关联国标上级平台
+ */
+public class GbStream extends PlatformGbStream{
+
+    private Integer gbStreamId;
+    private String app;
+    private String stream;
+    private String gbId;
+    private String name;
+    private String mediaServerId;
+    private double longitude;
+    private double latitude;
+    private String streamType;
+    private boolean status;
+    /**
+     * GMT unix系统时间戳,单位秒
+     */
+    public Long createStamp;
+
+    @Override
+    public Integer getGbStreamId() {
+        return gbStreamId;
+    }
+
+    public void setGbStreamId(Integer gbStreamId) {
+        this.gbStreamId = gbStreamId;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public String getGbId() {
+        return gbId;
+    }
+
+    public void setGbId(String gbId) {
+        this.gbId = gbId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(double longitude) {
+        this.longitude = longitude;
+    }
+
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(double latitude) {
+        this.latitude = latitude;
+    }
+
+    public String getStreamType() {
+        return streamType;
+    }
+
+    public void setStreamType(String streamType) {
+        this.streamType = streamType;
+    }
+
+    public boolean isStatus() {
+        return status;
+    }
+
+    public void setStatus(boolean status) {
+        this.status = status;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+
+    public Long getCreateStamp() {
+        return createStamp;
+    }
+
+    public void setCreateStamp(Long createStamp) {
+        this.createStamp = createStamp;
+    }
+}

+ 35 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Host.java

@@ -0,0 +1,35 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+
+
+public class Host {
+	
+	private String ip;
+	private int port;
+	private String address;
+
+
+	public String getIp() {
+		return ip;
+	}
+
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
+
+	public int getPort() {
+		return port;
+	}
+
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+}

+ 179 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java

@@ -0,0 +1,179 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+/**
+ * @description: 移动位置bean
+ * @author: lawrencehj
+ * @date: 2021年1月23日
+ */
+
+public class MobilePosition {
+    /**
+     * 设备Id
+     */
+    private String deviceId;
+
+    /**
+     * 通道Id
+     */
+    private String channelId;
+
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+
+    /**
+     * 通知时间
+     */
+    private String time;
+
+    /**
+     * 经度
+     */
+    private double longitude;
+
+    /**
+     * 纬度
+     */
+    private double latitude;
+
+    /**
+     * 海拔高度
+     */
+    private double altitude;
+
+    /**
+     * 速度
+     */
+    private double speed;
+
+    /**
+     * 方向
+     */
+    private double direction;
+
+    /**
+     * 位置信息上报来源(Mobile Position、GPS Alarm)
+     */
+    private String reportSource;
+
+    /**
+     * 国内地理坐标系(GCJ-02 / BD-09)
+     */
+    private String GeodeticSystem;
+
+    /**
+     * 国内坐标系:经度坐标
+     */
+    private String cnLng;
+
+    /**
+     * 国内坐标系:纬度坐标
+     */
+    private String cnLat;
+
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getDeviceName() {
+        return deviceName;
+    }
+
+    public void setDeviceName(String deviceName) {
+        this.deviceName = deviceName;
+    }
+
+    public String getTime() {
+        return time;
+    }
+
+    public void setTime(String time) {
+        this.time = time;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(double longitude) {
+        this.longitude = longitude;
+    }
+    
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(double latitude) {
+        this.latitude = latitude;
+    }
+
+    public double getAltitude() {
+        return altitude;
+    }
+
+    public void setAltitude(double altitude) {
+        this.altitude = altitude;
+    }
+
+    public double getSpeed() {
+        return speed;
+    }
+
+    public void setSpeed(double speed) {
+        this.speed = speed;
+    }
+
+    public double getDirection() {
+        return direction;
+    }
+
+    public void setDirection(double direction) {
+        this.direction = direction;
+    }
+
+    public String getReportSource() {
+        return reportSource;
+    }
+
+    public void setReportSource(String reportSource) {
+        this.reportSource = reportSource;
+    }
+
+    public String getGeodeticSystem() {
+        return GeodeticSystem;
+    }
+
+    public void setGeodeticSystem(String geodeticSystem) {
+        GeodeticSystem = geodeticSystem;
+    }
+
+    public String getCnLng() {
+        return cnLng;
+    }
+
+    public void setCnLng(String cnLng) {
+        this.cnLng = cnLng;
+    }
+
+    public String getCnLat() {
+        return cnLat;
+    }
+
+    public void setCnLat(String cnLat) {
+        this.cnLat = cnLat;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
+    }
+}

+ 293 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java

@@ -0,0 +1,293 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class ParentPlatform {
+
+    /**
+     * id
+     */
+    private Integer id;
+
+    /**
+     * 是否启用
+     */
+    private boolean enable;
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * SIP服务国标编码
+     */
+    private String serverGBId;
+
+    /**
+     * SIP服务国标域
+     */
+    private String serverGBDomain;
+
+    /**
+     * SIP服务IP
+     */
+    private String serverIP;
+
+    /**
+     * SIP服务端口
+     */
+    private int serverPort;
+
+    /**
+     * 设备国标编号
+     */
+    private String deviceGBId;
+
+    /**
+     * 设备ip
+     */
+    private String deviceIp;
+
+    /**
+     * 设备端口
+     */
+    private String devicePort;
+
+    /**
+     * SIP认证用户名(默认使用设备国标编号)
+     */
+    private String username;
+
+    /**
+     * SIP认证密码
+     */
+    private String password;
+
+    /**
+     * 注册周期 (秒)
+     */
+    private String expires;
+
+    /**
+     * 心跳周期(秒)
+     */
+    private String keepTimeout;
+
+    /**
+     * 传输协议
+     * UDP/TCP
+     */
+    private String transport;
+
+    /**
+     * 字符集
+     */
+    private String characterSet;
+
+    /**
+     * 允许云台控制
+     */
+    private boolean ptz;
+
+    /**
+     * RTCP流保活
+     * TODO 预留, 暂不实现
+     */
+    private boolean rtcp;
+
+    /**
+     * 在线状态
+     */
+    private boolean status;
+
+    /**
+     * 在线状态
+     */
+    private int channelCount;
+
+    /**
+     * 共享所有的直播流
+     */
+    private boolean shareAllLiveStream;
+
+    /**
+     * 默认目录Id,自动添加的通道多放在这个目录下
+     */
+    private String catalogId;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public void setEnable(boolean enable) {
+        this.enable = enable;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getServerGBId() {
+        return serverGBId;
+    }
+
+    public void setServerGBId(String serverGBId) {
+        this.serverGBId = serverGBId;
+    }
+
+    public String getServerGBDomain() {
+        return serverGBDomain;
+    }
+
+    public void setServerGBDomain(String serverGBDomain) {
+        this.serverGBDomain = serverGBDomain;
+    }
+
+    public String getServerIP() {
+        return serverIP;
+    }
+
+    public void setServerIP(String serverIP) {
+        this.serverIP = serverIP;
+    }
+
+    public int getServerPort() {
+        return serverPort;
+    }
+
+    public void setServerPort(int serverPort) {
+        this.serverPort = serverPort;
+    }
+
+    public String getDeviceGBId() {
+        return deviceGBId;
+    }
+
+    public void setDeviceGBId(String deviceGBId) {
+        this.deviceGBId = deviceGBId;
+    }
+
+    public String getDeviceIp() {
+        return deviceIp;
+    }
+
+    public void setDeviceIp(String deviceIp) {
+        this.deviceIp = deviceIp;
+    }
+
+    public String getDevicePort() {
+        return devicePort;
+    }
+
+    public void setDevicePort(String devicePort) {
+        this.devicePort = devicePort;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getExpires() {
+        return expires;
+    }
+
+    public void setExpires(String expires) {
+        this.expires = expires;
+    }
+
+    public String getKeepTimeout() {
+        return keepTimeout;
+    }
+
+    public void setKeepTimeout(String keepTimeout) {
+        this.keepTimeout = keepTimeout;
+    }
+
+    public String getTransport() {
+        return transport;
+    }
+
+    public void setTransport(String transport) {
+        this.transport = transport;
+    }
+
+    public String getCharacterSet() {
+        return characterSet;
+    }
+
+    public void setCharacterSet(String characterSet) {
+        this.characterSet = characterSet;
+    }
+
+    public boolean isPtz() {
+        return ptz;
+    }
+
+    public void setPtz(boolean ptz) {
+        this.ptz = ptz;
+    }
+
+    public boolean isRtcp() {
+        return rtcp;
+    }
+
+    public void setRtcp(boolean rtcp) {
+        this.rtcp = rtcp;
+    }
+
+    public boolean isStatus() {
+        return status;
+    }
+
+    public void setStatus(boolean status) {
+        this.status = status;
+    }
+
+    public int getChannelCount() {
+        return channelCount;
+    }
+
+    public void setChannelCount(int channelCount) {
+        this.channelCount = channelCount;
+    }
+
+
+    public boolean isShareAllLiveStream() {
+        return shareAllLiveStream;
+    }
+
+    public void setShareAllLiveStream(boolean shareAllLiveStream) {
+        this.shareAllLiveStream = shareAllLiveStream;
+    }
+
+    public String getCatalogId() {
+        return catalogId;
+    }
+
+    public void setCatalogId(String catalogId) {
+        this.catalogId = catalogId;
+    }
+}

+ 56 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java

@@ -0,0 +1,56 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class ParentPlatformCatch {
+
+    private String id;
+
+    // 心跳未回复次数
+    private int keepAliveReply;
+
+    // 注册未回复次数
+    private int registerAliveReply;
+
+    private String callId;
+
+    private ParentPlatform parentPlatform;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getKeepAliveReply() {
+        return keepAliveReply;
+    }
+
+    public void setKeepAliveReply(int keepAliveReply) {
+        this.keepAliveReply = keepAliveReply;
+    }
+
+    public int getRegisterAliveReply() {
+        return registerAliveReply;
+    }
+
+    public void setRegisterAliveReply(int registerAliveReply) {
+        this.registerAliveReply = registerAliveReply;
+    }
+
+    public ParentPlatform getParentPlatform() {
+        return parentPlatform;
+    }
+
+    public void setParentPlatform(ParentPlatform parentPlatform) {
+        this.parentPlatform = parentPlatform;
+    }
+
+    public String getCallId() {
+        return callId;
+    }
+
+    public void setCallId(String callId) {
+        this.callId = callId;
+    }
+}

+ 71 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java

@@ -0,0 +1,71 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class PlatformCatalog {
+    private String id;
+    private String name;
+    private String platformId;
+    private String parentId;
+    private int childrenCount; // 子节点数
+    private int type; // 0 目录, 1 国标通道, 2 直播流
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPlatformId() {
+        return platformId;
+    }
+
+    public void setPlatformId(String platformId) {
+        this.platformId = platformId;
+    }
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+
+    public int getChildrenCount() {
+        return childrenCount;
+    }
+
+    public void setChildrenCount(int childrenCount) {
+        this.childrenCount = childrenCount;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public void setTypeForCatalog() {
+        this.type = 0;
+    }
+
+    public void setTypeForGb() {
+        this.type = 1;
+    }
+
+    public void setTypeForStream() {
+        this.type = 2;
+    }
+
+}

+ 31 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformGbStream.java

@@ -0,0 +1,31 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class PlatformGbStream {
+    private Integer gbStreamId;
+    private String platformId;
+    private String catalogId;
+
+    public Integer getGbStreamId() {
+        return gbStreamId;
+    }
+
+    public void setGbStreamId(Integer gbStreamId) {
+        this.gbStreamId = gbStreamId;
+    }
+
+    public String getPlatformId() {
+        return platformId;
+    }
+
+    public void setPlatformId(String platformId) {
+        this.platformId = platformId;
+    }
+
+    public String getCatalogId() {
+        return catalogId;
+    }
+
+    public void setCatalogId(String catalogId) {
+        this.catalogId = catalogId;
+    }
+}

+ 15 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformRegister.java

@@ -0,0 +1,15 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class PlatformRegister {
+
+    // 未回复次数
+    private int reply;
+
+    public int getReply() {
+        return reply;
+    }
+
+    public void setReply(int reply) {
+        this.reply = reply;
+    }
+}

+ 74 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java

@@ -0,0 +1,74 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+
+//import gov.nist.javax.sip.header.SIPDate;
+
+import java.util.List;
+
+/**    
+ * @description:设备录像信息bean 
+ * @author: swwheihei
+ * @date:   2020年5月8日 下午2:05:56     
+ */
+public class RecordInfo {
+
+	private String deviceId;
+
+	private String channelId;
+
+	private String sn;
+
+	private String name;
+	
+	private int sumNum;
+	
+	private List<RecordItem> recordList;
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public int getSumNum() {
+		return sumNum;
+	}
+
+	public void setSumNum(int sumNum) {
+		this.sumNum = sumNum;
+	}
+
+	public List<RecordItem> getRecordList() {
+		return recordList;
+	}
+
+	public void setRecordList(List<RecordItem> recordList) {
+		this.recordList = recordList;
+	}
+
+	public String getChannelId() {
+		return channelId;
+	}
+
+	public void setChannelId(String channelId) {
+		this.channelId = channelId;
+	}
+
+	public String getSn() {
+		return sn;
+	}
+
+	public void setSn(String sn) {
+		this.sn = sn;
+	}
+}

+ 133 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java

@@ -0,0 +1,133 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+
+import org.jetbrains.annotations.NotNull;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @description:设备录像bean 
+ * @author: swwheihei
+ * @date:   2020年5月8日 下午2:06:54     
+ */
+public class RecordItem  implements Comparable<RecordItem>{
+
+	private String deviceId;
+	
+	private String name;
+	
+	private String filePath;
+
+	private String fileSize;
+
+	private String address;
+	
+	private String startTime;
+	
+	private String endTime;
+	
+	private int secrecy;
+	
+	private String type;
+	
+	private String recorderId;
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getFilePath() {
+		return filePath;
+	}
+
+	public void setFilePath(String filePath) {
+		this.filePath = filePath;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+
+	public String getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(String startTime) {
+		this.startTime = startTime;
+	}
+
+	public String getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	public int getSecrecy() {
+		return secrecy;
+	}
+
+	public void setSecrecy(int secrecy) {
+		this.secrecy = secrecy;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getRecorderId() {
+		return recorderId;
+	}
+
+	public void setRecorderId(String recorderId) {
+		this.recorderId = recorderId;
+	}
+
+	public String getFileSize() {
+		return fileSize;
+	}
+
+	public void setFileSize(String fileSize) {
+		this.fileSize = fileSize;
+	}
+
+	@Override
+	public int compareTo(@NotNull RecordItem recordItem) {
+		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		try {
+			Date startTime_now = sdf.parse(startTime);
+			Date startTime_param = sdf.parse(recordItem.getStartTime());
+			if (startTime_param.compareTo(startTime_now) > 0) {
+				return -1;
+			}else {
+				return 1;
+			}
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		return 0;
+	}
+}

+ 14 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SDPInfo.java

@@ -0,0 +1,14 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import javax.sdp.SessionDescription;
+
+public class SDPInfo {
+    private byte[] source;
+    private SessionDescription sdpSource;
+    private String sessionName;
+    private Long startTime;
+    private Long stopTime;
+    private String username;
+    private String address;
+    private String ssrc;
+}

+ 203 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java

@@ -0,0 +1,203 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class SendRtpItem {
+
+    /**
+     * 推流ip
+     */
+    private String ip;
+
+    /**
+     * 推流端口
+     */
+    private int port;
+
+    /**
+     * 推流标识
+     */
+    private String ssrc;
+
+    /**
+     * 平台id
+     */
+    private String platformId;
+
+     /**
+     * 对应设备id
+     */
+    private String deviceId;
+
+    /**
+     * 直播流的应用名
+     */
+    private String app;
+
+   /**
+     * 通道id
+     */
+    private String channelId;
+
+    /**
+     * 推流状态
+     * 0 等待设备推流上来
+     * 1 等待上级平台回复ack
+     * 2 推流中
+     */
+    private int status = 0;
+
+
+    /**
+     * 设备推流的streamId
+     */
+    private String streamId;
+
+    /**
+     * 是否为tcp
+     */
+    private boolean tcp;
+
+    /**
+     * 是否为tcp主动模式
+     */
+    private boolean tcpActive;
+
+    /**
+     * 自己推流使用的端口
+     */
+    private int localPort;
+
+    /**
+     * 使用的流媒体
+     */
+    private String mediaServerId;
+
+    /**
+     *  invite的callId
+     */
+    private String CallId;
+
+    /**
+     * 是否是play, false是playback
+     */
+    private boolean isPlay;
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public String getPlatformId() {
+        return platformId;
+    }
+
+    public void setPlatformId(String platformId) {
+        this.platformId = platformId;
+    }
+
+    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 int getStatus() {
+        return status;
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStreamId() {
+        return streamId;
+    }
+
+    public void setStreamId(String streamId) {
+        this.streamId = streamId;
+    }
+
+    public boolean isTcp() {
+        return tcp;
+    }
+
+    public void setTcp(boolean tcp) {
+        this.tcp = tcp;
+    }
+
+    public int getLocalPort() {
+        return localPort;
+    }
+
+    public void setLocalPort(int localPort) {
+        this.localPort = localPort;
+    }
+
+    public boolean isTcpActive() {
+        return tcpActive;
+    }
+
+    public void setTcpActive(boolean tcpActive) {
+        this.tcpActive = tcpActive;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public String getCallId() {
+        return CallId;
+    }
+
+    public void setCallId(String callId) {
+        CallId = callId;
+    }
+
+    public boolean isPlay() {
+        return isPlay;
+    }
+
+    public void setPlay(boolean play) {
+        isPlay = play;
+    }
+}

+ 77 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java

@@ -0,0 +1,77 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+public class SsrcTransaction {
+
+    private String deviceId;
+    private String channelId;
+    private String callId;
+    private String stream;
+    private byte[] transaction;
+    private byte[] dialog;
+    private String mediaServerId;
+    private String ssrc;
+
+    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 getCallId() {
+        return callId;
+    }
+
+    public void setCallId(String callId) {
+        this.callId = callId;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+
+    public byte[] getTransaction() {
+        return transaction;
+    }
+
+    public void setTransaction(byte[] transaction) {
+        this.transaction = transaction;
+    }
+
+    public byte[] getDialog() {
+        return dialog;
+    }
+
+    public void setDialog(byte[] dialog) {
+        this.dialog = dialog;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+}

+ 100 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java

@@ -0,0 +1,100 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import javax.sip.RequestEvent;
+import javax.sip.header.*;
+import javax.sip.message.Request;
+
+public class SubscribeInfo {
+
+    public SubscribeInfo() {
+    }
+
+    public SubscribeInfo(RequestEvent evt, String id) {
+        this.id = id;
+        Request request = evt.getRequest();
+        CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
+        this.callId = callIdHeader.getCallId();
+        FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
+        this.fromTag = fromHeader.getTag();
+        ExpiresHeader expiresHeader = (ExpiresHeader)request.getHeader(ExpiresHeader.NAME);
+        this.expires = expiresHeader.getExpires();
+        EventHeader eventHeader = (EventHeader)request.getHeader(EventHeader.NAME);
+        this.eventId = eventHeader.getEventId();
+        this.eventType = eventHeader.getEventType();
+        ViaHeader viaHeader = (ViaHeader)request.getHeader(ViaHeader.NAME);
+        this.branch = viaHeader.getBranch();
+    }
+
+    private String id;
+    private int expires;
+    private String callId;
+    private String eventId;
+    private String eventType;
+    private String fromTag;
+    private String toTag;
+    private String branch;
+
+    public String getId() {
+        return id;
+    }
+
+    public int getExpires() {
+        return expires;
+    }
+
+    public String getCallId() {
+        return callId;
+    }
+
+    public String getFromTag() {
+        return fromTag;
+    }
+
+    public void setToTag(String toTag) {
+        this.toTag = toTag;
+    }
+
+    public String getToTag() {
+        return toTag;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setExpires(int expires) {
+        this.expires = expires;
+    }
+
+    public void setCallId(String callId) {
+        this.callId = callId;
+    }
+
+    public void setFromTag(String fromTag) {
+        this.fromTag = fromTag;
+    }
+
+    public String getEventId() {
+        return eventId;
+    }
+
+    public void setEventId(String eventId) {
+        this.eventId = eventId;
+    }
+
+    public String getEventType() {
+        return eventType;
+    }
+
+    public void setEventType(String eventType) {
+        this.eventType = eventType;
+    }
+
+    public String getBranch() {
+        return branch;
+    }
+
+    public void setBranch(String branch) {
+        this.branch = branch;
+    }
+}

+ 149 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java

@@ -0,0 +1,149 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import gov.nist.core.InternalErrorHandler;
+import gov.nist.javax.sip.header.SIPDate;
+
+import java.util.*;
+
+/**
+ * 重写jain sip的SIPDate解决与国标时间格式不一致的问题
+ */
+public class WvpSipDate extends SIPDate {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+    
+    private Calendar javaCal;
+
+    public WvpSipDate(long timeMillis) {
+        this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
+        Date date = new Date(timeMillis);
+        this.javaCal.setTime(date);
+        this.wkday = this.javaCal.get(7);
+        switch(this.wkday) {
+            case 1:
+                this.sipWkDay = "Sun";
+                break;
+            case 2:
+                this.sipWkDay = "Mon";
+                break;
+            case 3:
+                this.sipWkDay = "Tue";
+                break;
+            case 4:
+                this.sipWkDay = "Wed";
+                break;
+            case 5:
+                this.sipWkDay = "Thu";
+                break;
+            case 6:
+                this.sipWkDay = "Fri";
+                break;
+            case 7:
+                this.sipWkDay = "Sat";
+                break;
+            default:
+                InternalErrorHandler.handleException("No date map for wkday " + this.wkday);
+        }
+
+        this.day = this.javaCal.get(5);
+        this.month = this.javaCal.get(2);
+        switch(this.month) {
+            case 0:
+                this.sipMonth = "Jan";
+                break;
+            case 1:
+                this.sipMonth = "Feb";
+                break;
+            case 2:
+                this.sipMonth = "Mar";
+                break;
+            case 3:
+                this.sipMonth = "Apr";
+                break;
+            case 4:
+                this.sipMonth = "May";
+                break;
+            case 5:
+                this.sipMonth = "Jun";
+                break;
+            case 6:
+                this.sipMonth = "Jul";
+                break;
+            case 7:
+                this.sipMonth = "Aug";
+                break;
+            case 8:
+                this.sipMonth = "Sep";
+                break;
+            case 9:
+                this.sipMonth = "Oct";
+                break;
+            case 10:
+                this.sipMonth = "Nov";
+                break;
+            case 11:
+                this.sipMonth = "Dec";
+                break;
+            default:
+                InternalErrorHandler.handleException("No date map for month " + this.month);
+        }
+
+        this.year = this.javaCal.get(1);
+        this.hour = this.javaCal.get(11);
+        this.minute = this.javaCal.get(12);
+        this.second = this.javaCal.get(13);
+    }
+
+    @Override
+    public StringBuilder encode(StringBuilder var1) {
+        String var2;
+        if (this.month < 9) {
+            var2 = "0" + (this.month + 1);
+        } else {
+            var2 = "" + (this.month + 1);
+        }
+
+        String var3;
+        if (this.day < 10) {
+            var3 = "0" + this.day;
+        } else {
+            var3 = "" + this.day;
+        }
+
+        String var4;
+        if (this.hour < 10) {
+            var4 = "0" + this.hour;
+        } else {
+            var4 = "" + this.hour;
+        }
+
+        String var5;
+        if (this.minute < 10) {
+            var5 = "0" + this.minute;
+        } else {
+            var5 = "" + this.minute;
+        }
+
+        String var6;
+        if (this.second < 10) {
+            var6 = "0" + this.second;
+        } else {
+            var6 = "" + this.second;
+        }
+
+        int var8 = this.javaCal.get(14);
+        String var7;
+        if (var8 < 10) {
+            var7 = "00" + var8;
+        } else if (var8 < 100) {
+            var7 = "0" + var8;
+        } else {
+            var7 = "" + var8;
+        }
+
+        return var1.append(this.year).append("-").append(var2).append("-").append(var3).append("T").append(var4).append(":").append(var5).append(":").append(var6).append(".").append(var7);
+    }
+}

+ 28 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java

@@ -0,0 +1,28 @@
+package com.genersoft.iot.vmp.gb28181.event;
+
+import com.genersoft.iot.vmp.conf.UserSetup;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+
+/**    
+ * @description:设备离在线状态检测器,用于检测设备状态
+ * @author: swwheihei
+ * @date:   2020年5月13日 下午2:40:29     
+ */
+@Component
+public class DeviceOffLineDetector {
+
+	@Autowired
+    private RedisUtil redis;
+
+	@Autowired
+    private UserSetup userSetup;
+	
+	public boolean isOnline(String deviceId) {
+		String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + deviceId;
+		return redis.hasKey(key);
+	}
+}

+ 153 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java

@@ -0,0 +1,153 @@
+package com.genersoft.iot.vmp.gb28181.event;
+
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent;
+import com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire.PlatformKeepaliveExpireEvent;
+import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformCycleRegisterEvent;
+import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformNotRegisterEvent;
+import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEvent;
+import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
+import com.genersoft.iot.vmp.media.zlm.event.ZLMOfflineEvent;
+import com.genersoft.iot.vmp.media.zlm.event.ZLMOnlineEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent;
+import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**    
+ * @description:Event事件通知推送器,支持推送在线事件、离线事件
+ * @author: swwheihei
+ * @date:   2020年5月6日 上午11:30:50     
+ */
+@Component
+public class EventPublisher {
+
+	@Autowired
+    private ApplicationEventPublisher applicationEventPublisher;
+	
+	public void onlineEventPublish(Device device, String from) {
+		OnlineEvent onEvent = new OnlineEvent(this);
+		onEvent.setDevice(device);
+		onEvent.setFrom(from);
+        applicationEventPublisher.publishEvent(onEvent);
+    }
+	
+	public void outlineEventPublish(String deviceId, String from){
+		OfflineEvent outEvent = new OfflineEvent(this);
+		outEvent.setDeviceId(deviceId);
+		outEvent.setFrom(from);
+        applicationEventPublisher.publishEvent(outEvent);
+    }
+
+	/**
+	 * 平台心跳到期事件
+	 * @param platformGbId
+	 */
+	public void platformKeepaliveExpireEventPublish(String platformGbId){
+		PlatformKeepaliveExpireEvent platformNotRegisterEvent = new PlatformKeepaliveExpireEvent(this);
+		platformNotRegisterEvent.setPlatformGbID(platformGbId);
+        applicationEventPublisher.publishEvent(platformNotRegisterEvent);
+    }
+
+	/**
+	 * 平台未注册事件
+	 * @param platformGbId
+	 */
+	public void platformNotRegisterEventPublish(String platformGbId){
+		PlatformNotRegisterEvent platformNotRegisterEvent = new PlatformNotRegisterEvent(this);
+		platformNotRegisterEvent.setPlatformGbID(platformGbId);
+        applicationEventPublisher.publishEvent(platformNotRegisterEvent);
+	}
+
+	/**
+	 * 平台周期注册事件
+	 * @param paltformGbId
+	 */
+	public void platformRegisterCycleEventPublish(String paltformGbId) {
+		PlatformCycleRegisterEvent platformCycleRegisterEvent = new PlatformCycleRegisterEvent(this);
+		platformCycleRegisterEvent.setPlatformGbID(paltformGbId);
+		applicationEventPublisher.publishEvent(platformCycleRegisterEvent);
+	}
+	
+	/**
+	 * 设备报警事件
+	 * @param deviceAlarm
+	 */
+	public void deviceAlarmEventPublish(DeviceAlarm deviceAlarm) {
+		AlarmEvent alarmEvent = new AlarmEvent(this);
+		alarmEvent.setAlarmInfo(deviceAlarm);
+		applicationEventPublisher.publishEvent(alarmEvent);
+	}
+
+	public void zlmOfflineEventPublish(String mediaServerId){
+		ZLMOfflineEvent outEvent = new ZLMOfflineEvent(this);
+		outEvent.setMediaServerId(mediaServerId);
+		applicationEventPublisher.publishEvent(outEvent);
+	}
+
+	public void zlmOnlineEventPublish(String mediaServerId) {
+		ZLMOnlineEvent outEvent = new ZLMOnlineEvent(this);
+		outEvent.setMediaServerId(mediaServerId);
+		applicationEventPublisher.publishEvent(outEvent);
+	}
+
+
+	public void catalogEventPublish(String platformId, DeviceChannel deviceChannel, String type) {
+		List<DeviceChannel> deviceChannelList = new ArrayList<>();
+		deviceChannelList.add(deviceChannel);
+		catalogEventPublish(platformId, deviceChannelList, type);
+	}
+
+
+	public void catalogEventPublish(String platformId, List<DeviceChannel> deviceChannels, String type) {
+		CatalogEvent outEvent = new CatalogEvent(this);
+		List<DeviceChannel> channels = new ArrayList<>();
+		if (deviceChannels.size() > 1) {
+			// 数据去重
+			Set<String> gbIdSet = new HashSet<>();
+			for (DeviceChannel deviceChannel : deviceChannels) {
+				if (!gbIdSet.contains(deviceChannel.getChannelId())) {
+					gbIdSet.add(deviceChannel.getChannelId());
+					channels.add(deviceChannel);
+				}
+			}
+		}else {
+			channels = deviceChannels;
+		}
+		outEvent.setDeviceChannels(channels);
+		outEvent.setType(type);
+		outEvent.setPlatformId(platformId);
+		applicationEventPublisher.publishEvent(outEvent);
+	}
+
+
+	public void catalogEventPublishForStream(String platformId, List<GbStream> gbStreams, String type) {
+		CatalogEvent outEvent = new CatalogEvent(this);
+		outEvent.setGbStreams(gbStreams);
+		outEvent.setType(type);
+		outEvent.setPlatformId(platformId);
+		applicationEventPublisher.publishEvent(outEvent);
+	}
+
+
+	public void catalogEventPublishForStream(String platformId, GbStream gbStream, String type) {
+		List<GbStream> gbStreamList = new ArrayList<>();
+		gbStreamList.add(gbStream);
+		catalogEventPublishForStream(platformId, gbStreamList, type);
+	}
+
+	public void recordEndEventPush(RecordInfo recordInfo) {
+		RecordEndEvent outEvent = new RecordEndEvent(this);
+		outEvent.setRecordInfo(recordInfo);
+		applicationEventPublisher.publishEvent(outEvent);
+	}
+
+}

+ 151 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java

@@ -0,0 +1,151 @@
+package com.genersoft.iot.vmp.gb28181.event;
+
+import com.genersoft.iot.vmp.gb28181.bean.DeviceNotFoundEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.sip.*;
+import javax.sip.header.CallIdHeader;
+import javax.sip.message.Response;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+public class SipSubscribe {
+
+    private final Logger logger = LoggerFactory.getLogger(SipSubscribe.class);
+
+    private Map<String, SipSubscribe.Event> errorSubscribes = new ConcurrentHashMap<>();
+
+    private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>();
+
+    private Map<String, Date> okTimeSubscribes = new ConcurrentHashMap<>();
+    private Map<String, Date> errorTimeSubscribes = new ConcurrentHashMap<>();
+
+    //    @Scheduled(cron="*/5 * * * * ?")   //每五秒执行一次
+//    @Scheduled(fixedRate= 100 * 60 * 60 )
+    @Scheduled(cron="0 0/5 * * * ?")   //每5分钟执行一次
+    public void execute(){
+        logger.info("[定时任务] 清理过期的订阅信息");
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) - 5);
+
+        for (String key : okTimeSubscribes.keySet()) {
+            if (okTimeSubscribes.get(key).before(calendar.getTime())){
+//                logger.info("[定时任务] 清理过期的订阅信息: {}", key);
+                okSubscribes.remove(key);
+                okTimeSubscribes.remove(key);
+            }
+        }
+        for (String key : errorTimeSubscribes.keySet()) {
+            if (errorTimeSubscribes.get(key).before(calendar.getTime())){
+//                logger.info("[定时任务] 清理过期的订阅信息: {}", key);
+                errorSubscribes.remove(key);
+                errorTimeSubscribes.remove(key);
+            }
+        }
+        logger.info("okTimeSubscribes.size:{}",okTimeSubscribes.size());
+        logger.info("okSubscribes.size:{}",okSubscribes.size());
+        logger.info("errorTimeSubscribes.size:{}",errorTimeSubscribes.size());
+        logger.info("errorSubscribes.size:{}",errorSubscribes.size());
+    }
+
+    public interface Event {
+        void response(EventResult eventResult);
+    }
+
+    public static class EventResult<EventObject>{
+        public int statusCode;
+        public String type;
+        public String msg;
+        public String callId;
+        public Dialog dialog;
+        public EventObject event;
+
+        public EventResult() {
+        }
+
+        public EventResult(EventObject event) {
+            this.event = event;
+            if (event instanceof ResponseEvent) {
+                ResponseEvent responseEvent = (ResponseEvent)event;
+                Response response = responseEvent.getResponse();
+                this.dialog = responseEvent.getDialog();
+                this.type = "response";
+                if (response != null) {
+                    this.msg = response.getReasonPhrase();
+                    this.statusCode = response.getStatusCode();
+                }
+                this.callId = ((CallIdHeader)response.getHeader(CallIdHeader.NAME)).getCallId();
+
+            }else if (event instanceof TimeoutEvent) {
+                TimeoutEvent timeoutEvent = (TimeoutEvent)event;
+                this.type = "timeout";
+                this.msg = "消息超时未回复";
+                this.statusCode = -1024;
+                this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId();
+                this.dialog = timeoutEvent.getClientTransaction().getDialog();
+            }else if (event instanceof TransactionTerminatedEvent) {
+                TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event;
+                this.type = "transactionTerminated";
+                this.msg = "事务已结束";
+                this.statusCode = -1024;
+                this.callId = transactionTerminatedEvent.getClientTransaction().getDialog().getCallId().getCallId();
+                this.dialog = transactionTerminatedEvent.getClientTransaction().getDialog();
+            }else if (event instanceof DialogTerminatedEvent) {
+                DialogTerminatedEvent dialogTerminatedEvent = (DialogTerminatedEvent)event;
+                this.type = "dialogTerminated";
+                this.msg = "会话已结束";
+                this.statusCode = -1024;
+                this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId();
+                this.dialog = dialogTerminatedEvent.getDialog();
+            }else if (event instanceof DeviceNotFoundEvent) {
+                DeviceNotFoundEvent deviceNotFoundEvent = (DeviceNotFoundEvent)event;
+                this.type = "deviceNotFoundEvent";
+                this.msg = "设备未找到";
+                this.statusCode = -1024;
+                this.callId = deviceNotFoundEvent.getDialog().getCallId().getCallId();
+                this.dialog = deviceNotFoundEvent.getDialog();
+            }
+        }
+    }
+
+    public void addErrorSubscribe(String key, SipSubscribe.Event event) {
+        errorSubscribes.put(key, event);
+        errorTimeSubscribes.put(key, new Date());
+    }
+
+    public void addOkSubscribe(String key, SipSubscribe.Event event) {
+        okSubscribes.put(key, event);
+        okTimeSubscribes.put(key, new Date());
+    }
+
+    public SipSubscribe.Event getErrorSubscribe(String key) {
+        return errorSubscribes.get(key);
+    }
+
+    public void removeErrorSubscribe(String key) {
+        errorSubscribes.remove(key);
+        errorTimeSubscribes.remove(key);
+    }
+
+    public SipSubscribe.Event getOkSubscribe(String key) {
+        return okSubscribes.get(key);
+    }
+
+    public void removeOkSubscribe(String key) {
+        okSubscribes.remove(key);
+        okTimeSubscribes.remove(key);
+    }
+    public int getErrorSubscribesSize(){
+        return errorSubscribes.size();
+    }
+    public int getOkSubscribesSize(){
+        return okSubscribes.size();
+    }
+}

+ 31 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEvent.java

@@ -0,0 +1,31 @@
+package com.genersoft.iot.vmp.gb28181.event.alarm;
+
+import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @description: 报警事件
+ * @author: lawrencehj
+ * @data: 2021-01-20
+ */
+
+public class AlarmEvent extends ApplicationEvent {
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    public AlarmEvent(Object source) {
+        super(source);
+    }
+
+    private DeviceAlarm deviceAlarm;
+
+    public DeviceAlarm getAlarmInfo() {
+        return deviceAlarm;
+    }
+    
+    public void setAlarmInfo(DeviceAlarm deviceAlarm) {
+        this.deviceAlarm = deviceAlarm;
+    }
+}

+ 58 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java

@@ -0,0 +1,58 @@
+package com.genersoft.iot.vmp.gb28181.event.alarm;
+
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @description: 报警事件监听
+ * @author: lawrencehj
+ * @data: 2021-01-20
+ */
+
+@Component
+public class AlarmEventListener implements ApplicationListener<AlarmEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class);
+
+    private static Map<String, SseEmitter> sseEmitters = new Hashtable<>();
+
+    public void addSseEmitters(String browserId, SseEmitter sseEmitter) {
+        sseEmitters.put(browserId, sseEmitter);
+    }
+
+    @Override
+    public void onApplicationEvent(AlarmEvent event) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("设备报警事件触发,deviceId:" + event.getAlarmInfo().getDeviceId() + ", "
+                    + event.getAlarmInfo().getAlarmDescription());
+        }
+        String msg = "<strong>设备编码:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"
+                    + "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"
+                    + "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>"
+                    + "<br><strong>报警位置:</strong> <i>" + event.getAlarmInfo().getLongitude() + "</i>"
+                    + ", <i>" + event.getAlarmInfo().getLatitude() + "</i>";
+
+        for (Iterator<Map.Entry<String, SseEmitter>> it = sseEmitters.entrySet().iterator(); it.hasNext();) {
+            Map.Entry<String, SseEmitter> emitter = it.next();
+            logger.info("推送到SSE连接,浏览器ID: " + emitter.getKey());
+            try {
+                emitter.getValue().send(msg);
+            } catch (IOException | IllegalStateException e) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("SSE连接已关闭");
+                }
+                // 移除已关闭的连接
+                it.remove();
+                // e.printStackTrace();
+            }
+        }
+    }
+}

+ 83 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java

@@ -0,0 +1,83 @@
+package com.genersoft.iot.vmp.gb28181.event.offline;
+
+import com.genersoft.iot.vmp.conf.RedisKeyExpirationEventMessageListener;
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.stereotype.Component;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import org.springframework.util.StringUtils;
+
+import java.util.Properties;
+
+/**    
+ * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
+ * @author: swwheihei
+ * @date:   2020年5月6日 上午11:35:46     
+ */
+@Component
+public class KeepaliveTimeoutListenerForPlatform extends RedisKeyExpirationEventMessageListener {
+
+    private Logger logger = LoggerFactory.getLogger(KeepaliveTimeoutListenerForPlatform.class);
+
+	@Autowired
+	private EventPublisher publisher;
+
+	@Autowired
+	private UserSetup userSetup;
+
+	@Autowired
+	private SipSubscribe sipSubscribe;
+
+    public KeepaliveTimeoutListenerForPlatform(RedisMessageListenerContainer listenerContainer, UserSetup userSetup) {
+        super(listenerContainer, userSetup);
+    }
+
+
+    /**
+     * 监听失效的key
+     * @param message
+     * @param pattern
+     */
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        //  获取失效的key
+        String expiredKey = message.toString();
+        logger.debug(expiredKey);
+        // 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线
+        String PLATFORM_KEEPLIVEKEY_PREFIX = VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetup.getServerId() + "_";
+        String PLATFORM_REGISTER_PREFIX = VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetup.getServerId() + "_";
+        String KEEPLIVEKEY_PREFIX = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_";
+        String REGISTER_INFO_PREFIX = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetup.getServerId() + "_";
+        if (expiredKey.startsWith(PLATFORM_KEEPLIVEKEY_PREFIX)) {
+            String platformGBId = expiredKey.substring(PLATFORM_KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
+
+            publisher.platformKeepaliveExpireEventPublish(platformGBId);
+        }else if (expiredKey.startsWith(PLATFORM_REGISTER_PREFIX)) {
+            String platformGBId = expiredKey.substring(PLATFORM_REGISTER_PREFIX.length(),expiredKey.length());
+
+            publisher.platformRegisterCycleEventPublish(platformGBId);
+        }else if (expiredKey.startsWith(KEEPLIVEKEY_PREFIX)){
+            String deviceId = expiredKey.substring(KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
+            publisher.outlineEventPublish(deviceId, KEEPLIVEKEY_PREFIX);
+        }else if (expiredKey.startsWith(REGISTER_INFO_PREFIX)) {
+            String callid = expiredKey.substring(REGISTER_INFO_PREFIX.length());
+            SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult();
+            eventResult.callId = callid;
+            eventResult.msg = "注册超时";
+            eventResult.type = "register timeout";
+            sipSubscribe.getErrorSubscribe(callid).response(eventResult);
+        }
+
+    }
+}

+ 66 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java

@@ -0,0 +1,66 @@
+package com.genersoft.iot.vmp.gb28181.event.offline;
+
+import com.genersoft.iot.vmp.conf.RedisKeyExpirationEventMessageListener;
+import com.genersoft.iot.vmp.conf.UserSetup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.stereotype.Component;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+
+/**    
+ * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
+ * @author: swwheihei
+ * @date:   2020年5月6日 上午11:35:46     
+ */
+@Component
+public class KeepliveTimeoutListener extends RedisKeyExpirationEventMessageListener {
+
+    private Logger logger = LoggerFactory.getLogger(KeepliveTimeoutListener.class);
+
+	@Autowired
+	private EventPublisher publisher;
+
+	@Autowired
+	private UserSetup userSetup;
+
+    public KeepliveTimeoutListener(RedisMessageListenerContainer listenerContainer, UserSetup userSetup) {
+        super(listenerContainer, userSetup);
+    }
+
+    @Override
+    public void init() {
+        if (!userSetup.getRedisConfig()) {
+            // 配置springboot默认Config为空,即不让应用去修改redis的默认配置,因为Redis服务出于安全会禁用CONFIG命令给远程用户使用
+            setKeyspaceNotificationsConfigParameter("");
+        }
+        super.init();
+    }
+
+
+	/**
+     * 监听失效的key,key格式为keeplive_deviceId
+     * @param message
+     * @param pattern
+     */
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        //  获取失效的key
+        String expiredKey = message.toString();
+        String KEEPLIVEKEY_PREFIX = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_";
+        if(!expiredKey.startsWith(KEEPLIVEKEY_PREFIX)){
+        	logger.debug("收到redis过期监听,但开头不是"+KEEPLIVEKEY_PREFIX+",忽略");
+        	return;
+        }
+        
+        String deviceId = expiredKey.substring(KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
+        publisher.outlineEventPublish(deviceId, VideoManagerConstants.EVENT_OUTLINE_TIMEOUT);
+    }
+}

+ 40 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java

@@ -0,0 +1,40 @@
+package com.genersoft.iot.vmp.gb28181.event.offline;
+
+import org.springframework.context.ApplicationEvent;
+
+/**    
+ * @description: 离线事件类   
+ * @author: swwheihei
+ * @date:   2020年5月6日 上午11:33:13     
+ */
+public class OfflineEvent extends ApplicationEvent {
+	
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = 1L;
+
+	public OfflineEvent(Object source) {
+		super(source);
+	}
+
+	private String deviceId;
+	
+	private String from;
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getFrom() {
+		return from;
+	}
+
+	public void setFrom(String from) {
+		this.from = from;
+	}
+}

+ 77 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java

@@ -0,0 +1,77 @@
+package com.genersoft.iot.vmp.gb28181.event.offline;
+
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+
+import java.util.List;
+
+/**
+ * @description: 离线事件监听器,监听到离线后,修改设备离在线状态。 设备离线有两个来源:
+ *               1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.RegisterRequestProcessor}
+ *               2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.event.offline.OfflineEventListener}
+ * @author: swwheihei
+ * @date: 2020年5月6日 下午1:51:23
+ */
+@Component
+public class OfflineEventListener implements ApplicationListener<OfflineEvent> {
+
+	private final static Logger logger = LoggerFactory.getLogger(OfflineEventListener.class);
+	
+	@Autowired
+	private IVideoManagerStorager storager;
+	
+	@Autowired
+    private RedisUtil redis;
+
+	@Autowired
+    private UserSetup userSetup;
+
+	@Autowired
+    private EventPublisher eventPublisher;
+
+	@Override
+	public void onApplicationEvent(OfflineEvent event) {
+		
+		if (logger.isDebugEnabled()) {
+			logger.debug("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom());
+		}
+
+		String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + event.getDeviceId();
+
+		switch (event.getFrom()) {
+			// 心跳超时触发的离线事件,说明redis中已删除,无需处理
+			case VideoManagerConstants.EVENT_OUTLINE_TIMEOUT:
+				break;
+			// 设备主动注销触发的离线事件,需要删除redis中的超时监听
+			case VideoManagerConstants.EVENT_OUTLINE_UNREGISTER:
+				redis.del(key);
+				break;
+			default:
+				boolean exist = redis.hasKey(key);
+				if (exist) {
+					redis.del(key);
+				}
+		}
+
+		List<DeviceChannel> deviceChannelList = storager.queryOnlineChannelsByDeviceId(event.getDeviceId());
+		eventPublisher.catalogEventPublish(null, deviceChannelList, CatalogEvent.OFF);
+		// 处理离线监听
+		storager.outline(event.getDeviceId());
+
+		// TODO 离线取消订阅
+
+	}
+}

+ 42 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java

@@ -0,0 +1,42 @@
+package com.genersoft.iot.vmp.gb28181.event.online;
+
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import org.springframework.context.ApplicationEvent;
+
+/**    
+ * @description: 在线事件类   
+ * @author: swwheihei
+ * @date:   2020年5月6日 上午11:32:56     
+ */
+public class OnlineEvent extends ApplicationEvent {
+
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = 1L;
+
+	public OnlineEvent(Object source) {
+		super(source);
+	}
+
+	private Device device;
+	
+	private String from;
+
+	public Device getDevice() {
+		return device;
+	}
+
+	public void setDevice(Device device) {
+		this.device = device;
+	}
+
+	public String getFrom() {
+		return from;
+	}
+
+	public void setFrom(String from) {
+		this.from = from;
+	}
+	
+}

+ 105 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java

@@ -0,0 +1,105 @@
+package com.genersoft.iot.vmp.gb28181.event.online;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
+import com.genersoft.iot.vmp.service.IDeviceService;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+/**
+ * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源:
+ *               1、设备主动注销,发送注销指令
+ *               2、设备未知原因离线,心跳超时
+ * @author: swwheihei
+ * @date: 2020年5月6日 下午1:51:23
+ */
+@Component
+public class OnlineEventListener implements ApplicationListener<OnlineEvent> {
+	
+	private final static Logger logger = LoggerFactory.getLogger(OnlineEventListener.class);
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+	private IDeviceService deviceService;
+	
+	@Autowired
+    private RedisUtil redis;
+
+	@Autowired
+    private SipConfig sipConfig;
+
+	@Autowired
+    private UserSetup userSetup;
+
+	@Autowired
+    private EventPublisher eventPublisher;
+
+	private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+	@Override
+	public void onApplicationEvent(OnlineEvent event) {
+		
+		if (logger.isDebugEnabled()) {
+			logger.debug("设备上线事件触发,deviceId:" + event.getDevice().getDeviceId() + ",from:" + event.getFrom());
+		}
+		Device device = event.getDevice();
+		if (device == null) return;
+		String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + event.getDevice().getDeviceId();
+
+		switch (event.getFrom()) {
+		// 注册时触发的在线事件,先在redis中增加超时超时监听
+		case VideoManagerConstants.EVENT_ONLINE_REGISTER:
+			// 超时时间
+			redis.set(key, event.getDevice().getDeviceId(), sipConfig.getKeepaliveTimeOut());
+			device.setRegisterTime(format.format(System.currentTimeMillis()));
+			break;
+		// 设备主动发送心跳触发的在线事件
+		case VideoManagerConstants.EVENT_ONLINE_KEEPLIVE:
+			boolean exist = redis.hasKey(key);
+			// 先判断是否还存在,当设备先心跳超时后又发送心跳时,redis没有监听,需要增加
+			if (!exist) {
+				redis.set(key, event.getDevice().getDeviceId(), sipConfig.getKeepaliveTimeOut());
+			} else {
+				redis.expire(key, sipConfig.getKeepaliveTimeOut());
+			}
+			device.setKeepaliveTime(format.format(System.currentTimeMillis()));
+			break;
+		// 设备主动发送消息触发的在线事件
+		case VideoManagerConstants.EVENT_ONLINE_MESSAGE:
+
+			break;
+		}
+
+		device.setOnline(1);
+		Device deviceInStore = storager.queryVideoDevice(device.getDeviceId());
+		if (deviceInStore != null && deviceInStore.getOnline() == 0) {
+			List<DeviceChannel> deviceChannelList = storager.queryOnlineChannelsByDeviceId(device.getDeviceId());
+			eventPublisher.catalogEventPublish(null, deviceChannelList, CatalogEvent.ON);
+		}
+		// 处理上线监听
+		storager.updateDevice(device);
+
+		// 上线添加订阅
+		if (device.getSubscribeCycleForCatalog() > 0) {
+			deviceService.addCatalogSubscribe(device);
+		}
+
+	}
+}

+ 28 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEvent.java

@@ -0,0 +1,28 @@
+package com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire;
+
+import org.springframework.context.ApplicationEvent;
+
+/**
+ *  平台心跳超时事件
+ */
+public class PlatformKeepaliveExpireEvent extends ApplicationEvent {
+
+    /**
+     * Add default serial version ID
+     */
+    private static final long serialVersionUID = 1L;
+    
+    private String platformGbID;
+
+    public PlatformKeepaliveExpireEvent(Object source) {
+        super(source);
+    }
+
+    public String getPlatformGbID() {
+        return platformGbID;
+    }
+
+    public void setPlatformGbID(String platformGbID) {
+        this.platformGbID = platformGbID;
+    }
+}

+ 88 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java

@@ -0,0 +1,88 @@
+package com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire;
+
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import javax.sip.ResponseEvent;
+import javax.sip.message.Response;
+
+/**
+ * @description: 平台心跳超时事件
+ * @author: panll
+ * @date: 2020年11月5日 10:00
+ */
+@Component
+public class PlatformKeepaliveExpireEventLister implements ApplicationListener<PlatformKeepaliveExpireEvent> {
+
+
+    private final static Logger logger = LoggerFactory.getLogger(PlatformKeepaliveExpireEventLister.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private ISIPCommanderForPlatform sipCommanderForPlatform;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void onApplicationEvent(@NotNull PlatformKeepaliveExpireEvent event) {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("平台心跳到期事件事件触发,平台国标ID:" + event.getPlatformGbID());
+        }
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID());
+        ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(event.getPlatformGbID());
+        if (parentPlatformCatch == null) {
+            return;
+        }
+        if (parentPlatform == null) {
+            logger.debug("平台心跳到期事件事件触发,但平台已经删除!!! 平台国标ID:" + event.getPlatformGbID());
+            return;
+        }
+        parentPlatformCatch.setParentPlatform(parentPlatform);
+        // 发送心跳
+        if (parentPlatformCatch.getKeepAliveReply() >= 3) {
+            // 有3次未收到心跳回复, 设置平台状态为离线, 开始重新注册
+            logger.warn("有3次未收到心跳回复,标记设置平台状态为离线, 并重新注册 平台国标ID:" + event.getPlatformGbID());
+            storager.updateParentPlatformStatus(event.getPlatformGbID(), false);
+            publisher.platformNotRegisterEventPublish(event.getPlatformGbID());
+            parentPlatformCatch.setKeepAliveReply(0);
+            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+        }else {
+            // 再次发送心跳
+            String callId = sipCommanderForPlatform.keepalive(parentPlatform);
+
+            parentPlatformCatch.setKeepAliveReply( parentPlatformCatch.getKeepAliveReply() + 1);
+            // 存储心跳信息, 并设置状态为未回复, 如果多次过期仍未收到回复,则认为上级平台已经离线
+            redisCatchStorage.updatePlatformKeepalive(parentPlatform);
+            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+
+            sipSubscribe.addOkSubscribe(callId, (SipSubscribe.EventResult eventResult) ->{
+                if (eventResult.statusCode == Response.OK) {
+                    // 收到心跳响应信息,
+                    parentPlatformCatch.setKeepAliveReply(0);
+                    redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+                }
+            } );
+        }
+    }
+}

+ 24 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformCycleRegisterEvent.java

@@ -0,0 +1,24 @@
+package com.genersoft.iot.vmp.gb28181.event.platformNotRegister;
+
+import org.springframework.context.ApplicationEvent;
+
+public class PlatformCycleRegisterEvent extends ApplicationEvent {
+    /**
+     * Add default serial version ID
+     */
+    private static final long serialVersionUID = 1L;
+
+    private String platformGbID;
+
+    public String getPlatformGbID() {
+        return platformGbID;
+    }
+
+    public void setPlatformGbID(String platformGbID) {
+        this.platformGbID = platformGbID;
+    }
+
+    public PlatformCycleRegisterEvent(Object source) {
+        super(source);
+    }
+}

+ 47 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformCycleRegisterEventLister.java

@@ -0,0 +1,47 @@
+package com.genersoft.iot.vmp.gb28181.event.platformNotRegister;
+
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+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.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+@Component
+public class PlatformCycleRegisterEventLister implements ApplicationListener<PlatformCycleRegisterEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(PlatformCycleRegisterEventLister.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+    @Autowired
+    private ISIPCommanderForPlatform sipCommanderFroPlatform;
+
+    @Override
+    public void onApplicationEvent(PlatformCycleRegisterEvent event) {
+        logger.info("上级平台周期注册事件");
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID());
+        if (parentPlatform == null) {
+            logger.info("[ 平台未注册事件 ] 平台已经删除!!! 平台国标ID:" + event.getPlatformGbID());
+            return;
+        }
+        Timer timer = new Timer();
+        SipSubscribe.Event okEvent = (responseEvent)->{
+            timer.cancel();
+        };
+        sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
+        timer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                logger.info("[平台注册]再次向平台注册,平台国标ID:" + event.getPlatformGbID());
+                sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
+            }
+        }, 15*1000 ,Long.parseLong(parentPlatform.getExpires())* 1000);
+    }
+}

+ 25 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEvent.java

@@ -0,0 +1,25 @@
+package com.genersoft.iot.vmp.gb28181.event.platformNotRegister;
+
+import org.springframework.context.ApplicationEvent;
+
+public class PlatformNotRegisterEvent extends ApplicationEvent {
+
+    /**
+     * Add default serial version ID
+     */
+    private static final long serialVersionUID = 1L;
+    
+    private String platformGbID;
+
+    public PlatformNotRegisterEvent(Object source) {
+        super(source);
+    }
+
+    public String getPlatformGbID() {
+        return platformGbID;
+    }
+
+    public void setPlatformGbID(String platformGbID) {
+        this.platformGbID = platformGbID;
+    }
+}

+ 105 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java

@@ -0,0 +1,105 @@
+package com.genersoft.iot.vmp.gb28181.event.platformNotRegister;
+
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+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.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @description: 平台未注册事件,来源有二:
+ *               1、平台新添加
+ *               2、平台心跳超时
+ * @author: panll
+ * @date: 2020年11月24日 10:00
+ */
+@Component
+public class PlatformNotRegisterEventLister implements ApplicationListener<PlatformNotRegisterEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(PlatformNotRegisterEventLister.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private SIPCommanderFroPlatform sipCommanderFroPlatform;
+
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
+
+    @Autowired
+    private SipConfig config;
+
+    // @Autowired
+    // private RedisUtil redis;
+
+    @Override
+    public void onApplicationEvent(PlatformNotRegisterEvent event) {
+
+        logger.info("[ 平台未注册事件 ]平台国标ID:" + event.getPlatformGbID());
+
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID());
+        if (parentPlatform == null) {
+            logger.info("[ 平台未注册事件 ] 平台已经删除!!! 平台国标ID:" + event.getPlatformGbID());
+            return;
+        }
+        // 查询是否有推流, 如果有则都停止
+        List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(event.getPlatformGbID());
+        logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流size", sendRtpItems.size());
+        if (sendRtpItems != null && sendRtpItems.size() > 0) {
+            logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流", event.getPlatformGbID());
+            StringBuilder app = new StringBuilder();
+            StringBuilder stream = new StringBuilder();
+            for (SendRtpItem sendRtpItem : sendRtpItems) {
+                if (app.length() != 0) {
+                    app.append(",");
+                }
+                app.append(sendRtpItem.getApp());
+                if (stream.length() != 0) {
+                    stream.append(",");
+                }
+                stream.append(sendRtpItem.getStreamId());
+                redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId());
+                MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                Map<String, Object> param = new HashMap<>();
+                param.put("vhost", "__defaultVhost__");
+                param.put("app", app.toString());
+                param.put("stream", stream.toString());
+                zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
+            }
+
+
+        }
+
+        Timer timer = new Timer();
+        SipSubscribe.Event okEvent = (responseEvent)->{
+            timer.cancel();
+        };
+        logger.info("[平台注册]平台国标ID:" + event.getPlatformGbID());
+        sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
+        // 设置注册失败则每隔15秒发起一次注册
+        timer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                logger.info("[平台注册]再次向平台注册,平台国标ID:" + event.getPlatformGbID());
+                sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
+            }
+        }, config.getRegisterTimeInterval()* 1000, config.getRegisterTimeInterval()* 1000);//十五秒后再次发起注册
+    }
+}

+ 32 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEvent.java

@@ -0,0 +1,32 @@
+package com.genersoft.iot.vmp.gb28181.event.record;
+
+import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
+import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @description: 录像查询结束时间
+ * @author: pan
+ * @data: 2022-02-23
+ */
+
+public class RecordEndEvent extends ApplicationEvent {
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    public RecordEndEvent(Object source) {
+        super(source);
+    }
+
+    private RecordInfo recordInfo;
+
+    public RecordInfo getRecordInfo() {
+        return recordInfo;
+    }
+
+    public void setRecordInfo(RecordInfo recordInfo) {
+        this.recordInfo = recordInfo;
+    }
+}

+ 49 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java

@@ -0,0 +1,49 @@
+package com.genersoft.iot.vmp.gb28181.event.record;
+
+import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @description: 录像查询结束时间
+ * @author: pan
+ * @data: 2022-02-23
+ */
+
+@Component
+public class RecordEndEventListener implements ApplicationListener<RecordEndEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class);
+
+    private static Map<String, SseEmitter> sseEmitters = new Hashtable<>();
+
+    public interface RecordEndEventHandler{
+        void  handler(RecordInfo recordInfo);
+    }
+
+    private Map<String, RecordEndEventHandler> handlerMap = new HashMap<>();
+    @Override
+    public void onApplicationEvent(RecordEndEvent event) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(),
+                    event.getRecordInfo().getChannelId(), event.getRecordInfo().getRecordList().size() );
+        }
+        if (handlerMap.size() > 0) {
+            for (RecordEndEventHandler recordEndEventHandler : handlerMap.values()) {
+                recordEndEventHandler.handler(event.getRecordInfo());
+            }
+        }
+
+    }
+
+    public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {
+        handlerMap.put(device + channelId, recordEndEventHandler);
+    }
+}

+ 52 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/SubscribeListenerForPlatform.java

@@ -0,0 +1,52 @@
+package com.genersoft.iot.vmp.gb28181.event.subscribe;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.DynamicTask;
+import com.genersoft.iot.vmp.conf.RedisKeyExpirationEventMessageListener;
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
+import org.checkerframework.checker.units.qual.A;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.stereotype.Component;
+
+/**    
+ * 平台订阅到期事件
+ */
+@Component
+public class SubscribeListenerForPlatform extends RedisKeyExpirationEventMessageListener {
+
+    private Logger logger = LoggerFactory.getLogger(SubscribeListenerForPlatform.class);
+
+	@Autowired
+	private UserSetup userSetup;
+
+    @Autowired
+    private DynamicTask dynamicTask;
+
+    public SubscribeListenerForPlatform(RedisMessageListenerContainer listenerContainer, UserSetup userSetup) {
+        super(listenerContainer, userSetup);
+    }
+
+
+    /**
+     * 监听失效的key
+     * @param message
+     * @param pattern
+     */
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        //  获取失效的key
+        String expiredKey = message.toString();
+        logger.debug(expiredKey);
+        // 订阅到期
+        String PLATFORM_KEEPLIVEKEY_PREFIX = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_";
+        if (expiredKey.startsWith(PLATFORM_KEEPLIVEKEY_PREFIX)) {
+            // 取消定时任务
+            dynamicTask.stopCron(expiredKey);
+        }
+    }
+}

+ 58 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java

@@ -0,0 +1,58 @@
+package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog;
+
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.List;
+
+public class CatalogEvent  extends ApplicationEvent {
+    public CatalogEvent(Object source) {
+        super(source);
+    }
+
+    public static final String ON = "ON";         // 上线
+    public static final String OFF = "OFF";       // 离线
+    public static final String VLOST = "VLOST";   // 视频丢失
+    public static final String DEFECT = "DEFECT"; // 故障
+    public static final String ADD = "ADD";       // 增加
+    public static final String DEL = "DEL";       // 删除
+    public static final String UPDATE = "UPDATE";       // 更新
+
+    private List<DeviceChannel> deviceChannels;
+    private List<GbStream> gbStreams;
+    private String type;
+    private String platformId;
+
+    public List<DeviceChannel> getDeviceChannels() {
+        return deviceChannels;
+    }
+
+    public void setDeviceChannels(List<DeviceChannel> deviceChannels) {
+        this.deviceChannels = deviceChannels;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getPlatformId() {
+        return platformId;
+    }
+
+    public void setPlatformId(String platformId) {
+        this.platformId = platformId;
+    }
+
+    public List<GbStream> getGbStreams() {
+        return gbStreams;
+    }
+
+    public void setGbStreams(List<GbStream> gbStreams) {
+        this.gbStreams = gbStreams;
+    }
+}

+ 176 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java

@@ -0,0 +1,176 @@
+package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformNotRegisterEvent;
+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.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IGbStreamService;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+
+/**
+ * catalog事件
+ */
+@Component
+public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(CatalogEventLister.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private SIPCommanderFroPlatform sipCommanderFroPlatform;
+
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
+
+    @Autowired
+    private SipConfig config;
+
+    @Autowired
+    private UserSetup userSetup;
+
+    @Autowired
+    private IGbStreamService gbStreamService;
+
+    @Override
+    public void onApplicationEvent(CatalogEvent event) {
+        SubscribeInfo subscribe = null;
+        ParentPlatform parentPlatform = null;
+
+        Map<String, List<ParentPlatform>> parentPlatformMap = new HashMap<>();
+        if (event.getPlatformId() != null) {
+            parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformId());
+            if (parentPlatform != null && !parentPlatform.isStatus())return;
+            String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() +  "_Catalog_" + event.getPlatformId();
+            subscribe = redisCatchStorage.getSubscribe(key);
+
+            if (subscribe == null) {
+                logger.debug("发送订阅消息时发现订阅信息已经不存在");
+                return;
+            }
+        }else {
+            // 获取所用订阅
+            List<String> platforms = redisCatchStorage.getAllSubscribePlatform();
+            if (event.getDeviceChannels() != null) {
+                if (platforms.size() > 0) {
+                    for (DeviceChannel deviceChannel : event.getDeviceChannels()) {
+                        List<ParentPlatform> parentPlatformsForGB = storager.queryPlatFormListForGBWithGBId(deviceChannel.getChannelId(), platforms);
+                        parentPlatformMap.put(deviceChannel.getChannelId(), parentPlatformsForGB);
+                    }
+                }
+            }else if (event.getGbStreams() != null) {
+                if (platforms.size() > 0) {
+                    for (GbStream gbStream : event.getGbStreams()) {
+                        if (gbStream == null || StringUtils.isEmpty(gbStream.getGbId())) continue;
+                        List<ParentPlatform> parentPlatformsForGB = storager.queryPlatFormListForStreamWithGBId(gbStream.getApp(),gbStream.getStream(), platforms);
+                        parentPlatformMap.put(gbStream.getGbId(), parentPlatformsForGB);
+                    }
+                }
+            }
+        }
+        switch (event.getType()) {
+            case CatalogEvent.ON:
+            case CatalogEvent.OFF:
+            case CatalogEvent.DEL:
+
+                if (parentPlatform != null || subscribe != null) {
+                    List<DeviceChannel> deviceChannelList = new ArrayList<>();
+                    if (event.getDeviceChannels() != null) {
+                        deviceChannelList.addAll(event.getDeviceChannels());
+                    }
+                    if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
+                        for (GbStream gbStream : event.getGbStreams()) {
+                            DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform.getDeviceGBId());
+                            deviceChannelList.add(deviceChannelByStream);
+                        }
+                    }
+                    if (deviceChannelList.size() > 0) {
+                        logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
+                        sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
+                    }
+                }else if (parentPlatformMap.keySet().size() > 0) {
+                    for (String gbId : parentPlatformMap.keySet()) {
+                        List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId);
+                        if (parentPlatforms != null && parentPlatforms.size() > 0) {
+                            for (ParentPlatform platform : parentPlatforms) {
+                                String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() +  "_Catalog_" + platform.getServerGBId();
+                                SubscribeInfo subscribeInfo = redisCatchStorage.getSubscribe(key);
+                                if (subscribeInfo == null) continue;
+                                logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId);
+                                List<DeviceChannel> deviceChannelList = new ArrayList<>();
+                                DeviceChannel deviceChannel = new DeviceChannel();
+                                deviceChannel.setChannelId(gbId);
+                                deviceChannelList.add(deviceChannel);
+                                sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null);
+                            }
+                        }
+                    }
+                }
+                break;
+            case CatalogEvent.VLOST:
+                break;
+            case CatalogEvent.DEFECT:
+                break;
+            case CatalogEvent.ADD:
+            case CatalogEvent.UPDATE:
+                if (parentPlatform != null || subscribe != null) {
+                     List<DeviceChannel> deviceChannelList = new ArrayList<>();
+                     if (event.getDeviceChannels() != null) {
+                         deviceChannelList.addAll(event.getDeviceChannels());
+                     }
+                    if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
+                        for (GbStream gbStream : event.getGbStreams()) {
+                            DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform.getDeviceGBId());
+                            deviceChannelList.add(deviceChannelByStream);
+                        }
+                    }
+                    if (deviceChannelList.size() > 0) {
+                        logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
+                        sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
+                    }
+                }else if (parentPlatformMap.keySet().size() > 0) {
+                    for (String gbId : parentPlatformMap.keySet()) {
+                        List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId);
+                        if (parentPlatforms != null && parentPlatforms.size() > 0) {
+                            for (ParentPlatform platform : parentPlatforms) {
+                                String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_Catalog_" + platform.getServerGBId();
+                                SubscribeInfo subscribeInfo = redisCatchStorage.getSubscribe(key);
+                                if (subscribeInfo == null) continue;
+                                logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId);
+                                List<DeviceChannel> deviceChannelList = new ArrayList<>();
+                                DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(platform.getServerGBId(), gbId);
+                                deviceChannelList.add(deviceChannel);
+                                GbStream gbStream = storager.queryStreamInParentPlatform(platform.getServerGBId(), gbId);
+                                DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), platform.getDeviceGBId());
+                                deviceChannelList.add(deviceChannelByStream);
+                                sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null);
+                            }
+                        }
+                    }
+                }
+                break;
+            default:
+                break;
+        }
+    }
+}

+ 74 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java

@@ -0,0 +1,74 @@
+package com.genersoft.iot.vmp.gb28181.session;
+
+import com.genersoft.iot.vmp.gb28181.bean.CatalogData;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+public class CatalogDataCatch {
+
+    public static Map<String, CatalogData> data = new ConcurrentHashMap<>();
+
+    @Autowired
+    private DeferredResultHolder deferredResultHolder;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    public void put(String key, int total, Device device, List<DeviceChannel> deviceChannelList) {
+        CatalogData catalogData = data.get(key);
+        if (catalogData == null) {
+            catalogData = new CatalogData();
+            catalogData.setTotal(total);
+            catalogData.setDevice(device);
+            catalogData.setChannelList(new ArrayList<>());
+            data.put(key, catalogData);
+        }
+        catalogData.getChannelList().addAll(deviceChannelList);
+        catalogData.setLastTime(new Date(System.currentTimeMillis()));
+    }
+
+    public List<DeviceChannel> get(String key) {
+        CatalogData catalogData = data.get(key);
+        if (catalogData == null) return null;
+        return catalogData.getChannelList();
+    }
+
+    public void del(String key) {
+        data.remove(key);
+    }
+
+    @Scheduled(fixedRate = 5 * 1000)   //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时
+    private void timerTask(){
+        Set<String> keys = data.keySet();
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND) - 5);
+        for (String key : keys) {
+            CatalogData catalogData = data.get(key);
+            if (catalogData.getLastTime().before(calendar.getTime())) {
+
+                storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
+                RequestMessage msg = new RequestMessage();
+                msg.setKey(key);
+                WVPResult<Object> result = new WVPResult<>();
+                result.setCode(0);
+                result.setMsg("更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条");
+                result.setData(catalogData.getDevice());
+                msg.setData(result);
+                deferredResultHolder.invokeAllResult(msg);
+                data.remove(key);
+            }
+        }
+    }
+}

+ 139 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java

@@ -0,0 +1,139 @@
+package com.genersoft.iot.vmp.gb28181.session;
+
+import com.genersoft.iot.vmp.utils.ConfigConst;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+public class SsrcConfig {
+
+    /**
+     * zlm流媒体服务器Id
+     */
+    private String mediaServerId;
+
+    private String ssrcPrefix;
+    /**
+     * zlm流媒体服务器已用会话句柄
+     */
+    private List<String> isUsed;
+    /**
+     * zlm流媒体服务器可用会话句柄
+     */
+    private List<String> notUsed;
+
+    public SsrcConfig() {
+    }
+
+    public SsrcConfig(String mediaServerId, Set<String> usedSet, String sipDomain) {
+        this.mediaServerId = mediaServerId;
+        this.isUsed = new ArrayList<>();
+        this.ssrcPrefix = sipDomain.substring(3, 8);
+        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);
+            }
+        }
+    }
+
+
+    /**
+     * 获取视频预览的SSRC值,第一位固定为0
+     * @return ssrc
+     */
+    public String getPlaySsrc() {
+        return "0" + getSsrcPrefix() + getSN();
+    }
+
+    /**
+     * 获取录像回放的SSRC值,第一位固定为1
+     *
+     */
+    public String getPlayBackSsrc() {
+        return "1" + getSsrcPrefix() + getSN();
+    }
+
+    /**
+     * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
+     * @param ssrc 需要重置的ssrc
+     */
+    public void releaseSsrc(String ssrc) {
+        if (ssrc == null) {
+            return;
+        }
+        String sn = ssrc.substring(6);
+        try {
+            isUsed.remove(sn);
+            notUsed.add(sn);
+        }catch (NullPointerException e){
+        }
+    }
+
+    /**
+     * 获取后四位数SN,随机数
+     *
+     */
+    private 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;
+    }
+
+    public String getSsrcPrefix() {
+        return ssrcPrefix;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public void setSsrcPrefix(String ssrcPrefix) {
+        this.ssrcPrefix = ssrcPrefix;
+    }
+
+    public List<String> getIsUsed() {
+        return isUsed;
+    }
+
+    public void setIsUsed(List<String> isUsed) {
+        this.isUsed = isUsed;
+    }
+
+    public List<String> getNotUsed() {
+        return notUsed;
+    }
+
+    public void setNotUsed(List<String> notUsed) {
+        this.notUsed = notUsed;
+    }
+
+}

+ 128 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java

@@ -0,0 +1,128 @@
+package com.genersoft.iot.vmp.gb28181.session;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sip.ClientTransaction;
+import javax.sip.Dialog;
+
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
+import com.genersoft.iot.vmp.conf.UserSetup;
+import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
+import com.genersoft.iot.vmp.utils.SerializeUtils;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import gov.nist.javax.sip.stack.SIPDialog;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+/**    
+ * @description:视频流session管理器,管理视频预览、预览回放的通信句柄 
+ * @author: swwheihei
+ * @date:   2020年5月13日 下午4:03:02     
+ */
+@Component
+public class VideoStreamSessionManager {
+
+	@Autowired
+	private RedisUtil redisUtil;
+
+	@Autowired
+	private UserSetup userSetup;
+
+	/**
+	 * 添加一个点播/回放的事务信息
+	 * 后续可以通过流Id/callID
+	 * @param deviceId 设备ID
+	 * @param channelId 通道ID
+	 * @param callId 一次请求的CallID
+	 * @param stream 流名称
+	 * @param mediaServerId 所使用的流媒体ID
+	 * @param transaction 事务
+	 */
+	public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, ClientTransaction transaction){
+		SsrcTransaction ssrcTransaction = new SsrcTransaction();
+		ssrcTransaction.setDeviceId(deviceId);
+		ssrcTransaction.setChannelId(channelId);
+		ssrcTransaction.setStream(stream);
+		byte[] transactionByteArray = SerializeUtils.serialize(transaction);
+		ssrcTransaction.setTransaction(transactionByteArray);
+		ssrcTransaction.setCallId(callId);
+		ssrcTransaction.setSsrc(ssrc);
+		ssrcTransaction.setMediaServerId(mediaServerId);
+
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId()
+				+ "_" +  deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId()
+				+ "_" +  deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
+	}
+
+	public void put(String deviceId, String channelId, String callId, Dialog dialog){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
+		if (ssrcTransaction != null) {
+			byte[] dialogByteArray = SerializeUtils.serialize(dialog);
+			ssrcTransaction.setDialog(dialogByteArray);
+		}
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId()
+				+ "_" +  deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_"
+				+ ssrcTransaction.getStream(), ssrcTransaction);
+	}
+
+	
+	public ClientTransaction getTransactionByStream(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
+		if (ssrcTransaction == null) return null;
+		byte[] transactionByteArray = ssrcTransaction.getTransaction();
+		ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray);
+		return clientTransaction;
+	}
+
+	public SIPDialog getDialogByStream(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
+		if (ssrcTransaction == null) return null;
+		byte[] dialogByteArray = ssrcTransaction.getDialog();
+		if (dialogByteArray == null) return null;
+		SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
+		return dialog;
+	}
+
+	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){
+		if (StringUtils.isEmpty(callId)) callId ="*";
+		if (StringUtils.isEmpty(stream)) stream ="*";
+		String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" + deviceId + "_" + channelId + "_" + callId+ "_" + stream;
+		List<Object> scanResult = redisUtil.scan(key);
+		if (scanResult.size() == 0) return null;
+		return (SsrcTransaction)redisUtil.get((String) scanResult.get(0));
+	}
+
+	public String getMediaServerId(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getMediaServerId();
+	}
+
+	public String getSSRC(String deviceId, String channelId, String stream){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getSsrc();
+	}
+	
+	public void remove(String deviceId, String channelId, String stream) {
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
+		if (ssrcTransaction == null) return;
+		redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_"
+				+  deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream());
+	}
+
+
+	public List<SsrcTransaction> getAllSsrc() {
+		List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetup.getServerId() + "_" ));
+		List<SsrcTransaction> result= new ArrayList<>();
+		for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
+			String key = (String)ssrcTransactionKeys.get(i);
+			SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(key);
+			result.add(ssrcTransaction);
+		}
+		return result;
+	}
+}

+ 69 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/task/GPSSubscribeTask.java

@@ -0,0 +1,69 @@
+package com.genersoft.iot.vmp.gb28181.task;
+
+import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+public class GPSSubscribeTask implements Runnable{
+
+    private IRedisCatchStorage redisCatchStorage;
+    private IVideoManagerStorager storager;
+    private ISIPCommanderForPlatform sipCommanderForPlatform;
+    private String platformId;
+    private String sn;
+    private String key;
+
+    private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    public GPSSubscribeTask(IRedisCatchStorage redisCatchStorage, ISIPCommanderForPlatform sipCommanderForPlatform, IVideoManagerStorager storager, String platformId, String sn, String key) {
+        this.redisCatchStorage = redisCatchStorage;
+        this.storager = storager;
+        this.platformId = platformId;
+        this.sn = sn;
+        this.key = key;
+        this.sipCommanderForPlatform = sipCommanderForPlatform;
+    }
+
+    @Override
+    public void run() {
+
+        SubscribeInfo subscribe = redisCatchStorage.getSubscribe(key);
+        if (subscribe != null) {
+            ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
+            if (parentPlatform == null || parentPlatform.isStatus()) {
+                // TODO 暂时只处理视频流的回复,后续增加对国标设备的支持
+                List<GbStream> gbStreams = storager.queryGbStreamListInPlatform(platformId);
+                if (gbStreams.size() > 0) {
+                    for (GbStream gbStream : gbStreams) {
+                        String gbId = gbStream.getGbId();
+                        GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId);
+                        if (gbStream.isStatus()) {
+                            if (gpsMsgInfo != null) {
+                                // 发送GPS消息
+                                sipCommanderForPlatform.sendNotifyMobilePosition(parentPlatform, gpsMsgInfo, subscribe);
+                            }else {
+                                // 没有在redis找到新的消息就使用数据库的消息
+                                gpsMsgInfo = new GPSMsgInfo();
+                                gpsMsgInfo.setId(gbId);
+                                gpsMsgInfo.setLat(gbStream.getLongitude());
+                                gpsMsgInfo.setLng(gbStream.getLongitude());
+                                // 发送GPS消息
+                                sipCommanderForPlatform.sendNotifyMobilePosition(parentPlatform, gpsMsgInfo, subscribe);
+                            }
+                        }
+
+                    }
+                }
+            }
+        }
+
+
+    }
+}

+ 6 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/ISIPProcessorObserver.java

@@ -0,0 +1,6 @@
+package com.genersoft.iot.vmp.gb28181.transmit;
+
+import javax.sip.SipListener;
+
+public interface ISIPProcessorObserver extends SipListener {
+}

+ 179 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java

@@ -0,0 +1,179 @@
+package com.genersoft.iot.vmp.gb28181.transmit;
+
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.response.ISIPResponseProcessor;
+import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor;
+import gov.nist.javax.sip.message.SIPRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import javax.sip.*;
+import javax.sip.header.CSeqHeader;
+import javax.sip.header.CallIdHeader;
+import javax.sip.header.Header;
+import javax.sip.message.Response;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @description: SIP信令处理类观察者
+ * @author: panlinlin
+ * @date:   2021年11月5日 下午15:32
+ */
+@Component
+public class SIPProcessorObserver implements ISIPProcessorObserver {
+
+    private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class);
+
+    private static Map<String, ISIPRequestProcessor> requestProcessorMap = new ConcurrentHashMap<>();
+    private static Map<String, ISIPResponseProcessor> responseProcessorMap = new ConcurrentHashMap<>();
+    private static ITimeoutProcessor timeoutProcessor;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+//    @Autowired
+//    @Qualifier(value = "taskExecutor")
+//    private ThreadPoolTaskExecutor poolTaskExecutor;
+
+    /**
+     * 添加 request订阅
+     * @param method 方法名
+     * @param processor 处理程序
+     */
+    public void addRequestProcessor(String method, ISIPRequestProcessor processor) {
+        requestProcessorMap.put(method, processor);
+    }
+
+    /**
+     * 添加 response订阅
+     * @param method 方法名
+     * @param processor 处理程序
+     */
+    public void addResponseProcessor(String method, ISIPResponseProcessor processor) {
+        responseProcessorMap.put(method, processor);
+    }
+
+    /**
+     * 添加 超时事件订阅
+     * @param processor 处理程序
+     */
+    public void addTimeoutProcessor(ITimeoutProcessor processor) {
+        this.timeoutProcessor = processor;
+    }
+
+    /**
+     * 分发RequestEvent事件
+     * @param requestEvent RequestEvent事件
+     */
+    @Override
+    @Async
+    public void processRequest(RequestEvent requestEvent) {
+        logger.debug("\n收到请求:\n{}", requestEvent.getRequest());
+        String method = requestEvent.getRequest().getMethod();
+        ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method);
+        if (sipRequestProcessor == null) {
+            logger.warn("不支持方法{}的request", method);
+            return;
+        }
+        requestProcessorMap.get(method).process(requestEvent);
+
+    }
+
+    /**
+     * 分发ResponseEvent事件
+     * @param responseEvent responseEvent事件
+     */
+    @Override
+    @Async
+    public void processResponse(ResponseEvent responseEvent) {
+        Response response = responseEvent.getResponse();
+        logger.debug("\n收到响应:\n{}", responseEvent.getResponse());
+        int status = response.getStatusCode();
+        if (((status >= 200) && (status < 300)) || status == 401) { // Success!
+            CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);
+            String method = cseqHeader.getMethod();
+            ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method);
+            if (sipRequestProcessor != null) {
+                sipRequestProcessor.process(responseEvent);
+            }
+            if (responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
+                CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
+                if (callIdHeader != null) {
+                    SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId());
+                    if (subscribe != null) {
+                        SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent);
+                        sipSubscribe.removeOkSubscribe(callIdHeader.getCallId());
+                        subscribe.response(eventResult);
+                    }
+                }
+            }
+        } else if ((status >= 100) && (status < 200)) {
+            // 增加其它无需回复的响应,如101、180等
+        } else {
+            logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);
+            if (responseEvent.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) {
+                CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
+                if (callIdHeader != null) {
+                    SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
+                    if (subscribe != null) {
+                        SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent);
+                        subscribe.response(eventResult);
+                        sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId());
+                    }
+                }
+            }
+            if (responseEvent.getDialog() != null) {
+                responseEvent.getDialog().delete();
+            }
+        }
+
+
+    }
+
+    /**
+     * 向超时订阅发送消息
+     * @param timeoutEvent timeoutEvent事件
+     */
+    @Override
+    public void processTimeout(TimeoutEvent timeoutEvent) {
+        if(timeoutProcessor != null) {
+            timeoutProcessor.process(timeoutEvent);
+        }
+    }
+
+    @Override
+    public void processIOException(IOExceptionEvent exceptionEvent) {
+        System.out.println("processIOException");
+    }
+
+    @Override
+    public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
+//        Transaction transaction = null;
+//        System.out.println("processTransactionTerminated");
+//        if (transactionTerminatedEvent.isServerTransaction()) {
+//            transaction = transactionTerminatedEvent.getServerTransaction();
+//        }else {
+//            transaction = transactionTerminatedEvent.getClientTransaction();
+//        }
+//
+//        System.out.println(transaction.getBranchId());
+//        System.out.println(transaction.getState());
+//        System.out.println(transaction.getRequest().getMethod());
+//        CallIdHeader header = (CallIdHeader)transaction.getRequest().getHeader(CallIdHeader.NAME);
+//        SipSubscribe.EventResult<TransactionTerminatedEvent> terminatedEventEventResult = new SipSubscribe.EventResult<>(transactionTerminatedEvent);
+
+//        sipSubscribe.getErrorSubscribe(header.getCallId()).response(terminatedEventEventResult);
+    }
+
+    @Override
+    public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
+        CallIdHeader callId = dialogTerminatedEvent.getDialog().getCallId();
+    }
+
+
+}

+ 75 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java

@@ -0,0 +1,75 @@
+package com.genersoft.iot.vmp.gb28181.transmit.callback;
+
+import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
+import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.RecordInfoResponseMessageHandler;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@SuppressWarnings("unchecked")
+public class CheckForAllRecordsThread extends Thread {
+
+    private String key;
+
+    private RecordInfo recordInfo;
+
+    private RedisUtil redis;
+
+    private Logger logger;
+
+    private DeferredResultHolder deferredResultHolder;
+
+    public CheckForAllRecordsThread(String key, RecordInfo recordInfo) {
+        this.key = key;
+        this.recordInfo = recordInfo;
+    }
+
+    public void run() {
+
+        String cacheKey = this.key;
+
+        for (long stop = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); stop > System.nanoTime();) {
+            List<Object> cacheKeys = redis.scan(cacheKey + "_*");
+            List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
+            for (int i = 0; i < cacheKeys.size(); i++) {
+                totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
+            }
+            if (totalRecordList.size() < this.recordInfo.getSumNum()) {
+                logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + this.recordInfo.getSumNum() + "项");
+            } else {
+                logger.info("录像数据已全部获取,共 {} 项", this.recordInfo.getSumNum());
+                this.recordInfo.setRecordList(totalRecordList);
+                for (int i = 0; i < cacheKeys.size(); i++) {
+                    redis.del(cacheKeys.get(i).toString());
+                }
+                break;
+            }
+        }
+        // 自然顺序排序, 元素进行升序排列
+        this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
+        RequestMessage msg = new RequestMessage();
+        msg.setKey(DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn());
+        msg.setData(recordInfo);
+        deferredResultHolder.invokeAllResult(msg);
+        logger.info("处理完成,返回结果");
+        RecordInfoResponseMessageHandler.threadNameList.remove(cacheKey);
+    }
+    
+	public void setRedis(RedisUtil redis) {
+		this.redis = redis;
+	}
+
+	public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
+		this.deferredResultHolder = deferredResultHolder;
+    }
+
+    public void setLogger(Logger logger) {
+        this.logger = logger;
+    }
+
+}

+ 122 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java

@@ -0,0 +1,122 @@
+package com.genersoft.iot.vmp.gb28181.transmit.callback;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.async.DeferredResult;
+
+/**    
+ * @description: 异步请求处理
+ * @author: swwheihei
+ * @date:   2020年5月8日 下午7:59:05     
+ */
+@SuppressWarnings(value = {"rawtypes", "unchecked"})
+@Component
+public class DeferredResultHolder {
+	
+	public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
+	
+	public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
+	
+	public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
+	
+	public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
+
+	public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
+	
+	public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
+	
+	public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
+
+	public static final String CALLBACK_CMD_PLAY = "CALLBACK_PLAY";
+
+	public static final String CALLBACK_CMD_PLAYBACK = "CALLBACK_PLAY";
+
+	public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD";
+
+	public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
+
+	public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL";
+
+	public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
+
+	public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
+
+	public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
+
+	public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
+
+	private Map<String, Map<String, DeferredResult>> map = new ConcurrentHashMap<>();
+
+
+	public void put(String key, String id, DeferredResult result) {
+		Map<String, DeferredResult> deferredResultMap = map.get(key);
+		if (deferredResultMap == null) {
+			deferredResultMap = new ConcurrentHashMap<>();
+			map.put(key, deferredResultMap);
+		}
+		deferredResultMap.put(id, result);
+	}
+	
+	public DeferredResult get(String key, String id) {
+		Map<String, DeferredResult> deferredResultMap = map.get(key);
+		if (deferredResultMap == null) return null;
+		return deferredResultMap.get(id);
+	}
+
+	public boolean exist(String key, String id){
+		if (key == null) return false;
+		Map<String, DeferredResult> deferredResultMap = map.get(key);
+		if (id == null) {
+			return deferredResultMap != null;
+		}else {
+			return deferredResultMap != null && deferredResultMap.get(id) != null;
+		}
+	}
+
+	/**
+	 * 释放单个请求
+	 * @param msg
+	 */
+	public void invokeResult(RequestMessage msg) {
+		Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
+		if (deferredResultMap == null) {
+			return;
+		}
+		DeferredResult result = deferredResultMap.get(msg.getId());
+		if (result == null) {
+			return;
+		}
+		result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
+		deferredResultMap.remove(msg.getId());
+		if (deferredResultMap.size() == 0) {
+			map.remove(msg.getKey());
+		}
+	}
+
+	/**
+	 * 释放所有的请求
+	 * @param msg
+	 */
+	public void invokeAllResult(RequestMessage msg) {
+		Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
+		if (deferredResultMap == null) {
+			return;
+		}
+		Set<String> ids = deferredResultMap.keySet();
+		for (String id : ids) {
+			DeferredResult result = deferredResultMap.get(id);
+			if (result == null) {
+				return;
+			}
+			result.setResult(ResponseEntity.ok().body(msg.getData()));
+		}
+		map.remove(msg.getKey());
+
+	}
+}

+ 39 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java

@@ -0,0 +1,39 @@
+package com.genersoft.iot.vmp.gb28181.transmit.callback;
+
+/**    
+ * @description: 请求信息定义   
+ * @author: swwheihei
+ * @date:   2020年5月8日 下午1:09:18     
+ */
+public class RequestMessage {
+	
+	private String id;
+
+	private String key;
+
+	private Object data;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public String getKey() {
+		return key;
+	}
+
+	public Object getData() {
+		return data;
+	}
+
+	public void setData(Object data) {
+		this.data = data;
+	}
+}

+ 340 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

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

Some files were not shown because too many files changed in this diff