Commit a16b4f71ec5a4d3d526c9c35a1e1f7843a49e921

Authored by hotcoffie
Committed by GitHub
2 parents f8967275 019827fd

Merge branch 'wvp-28181-2.0' into wvp-28181-2.0

Showing 56 changed files with 1784 additions and 1494 deletions

Too many changes to show.

To preserve performance only 56 of 189 files are displayed.

.github/ISSUE_TEMPLATE/-------.md 0 → 100644
  1 +---
  2 +name: "[ 新功能 ]"
  3 +about: 新功能
  4 +title: ''
  5 +labels: ''
  6 +assignees: ''
  7 +
  8 +---
  9 +
  10 +
.github/ISSUE_TEMPLATE/--bug---.md 0 → 100644
  1 +---
  2 +name: "[ BUG ] "
  3 +about: Create a report to help us improve
  4 +title: ''
  5 +labels: ''
  6 +assignees: ''
  7 +
  8 +---
  9 +
  10 +**描述错误**
  11 +描述下您遇到的问题
  12 +
  13 +**如何复现**
  14 +有明确复现步骤的问题会很容易被解决
  15 +
  16 +**预期行为**
  17 +清晰简洁的描述您期望发生的事情
  18 +
  19 +**截图**
  20 +
  21 +
  22 +**环境信息:**
  23 + - 1. 部署方式 wvp-pro docker / zlm(docker) + 编译wvp-pro/ wvp-prp + zlm都是编译部署/
  24 + - 2. 部署环境 windows / ubuntu/ centos ...
  25 + - 3. 端口开放情况
  26 + - 4. 是否是公网部署
  27 + - 5. 是否使用https
  28 + - 6. 方便的话提供下使用的设备品牌或平台
  29 + - 7. 你做过哪些尝试
.gitmodules
1 [submodule "be.teletask.onvif-java"] 1 [submodule "be.teletask.onvif-java"]
2 path = be.teletask.onvif-java 2 path = be.teletask.onvif-java
3 - url = https://gitee.com/18010473990/be.teletask.onvif-java.git 3 + url = https://gitee.com/pan648540858/be.teletask.onvif-java.git
README.md
1 -![logo](https://gitee.com/18010473990/wvp-GB28181/raw/wvp-28181-2.0/web_src/static/logo.png) 1 +![logo](https://gitee.com/pan648540858/wvp-GB28181-pro/raw/wvp-28181-2.0/web_src/static/logo.png)
2 # 撘蝞勗28181悅閫像 2 # 撘蝞勗28181悅閫像
3 3
4 [![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit) 4 [![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit)
@@ -13,13 +13,7 @@ WEB VIDEO PLATFORM銝銝芸鈭B28181-2016蝞勗 @@ -13,13 +13,7 @@ WEB VIDEO PLATFORM銝銝芸鈭B28181-2016蝞勗
13 瘚鈭LMediaKit-https://github.com/xiongziliang/ZLMediaKit 13 瘚鈭LMediaKit-https://github.com/xiongziliang/ZLMediaKit
14 14
15 垢憿菟鈭ediaServerUI餈耨. 15 垢憿菟鈭ediaServerUI餈耨.
16 -# 敹恍  
17 -```shell  
18 -docker pull 648540858/wvp_pro  
19 16
20 -docker run --env WVP_IP="雿P" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro  
21 -```  
22 -docker雿輻霂行https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro)  
23 # 摨嚗 17 # 摨嚗
24 辣仍閫 18 辣仍閫
25 像VR蝑挽憭 19 像VR蝑挽憭
@@ -34,7 +28,7 @@ docker雿輻霂行https://hub.docker.com/r/648540858/wvp_pro](https:// @@ -34,7 +28,7 @@ docker雿輻霂行https://hub.docker.com/r/648540858/wvp_pro](https://
34 [https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki) 28 [https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki)
35 29
36 # gitee郊隞 30 # gitee郊隞
37 -https://gitee.com/18010473990/wvp-GB28181.git 31 +https://gitee.com/pan648540858/wvp-GB28181-pro.git
38 32
39 # 33 #
40 ![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_1.png) 34 ![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_1.png)
@@ -106,20 +100,28 @@ https://gitee.com/18010473990/wvp-GB28181.git @@ -106,20 +100,28 @@ https://gitee.com/18010473990/wvp-GB28181.git
106 - [ ] 瘛餃NVIF瘚挽憭 100 - [ ] 瘛餃NVIF瘚挽憭
107 - [X] 瘛餃TMP閫 101 - [X] 瘛餃TMP閫
108 - [X] 鈭垢敶閬蝵脣蝙嚗 102 - [X] 鈭垢敶閬蝵脣蝙嚗
  103 +- [X] 憭嚗韐蝸雿雿輻
109 - [X] 蝙mysql雿蛹摨恕sqlite3,撘蝞勗 104 - [X] 蝙mysql雿蛹摨恕sqlite3,撘蝞勗
110 - [ ] 瘛餃頂蝏蔭 105 - [ ] 瘛餃頂蝏蔭
111 - [ ] 瘛餃蝞∠ 106 - [ ] 瘛餃蝞∠
112 - [X] WEB蝡舀H264銝265嚗憸.711A/G.711U/AAC,閬虜蝻撘 107 - [X] WEB蝡舀H264銝265嚗憸.711A/G.711U/AAC,閬虜蝻撘
113 108
  109 +# docker敹恍
  110 +```shell
  111 +docker pull 648540858/wvp_pro
114 112
  113 +docker run --env WVP_IP="雿P" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro
  114 +```
  115 +docker雿輻霂行https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro)
115 116
116 # gitee郊隞 117 # gitee郊隞
117 -https://gitee.com/18010473990/wvp-GB28181.git 118 +https://gitee.com/pan648540858/wvp-GB28181-pro.git
118 119
119 # 雿輻撣桀 120 # 雿輻撣桀
120 QQ蝢: 901799015, 690854210(ZLM憭抒黎) 121 QQ蝢: 901799015, 690854210(ZLM憭抒黎)
121 QQ蝘縑銝銝, 蝎曉.甈Z之摰嗅蝢日悄霈.閫★撖嫣葬嚗洽餈tar漱pr 122 QQ蝘縑銝銝, 蝎曉.甈Z之摰嗅蝢日悄霈.閫★撖嫣葬嚗洽餈tar漱pr
122 123
  124 +
123 # 靚 125 # 靚
124 陝雿憭(https://github.com/xia-chu) 皞獢 126 陝雿憭(https://github.com/xia-chu) 皞獢
125 127
libs/onvif-java-1.0.2.jar deleted 100644 → 0
No preview for this file type
@@ -212,17 +212,6 @@ @@ -212,17 +212,6 @@
212 <!-- <version>1.0.8</version>--> 212 <!-- <version>1.0.8</version>-->
213 <!-- </dependency>--> 213 <!-- </dependency>-->
214 214
215 -  
216 -  
217 - <!-- onvif协议栈 -->  
218 - <dependency>  
219 - <groupId>be.teletask</groupId>  
220 - <artifactId>onvif-java</artifactId>  
221 - <version>1.0.2</version>  
222 - <scope>system</scope>  
223 - <systemPath>${project.basedir}/libs/onvif-java-1.0.2.jar</systemPath>  
224 - </dependency>  
225 -  
226 <dependency> 215 <dependency>
227 <groupId>org.springframework.boot</groupId> 216 <groupId>org.springframework.boot</groupId>
228 <artifactId>spring-boot-starter-test</artifactId> 217 <artifactId>spring-boot-starter-test</artifactId>
sql/mysql.sql
@@ -23,6 +23,7 @@ create table device @@ -23,6 +23,7 @@ create table device
23 updateTime varchar(50) not null, 23 updateTime varchar(50) not null,
24 port int not null, 24 port int not null,
25 expires int not null, 25 expires int not null,
  26 + subscribeCycleForCatalog int not null,
26 hostAddress varchar(50) not null, 27 hostAddress varchar(50) not null,
27 charset varchar(50) not null 28 charset varchar(50) not null
28 ); 29 );
@@ -207,6 +208,7 @@ create table stream_proxy @@ -207,6 +208,7 @@ create table stream_proxy
207 enable_hls bit null, 208 enable_hls bit null,
208 enable_mp4 bit null, 209 enable_mp4 bit null,
209 enable bit not null, 210 enable bit not null,
  211 + enable_remove_none_reader bit not null,
210 createTime varchar(50) not null, 212 createTime varchar(50) not null,
211 primary key (app, stream) 213 primary key (app, stream)
212 ); 214 );
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
@@ -6,6 +6,7 @@ import org.springframework.boot.SpringApplication; @@ -6,6 +6,7 @@ import org.springframework.boot.SpringApplication;
6 import org.springframework.boot.autoconfigure.SpringBootApplication; 6 import org.springframework.boot.autoconfigure.SpringBootApplication;
7 import org.springframework.boot.web.servlet.ServletComponentScan; 7 import org.springframework.boot.web.servlet.ServletComponentScan;
8 import org.springframework.context.ConfigurableApplicationContext; 8 import org.springframework.context.ConfigurableApplicationContext;
  9 +import org.springframework.scheduling.annotation.EnableAsync;
9 import org.springframework.scheduling.annotation.EnableScheduling; 10 import org.springframework.scheduling.annotation.EnableScheduling;
10 import springfox.documentation.oas.annotations.EnableOpenApi; 11 import springfox.documentation.oas.annotations.EnableOpenApi;
11 12
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
@@ -30,7 +30,7 @@ public class StreamInfo { @@ -30,7 +30,7 @@ public class StreamInfo {
30 private String rtsps; 30 private String rtsps;
31 private String rtc; 31 private String rtc;
32 private String mediaServerId; 32 private String mediaServerId;
33 - private JSONArray tracks; 33 + private Object tracks;
34 34
35 public static class TransactionInfo{ 35 public static class TransactionInfo{
36 public String callId; 36 public String callId;
@@ -105,11 +105,11 @@ public class StreamInfo { @@ -105,11 +105,11 @@ public class StreamInfo {
105 this.rtsp = rtsp; 105 this.rtsp = rtsp;
106 } 106 }
107 107
108 - public JSONArray getTracks() { 108 + public Object getTracks() {
109 return tracks; 109 return tracks;
110 } 110 }
111 111
112 - public void setTracks(JSONArray tracks) { 112 + public void setTracks(Object tracks) {
113 this.tracks = tracks; 113 this.tracks = tracks;
114 } 114 }
115 115
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
1 package com.genersoft.iot.vmp.common; 1 package com.genersoft.iot.vmp.common;
2 2
3 /** 3 /**
4 - * @Description: 定义常量 4 + * @description: 定义常量
5 * @author: swwheihei 5 * @author: swwheihei
6 * @date: 2019年5月30日 下午3:04:04 6 * @date: 2019年5月30日 下午3:04:04
7 * 7 *
8 */ 8 */
9 public class VideoManagerConstants { 9 public class VideoManagerConstants {
10 10
11 - public static final String WVP_SERVER_PREFIX = "VMP_wvp_server"; 11 + public static final String WVP_SERVER_PREFIX = "VMP_SIGNALLING_SERVER_INFO_";
  12 +
  13 + public static final String WVP_SERVER_STREAM_PUSH_PREFIX = "VMP_SIGNALLING_STREAM_PUSH_";
12 14
13 public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_"; 15 public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
14 16
@@ -25,6 +27,7 @@ public class VideoManagerConstants { @@ -25,6 +27,7 @@ public class VideoManagerConstants {
25 public static final String PLAYER_PREFIX = "VMP_PLAYER_"; 27 public static final String PLAYER_PREFIX = "VMP_PLAYER_";
26 28
27 public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_"; 29 public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
  30 + public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
28 31
29 public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_"; 32 public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_";
30 33
@@ -51,4 +54,7 @@ public class VideoManagerConstants { @@ -51,4 +54,7 @@ public class VideoManagerConstants {
51 public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_"; 54 public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
52 55
53 public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_"; 56 public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_";
  57 +
  58 + //************************** redis 消息*********************************
  59 + public static final String WVP_MSG_STREAM_PUSH_CHANGE_PREFIX = "WVP_MSG_STREAM_PUSH_CHANGE";
54 } 60 }
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +import org.springframework.beans.factory.annotation.Autowired;
  4 +import org.springframework.context.annotation.Bean;
  5 +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
  6 +import org.springframework.scheduling.support.CronTrigger;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import java.util.Map;
  10 +import java.util.concurrent.ConcurrentHashMap;
  11 +import java.util.concurrent.ScheduledFuture;
  12 +
  13 +/**
  14 + * 动态定时任务
  15 + */
  16 +@Component
  17 +public class DynamicTask {
  18 +
  19 + @Autowired
  20 + private ThreadPoolTaskScheduler threadPoolTaskScheduler;
  21 +
  22 + private Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
  23 +
  24 + @Bean
  25 + public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
  26 + return new ThreadPoolTaskScheduler();
  27 + }
  28 +
  29 + public String startCron(String key, Runnable task, int cycleForCatalog) {
  30 + stopCron(key);
  31 + // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔
  32 + ScheduledFuture future = threadPoolTaskScheduler.scheduleWithFixedDelay(task, cycleForCatalog * 1000L);
  33 + futureMap.put(key, future);
  34 + return "startCron";
  35 + }
  36 +
  37 + public void stopCron(String key) {
  38 + if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
  39 + futureMap.get(key).cancel(true);
  40 + }
  41 + }
  42 +
  43 +}
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
@@ -163,9 +163,9 @@ public class ProxyServletConfig { @@ -163,9 +163,9 @@ public class ProxyServletConfig {
163 * 异常处理 163 * 异常处理
164 */ 164 */
165 @Override 165 @Override
166 - protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){ 166 + protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e){
167 try { 167 try {
168 - super.handleRequestException(proxyRequest, proxyResonse, e); 168 + super.handleRequestException(proxyRequest, proxyResponse, e);
169 } catch (ServletException servletException) { 169 } catch (ServletException servletException) {
170 logger.error("录像服务 代理失败: ", e); 170 logger.error("录像服务 代理失败: ", e);
171 } catch (IOException ioException) { 171 } catch (IOException ioException) {
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
@@ -16,7 +16,7 @@ import redis.clients.jedis.JedisPool; @@ -16,7 +16,7 @@ import redis.clients.jedis.JedisPool;
16 import redis.clients.jedis.JedisPoolConfig; 16 import redis.clients.jedis.JedisPoolConfig;
17 17
18 /** 18 /**
19 - * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 19 + * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置
20 * @author: swwheihei 20 * @author: swwheihei
21 * @date: 2019年5月30日 上午10:58:25 21 * @date: 2019年5月30日 上午10:58:25
22 * 22 *
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
1 package com.genersoft.iot.vmp.conf; 1 package com.genersoft.iot.vmp.conf;
2 2
3 3
4 -import org.springframework.beans.factory.annotation.Value;  
5 import org.springframework.boot.context.properties.ConfigurationProperties; 4 import org.springframework.boot.context.properties.ConfigurationProperties;
6 -import org.springframework.context.annotation.Configuration;  
7 import org.springframework.stereotype.Component; 5 import org.springframework.stereotype.Component;
8 6
9 @Component 7 @Component
@@ -27,7 +25,7 @@ public class SipConfig { @@ -27,7 +25,7 @@ public class SipConfig {
27 25
28 Integer ptzSpeed = 50; 26 Integer ptzSpeed = 50;
29 27
30 - Integer keepaliveTimeOut = 180; 28 + Integer keepaliveTimeOut = 255;
31 29
32 Integer registerTimeInterval = 60; 30 Integer registerTimeInterval = 60;
33 31
src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java
@@ -32,5 +32,7 @@ public class SipDeviceRunner implements CommandLineRunner { @@ -32,5 +32,7 @@ public class SipDeviceRunner implements CommandLineRunner {
32 for (String deviceId : onlineForAll) { 32 for (String deviceId : onlineForAll) {
33 storager.online(deviceId); 33 storager.online(deviceId);
34 } 34 }
  35 +
  36 + // TODO 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
35 } 37 }
36 } 38 }
src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +import org.springframework.context.annotation.Bean;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.scheduling.annotation.EnableAsync;
  6 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  7 +
  8 +import java.util.concurrent.ThreadPoolExecutor;
  9 +
  10 +@Configuration
  11 +@EnableAsync(proxyTargetClass = true)
  12 +public class ThreadPoolTaskConfig {
  13 +
  14 + public static final int cpuNum = Runtime.getRuntime().availableProcessors();
  15 +
  16 + /**
  17 + * 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
  18 + * 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
  19 + * 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
  20 + */
  21 +
  22 + /**
  23 + * 核心线程数(默认线程数)
  24 + */
  25 + private static final int corePoolSize = cpuNum;
  26 + /**
  27 + * 最大线程数
  28 + */
  29 + private static final int maxPoolSize = cpuNum*2;
  30 + /**
  31 + * 允许线程空闲时间(单位:默认为秒)
  32 + */
  33 + private static final int keepAliveTime = 30;
  34 + /**
  35 + * 缓冲队列大小
  36 + */
  37 + private static final int queueCapacity = 500;
  38 + /**
  39 + * 线程池名前缀
  40 + */
  41 + private static final String threadNamePrefix = "wvp-sip-handle-";
  42 +
  43 + @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
  44 + public ThreadPoolTaskExecutor taskExecutor() {
  45 + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  46 + executor.setCorePoolSize(corePoolSize);
  47 + executor.setMaxPoolSize(maxPoolSize);
  48 + executor.setQueueCapacity(queueCapacity);
  49 + executor.setKeepAliveSeconds(keepAliveTime);
  50 + executor.setThreadNamePrefix(threadNamePrefix);
  51 +
  52 + // 线程池对拒绝任务的处理策略
  53 + // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
  54 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  55 + // 初始化
  56 + executor.initialize();
  57 + return executor;
  58 + }
  59 +}
src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java
@@ -27,6 +27,8 @@ public class UserSetup { @@ -27,6 +27,8 @@ public class UserSetup {
27 27
28 private Boolean logInDatebase = Boolean.TRUE; 28 private Boolean logInDatebase = Boolean.TRUE;
29 29
  30 + private String serverId = "000000";
  31 +
30 private List<String> interfaceAuthenticationExcludes = new ArrayList<>(); 32 private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
31 33
32 public Boolean getSavePositionHistory() { 34 public Boolean getSavePositionHistory() {
@@ -104,4 +106,12 @@ public class UserSetup { @@ -104,4 +106,12 @@ public class UserSetup {
104 public void setLogInDatebase(Boolean logInDatebase) { 106 public void setLogInDatebase(Boolean logInDatebase) {
105 this.logInDatebase = logInDatebase; 107 this.logInDatebase = logInDatebase;
106 } 108 }
  109 +
  110 + public String getServerId() {
  111 + return serverId;
  112 + }
  113 +
  114 + public void setServerId(String serverId) {
  115 + this.serverId = serverId;
  116 + }
107 } 117 }
src/main/java/com/genersoft/iot/vmp/conf/VManagerConfig.java deleted 100644 → 0
1 -package com.genersoft.iot.vmp.conf;  
2 -  
3 -import org.springframework.beans.factory.annotation.Value;  
4 -import org.springframework.context.annotation.Configuration;  
5 -  
6 -/**  
7 - * @Description: 获取数据库配置  
8 - * @author: swwheihei  
9 - * @date: 2020年5月6日 下午2:46:00  
10 - */  
11 -@Configuration("vmConfig")  
12 -public class VManagerConfig {  
13 -  
14 - @Value("${spring.application.database:redis}")  
15 - private String database;  
16 -  
17 -  
18 - public String getDatabase() {  
19 - return database;  
20 - }  
21 -  
22 - public void setDatabase(String database) {  
23 - this.database = database;  
24 - }  
25 -}  
src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java
1 package com.genersoft.iot.vmp.conf; 1 package com.genersoft.iot.vmp.conf;
2 2
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.service.IMediaServerService;
3 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 5 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
4 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.beans.factory.annotation.Autowired;
  7 +import org.springframework.beans.factory.annotation.Value;
5 import org.springframework.scheduling.annotation.Scheduled; 8 import org.springframework.scheduling.annotation.Scheduled;
6 import org.springframework.stereotype.Component; 9 import org.springframework.stereotype.Component;
7 10
@@ -12,13 +15,22 @@ public class WVPTimerTask { @@ -12,13 +15,22 @@ public class WVPTimerTask {
12 private IRedisCatchStorage redisCatchStorage; 15 private IRedisCatchStorage redisCatchStorage;
13 16
14 @Autowired 17 @Autowired
15 - private SipConfig sipConfig; 18 + private IMediaServerService mediaServerService;
16 19
17 @Autowired 20 @Autowired
18 - private MediaConfig mediaConfig; 21 + private UserSetup userSetup;
  22 +
  23 + @Value("${server.port}")
  24 + private int serverPort;
  25 +
  26 + @Autowired
  27 + private SipConfig sipConfig;
19 28
20 - @Scheduled(cron="0/2 * * * * ? ") //每3秒执行一次 29 + @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次
21 public void execute(){ 30 public void execute(){
22 -// redisCatchStorage.updateWVPInfo(); 31 + JSONObject jsonObject = new JSONObject();
  32 + jsonObject.put("ip", sipConfig.getIp());
  33 + jsonObject.put("port", serverPort);
  34 + redisCatchStorage.updateWVPInfo(userSetup.getServerId(), jsonObject, 3);
23 } 35 }
24 } 36 }
src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java
@@ -34,7 +34,7 @@ public class LoginFailureHandler implements AuthenticationFailureHandler { @@ -34,7 +34,7 @@ public class LoginFailureHandler implements AuthenticationFailureHandler {
34 34
35 } else if (e instanceof BadCredentialsException) { 35 } else if (e instanceof BadCredentialsException) {
36 // 密码错误 36 // 密码错误
37 - logger.info("[登录失败] - 用户[{}]密码错误", username); 37 + logger.info("[登录失败] - 用户[{}]密码/SIP服务器ID 错误", username);
38 38
39 } else if (e instanceof CredentialsExpiredException) { 39 } else if (e instanceof CredentialsExpiredException) {
40 // 密码过期 40 // 密码过期
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
1 package com.genersoft.iot.vmp.gb28181; 1 package com.genersoft.iot.vmp.gb28181;
2 2
3 -import java.text.ParseException;  
4 -import java.util.Properties;  
5 -import java.util.TooManyListenersException;  
6 -import java.util.concurrent.LinkedBlockingQueue;  
7 -import java.util.concurrent.ThreadPoolExecutor;  
8 -import java.util.concurrent.TimeUnit;  
9 -  
10 -import javax.sip.*;  
11 -import javax.sip.header.CallIdHeader;  
12 -import javax.sip.message.Response;  
13 - 3 +import com.genersoft.iot.vmp.conf.SipConfig;
14 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; 4 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
15 import gov.nist.javax.sip.SipProviderImpl; 7 import gov.nist.javax.sip.SipProviderImpl;
  8 +import gov.nist.javax.sip.SipStackImpl;
16 import org.slf4j.Logger; 9 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory; 10 import org.slf4j.LoggerFactory;
18 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Autowired;
@@ -20,14 +13,15 @@ import org.springframework.context.annotation.Bean; @@ -20,14 +13,15 @@ import org.springframework.context.annotation.Bean;
20 import org.springframework.context.annotation.DependsOn; 13 import org.springframework.context.annotation.DependsOn;
21 import org.springframework.stereotype.Component; 14 import org.springframework.stereotype.Component;
22 15
23 -import com.genersoft.iot.vmp.conf.SipConfig;  
24 -import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorFactory;  
25 -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;  
26 -  
27 -import gov.nist.javax.sip.SipStackImpl; 16 +import javax.sip.*;
  17 +import java.util.Properties;
  18 +import java.util.TooManyListenersException;
  19 +import java.util.concurrent.LinkedBlockingQueue;
  20 +import java.util.concurrent.ThreadPoolExecutor;
  21 +import java.util.concurrent.TimeUnit;
28 22
29 @Component 23 @Component
30 -public class SipLayer implements SipListener { 24 +public class SipLayer{
31 25
32 private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); 26 private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
33 27
@@ -35,33 +29,14 @@ public class SipLayer implements SipListener { @@ -35,33 +29,14 @@ public class SipLayer implements SipListener {
35 private SipConfig sipConfig; 29 private SipConfig sipConfig;
36 30
37 @Autowired 31 @Autowired
38 - private SIPProcessorFactory processorFactory;  
39 -  
40 - @Autowired  
41 - private SipSubscribe sipSubscribe; 32 + private ISIPProcessorObserver sipProcessorObserver;
42 33
43 private SipStackImpl sipStack; 34 private SipStackImpl sipStack;
44 35
45 private SipFactory sipFactory; 36 private SipFactory sipFactory;
46 37
47 - /**  
48 - * 消息处理器线程池  
49 - */  
50 - private ThreadPoolExecutor processThreadPool;  
51 38
52 - @Bean("initSipServer")  
53 - private ThreadPoolExecutor initSipServer() {  
54 -  
55 - int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;  
56 - LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);  
57 - processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum,  
58 - 0L,TimeUnit.MILLISECONDS,processQueue,  
59 - new ThreadPoolExecutor.CallerRunsPolicy());  
60 - return processThreadPool;  
61 - }  
62 -  
63 @Bean("sipFactory") 39 @Bean("sipFactory")
64 - @DependsOn("initSipServer")  
65 private SipFactory createSipFactory() { 40 private SipFactory createSipFactory() {
66 sipFactory = SipFactory.getInstance(); 41 sipFactory = SipFactory.getInstance();
67 sipFactory.setPathName("gov.nist"); 42 sipFactory.setPathName("gov.nist");
@@ -69,7 +44,7 @@ public class SipLayer implements SipListener { @@ -69,7 +44,7 @@ public class SipLayer implements SipListener {
69 } 44 }
70 45
71 @Bean("sipStack") 46 @Bean("sipStack")
72 - @DependsOn({"initSipServer", "sipFactory"}) 47 + @DependsOn({"sipFactory"})
73 private SipStack createSipStack() throws PeerUnavailableException { 48 private SipStack createSipStack() throws PeerUnavailableException {
74 Properties properties = new Properties(); 49 Properties properties = new Properties();
75 properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); 50 properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
@@ -87,7 +62,7 @@ public class SipLayer implements SipListener { @@ -87,7 +62,7 @@ public class SipLayer implements SipListener {
87 return sipStack; 62 return sipStack;
88 } 63 }
89 64
90 - @Bean("tcpSipProvider") 65 + @Bean(name = "tcpSipProvider")
91 @DependsOn("sipStack") 66 @DependsOn("sipStack")
92 private SipProviderImpl startTcpListener() { 67 private SipProviderImpl startTcpListener() {
93 ListeningPoint tcpListeningPoint = null; 68 ListeningPoint tcpListeningPoint = null;
@@ -95,7 +70,7 @@ public class SipLayer implements SipListener { @@ -95,7 +70,7 @@ public class SipLayer implements SipListener {
95 try { 70 try {
96 tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP"); 71 tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP");
97 tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint); 72 tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
98 - tcpSipProvider.addSipListener(this); 73 + tcpSipProvider.addSipListener(sipProcessorObserver);
99 logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}"); 74 logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}");
100 } catch (TransportNotSupportedException e) { 75 } catch (TransportNotSupportedException e) {
101 e.printStackTrace(); 76 e.printStackTrace();
@@ -110,7 +85,7 @@ public class SipLayer implements SipListener { @@ -110,7 +85,7 @@ public class SipLayer implements SipListener {
110 return tcpSipProvider; 85 return tcpSipProvider;
111 } 86 }
112 87
113 - @Bean("udpSipProvider") 88 + @Bean(name = "udpSipProvider")
114 @DependsOn("sipStack") 89 @DependsOn("sipStack")
115 private SipProviderImpl startUdpListener() { 90 private SipProviderImpl startUdpListener() {
116 ListeningPoint udpListeningPoint = null; 91 ListeningPoint udpListeningPoint = null;
@@ -118,8 +93,7 @@ public class SipLayer implements SipListener { @@ -118,8 +93,7 @@ public class SipLayer implements SipListener {
118 try { 93 try {
119 udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP"); 94 udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
120 udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint); 95 udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
121 - udpSipProvider.addSipListener(this);  
122 -// udpSipProvider.setAutomaticDialogSupportEnabled(false); 96 + udpSipProvider.addSipListener(sipProcessorObserver);
123 } catch (TransportNotSupportedException e) { 97 } catch (TransportNotSupportedException e) {
124 e.printStackTrace(); 98 e.printStackTrace();
125 } catch (InvalidArgumentException e) { 99 } catch (InvalidArgumentException e) {
@@ -134,123 +108,4 @@ public class SipLayer implements SipListener { @@ -134,123 +108,4 @@ public class SipLayer implements SipListener {
134 return udpSipProvider; 108 return udpSipProvider;
135 } 109 }
136 110
137 - /**  
138 - * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a  
139 - * new request arrives.  
140 - */  
141 - @Override  
142 - public void processRequest(RequestEvent evt) {  
143 - logger.debug(evt.getRequest().toString());  
144 - // 由于jainsip是单线程程序,为提高性能并发处理  
145 - processThreadPool.execute(() -> {  
146 - if (processorFactory != null) {  
147 - processorFactory.createRequestProcessor(evt).process();  
148 - }  
149 - });  
150 - }  
151 -  
152 - @Override  
153 - public void processResponse(ResponseEvent evt) {  
154 - Response response = evt.getResponse();  
155 - logger.debug(evt.getResponse().toString());  
156 - int status = response.getStatusCode();  
157 - if (((status >= 200) && (status < 300)) || status == 401) { // Success!  
158 - ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);  
159 - try {  
160 - processor.process(evt, this, sipConfig);  
161 - } catch (ParseException e) {  
162 - // TODO Auto-generated catch block  
163 - e.printStackTrace();  
164 - }  
165 -  
166 - if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {  
167 - CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);  
168 - if (callIdHeader != null) {  
169 - SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId());  
170 - if (subscribe != null) {  
171 - subscribe.response(evt);  
172 - }  
173 - }  
174 - }  
175 - } else if ((status >= 100) && (status < 200)) {  
176 - // 增加其它无需回复的响应,如101、180等  
177 - } else {  
178 - logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);  
179 - if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) {  
180 - CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);  
181 - if (callIdHeader != null) {  
182 - SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());  
183 - if (subscribe != null) {  
184 - subscribe.response(evt);  
185 - }  
186 - }  
187 - }  
188 - }  
189 -  
190 -  
191 -  
192 - }  
193 -  
194 - /**  
195 - * <p>  
196 - * Title: processTimeout  
197 - * </p>  
198 - * <p>  
199 - * Description:  
200 - * </p>  
201 - *  
202 - * @param timeoutEvent  
203 - */  
204 - @Override  
205 - public void processTimeout(TimeoutEvent timeoutEvent) {  
206 - // TODO Auto-generated method stub  
207 -  
208 - }  
209 -  
210 - /**  
211 - * <p>  
212 - * Title: processIOException  
213 - * </p>  
214 - * <p>  
215 - * Description:  
216 - * </p>  
217 - *  
218 - * @param exceptionEvent  
219 - */  
220 - @Override  
221 - public void processIOException(IOExceptionEvent exceptionEvent) {  
222 - // TODO Auto-generated method stub  
223 - }  
224 -  
225 - /**  
226 - * <p>  
227 - * Title: processTransactionTerminated  
228 - * </p>  
229 - * <p>  
230 - * Description:  
231 - * </p>  
232 - *  
233 - * @param transactionTerminatedEvent  
234 - */  
235 - @Override  
236 - public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {  
237 - // TODO Auto-generated method stub  
238 - }  
239 -  
240 - /**  
241 - * <p>  
242 - * Title: processDialogTerminated  
243 - * </p>  
244 - * <p>  
245 - * Description:  
246 - * </p>  
247 - *  
248 - * @param dialogTerminatedEvent  
249 - */  
250 - @Override  
251 - public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {  
252 - // TODO Auto-generated method stub  
253 -  
254 - }  
255 -  
256 } 111 }
src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java
@@ -9,7 +9,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; @@ -9,7 +9,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
10 10
11 /** 11 /**
12 - * @Description:注册逻辑处理,当设备注册后触发逻辑。 12 + * @description:注册逻辑处理,当设备注册后触发逻辑。
13 * @author: swwheihei 13 * @author: swwheihei
14 * @date: 2020年5月8日 下午9:41:46 14 * @date: 2020年5月8日 下午9:41:46
15 */ 15 */
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
@@ -109,6 +109,11 @@ public class Device { @@ -109,6 +109,11 @@ public class Device {
109 */ 109 */
110 private String charset ; 110 private String charset ;
111 111
  112 + /**
  113 + * 目录订阅周期,0为不订阅
  114 + */
  115 + private int subscribeCycleForCatalog ;
  116 +
112 117
113 118
114 public String getDeviceId() { 119 public String getDeviceId() {
@@ -270,4 +275,12 @@ public class Device { @@ -270,4 +275,12 @@ public class Device {
270 public void setCharset(String charset) { 275 public void setCharset(String charset) {
271 this.charset = charset; 276 this.charset = charset;
272 } 277 }
  278 +
  279 + public int getSubscribeCycleForCatalog() {
  280 + return subscribeCycleForCatalog;
  281 + }
  282 +
  283 + public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) {
  284 + this.subscribeCycleForCatalog = subscribeCycleForCatalog;
  285 + }
273 } 286 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java
1 package com.genersoft.iot.vmp.gb28181.bean; 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2
3 /** 3 /**
4 - * @Description: 移动位置bean 4 + * @description: 移动位置bean
5 * @author: lawrencehj 5 * @author: lawrencehj
6 * @date: 2021年1月23日 6 * @date: 2021年1月23日
7 */ 7 */
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
@@ -6,14 +6,18 @@ package com.genersoft.iot.vmp.gb28181.bean; @@ -6,14 +6,18 @@ package com.genersoft.iot.vmp.gb28181.bean;
6 import java.util.List; 6 import java.util.List;
7 7
8 /** 8 /**
9 - * @Description:设备录像信息bean 9 + * @description:设备录像信息bean
10 * @author: swwheihei 10 * @author: swwheihei
11 * @date: 2020年5月8日 下午2:05:56 11 * @date: 2020年5月8日 下午2:05:56
12 */ 12 */
13 public class RecordInfo { 13 public class RecordInfo {
14 14
15 private String deviceId; 15 private String deviceId;
16 - 16 +
  17 + private String channelId;
  18 +
  19 + private String sn;
  20 +
17 private String name; 21 private String name;
18 22
19 private int sumNum; 23 private int sumNum;
@@ -52,4 +56,19 @@ public class RecordInfo { @@ -52,4 +56,19 @@ public class RecordInfo {
52 this.recordList = recordList; 56 this.recordList = recordList;
53 } 57 }
54 58
  59 + public String getChannelId() {
  60 + return channelId;
  61 + }
  62 +
  63 + public void setChannelId(String channelId) {
  64 + this.channelId = channelId;
  65 + }
  66 +
  67 + public String getSn() {
  68 + return sn;
  69 + }
  70 +
  71 + public void setSn(String sn) {
  72 + this.sn = sn;
  73 + }
55 } 74 }
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
@@ -8,7 +8,7 @@ import java.text.SimpleDateFormat; @@ -8,7 +8,7 @@ import java.text.SimpleDateFormat;
8 import java.util.Date; 8 import java.util.Date;
9 9
10 /** 10 /**
11 - * @Description:设备录像bean 11 + * @description:设备录像bean
12 * @author: swwheihei 12 * @author: swwheihei
13 * @date: 2020年5月8日 下午2:06:54 13 * @date: 2020年5月8日 下午2:06:54
14 */ 14 */
src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java
@@ -7,7 +7,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; @@ -7,7 +7,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
7 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 7 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
8 8
9 /** 9 /**
10 - * @Description:设备离在线状态检测器,用于检测设备状态 10 + * @description:设备离在线状态检测器,用于检测设备状态
11 * @author: swwheihei 11 * @author: swwheihei
12 * @date: 2020年5月13日 下午2:40:29 12 * @date: 2020年5月13日 下午2:40:29
13 */ 13 */
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
@@ -13,7 +13,7 @@ import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent; @@ -13,7 +13,7 @@ import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent;
13 import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent; 13 import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
14 14
15 /** 15 /**
16 - * @Description:Event事件通知推送器,支持推送在线事件、离线事件 16 + * @description:Event事件通知推送器,支持推送在线事件、离线事件
17 * @author: swwheihei 17 * @author: swwheihei
18 * @date: 2020年5月6日 上午11:30:50 18 * @date: 2020年5月6日 上午11:30:50
19 */ 19 */
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
1 package com.genersoft.iot.vmp.gb28181.event; 1 package com.genersoft.iot.vmp.gb28181.event;
2 2
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +import org.springframework.scheduling.annotation.Scheduled;
3 import org.springframework.stereotype.Component; 6 import org.springframework.stereotype.Component;
4 7
5 -import javax.sip.ResponseEvent; 8 +import javax.sip.*;
  9 +import javax.sip.header.CallIdHeader;
  10 +import javax.sip.message.Response;
  11 +import java.util.Calendar;
  12 +import java.util.Date;
6 import java.util.Map; 13 import java.util.Map;
7 import java.util.concurrent.ConcurrentHashMap; 14 import java.util.concurrent.ConcurrentHashMap;
8 15
9 @Component 16 @Component
10 public class SipSubscribe { 17 public class SipSubscribe {
11 18
  19 + private final Logger logger = LoggerFactory.getLogger(SipSubscribe.class);
  20 +
12 private Map<String, SipSubscribe.Event> errorSubscribes = new ConcurrentHashMap<>(); 21 private Map<String, SipSubscribe.Event> errorSubscribes = new ConcurrentHashMap<>();
13 22
14 private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>(); 23 private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>();
15 24
  25 + private Map<String, Date> timeSubscribes = new ConcurrentHashMap<>();
  26 +
  27 +// @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次
  28 +// @Scheduled(fixedRate= 100 * 60 * 60 )
  29 + @Scheduled(cron="0 0 * * * ?") //每小时执行一次, 每个整点
  30 + public void execute(){
  31 + logger.info("[定时任务] 清理过期的订阅信息");
  32 + Calendar calendar = Calendar.getInstance();
  33 + calendar.setTime(new Date());
  34 + calendar.set(Calendar.HOUR, calendar.get(Calendar.HOUR) - 1);
  35 + for (String key : timeSubscribes.keySet()) {
  36 + if (timeSubscribes.get(key).before(calendar.getTime())){
  37 + logger.info("[定时任务] 清理过期的订阅信息: {}", key);
  38 + errorSubscribes.remove(key);
  39 + okSubscribes.remove(key);
  40 + timeSubscribes.remove(key);
  41 + }
  42 + }
  43 + }
  44 +
16 public interface Event { 45 public interface Event {
17 - void response(ResponseEvent event); 46 + void response(EventResult eventResult);
  47 + }
  48 +
  49 + public static class EventResult<EventObject>{
  50 + public int statusCode;
  51 + public String type;
  52 + public String msg;
  53 + public String callId;
  54 + public Dialog dialog;
  55 + public EventObject event;
  56 +
  57 + public EventResult() {
  58 + }
  59 +
  60 + public EventResult(EventObject event) {
  61 + this.event = event;
  62 + if (event instanceof ResponseEvent) {
  63 + ResponseEvent responseEvent = (ResponseEvent)event;
  64 + Response response = responseEvent.getResponse();
  65 + this.dialog = responseEvent.getDialog();
  66 + this.type = "response";
  67 + if (response != null) {
  68 + this.msg = response.getReasonPhrase();
  69 + this.statusCode = response.getStatusCode();
  70 + }
  71 + this.callId = ((CallIdHeader)response.getHeader(CallIdHeader.NAME)).getCallId();
  72 +
  73 + }else if (event instanceof TimeoutEvent) {
  74 + TimeoutEvent timeoutEvent = (TimeoutEvent)event;
  75 + this.type = "timeout";
  76 + this.msg = "消息超时未回复";
  77 + this.statusCode = -1024;
  78 + this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId();
  79 + this.dialog = timeoutEvent.getClientTransaction().getDialog();
  80 + }else if (event instanceof TransactionTerminatedEvent) {
  81 + TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event;
  82 + this.type = "transactionTerminated";
  83 + this.msg = "事务已结束";
  84 + this.statusCode = -1024;
  85 + this.callId = transactionTerminatedEvent.getClientTransaction().getDialog().getCallId().getCallId();
  86 + this.dialog = transactionTerminatedEvent.getClientTransaction().getDialog();
  87 + }else if (event instanceof DialogTerminatedEvent) {
  88 + DialogTerminatedEvent dialogTerminatedEvent = (DialogTerminatedEvent)event;
  89 + this.type = "dialogTerminated";
  90 + this.msg = "会话已结束";
  91 + this.statusCode = -1024;
  92 + this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId();
  93 + this.dialog = dialogTerminatedEvent.getDialog();
  94 + }
  95 + }
18 } 96 }
19 97
20 public void addErrorSubscribe(String key, SipSubscribe.Event event) { 98 public void addErrorSubscribe(String key, SipSubscribe.Event event) {
21 errorSubscribes.put(key, event); 99 errorSubscribes.put(key, event);
  100 + timeSubscribes.put(key, new Date());
22 } 101 }
23 102
24 public void addOkSubscribe(String key, SipSubscribe.Event event) { 103 public void addOkSubscribe(String key, SipSubscribe.Event event) {
25 okSubscribes.put(key, event); 104 okSubscribes.put(key, event);
  105 + timeSubscribes.put(key, new Date());
26 } 106 }
27 107
28 public SipSubscribe.Event getErrorSubscribe(String key) { 108 public SipSubscribe.Event getErrorSubscribe(String key) {
29 return errorSubscribes.get(key); 109 return errorSubscribes.get(key);
30 } 110 }
31 111
  112 + public void removeErrorSubscribe(String key) {
  113 + errorSubscribes.remove(key);
  114 + timeSubscribes.remove(key);
  115 + }
  116 +
32 public SipSubscribe.Event getOkSubscribe(String key) { 117 public SipSubscribe.Event getOkSubscribe(String key) {
33 return okSubscribes.get(key); 118 return okSubscribes.get(key);
34 } 119 }
35 120
  121 + public void removeOkSubscribe(String key) {
  122 + okSubscribes.remove(key);
  123 + timeSubscribes.remove(key);
  124 + }
36 public int getErrorSubscribesSize(){ 125 public int getErrorSubscribesSize(){
37 return errorSubscribes.size(); 126 return errorSubscribes.size();
38 } 127 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java
@@ -12,7 +12,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; @@ -12,7 +12,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
13 13
14 /** 14 /**
15 - * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件 15 + * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
16 * @author: swwheihei 16 * @author: swwheihei
17 * @date: 2020年5月6日 上午11:35:46 17 * @date: 2020年5月6日 上午11:35:46
18 */ 18 */
@@ -39,10 +39,6 @@ public class KeepaliveTimeoutListenerForPlatform extends KeyExpirationEventMessa @@ -39,10 +39,6 @@ public class KeepaliveTimeoutListenerForPlatform extends KeyExpirationEventMessa
39 // 获取失效的key 39 // 获取失效的key
40 String expiredKey = message.toString(); 40 String expiredKey = message.toString();
41 logger.debug(expiredKey); 41 logger.debug(expiredKey);
42 - if(!expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)){  
43 - logger.debug("收到redis过期监听,但开头不是"+VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX+",忽略");  
44 - return;  
45 - }  
46 // 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线 42 // 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线
47 if (expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)) { 43 if (expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)) {
48 String platformGBId = expiredKey.substring(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX.length(),expiredKey.length()); 44 String platformGBId = expiredKey.substring(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java
@@ -12,7 +12,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; @@ -12,7 +12,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
13 13
14 /** 14 /**
15 - * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件 15 + * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
16 * @author: swwheihei 16 * @author: swwheihei
17 * @date: 2020年5月6日 上午11:35:46 17 * @date: 2020年5月6日 上午11:35:46
18 */ 18 */
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java
@@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.gb28181.event.offline; @@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.gb28181.event.offline;
3 import org.springframework.context.ApplicationEvent; 3 import org.springframework.context.ApplicationEvent;
4 4
5 /** 5 /**
6 - * @Description: 离线事件类 6 + * @description: 离线事件类
7 * @author: swwheihei 7 * @author: swwheihei
8 * @date: 2020年5月6日 上午11:33:13 8 * @date: 2020年5月6日 上午11:33:13
9 */ 9 */
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java
@@ -11,8 +11,8 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @@ -11,8 +11,8 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
11 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 11 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
12 12
13 /** 13 /**
14 - * @Description: 离线事件监听器,监听到离线后,修改设备离在线状态。 设备离线有两个来源:  
15 - * 1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor} 14 + * @description: 离线事件监听器,监听到离线后,修改设备离在线状态。 设备离线有两个来源:
  15 + * 1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.RegisterRequestProcessor}
16 * 2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.event.offline.OfflineEventListener} 16 * 2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.event.offline.OfflineEventListener}
17 * @author: swwheihei 17 * @author: swwheihei
18 * @date: 2020年5月6日 下午1:51:23 18 * @date: 2020年5月6日 下午1:51:23
@@ -54,5 +54,8 @@ public class OfflineEventListener implements ApplicationListener&lt;OfflineEvent&gt; { @@ -54,5 +54,8 @@ public class OfflineEventListener implements ApplicationListener&lt;OfflineEvent&gt; {
54 54
55 // 处理离线监听 55 // 处理离线监听
56 storager.outline(event.getDeviceId()); 56 storager.outline(event.getDeviceId());
  57 +
  58 + // TODO 离线取消订阅
  59 +
57 } 60 }
58 } 61 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java
@@ -4,7 +4,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; @@ -4,7 +4,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
4 import org.springframework.context.ApplicationEvent; 4 import org.springframework.context.ApplicationEvent;
5 5
6 /** 6 /**
7 - * @Description: 在线事件类 7 + * @description: 在线事件类
8 * @author: swwheihei 8 * @author: swwheihei
9 * @date: 2020年5月6日 上午11:32:56 9 * @date: 2020年5月6日 上午11:32:56
10 */ 10 */
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java
@@ -13,12 +13,11 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @@ -13,12 +13,11 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
13 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 13 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
14 14
15 import java.text.SimpleDateFormat; 15 import java.text.SimpleDateFormat;
16 -import java.util.Date;  
17 16
18 /** 17 /**
19 - * @Description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源:  
20 - * 1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor}  
21 - * 2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor} 18 + * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源:
  19 + * 1、设备主动注销,发送注销指令
  20 + * 2、设备未知原因离线,心跳超时
22 * @author: swwheihei 21 * @author: swwheihei
23 * @date: 2020年5月6日 下午1:51:23 22 * @date: 2020年5月6日 下午1:51:23
24 */ 23 */
@@ -74,5 +73,8 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; { @@ -74,5 +73,8 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
74 device.setOnline(1); 73 device.setOnline(1);
75 // 处理上线监听 74 // 处理上线监听
76 storager.updateDevice(device); 75 storager.updateDevice(device);
  76 +
  77 + // TODO 上线添加订阅
  78 +
77 } 79 }
78 } 80 }
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java
@@ -18,7 +18,7 @@ import javax.sip.ResponseEvent; @@ -18,7 +18,7 @@ import javax.sip.ResponseEvent;
18 import javax.sip.message.Response; 18 import javax.sip.message.Response;
19 19
20 /** 20 /**
21 - * @Description: 平台心跳超时事件 21 + * @description: 平台心跳超时事件
22 * @author: panll 22 * @author: panll
23 * @date: 2020年11月5日 10:00 23 * @date: 2020年11月5日 10:00
24 */ 24 */
@@ -66,6 +66,7 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener&lt;P @@ -66,6 +66,7 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener&lt;P
66 storager.updateParentPlatformStatus(event.getPlatformGbID(), false); 66 storager.updateParentPlatformStatus(event.getPlatformGbID(), false);
67 publisher.platformNotRegisterEventPublish(event.getPlatformGbID()); 67 publisher.platformNotRegisterEventPublish(event.getPlatformGbID());
68 parentPlatformCatch.setKeepAliveReply(0); 68 parentPlatformCatch.setKeepAliveReply(0);
  69 + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
69 }else { 70 }else {
70 // 再次发送心跳 71 // 再次发送心跳
71 String callId = sipCommanderForPlatform.keepalive(parentPlatform); 72 String callId = sipCommanderForPlatform.keepalive(parentPlatform);
@@ -75,8 +76,8 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener&lt;P @@ -75,8 +76,8 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener&lt;P
75 redisCatchStorage.updatePlatformKeepalive(parentPlatform); 76 redisCatchStorage.updatePlatformKeepalive(parentPlatform);
76 redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); 77 redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
77 78
78 - sipSubscribe.addOkSubscribe(callId, (ResponseEvent responseEvent) ->{  
79 - if (responseEvent.getResponse().getStatusCode() == Response.OK) { 79 + sipSubscribe.addOkSubscribe(callId, (SipSubscribe.EventResult eventResult) ->{
  80 + if (eventResult.statusCode == Response.OK) {
80 // 收到心跳响应信息, 81 // 收到心跳响应信息,
81 parentPlatformCatch.setKeepAliveReply(0); 82 parentPlatformCatch.setKeepAliveReply(0);
82 redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); 83 redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
@@ -19,7 +19,7 @@ import org.springframework.stereotype.Component; @@ -19,7 +19,7 @@ import org.springframework.stereotype.Component;
19 import java.util.*; 19 import java.util.*;
20 20
21 /** 21 /**
22 - * @Description: 平台未注册事件,来源有二: 22 + * @description: 平台未注册事件,来源有二:
23 * 1、平台新添加 23 * 1、平台新添加
24 * 2、平台心跳超时 24 * 2、平台心跳超时
25 * @author: panll 25 * @author: panll
@@ -100,6 +100,6 @@ public class PlatformNotRegisterEventLister implements ApplicationListener&lt;Platf @@ -100,6 +100,6 @@ public class PlatformNotRegisterEventLister implements ApplicationListener&lt;Platf
100 logger.info("再次向平台注册,平台国标ID:" + event.getPlatformGbID()); 100 logger.info("再次向平台注册,平台国标ID:" + event.getPlatformGbID());
101 sipCommanderFroPlatform.register(parentPlatform, null, okEvent); 101 sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
102 } 102 }
103 - }, config.getRegisterTimeInterval(), config.getRegisterTimeInterval());//十五秒后再次发起注册 103 + }, config.getRegisterTimeInterval()* 1000, config.getRegisterTimeInterval()* 1000);//十五秒后再次发起注册
104 } 104 }
105 } 105 }
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
@@ -15,7 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -15,7 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.stereotype.Component; 15 import org.springframework.stereotype.Component;
16 16
17 /** 17 /**
18 - * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄 18 + * @description:视频流session管理器,管理视频预览、预览回放的通信句柄
19 * @author: swwheihei 19 * @author: swwheihei
20 * @date: 2020年5月13日 下午4:03:02 20 * @date: 2020年5月13日 下午4:03:02
21 */ 21 */
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/ISIPProcessorObserver.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit;
  2 +
  3 +import javax.sip.SipListener;
  4 +
  5 +public interface ISIPProcessorObserver extends SipListener {
  6 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java deleted 100644 → 0
1 -package com.genersoft.iot.vmp.gb28181.transmit;  
2 -  
3 -import javax.sip.RequestEvent;  
4 -import javax.sip.ResponseEvent;  
5 -import javax.sip.SipProvider;  
6 -import javax.sip.header.CSeqHeader;  
7 -import javax.sip.message.Request;  
8 -import javax.sip.message.Response;  
9 -  
10 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;  
11 -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;  
12 -import com.genersoft.iot.vmp.service.IDeviceAlarmService;  
13 -import com.genersoft.iot.vmp.service.IMediaServerService;  
14 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
15 -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*;  
16 -import com.genersoft.iot.vmp.service.IPlayService;  
17 -import org.springframework.beans.factory.annotation.Autowired;  
18 -import org.springframework.context.annotation.Lazy;  
19 -import org.springframework.stereotype.Component;  
20 -  
21 -import com.genersoft.iot.vmp.conf.SipConfig;  
22 -import com.genersoft.iot.vmp.gb28181.auth.RegisterLogicHandler;  
23 -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector;  
24 -import com.genersoft.iot.vmp.gb28181.event.EventPublisher;  
25 -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;  
26 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;  
27 -import com.genersoft.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;  
28 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.AckRequestProcessor;  
29 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.ByeRequestProcessor;  
30 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.CancelRequestProcessor;  
31 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.InviteRequestProcessor;  
32 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;  
33 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.NotifyRequestProcessor;  
34 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.OtherRequestProcessor;  
35 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;  
36 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.SubscribeRequestProcessor;  
37 -import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;  
38 -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.ByeResponseProcessor;  
39 -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.CancelResponseProcessor;  
40 -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.InviteResponseProcessor;  
41 -import com.genersoft.iot.vmp.gb28181.transmit.response.impl.OtherResponseProcessor;  
42 -import com.genersoft.iot.vmp.storager.IVideoManagerStorager;  
43 -import com.genersoft.iot.vmp.utils.SpringBeanFactory;  
44 -import com.genersoft.iot.vmp.utils.redis.RedisUtil;  
45 -  
46 -/**  
47 - * @Description: SIP信令处理分配  
48 - * @author: swwheihei  
49 - * @date: 2020年5月3日 下午4:24:37  
50 - */  
51 -@Component  
52 -public class SIPProcessorFactory {  
53 -  
54 - // private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);  
55 -  
56 - @Autowired  
57 - private SipConfig sipConfig;  
58 -  
59 - @Autowired  
60 - private RegisterLogicHandler handler;  
61 -  
62 - @Autowired  
63 - private IVideoManagerStorager storager;  
64 -  
65 - @Autowired  
66 - private IRedisCatchStorage redisCatchStorage;  
67 -  
68 - @Autowired  
69 - private EventPublisher publisher;  
70 -  
71 - @Autowired  
72 - private SIPCommander cmder;  
73 -  
74 - @Autowired  
75 - private SIPCommanderFroPlatform cmderFroPlatform;  
76 -  
77 - @Autowired  
78 - private IDeviceAlarmService deviceAlarmService;  
79 -  
80 - @Autowired  
81 - private RedisUtil redis;  
82 -  
83 - @Autowired  
84 - private DeferredResultHolder deferredResultHolder;  
85 -  
86 - @Autowired  
87 - private DeviceOffLineDetector offLineDetector;  
88 -  
89 - @Autowired  
90 - private InviteResponseProcessor inviteResponseProcessor;  
91 -  
92 - @Autowired  
93 - private ByeResponseProcessor byeResponseProcessor;  
94 -  
95 - @Autowired  
96 - private CancelResponseProcessor cancelResponseProcessor;  
97 -  
98 - @Autowired  
99 - @Lazy  
100 - private RegisterResponseProcessor registerResponseProcessor;  
101 -  
102 -  
103 - @Autowired  
104 - private OtherResponseProcessor otherResponseProcessor;  
105 -  
106 - @Autowired  
107 - private IPlayService playService;  
108 -  
109 - @Autowired  
110 - private ZLMRTPServerFactory zlmrtpServerFactory;  
111 -  
112 - @Autowired  
113 - private IMediaServerService mediaServerService;  
114 -  
115 - // 注:这里使用注解会导致循环依赖注入,暂用springBean  
116 - private SipProvider tcpSipProvider;  
117 -  
118 - // 注:这里使用注解会导致循环依赖注入,暂用springBean  
119 - private SipProvider udpSipProvider;  
120 -  
121 - public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) {  
122 - Request request = evt.getRequest();  
123 - String method = request.getMethod();  
124 -// logger.info("接收到消息:"+request.getMethod());  
125 -// sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);  
126 - if (Request.INVITE.equals(method)) {  
127 - InviteRequestProcessor processor = new InviteRequestProcessor();  
128 - processor.setRequestEvent(evt);  
129 - processor.setTcpSipProvider(getTcpSipProvider());  
130 - processor.setUdpSipProvider(getUdpSipProvider());  
131 -  
132 - processor.setCmder(cmder);  
133 - processor.setCmderFroPlatform(cmderFroPlatform);  
134 - processor.setPlayService(playService);  
135 - processor.setStorager(storager);  
136 - processor.setRedisCatchStorage(redisCatchStorage);  
137 - processor.setZlmrtpServerFactory(zlmrtpServerFactory);  
138 - processor.setMediaServerService(mediaServerService);  
139 - return processor;  
140 - } else if (Request.REGISTER.equals(method)) {  
141 - RegisterRequestProcessor processor = new RegisterRequestProcessor();  
142 - processor.setRequestEvent(evt);  
143 - processor.setTcpSipProvider(getTcpSipProvider());  
144 - processor.setUdpSipProvider(getUdpSipProvider());  
145 - processor.setHandler(handler);  
146 - processor.setPublisher(publisher);  
147 - processor.setSipConfig(sipConfig);  
148 - processor.setVideoManagerStorager(storager);  
149 - return processor;  
150 - } else if (Request.SUBSCRIBE.equals(method)) {  
151 - SubscribeRequestProcessor processor = new SubscribeRequestProcessor();  
152 - processor.setTcpSipProvider(getTcpSipProvider());  
153 - processor.setUdpSipProvider(getUdpSipProvider());  
154 - processor.setRequestEvent(evt);  
155 - return processor;  
156 - } else if (Request.ACK.equals(method)) {  
157 - AckRequestProcessor processor = new AckRequestProcessor();  
158 - processor.setRequestEvent(evt);  
159 - processor.setRedisCatchStorage(redisCatchStorage);  
160 - processor.setZlmrtpServerFactory(zlmrtpServerFactory);  
161 - processor.setMediaServerService(mediaServerService);  
162 - return processor;  
163 - } else if (Request.BYE.equals(method)) {  
164 - ByeRequestProcessor processor = new ByeRequestProcessor();  
165 - processor.setRequestEvent(evt);  
166 - processor.setRedisCatchStorage(redisCatchStorage);  
167 - processor.setStorager(storager);  
168 - processor.setZlmrtpServerFactory(zlmrtpServerFactory);  
169 - processor.setSIPCommander(cmder);  
170 - processor.setMediaServerService(mediaServerService);  
171 - return processor;  
172 - } else if (Request.CANCEL.equals(method)) {  
173 - CancelRequestProcessor processor = new CancelRequestProcessor();  
174 - processor.setRequestEvent(evt);  
175 - return processor;  
176 - } else if (Request.MESSAGE.equals(method)) {  
177 - MessageRequestProcessor processor = new MessageRequestProcessor();  
178 - processor.setRequestEvent(evt);  
179 - processor.setTcpSipProvider(getTcpSipProvider());  
180 - processor.setUdpSipProvider(getUdpSipProvider());  
181 - processor.setPublisher(publisher);  
182 - processor.setRedis(redis);  
183 - processor.setDeferredResultHolder(deferredResultHolder);  
184 - processor.setOffLineDetector(offLineDetector);  
185 - processor.setCmder(cmder);  
186 - processor.setCmderFroPlatform(cmderFroPlatform);  
187 - processor.setDeviceAlarmService(deviceAlarmService);  
188 - processor.setStorager(storager);  
189 - processor.setRedisCatchStorage(redisCatchStorage);  
190 - return processor;  
191 - } else if (Request.NOTIFY.equalsIgnoreCase(method)) {  
192 - NotifyRequestProcessor processor = new NotifyRequestProcessor();  
193 - processor.setRequestEvent(evt);  
194 - processor.setTcpSipProvider(getTcpSipProvider());  
195 - processor.setUdpSipProvider(getUdpSipProvider());  
196 - processor.setPublisher(publisher);  
197 - processor.setRedis(redis);  
198 - processor.setDeferredResultHolder(deferredResultHolder);  
199 - processor.setOffLineDetector(offLineDetector);  
200 - processor.setCmder(cmder);  
201 - processor.setStorager(storager);  
202 - processor.setRedisCatchStorage(redisCatchStorage);  
203 - return processor;  
204 - } else {  
205 - OtherRequestProcessor processor = new OtherRequestProcessor();  
206 - processor.setRequestEvent(evt);  
207 - return processor;  
208 - }  
209 - }  
210 -  
211 - public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {  
212 -  
213 - Response response = evt.getResponse();  
214 - CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);  
215 - String method = cseqHeader.getMethod();  
216 - if(Request.INVITE.equals(method)){  
217 - return inviteResponseProcessor;  
218 - } else if (Request.BYE.equals(method)) {  
219 - return byeResponseProcessor;  
220 - } else if (Request.CANCEL.equals(method)) {  
221 - return cancelResponseProcessor;  
222 - }else if (Request.REGISTER.equals(method)) {  
223 - return registerResponseProcessor;  
224 - } else {  
225 - return otherResponseProcessor;  
226 - }  
227 - }  
228 -  
229 - private SipProvider getTcpSipProvider() {  
230 - if (tcpSipProvider == null) {  
231 - tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider");  
232 - }  
233 - return tcpSipProvider;  
234 - }  
235 -  
236 - private SipProvider getUdpSipProvider() {  
237 - if (udpSipProvider == null) {  
238 - udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");  
239 - }  
240 - return udpSipProvider;  
241 - }  
242 -  
243 -}  
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  4 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.event.response.ISIPResponseProcessor;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.beans.factory.annotation.Qualifier;
  11 +import org.springframework.scheduling.annotation.Async;
  12 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  13 +import org.springframework.stereotype.Component;
  14 +
  15 +import javax.sip.*;
  16 +import javax.sip.header.CSeqHeader;
  17 +import javax.sip.header.CallIdHeader;
  18 +import javax.sip.message.Response;
  19 +import java.util.Map;
  20 +import java.util.concurrent.ConcurrentHashMap;
  21 +
  22 +/**
  23 + * @description: SIP信令处理类观察者
  24 + * @author: panlinlin
  25 + * @date: 2021年11月5日 下午15:32
  26 + */
  27 +@Component
  28 +public class SIPProcessorObserver implements ISIPProcessorObserver {
  29 +
  30 + private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class);
  31 +
  32 + private static Map<String, ISIPRequestProcessor> requestProcessorMap = new ConcurrentHashMap<>();
  33 + private static Map<String, ISIPResponseProcessor> responseProcessorMap = new ConcurrentHashMap<>();
  34 + private static ITimeoutProcessor timeoutProcessor;
  35 +
  36 + @Autowired
  37 + private SipSubscribe sipSubscribe;
  38 +
  39 +// @Autowired
  40 +// @Qualifier(value = "taskExecutor")
  41 +// private ThreadPoolTaskExecutor poolTaskExecutor;
  42 +
  43 + /**
  44 + * 添加 request订阅
  45 + * @param method 方法名
  46 + * @param processor 处理程序
  47 + */
  48 + public void addRequestProcessor(String method, ISIPRequestProcessor processor) {
  49 + requestProcessorMap.put(method, processor);
  50 + }
  51 +
  52 + /**
  53 + * 添加 response订阅
  54 + * @param method 方法名
  55 + * @param processor 处理程序
  56 + */
  57 + public void addResponseProcessor(String method, ISIPResponseProcessor processor) {
  58 + responseProcessorMap.put(method, processor);
  59 + }
  60 +
  61 + /**
  62 + * 添加 超时事件订阅
  63 + * @param processor 处理程序
  64 + */
  65 + public void addTimeoutProcessor(ITimeoutProcessor processor) {
  66 + this.timeoutProcessor = processor;
  67 + }
  68 +
  69 + /**
  70 + * 分发RequestEvent事件
  71 + * @param requestEvent RequestEvent事件
  72 + */
  73 + @Override
  74 + @Async
  75 + public void processRequest(RequestEvent requestEvent) {
  76 + String method = requestEvent.getRequest().getMethod();
  77 + ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method);
  78 + if (sipRequestProcessor == null) {
  79 + logger.warn("不支持方法{}的request", method);
  80 + return;
  81 + }
  82 + requestProcessorMap.get(method).process(requestEvent);
  83 +
  84 + }
  85 +
  86 + /**
  87 + * 分发ResponseEvent事件
  88 + * @param responseEvent responseEvent事件
  89 + */
  90 + @Override
  91 + @Async
  92 + public void processResponse(ResponseEvent responseEvent) {
  93 + logger.debug(responseEvent.getResponse().toString());
  94 + Response response = responseEvent.getResponse();
  95 + logger.debug(responseEvent.getResponse().toString());
  96 + int status = response.getStatusCode();
  97 + if (((status >= 200) && (status < 300)) || status == 401) { // Success!
  98 +// ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
  99 + CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);
  100 + String method = cseqHeader.getMethod();
  101 + ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method);
  102 + if (sipRequestProcessor != null) {
  103 + sipRequestProcessor.process(responseEvent);
  104 + }
  105 + if (responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
  106 + CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
  107 + if (callIdHeader != null) {
  108 + SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId());
  109 + if (subscribe != null) {
  110 + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent);
  111 + subscribe.response(eventResult);
  112 + }
  113 + }
  114 + }
  115 + } else if ((status >= 100) && (status < 200)) {
  116 + // 增加其它无需回复的响应,如101、180等
  117 + } else {
  118 + logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);
  119 + if (responseEvent.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) {
  120 + CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
  121 + if (callIdHeader != null) {
  122 + SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
  123 + if (subscribe != null) {
  124 + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent);
  125 + subscribe.response(eventResult);
  126 + }
  127 + }
  128 + }
  129 + if (responseEvent.getDialog() != null) {
  130 + responseEvent.getDialog().delete();
  131 + }
  132 + }
  133 +
  134 +
  135 + }
  136 +
  137 + /**
  138 + * 向超时订阅发送消息
  139 + * @param timeoutEvent timeoutEvent事件
  140 + */
  141 + @Override
  142 + public void processTimeout(TimeoutEvent timeoutEvent) {
  143 + if(timeoutProcessor != null) {
  144 + timeoutProcessor.process(timeoutEvent);
  145 + }
  146 + }
  147 +
  148 + @Override
  149 + public void processIOException(IOExceptionEvent exceptionEvent) {
  150 + }
  151 +
  152 + @Override
  153 + public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
  154 + }
  155 +
  156 + @Override
  157 + public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
  158 + CallIdHeader callId = dialogTerminatedEvent.getDialog().getCallId();
  159 + }
  160 +
  161 +
  162 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java
1 package com.genersoft.iot.vmp.gb28181.transmit.callback; 1 package com.genersoft.iot.vmp.gb28181.transmit.callback;
2 2
3 -import java.util.ArrayList;  
4 -import java.util.Comparator;  
5 -import java.util.List;  
6 -import java.util.concurrent.TimeUnit;  
7 -  
8 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; 3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
9 import com.genersoft.iot.vmp.gb28181.bean.RecordItem; 4 import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
10 -import com.genersoft.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor; 5 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.RecordInfoResponseMessageHandler;
11 import com.genersoft.iot.vmp.utils.redis.RedisUtil; 6 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
12 -  
13 import org.slf4j.Logger; 7 import org.slf4j.Logger;
14 8
  9 +import java.util.ArrayList;
  10 +import java.util.Comparator;
  11 +import java.util.List;
  12 +import java.util.concurrent.TimeUnit;
  13 +
15 @SuppressWarnings("unchecked") 14 @SuppressWarnings("unchecked")
16 public class CheckForAllRecordsThread extends Thread { 15 public class CheckForAllRecordsThread extends Thread {
17 16
@@ -54,13 +53,11 @@ public class CheckForAllRecordsThread extends Thread { @@ -54,13 +53,11 @@ public class CheckForAllRecordsThread extends Thread {
54 // 自然顺序排序, 元素进行升序排列 53 // 自然顺序排序, 元素进行升序排列
55 this.recordInfo.getRecordList().sort(Comparator.naturalOrder()); 54 this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
56 RequestMessage msg = new RequestMessage(); 55 RequestMessage msg = new RequestMessage();
57 - String deviceId = recordInfo.getDeviceId();  
58 - msg.setDeviceId(deviceId);  
59 - msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); 56 + msg.setKey(DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn());
60 msg.setData(recordInfo); 57 msg.setData(recordInfo);
61 - deferredResultHolder.invokeResult(msg); 58 + deferredResultHolder.invokeAllResult(msg);
62 logger.info("处理完成,返回结果"); 59 logger.info("处理完成,返回结果");
63 - MessageRequestProcessor.threadNameList.remove(cacheKey); 60 + RecordInfoResponseMessageHandler.threadNameList.remove(cacheKey);
64 } 61 }
65 62
66 public void setRedis(RedisUtil redis) { 63 public void setRedis(RedisUtil redis) {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
1 package com.genersoft.iot.vmp.gb28181.transmit.callback; 1 package com.genersoft.iot.vmp.gb28181.transmit.callback;
2 2
  3 +import java.util.HashMap;
3 import java.util.Map; 4 import java.util.Map;
  5 +import java.util.Set;
4 import java.util.concurrent.ConcurrentHashMap; 6 import java.util.concurrent.ConcurrentHashMap;
5 7
6 import org.springframework.http.HttpStatus; 8 import org.springframework.http.HttpStatus;
@@ -9,7 +11,7 @@ import org.springframework.stereotype.Component; @@ -9,7 +11,7 @@ import org.springframework.stereotype.Component;
9 import org.springframework.web.context.request.async.DeferredResult; 11 import org.springframework.web.context.request.async.DeferredResult;
10 12
11 /** 13 /**
12 - * @Description: 异步请求处理 14 + * @description: 异步请求处理
13 * @author: swwheihei 15 * @author: swwheihei
14 * @date: 2020年5月8日 下午7:59:05 16 * @date: 2020年5月8日 下午7:59:05
15 */ 17 */
@@ -31,11 +33,13 @@ public class DeferredResultHolder { @@ -31,11 +33,13 @@ public class DeferredResultHolder {
31 33
32 public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; 34 public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
33 35
34 - public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; 36 + public static final String CALLBACK_CMD_PLAY = "CALLBACK_PLAY";
35 37
36 - public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; 38 + public static final String CALLBACK_CMD_PLAYBACK = "CALLBACK_PLAY";
  39 +
  40 + public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD";
37 41
38 - public static final String CALLBACK_ONVIF = "CALLBACK_ONVIF"; 42 + public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
39 43
40 public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; 44 public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
41 45
@@ -45,21 +49,72 @@ public class DeferredResultHolder { @@ -45,21 +49,72 @@ public class DeferredResultHolder {
45 49
46 public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; 50 public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
47 51
48 - private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>(); 52 + private Map<String, Map<String, DeferredResult>> map = new ConcurrentHashMap<>();
49 53
50 - public void put(String key, DeferredResult result) {  
51 - map.put(key, result); 54 +
  55 + public void put(String key, String id, DeferredResult result) {
  56 + Map<String, DeferredResult> deferredResultMap = map.get(key);
  57 + if (deferredResultMap == null) {
  58 + deferredResultMap = new ConcurrentHashMap<>();
  59 + map.put(key, deferredResultMap);
  60 + }
  61 + deferredResultMap.put(id, result);
52 } 62 }
53 63
54 - public DeferredResult get(String key) {  
55 - return map.get(key); 64 + public DeferredResult get(String key, String id) {
  65 + Map<String, DeferredResult> deferredResultMap = map.get(key);
  66 + if (deferredResultMap == null) return null;
  67 + return deferredResultMap.get(id);
56 } 68 }
57 - 69 +
  70 + public boolean exist(String key, String id){
  71 + if (key == null) return false;
  72 + Map<String, DeferredResult> deferredResultMap = map.get(key);
  73 + if (id == null) {
  74 + return deferredResultMap != null;
  75 + }else {
  76 + return deferredResultMap != null && deferredResultMap.get(id) != null;
  77 + }
  78 + }
  79 +
  80 + /**
  81 + * 释放单个请求
  82 + * @param msg
  83 + */
58 public void invokeResult(RequestMessage msg) { 84 public void invokeResult(RequestMessage msg) {
59 - DeferredResult result = map.get(msg.getId()); 85 + Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
  86 + if (deferredResultMap == null) {
  87 + return;
  88 + }
  89 + DeferredResult result = deferredResultMap.get(msg.getId());
60 if (result == null) { 90 if (result == null) {
61 return; 91 return;
62 } 92 }
63 result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK)); 93 result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
  94 + deferredResultMap.remove(msg.getId());
  95 + if (deferredResultMap.size() == 0) {
  96 + map.remove(msg.getKey());
  97 + }
  98 + }
  99 +
  100 + /**
  101 + * 释放所有的请求
  102 + * @param msg
  103 + */
  104 + public void invokeAllResult(RequestMessage msg) {
  105 + Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
  106 + if (deferredResultMap == null) {
  107 + return;
  108 + }
  109 + Set<String> ids = deferredResultMap.keySet();
  110 + for (String id : ids) {
  111 + DeferredResult result = deferredResultMap.get(id);
  112 + if (result == null) {
  113 + return;
  114 + }
  115 + result.setResult(ResponseEntity.ok().body(msg.getData()));
  116 + }
  117 + map.remove(msg.getKey());
  118 +
64 } 119 }
65 } 120 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/RequestMessage.java
1 package com.genersoft.iot.vmp.gb28181.transmit.callback; 1 package com.genersoft.iot.vmp.gb28181.transmit.callback;
2 2
3 /** 3 /**
4 - * @Description: 请求信息定义 4 + * @description: 请求信息定义
5 * @author: swwheihei 5 * @author: swwheihei
6 * @date: 2020年5月8日 下午1:09:18 6 * @date: 2020年5月8日 下午1:09:18
7 */ 7 */
@@ -9,12 +9,10 @@ public class RequestMessage { @@ -9,12 +9,10 @@ public class RequestMessage {
9 9
10 private String id; 10 private String id;
11 11
12 - private String deviceId;  
13 -  
14 - private String type;  
15 - 12 + private String key;
  13 +
16 private Object data; 14 private Object data;
17 - 15 +
18 public String getId() { 16 public String getId() {
19 return id; 17 return id;
20 } 18 }
@@ -23,22 +21,12 @@ public class RequestMessage { @@ -23,22 +21,12 @@ public class RequestMessage {
23 this.id = id; 21 this.id = id;
24 } 22 }
25 23
26 - public String getDeviceId() {  
27 - return deviceId;  
28 - }  
29 -  
30 - public void setDeviceId(String deviceId) {  
31 - this.deviceId = deviceId;  
32 - this.id = type + deviceId;  
33 - }  
34 -  
35 - public String getType() {  
36 - return type; 24 + public void setKey(String key) {
  25 + this.key = key;
37 } 26 }
38 27
39 - public void setType(String type) {  
40 - this.type = type;  
41 - this.id = type + deviceId; 28 + public String getKey() {
  29 + return key;
42 } 30 }
43 31
44 public Object getData() { 32 public Object getData() {
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
1 package com.genersoft.iot.vmp.gb28181.transmit.cmd; 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
2 2
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
3 import com.genersoft.iot.vmp.gb28181.bean.Device; 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
4 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; 5 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
5 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; 6 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
@@ -7,7 +8,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; @@ -7,7 +8,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
7 import com.genersoft.iot.vmp.service.bean.SSRCInfo; 8 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
8 9
9 /** 10 /**
10 - * @Description:设备能力接口,用于定义设备的控制、查询能力 11 + * @description:设备能力接口,用于定义设备的控制、查询能力
11 * @author: swwheihei 12 * @author: swwheihei
12 * @date: 2020年5月3日 下午9:16:34 13 * @date: 2020年5月3日 下午9:16:34
13 */ 14 */
@@ -122,6 +123,26 @@ public interface ISIPCommander { @@ -122,6 +123,26 @@ public interface ISIPCommander {
122 void streamByeCmd(String deviceId, String channelId); 123 void streamByeCmd(String deviceId, String channelId);
123 124
124 /** 125 /**
  126 + * 回放暂停
  127 + */
  128 + void playPauseCmd(Device device, StreamInfo streamInfo);
  129 +
  130 + /**
  131 + * 回放恢复
  132 + */
  133 + void playResumeCmd(Device device, StreamInfo streamInfo);
  134 +
  135 + /**
  136 + * 回放拖动播放
  137 + */
  138 + void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime);
  139 +
  140 + /**
  141 + * 回放倍速播放
  142 + */
  143 + void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
  144 +
  145 + /**
125 * 语音广播 146 * 语音广播
126 * 147 *
127 * @param device 视频设备 148 * @param device 视频设备
@@ -235,8 +256,9 @@ public interface ISIPCommander { @@ -235,8 +256,9 @@ public interface ISIPCommander {
235 * @param device 视频设备 256 * @param device 视频设备
236 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss 257 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
237 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss 258 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  259 + * @param sn
238 */ 260 */
239 - boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime); 261 + boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent);
240 262
241 /** 263 /**
242 * 查询报警信息 264 * 查询报警信息
@@ -299,4 +321,11 @@ public interface ISIPCommander { @@ -299,4 +321,11 @@ public interface ISIPCommander {
299 * @return true = 命令发送成功 321 * @return true = 命令发送成功
300 */ 322 */
301 boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); 323 boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
  324 +
  325 + /**
  326 + * 订阅、取消订阅目录信息
  327 + * @param device 视频设备
  328 + * @return true = 命令发送成功
  329 + */
  330 + boolean catalogSubscribe(Device device, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent);
302 } 331 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
@@ -19,7 +19,7 @@ import java.util.List; @@ -19,7 +19,7 @@ import java.util.List;
19 import java.util.UUID; 19 import java.util.UUID;
20 20
21 /** 21 /**
22 - * @Description: 平台命令request创造器 TODO 冗余代码太多待优化 22 + * @description: 平台命令request创造器 TODO 冗余代码太多待优化
23 * @author: panll 23 * @author: panll
24 * @date: 2020年5月6日 上午9:29:02 24 * @date: 2020年5月6日 上午9:29:02
25 */ 25 */
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
3 import java.text.ParseException; 3 import java.text.ParseException;
4 import java.util.ArrayList; 4 import java.util.ArrayList;
5 5
  6 +import javax.sip.Dialog;
6 import javax.sip.InvalidArgumentException; 7 import javax.sip.InvalidArgumentException;
7 import javax.sip.PeerUnavailableException; 8 import javax.sip.PeerUnavailableException;
8 import javax.sip.SipFactory; 9 import javax.sip.SipFactory;
@@ -11,6 +12,9 @@ import javax.sip.address.SipURI; @@ -11,6 +12,9 @@ import javax.sip.address.SipURI;
11 import javax.sip.header.*; 12 import javax.sip.header.*;
12 import javax.sip.message.Request; 13 import javax.sip.message.Request;
13 14
  15 +import com.genersoft.iot.vmp.common.StreamInfo;
  16 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  17 +import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
14 import org.springframework.beans.factory.annotation.Autowired; 18 import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.stereotype.Component; 19 import org.springframework.stereotype.Component;
16 20
@@ -18,7 +22,7 @@ import com.genersoft.iot.vmp.conf.SipConfig; @@ -18,7 +22,7 @@ import com.genersoft.iot.vmp.conf.SipConfig;
18 import com.genersoft.iot.vmp.gb28181.bean.Device; 22 import com.genersoft.iot.vmp.gb28181.bean.Device;
19 23
20 /** 24 /**
21 - * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化 25 + * @description:摄像头命令request创造器 TODO 冗余代码太多待优化
22 * @author: swwheihei 26 * @author: swwheihei
23 * @date: 2020年5月6日 上午9:29:02 27 * @date: 2020年5月6日 上午9:29:02
24 */ 28 */
@@ -30,6 +34,9 @@ public class SIPRequestHeaderProvider { @@ -30,6 +34,9 @@ public class SIPRequestHeaderProvider {
30 34
31 @Autowired 35 @Autowired
32 private SipFactory sipFactory; 36 private SipFactory sipFactory;
  37 +
  38 + @Autowired
  39 + private VideoStreamSessionManager streamSession;
33 40
34 public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { 41 public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
35 Request request = null; 42 Request request = null;
@@ -99,13 +106,13 @@ public class SIPRequestHeaderProvider { @@ -99,13 +106,13 @@ public class SIPRequestHeaderProvider {
99 return request; 106 return request;
100 } 107 }
101 108
102 - public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { 109 + public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException {
103 Request request = null; 110 Request request = null;
104 //请求行 111 //请求行
105 - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); 112 + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
106 // via 113 // via
107 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); 114 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
108 - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); 115 + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
109 viaHeader.setRPort(); 116 viaHeader.setRPort();
110 viaHeaders.add(viaHeader); 117 viaHeaders.add(viaHeader);
111 //from 118 //from
@@ -113,7 +120,7 @@ public class SIPRequestHeaderProvider { @@ -113,7 +120,7 @@ public class SIPRequestHeaderProvider {
113 Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); 120 Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
114 FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack 121 FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
115 //to 122 //to
116 - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain()); 123 + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain());
117 Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); 124 Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
118 ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); 125 ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
119 126
@@ -127,7 +134,10 @@ public class SIPRequestHeaderProvider { @@ -127,7 +134,10 @@ public class SIPRequestHeaderProvider {
127 Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort())); 134 Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
128 // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); 135 // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
129 request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); 136 request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
130 - 137 + // Subject
  138 + SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
  139 + request.addHeader(subjectHeader);
  140 +
131 ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); 141 ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
132 request.setContent(content, contentTypeHeader); 142 request.setContent(content, contentTypeHeader);
133 return request; 143 return request;
@@ -207,4 +217,50 @@ public class SIPRequestHeaderProvider { @@ -207,4 +217,50 @@ public class SIPRequestHeaderProvider {
207 request.setContent(content, contentTypeHeader); 217 request.setContent(content, contentTypeHeader);
208 return request; 218 return request;
209 } 219 }
  220 +
  221 + public Request createInfoRequest(Device device, StreamInfo streamInfo, String content)
  222 + throws PeerUnavailableException, ParseException, InvalidArgumentException {
  223 + Request request = null;
  224 + Dialog dialog = streamSession.getDialog(streamInfo.getDeviceID(), streamInfo.getChannelId());
  225 +
  226 + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(),
  227 + device.getHostAddress());
  228 + // via
  229 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  230 + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(),
  231 + device.getTransport(), null);
  232 + viaHeader.setRPort();
  233 + viaHeaders.add(viaHeader);
  234 + // from
  235 + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),
  236 + sipConfig.getDomain());
  237 + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
  238 + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, dialog.getLocalTag());
  239 + // to
  240 + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(streamInfo.getChannelId(),
  241 + sipConfig.getDomain());
  242 + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
  243 + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, dialog.getRemoteTag());
  244 +
  245 + // callid
  246 + CallIdHeader callIdHeader = dialog.getCallId();
  247 +
  248 + // Forwards
  249 + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
  250 +
  251 + // ceq
  252 + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory()
  253 + .createCSeqHeader(InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()), Request.INFO);
  254 +
  255 + request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,
  256 + fromHeader, toHeader, viaHeaders, maxForwards);
  257 + Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
  258 + .createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
  259 + request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
  260 +
  261 + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
  262 + "MANSRTSP");
  263 + request.setContent(content, contentTypeHeader);
  264 + return request;
  265 + }
210 } 266 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2 2
3 -import java.lang.reflect.Field;  
4 -import java.text.ParseException;  
5 -import java.util.HashSet;  
6 -  
7 -import javax.sip.*;  
8 -import javax.sip.address.SipURI;  
9 -import javax.sip.header.CallIdHeader;  
10 -import javax.sip.header.ViaHeader;  
11 -import javax.sip.message.Request;  
12 -  
13 import com.alibaba.fastjson.JSONObject; 3 import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.conf.SipConfig;
14 import com.genersoft.iot.vmp.conf.UserSetup; 6 import com.genersoft.iot.vmp.conf.UserSetup;
  7 +import com.genersoft.iot.vmp.gb28181.bean.Device;
15 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; 8 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
16 -import com.genersoft.iot.vmp.media.zlm.*;  
17 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; 9 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  10 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  12 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
  13 +import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
  14 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  15 +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
18 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 16 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
19 import com.genersoft.iot.vmp.service.IMediaServerService; 17 import com.genersoft.iot.vmp.service.IMediaServerService;
20 import com.genersoft.iot.vmp.service.bean.SSRCInfo; 18 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
21 import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
22 import com.genersoft.iot.vmp.storager.IVideoManagerStorager; 20 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  21 +import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
23 import gov.nist.javax.sip.SipProviderImpl; 22 import gov.nist.javax.sip.SipProviderImpl;
24 import gov.nist.javax.sip.SipStackImpl; 23 import gov.nist.javax.sip.SipStackImpl;
25 import gov.nist.javax.sip.message.SIPRequest; 24 import gov.nist.javax.sip.message.SIPRequest;
@@ -29,20 +28,20 @@ import org.slf4j.LoggerFactory; @@ -29,20 +28,20 @@ import org.slf4j.LoggerFactory;
29 import org.springframework.beans.factory.annotation.Autowired; 28 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.beans.factory.annotation.Qualifier; 29 import org.springframework.beans.factory.annotation.Qualifier;
31 import org.springframework.context.annotation.DependsOn; 30 import org.springframework.context.annotation.DependsOn;
32 -import org.springframework.context.annotation.Lazy;  
33 import org.springframework.stereotype.Component; 31 import org.springframework.stereotype.Component;
34 -  
35 -import com.genersoft.iot.vmp.conf.SipConfig;  
36 -import com.genersoft.iot.vmp.gb28181.bean.Device;  
37 -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;  
38 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;  
39 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;  
40 -import com.genersoft.iot.vmp.gb28181.utils.DateUtil;  
41 -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;  
42 import org.springframework.util.StringUtils; 32 import org.springframework.util.StringUtils;
43 33
  34 +import javax.sip.*;
  35 +import javax.sip.address.SipURI;
  36 +import javax.sip.header.CallIdHeader;
  37 +import javax.sip.header.ViaHeader;
  38 +import javax.sip.message.Request;
  39 +import java.lang.reflect.Field;
  40 +import java.text.ParseException;
  41 +import java.util.HashSet;
  42 +
44 /** 43 /**
45 - * @Description:设备能力接口,用于定义设备的控制、查询能力 44 + * @description:设备能力接口,用于定义设备的控制、查询能力
46 * @author: swwheihei 45 * @author: swwheihei
47 * @date: 2020年5月3日 下午9:22:48 46 * @date: 2020年5月3日 下午9:22:48
48 */ 47 */
@@ -55,12 +54,10 @@ public class SIPCommander implements ISIPCommander { @@ -55,12 +54,10 @@ public class SIPCommander implements ISIPCommander {
55 @Autowired 54 @Autowired
56 private SipConfig sipConfig; 55 private SipConfig sipConfig;
57 56
58 - @Lazy  
59 @Autowired 57 @Autowired
60 @Qualifier(value="tcpSipProvider") 58 @Qualifier(value="tcpSipProvider")
61 private SipProviderImpl tcpSipProvider; 59 private SipProviderImpl tcpSipProvider;
62 60
63 - @Lazy  
64 @Autowired 61 @Autowired
65 @Qualifier(value="udpSipProvider") 62 @Qualifier(value="udpSipProvider")
66 private SipProviderImpl udpSipProvider; 63 private SipProviderImpl udpSipProvider;
@@ -89,11 +86,6 @@ public class SIPCommander implements ISIPCommander { @@ -89,11 +86,6 @@ public class SIPCommander implements ISIPCommander {
89 @Autowired 86 @Autowired
90 private IMediaServerService mediaServerService; 87 private IMediaServerService mediaServerService;
91 88
92 - private SIPDialog dialog;  
93 -  
94 - public SipConfig getSipConfig() {  
95 - return sipConfig;  
96 - }  
97 89
98 /** 90 /**
99 * 云台方向放控制,使用配置文件中的默认镜头移动速度 91 * 云台方向放控制,使用配置文件中的默认镜头移动速度
@@ -361,7 +353,7 @@ public class SIPCommander implements ISIPCommander { @@ -361,7 +353,7 @@ public class SIPCommander implements ISIPCommander {
361 // 353 //
362 StringBuffer content = new StringBuffer(200); 354 StringBuffer content = new StringBuffer(200);
363 content.append("v=0\r\n"); 355 content.append("v=0\r\n");
364 - content.append("o="+"00000"+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n"); 356 + content.append("o="+ sipConfig.getId()+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
365 content.append("s=Play\r\n"); 357 content.append("s=Play\r\n");
366 content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n"); 358 content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
367 content.append("t=0 0\r\n"); 359 content.append("t=0 0\r\n");
@@ -427,8 +419,8 @@ public class SIPCommander implements ISIPCommander { @@ -427,8 +419,8 @@ public class SIPCommander implements ISIPCommander {
427 mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc()); 419 mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc());
428 errorEvent.response(e); 420 errorEvent.response(e);
429 }), e ->{ 421 }), e ->{
430 - streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(),e.getClientTransaction());  
431 - streamSession.put(device.getDeviceId(), channelId , e.getDialog()); 422 + streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction());
  423 + streamSession.put(device.getDeviceId(), channelId , e.dialog);
432 }); 424 });
433 425
434 426
@@ -468,7 +460,7 @@ public class SIPCommander implements ISIPCommander { @@ -468,7 +460,7 @@ public class SIPCommander implements ISIPCommander {
468 460
469 StringBuffer content = new StringBuffer(200); 461 StringBuffer content = new StringBuffer(200);
470 content.append("v=0\r\n"); 462 content.append("v=0\r\n");
471 - content.append("o="+sipConfig.getId()+" 0 0 IN IP4 "+sipConfig.getIp()+"\r\n"); 463 + content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
472 content.append("s=Playback\r\n"); 464 content.append("s=Playback\r\n");
473 content.append("u="+channelId+":0\r\n"); 465 content.append("u="+channelId+":0\r\n");
474 content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); 466 content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
@@ -532,12 +524,12 @@ public class SIPCommander implements ISIPCommander { @@ -532,12 +524,12 @@ public class SIPCommander implements ISIPCommander {
532 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() 524 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
533 : udpSipProvider.getNewCallId(); 525 : udpSipProvider.getNewCallId();
534 526
535 - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader); 527 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
536 528
537 transmitRequest(device, request, errorEvent, okEvent -> { 529 transmitRequest(device, request, errorEvent, okEvent -> {
538 - Dialog dialog = okEvent.getClientTransaction().getDialog();  
539 - streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), okEvent.getClientTransaction());  
540 - streamSession.put(device.getDeviceId(), channelId, dialog); 530 + ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
  531 + streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), responseEvent.getClientTransaction());
  532 + streamSession.put(device.getDeviceId(), channelId, okEvent.dialog);
541 }); 533 });
542 } catch ( SipException | ParseException | InvalidArgumentException e) { 534 } catch ( SipException | ParseException | InvalidArgumentException e) {
543 e.printStackTrace(); 535 e.printStackTrace();
@@ -575,7 +567,7 @@ public class SIPCommander implements ISIPCommander { @@ -575,7 +567,7 @@ public class SIPCommander implements ISIPCommander {
575 567
576 StringBuffer content = new StringBuffer(200); 568 StringBuffer content = new StringBuffer(200);
577 content.append("v=0\r\n"); 569 content.append("v=0\r\n");
578 - content.append("o="+sipConfig.getId()+" 0 0 IN IP4 "+sipConfig.getIp()+"\r\n"); 570 + content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
579 content.append("s=Download\r\n"); 571 content.append("s=Download\r\n");
580 content.append("u="+channelId+":0\r\n"); 572 content.append("u="+channelId+":0\r\n");
581 content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); 573 content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
@@ -640,7 +632,7 @@ public class SIPCommander implements ISIPCommander { @@ -640,7 +632,7 @@ public class SIPCommander implements ISIPCommander {
640 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() 632 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
641 : udpSipProvider.getNewCallId(); 633 : udpSipProvider.getNewCallId();
642 634
643 - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader); 635 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
644 636
645 ClientTransaction transaction = transmitRequest(device, request, errorEvent); 637 ClientTransaction transaction = transmitRequest(device, request, errorEvent);
646 streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction); 638 streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction);
@@ -667,6 +659,10 @@ public class SIPCommander implements ISIPCommander { @@ -667,6 +659,10 @@ public class SIPCommander implements ISIPCommander {
667 ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId); 659 ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
668 if (transaction == null) { 660 if (transaction == null) {
669 logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId); 661 logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
  662 + SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
  663 + if (okEvent != null) {
  664 + okEvent.response(eventResult);
  665 + }
670 return; 666 return;
671 } 667 }
672 SIPDialog dialog = streamSession.getDialog(deviceId, channelId); 668 SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
@@ -1200,14 +1196,15 @@ public class SIPCommander implements ISIPCommander { @@ -1200,14 +1196,15 @@ public class SIPCommander implements ISIPCommander {
1200 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss 1196 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
1201 */ 1197 */
1202 @Override 1198 @Override
1203 - public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime) {  
1204 - 1199 + public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent) {
  1200 +
  1201 +
1205 try { 1202 try {
1206 StringBuffer recordInfoXml = new StringBuffer(200); 1203 StringBuffer recordInfoXml = new StringBuffer(200);
1207 recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n"); 1204 recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
1208 recordInfoXml.append("<Query>\r\n"); 1205 recordInfoXml.append("<Query>\r\n");
1209 recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n"); 1206 recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
1210 - recordInfoXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); 1207 + recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
1211 recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n"); 1208 recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
1212 recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n"); 1209 recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
1213 recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n"); 1210 recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
@@ -1224,7 +1221,7 @@ public class SIPCommander implements ISIPCommander { @@ -1224,7 +1221,7 @@ public class SIPCommander implements ISIPCommander {
1224 Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), 1221 Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
1225 "z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader); 1222 "z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader);
1226 1223
1227 - transmitRequest(device, request); 1224 + transmitRequest(device, request, errorEvent);
1228 } catch (SipException | ParseException | InvalidArgumentException e) { 1225 } catch (SipException | ParseException | InvalidArgumentException e) {
1229 e.printStackTrace(); 1226 e.printStackTrace();
1230 return false; 1227 return false;
@@ -1486,6 +1483,33 @@ public class SIPCommander implements ISIPCommander { @@ -1486,6 +1483,33 @@ public class SIPCommander implements ISIPCommander {
1486 } 1483 }
1487 } 1484 }
1488 1485
  1486 + @Override
  1487 + public boolean catalogSubscribe(Device device, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
  1488 + try {
  1489 + StringBuffer cmdXml = new StringBuffer(200);
  1490 + cmdXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
  1491 + cmdXml.append("<Query>\r\n");
  1492 + cmdXml.append("<CmdType>Catalog</CmdType>\r\n");
  1493 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1494 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1495 + cmdXml.append("</Query>\r\n");
  1496 +
  1497 + String tm = Long.toString(System.currentTimeMillis());
  1498 +
  1499 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1500 + : udpSipProvider.getNewCallId();
  1501 +
  1502 + Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, device.getSubscribeCycleForCatalog(), "Catalog" , callIdHeader);
  1503 + transmitRequest(device, request, errorEvent, okEvent);
  1504 +
  1505 + return true;
  1506 +
  1507 + } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) {
  1508 + e.printStackTrace();
  1509 + return false;
  1510 + }
  1511 + }
  1512 +
1489 1513
1490 private ClientTransaction transmitRequest(Device device, Request request) throws SipException { 1514 private ClientTransaction transmitRequest(Device device, Request request) throws SipException {
1491 return transmitRequest(device, request, null, null); 1515 return transmitRequest(device, request, null, null);
@@ -1506,14 +1530,127 @@ public class SIPCommander implements ISIPCommander { @@ -1506,14 +1530,127 @@ public class SIPCommander implements ISIPCommander {
1506 CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME); 1530 CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
1507 // 添加错误订阅 1531 // 添加错误订阅
1508 if (errorEvent != null) { 1532 if (errorEvent != null) {
1509 - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), errorEvent); 1533 + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
  1534 + errorEvent.response(eventResult);
  1535 + sipSubscribe.removeErrorSubscribe(eventResult.callId);
  1536 + }));
1510 } 1537 }
1511 // 添加订阅 1538 // 添加订阅
1512 if (okEvent != null) { 1539 if (okEvent != null) {
1513 - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); 1540 + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult ->{
  1541 + okEvent.response(eventResult);
  1542 + sipSubscribe.removeOkSubscribe(eventResult.callId);
  1543 + });
1514 } 1544 }
1515 1545
1516 clientTransaction.sendRequest(); 1546 clientTransaction.sendRequest();
1517 return clientTransaction; 1547 return clientTransaction;
1518 } 1548 }
  1549 +
  1550 + /**
  1551 + * 回放暂停
  1552 + */
  1553 + @Override
  1554 + public void playPauseCmd(Device device, StreamInfo streamInfo) {
  1555 + try {
  1556 +
  1557 + StringBuffer content = new StringBuffer(200);
  1558 + content.append("PAUSE RTSP/1.0\r\n");
  1559 + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
  1560 + content.append("PauseTime: now\r\n");
  1561 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1562 + logger.info(request.toString());
  1563 + ClientTransaction clientTransaction = null;
  1564 + if ("TCP".equals(device.getTransport())) {
  1565 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1566 + } else if ("UDP".equals(device.getTransport())) {
  1567 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1568 + }
  1569 + if (clientTransaction != null) {
  1570 + clientTransaction.sendRequest();
  1571 + }
  1572 +
  1573 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1574 + e.printStackTrace();
  1575 + }
  1576 + }
  1577 +
  1578 + /**
  1579 + * 回放恢复
  1580 + */
  1581 + @Override
  1582 + public void playResumeCmd(Device device, StreamInfo streamInfo) {
  1583 + try {
  1584 + StringBuffer content = new StringBuffer(200);
  1585 + content.append("PLAY RTSP/1.0\r\n");
  1586 + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
  1587 + content.append("Range: npt=now-\r\n");
  1588 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1589 + logger.info(request.toString());
  1590 + ClientTransaction clientTransaction = null;
  1591 + if ("TCP".equals(device.getTransport())) {
  1592 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1593 + } else if ("UDP".equals(device.getTransport())) {
  1594 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1595 + }
  1596 +
  1597 + clientTransaction.sendRequest();
  1598 +
  1599 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1600 + e.printStackTrace();
  1601 + }
  1602 + }
  1603 +
  1604 + /**
  1605 + * 回放拖动播放
  1606 + */
  1607 + @Override
  1608 + public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) {
  1609 + try {
  1610 + StringBuffer content = new StringBuffer(200);
  1611 + content.append("PLAY RTSP/1.0\r\n");
  1612 + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
  1613 + content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
  1614 +
  1615 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1616 + logger.info(request.toString());
  1617 + ClientTransaction clientTransaction = null;
  1618 + if ("TCP".equals(device.getTransport())) {
  1619 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1620 + } else if ("UDP".equals(device.getTransport())) {
  1621 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1622 + }
  1623 +
  1624 + clientTransaction.sendRequest();
  1625 +
  1626 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1627 + e.printStackTrace();
  1628 + }
  1629 + }
  1630 +
  1631 + /**
  1632 + * 回放倍速播放
  1633 + */
  1634 + @Override
  1635 + public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) {
  1636 + try {
  1637 + StringBuffer content = new StringBuffer(200);
  1638 + content.append("PLAY RTSP/1.0\r\n");
  1639 + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
  1640 + content.append("Scale: " + String.format("%.1f",speed) + "\r\n");
  1641 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1642 + logger.info(request.toString());
  1643 + ClientTransaction clientTransaction = null;
  1644 + if ("TCP".equals(device.getTransport())) {
  1645 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1646 + } else if ("UDP".equals(device.getTransport())) {
  1647 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1648 + }
  1649 +
  1650 + clientTransaction.sendRequest();
  1651 +
  1652 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1653 + e.printStackTrace();
  1654 + }
  1655 + }
1519 } 1656 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -100,7 +100,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { @@ -100,7 +100,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
100 if (event != null) { 100 if (event != null) {
101 logger.info("向上级平台 [ {} ] 注册发上错误: {} ", 101 logger.info("向上级平台 [ {} ] 注册发上错误: {} ",
102 parentPlatform.getServerGBId(), 102 parentPlatform.getServerGBId(),
103 - event.getResponse().getReasonPhrase()); 103 + event.msg);
104 } 104 }
105 if (errorEvent != null ) { 105 if (errorEvent != null ) {
106 errorEvent.response(event); 106 errorEvent.response(event);
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/ISIPRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request;
  2 +
  3 +import javax.sip.RequestEvent;
  4 +
  5 +/**
  6 + * @description: 对SIP事件进行处理,包括request, response, timeout, ioException, transactionTerminated,dialogTerminated
  7 + * @author: panlinlin
  8 + * @date: 2021年11月5日 15:47
  9 + */
  10 +public interface ISIPRequestProcessor {
  11 +
  12 + void process(RequestEvent event);
  13 +
  14 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request;
  2 +
  3 +import gov.nist.javax.sip.SipProviderImpl;
  4 +import org.springframework.beans.factory.annotation.Autowired;
  5 +import org.springframework.beans.factory.annotation.Qualifier;
  6 +
  7 +/**
  8 + * @description:处理接收IPCamera发来的SIP协议请求消息
  9 + * @author: songww
  10 + * @date: 2020年5月3日 下午4:42:22
  11 + */
  12 +public abstract class SIPRequestProcessorAbstract {
  13 +
  14 +
  15 + @Autowired
  16 + @Qualifier(value="tcpSipProvider")
  17 + private SipProviderImpl tcpSipProvider;
  18 +
  19 + @Autowired
  20 + @Qualifier(value="udpSipProvider")
  21 + private SipProviderImpl udpSipProvider;
  22 +
  23 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java renamed to src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
1 -package com.genersoft.iot.vmp.gb28181.transmit.request;  
2 -  
3 -import javax.sip.PeerUnavailableException;  
4 -import javax.sip.RequestEvent;  
5 -import javax.sip.ServerTransaction;  
6 -import javax.sip.SipFactory;  
7 -import javax.sip.SipProvider;  
8 -import javax.sip.TransactionAlreadyExistsException;  
9 -import javax.sip.TransactionUnavailableException;  
10 -import javax.sip.address.AddressFactory;  
11 -import javax.sip.header.HeaderFactory;  
12 -import javax.sip.header.ViaHeader;  
13 -import javax.sip.message.MessageFactory;  
14 -import javax.sip.message.Request;  
15 -  
16 -import gov.nist.javax.sip.SipStackImpl;  
17 -import gov.nist.javax.sip.message.SIPRequest;  
18 -import gov.nist.javax.sip.stack.SIPServerTransaction;  
19 -import org.slf4j.Logger;  
20 -import org.slf4j.LoggerFactory;  
21 -  
22 -/**  
23 - * @Description:处理接收IPCamera发来的SIP协议请求消息  
24 - * @author: songww  
25 - * @date: 2020年5月3日 下午4:42:22  
26 - */  
27 -public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcessor {  
28 -  
29 - private final static Logger logger = LoggerFactory.getLogger(SIPRequestAbstractProcessor.class);  
30 -  
31 - protected RequestEvent evt;  
32 -  
33 - private SipProvider tcpSipProvider;  
34 -  
35 - private SipProvider udpSipProvider;  
36 -  
37 - @Override  
38 - public void process() {  
39 - this.process(evt);  
40 - }  
41 -  
42 - public abstract void process(RequestEvent evt);  
43 -  
44 - public ServerTransaction getServerTransaction(RequestEvent evt) {  
45 - Request request = evt.getRequest();  
46 - ServerTransaction serverTransaction = evt.getServerTransaction();  
47 - // 判断TCP还是UDP  
48 - boolean isTcp = false;  
49 - ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);  
50 - String transport = reqViaHeader.getTransport();  
51 - if (transport.equals("TCP")) {  
52 - isTcp = true;  
53 - }  
54 -  
55 - if (serverTransaction == null) {  
56 - try {  
57 - if (isTcp) {  
58 - SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();  
59 - serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);  
60 - if (serverTransaction == null) {  
61 - serverTransaction = tcpSipProvider.getNewServerTransaction(request);  
62 - }  
63 - } else {  
64 - SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();  
65 - serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);  
66 - if (serverTransaction == null) {  
67 - serverTransaction = udpSipProvider.getNewServerTransaction(request);  
68 - }  
69 - }  
70 - } catch (TransactionAlreadyExistsException e) {  
71 - logger.error(e.getMessage());  
72 - } catch (TransactionUnavailableException e) {  
73 - logger.error(e.getMessage());  
74 - }  
75 - }  
76 - return serverTransaction;  
77 - }  
78 -  
79 - public AddressFactory getAddressFactory() {  
80 - try {  
81 - return SipFactory.getInstance().createAddressFactory();  
82 - } catch (PeerUnavailableException e) {  
83 - e.printStackTrace();  
84 - }  
85 - return null;  
86 - }  
87 -  
88 - public HeaderFactory getHeaderFactory() {  
89 - try {  
90 - return SipFactory.getInstance().createHeaderFactory();  
91 - } catch (PeerUnavailableException e) {  
92 - e.printStackTrace();  
93 - }  
94 - return null;  
95 - }  
96 -  
97 - public MessageFactory getMessageFactory() {  
98 - try {  
99 - return SipFactory.getInstance().createMessageFactory();  
100 - } catch (PeerUnavailableException e) {  
101 - e.printStackTrace();  
102 - }  
103 - return null;  
104 - }  
105 -  
106 - public RequestEvent getRequestEvent() {  
107 - return evt;  
108 - }  
109 -  
110 - public void setRequestEvent(RequestEvent evt) {  
111 - this.evt = evt;  
112 - }  
113 -  
114 - public SipProvider getTcpSipProvider() {  
115 - return tcpSipProvider;  
116 - }  
117 -  
118 - public void setTcpSipProvider(SipProvider tcpSipProvider) {  
119 - this.tcpSipProvider = tcpSipProvider;  
120 - }  
121 -  
122 - public SipProvider getUdpSipProvider() {  
123 - return udpSipProvider;  
124 - }  
125 -  
126 - public void setUdpSipProvider(SipProvider udpSipProvider) {  
127 - this.udpSipProvider = udpSipProvider;  
128 - }  
129 -  
130 -  
131 -} 1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request;
  2 +
  3 +import gov.nist.javax.sip.SipProviderImpl;
  4 +import gov.nist.javax.sip.SipStackImpl;
  5 +import gov.nist.javax.sip.message.SIPRequest;
  6 +import gov.nist.javax.sip.stack.SIPServerTransaction;
  7 +import org.dom4j.Document;
  8 +import org.dom4j.DocumentException;
  9 +import org.dom4j.Element;
  10 +import org.dom4j.io.SAXReader;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.beans.factory.annotation.Qualifier;
  15 +
  16 +import javax.sip.*;
  17 +import javax.sip.address.Address;
  18 +import javax.sip.address.AddressFactory;
  19 +import javax.sip.address.SipURI;
  20 +import javax.sip.header.ContentTypeHeader;
  21 +import javax.sip.header.HeaderFactory;
  22 +import javax.sip.header.ViaHeader;
  23 +import javax.sip.message.MessageFactory;
  24 +import javax.sip.message.Request;
  25 +import javax.sip.message.Response;
  26 +import java.io.ByteArrayInputStream;
  27 +import java.text.ParseException;
  28 +
  29 +/**
  30 + * @description:处理接收IPCamera发来的SIP协议请求消息
  31 + * @author: songww
  32 + * @date: 2020年5月3日 下午4:42:22
  33 + */
  34 +public abstract class SIPRequestProcessorParent {
  35 +
  36 + private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorParent.class);
  37 +
  38 + @Autowired
  39 + @Qualifier(value="tcpSipProvider")
  40 + private SipProviderImpl tcpSipProvider;
  41 +
  42 + @Autowired
  43 + @Qualifier(value="udpSipProvider")
  44 + private SipProviderImpl udpSipProvider;
  45 +
  46 + /**
  47 + * 根据 RequestEvent 获取 ServerTransaction
  48 + * @param evt
  49 + * @return
  50 + */
  51 + public ServerTransaction getServerTransaction(RequestEvent evt) {
  52 + Request request = evt.getRequest();
  53 + ServerTransaction serverTransaction = evt.getServerTransaction();
  54 + // 判断TCP还是UDP
  55 + boolean isTcp = false;
  56 + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
  57 + String transport = reqViaHeader.getTransport();
  58 + if (transport.equals("TCP")) {
  59 + isTcp = true;
  60 + }
  61 +
  62 + if (serverTransaction == null) {
  63 + try {
  64 + if (isTcp) {
  65 + SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
  66 + serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
  67 + if (serverTransaction == null) {
  68 + serverTransaction = tcpSipProvider.getNewServerTransaction(request);
  69 + }
  70 + } else {
  71 + SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
  72 + serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
  73 + if (serverTransaction == null) {
  74 + serverTransaction = udpSipProvider.getNewServerTransaction(request);
  75 + }
  76 + }
  77 + } catch (TransactionAlreadyExistsException e) {
  78 + logger.error(e.getMessage());
  79 + } catch (TransactionUnavailableException e) {
  80 + logger.error(e.getMessage());
  81 + }
  82 + }
  83 + return serverTransaction;
  84 + }
  85 +
  86 + public AddressFactory getAddressFactory() {
  87 + try {
  88 + return SipFactory.getInstance().createAddressFactory();
  89 + } catch (PeerUnavailableException e) {
  90 + e.printStackTrace();
  91 + }
  92 + return null;
  93 + }
  94 +
  95 + public HeaderFactory getHeaderFactory() {
  96 + try {
  97 + return SipFactory.getInstance().createHeaderFactory();
  98 + } catch (PeerUnavailableException e) {
  99 + e.printStackTrace();
  100 + }
  101 + return null;
  102 + }
  103 +
  104 + public MessageFactory getMessageFactory() {
  105 + try {
  106 + return SipFactory.getInstance().createMessageFactory();
  107 + } catch (PeerUnavailableException e) {
  108 + e.printStackTrace();
  109 + }
  110 + return null;
  111 + }
  112 +
  113 + /***
  114 + * 回复状态码
  115 + * 100 trying
  116 + * 200 OK
  117 + * 400
  118 + * 404
  119 + * @param evt
  120 + * @throws SipException
  121 + * @throws InvalidArgumentException
  122 + * @throws ParseException
  123 + */
  124 + public void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException {
  125 + Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
  126 + ServerTransaction serverTransaction = getServerTransaction(evt);
  127 + serverTransaction.sendResponse(response);
  128 + if (statusCode >= 200 && !"NOTIFY".equals(evt.getRequest().getMethod())) {
  129 +
  130 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  131 + }
  132 + }
  133 +
  134 + public void responseAck(RequestEvent evt, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
  135 + Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
  136 + response.setReasonPhrase(msg);
  137 + ServerTransaction serverTransaction = getServerTransaction(evt);
  138 + serverTransaction.sendResponse(response);
  139 + if (statusCode >= 200 && !"NOTIFY".equals(evt.getRequest().getMethod())) {
  140 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  141 + }
  142 + }
  143 +
  144 + /**
  145 + * 回复带sdp的200
  146 + * @param evt
  147 + * @param sdp
  148 + * @throws SipException
  149 + * @throws InvalidArgumentException
  150 + * @throws ParseException
  151 + */
  152 + public void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException {
  153 + Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
  154 + SipFactory sipFactory = SipFactory.getInstance();
  155 + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  156 + response.setContent(sdp, contentTypeHeader);
  157 +
  158 + SipURI sipURI = (SipURI)evt.getRequest().getRequestURI();
  159 +
  160 + Address concatAddress = sipFactory.createAddressFactory().createAddress(
  161 + sipFactory.createAddressFactory().createSipURI(sipURI.getUser(), sipURI.getHost()+":"+sipURI.getPort()
  162 + ));
  163 + response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
  164 + getServerTransaction(evt).sendResponse(response);
  165 + }
  166 +
  167 + public Element getRootElement(RequestEvent evt) throws DocumentException {
  168 + return getRootElement(evt, "gb2312");
  169 + }
  170 + public Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
  171 + if (charset == null) charset = "gb2312";
  172 + Request request = evt.getRequest();
  173 + SAXReader reader = new SAXReader();
  174 + reader.setEncoding(charset);
  175 + Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
  176 + return xml.getRootElement();
  177 + }
  178 +
  179 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java renamed to src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
1 -package com.genersoft.iot.vmp.gb28181.transmit.request.impl;  
2 -  
3 -import java.util.HashMap;  
4 -import java.util.Map;  
5 -  
6 -import javax.sip.*;  
7 -import javax.sip.address.SipURI;  
8 -import javax.sip.header.FromHeader;  
9 -import javax.sip.header.HeaderAddress;  
10 -import javax.sip.header.ToHeader;  
11 -  
12 -import com.genersoft.iot.vmp.common.StreamInfo;  
13 -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;  
14 -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;  
15 -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;  
16 -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;  
17 -import com.genersoft.iot.vmp.service.IMediaServerService;  
18 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
19 -import org.slf4j.Logger;  
20 -import org.slf4j.LoggerFactory;  
21 -  
22 -/**  
23 - * @Description:ACK请求处理器  
24 - * @author: swwheihei  
25 - * @date: 2020年5月3日 下午5:31:45  
26 - */  
27 -public class AckRequestProcessor extends SIPRequestAbstractProcessor {  
28 -  
29 - private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);  
30 -  
31 - private IRedisCatchStorage redisCatchStorage;  
32 -  
33 - private ZLMRTPServerFactory zlmrtpServerFactory;  
34 -  
35 - private IMediaServerService mediaServerService;  
36 -  
37 - /**  
38 - * 处理 ACK请求  
39 - *  
40 - * @param evt  
41 - */  
42 - @Override  
43 - public void process(RequestEvent evt) {  
44 - //Request request = evt.getRequest();  
45 - Dialog dialog = evt.getDialog();  
46 - if (dialog == null) return;  
47 - //DialogState state = dialog.getState();  
48 - if (/*request.getMecodewwthod().equals(Request.INVITE) &&*/ dialog.getState()== DialogState.CONFIRMED) {  
49 - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();  
50 - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();  
51 - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId);  
52 - String is_Udp = sendRtpItem.isTcp() ? "0" : "1";  
53 - String deviceId = sendRtpItem.getDeviceId();  
54 - StreamInfo streamInfo = null;  
55 - if (deviceId == null) {  
56 - streamInfo = new StreamInfo();  
57 - streamInfo.setApp(sendRtpItem.getApp());  
58 - streamInfo.setStreamId(sendRtpItem.getStreamId());  
59 - }else {  
60 - streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);  
61 - sendRtpItem.setStreamId(streamInfo.getStreamId());  
62 - streamInfo.setApp("rtp");  
63 - }  
64 -  
65 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
66 - logger.info(platformGbId);  
67 - logger.info(channelId);  
68 - Map<String, Object> param = new HashMap<>();  
69 - param.put("vhost","__defaultVhost__");  
70 - param.put("app",streamInfo.getApp());  
71 - param.put("stream",streamInfo.getStreamId());  
72 - param.put("ssrc", sendRtpItem.getSsrc());  
73 - param.put("dst_url",sendRtpItem.getIp());  
74 - param.put("dst_port", sendRtpItem.getPort());  
75 - param.put("is_udp", is_Udp);  
76 - //param.put ("src_port", sendRtpItem.getLocalPort());  
77 - // 设备推流查询,成功后才能转推  
78 - boolean rtpPushed = false;  
79 - long startTime = System.currentTimeMillis();  
80 - while (!rtpPushed) {  
81 - try {  
82 - if (System.currentTimeMillis() - startTime < 30 * 1000) {  
83 - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());  
84 - if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {  
85 - rtpPushed = true;  
86 - logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",  
87 - streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());  
88 - zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);  
89 - } else {  
90 - logger.info("等待设备推流[{}/{}].......",  
91 - streamInfo.getApp() ,streamInfo.getStreamId());  
92 - Thread.sleep(1000);  
93 - continue;  
94 - }  
95 - } else {  
96 - rtpPushed = true;  
97 - logger.info("设备推流[{}/{}]超时,终止向上级推流",  
98 - streamInfo.getApp() ,streamInfo.getStreamId());  
99 - }  
100 - } catch (InterruptedException e) {  
101 - e.printStackTrace();  
102 - }  
103 - }  
104 - }  
105 - // try {  
106 - // Request ackRequest = null;  
107 - // CSeq csReq = (CSeq) request.getHeader(CSeq.NAME);  
108 - // ackRequest = dialog.createAck(csReq.getSeqNumber());  
109 - // dialog.sendAck(ackRequest);  
110 - // logger.info("send ack to callee:" + ackRequest.toString());  
111 - // } catch (SipException e) {  
112 - // e.printStackTrace();  
113 - // } catch (InvalidArgumentException e) {  
114 - // e.printStackTrace();  
115 - // }  
116 -  
117 - }  
118 -  
119 - public IRedisCatchStorage getRedisCatchStorage() {  
120 - return redisCatchStorage;  
121 - }  
122 -  
123 - public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {  
124 - this.redisCatchStorage = redisCatchStorage;  
125 - }  
126 -  
127 - public ZLMRTPServerFactory getZlmrtpServerFactory() {  
128 - return zlmrtpServerFactory;  
129 - }  
130 -  
131 - public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {  
132 - this.zlmrtpServerFactory = zlmrtpServerFactory;  
133 - }  
134 -  
135 - public IMediaServerService getMediaServerService() {  
136 - return mediaServerService;  
137 - }  
138 -  
139 - public void setMediaServerService(IMediaServerService mediaServerService) {  
140 - this.mediaServerService = mediaServerService;  
141 - }  
142 -} 1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  8 +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  9 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  10 +import com.genersoft.iot.vmp.service.IMediaServerService;
  11 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  12 +import org.slf4j.Logger;
  13 +import org.slf4j.LoggerFactory;
  14 +import org.springframework.beans.factory.InitializingBean;
  15 +import org.springframework.beans.factory.annotation.Autowired;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +import javax.sip.Dialog;
  19 +import javax.sip.DialogState;
  20 +import javax.sip.RequestEvent;
  21 +import javax.sip.address.SipURI;
  22 +import javax.sip.header.FromHeader;
  23 +import javax.sip.header.HeaderAddress;
  24 +import javax.sip.header.ToHeader;
  25 +import java.util.HashMap;
  26 +import java.util.Map;
  27 +
  28 +/**
  29 + * SIP命令类型: ACK请求
  30 + */
  31 +@Component
  32 +public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
  33 +
  34 + private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
  35 + private String method = "ACK";
  36 +
  37 + @Autowired
  38 + private SIPProcessorObserver sipProcessorObserver;
  39 +
  40 + @Override
  41 + public void afterPropertiesSet() throws Exception {
  42 + // 添加消息处理的订阅
  43 + sipProcessorObserver.addRequestProcessor(method, this);
  44 + }
  45 +
  46 + @Autowired
  47 + private IRedisCatchStorage redisCatchStorage;
  48 +
  49 + @Autowired
  50 + private ZLMRTPServerFactory zlmrtpServerFactory;
  51 +
  52 + @Autowired
  53 + private IMediaServerService mediaServerService;
  54 +
  55 +
  56 + /**
  57 + * 处理 ACK请求
  58 + *
  59 + * @param evt
  60 + */
  61 + @Override
  62 + public void process(RequestEvent evt) {
  63 + Dialog dialog = evt.getDialog();
  64 + if (dialog == null) return;
  65 + if (dialog.getState()== DialogState.CONFIRMED) {
  66 + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
  67 + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
  68 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId);
  69 + String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
  70 + String deviceId = sendRtpItem.getDeviceId();
  71 + StreamInfo streamInfo = null;
  72 + if (deviceId == null) {
  73 + streamInfo = new StreamInfo();
  74 + streamInfo.setApp(sendRtpItem.getApp());
  75 + streamInfo.setStreamId(sendRtpItem.getStreamId());
  76 + }else {
  77 + streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
  78 + sendRtpItem.setStreamId(streamInfo.getStreamId());
  79 + streamInfo.setApp("rtp");
  80 + }
  81 +
  82 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  83 + logger.info(platformGbId);
  84 + logger.info(channelId);
  85 + Map<String, Object> param = new HashMap<>();
  86 + param.put("vhost","__defaultVhost__");
  87 + param.put("app",streamInfo.getApp());
  88 + param.put("stream",streamInfo.getStreamId());
  89 + param.put("ssrc", sendRtpItem.getSsrc());
  90 + param.put("dst_url",sendRtpItem.getIp());
  91 + param.put("dst_port", sendRtpItem.getPort());
  92 + param.put("is_udp", is_Udp);
  93 + //param.put ("src_port", sendRtpItem.getLocalPort());
  94 + // 设备推流查询,成功后才能转推
  95 + boolean rtpPushed = false;
  96 + long startTime = System.currentTimeMillis();
  97 + while (!rtpPushed) {
  98 + try {
  99 + if (System.currentTimeMillis() - startTime < 30 * 1000) {
  100 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  101 + if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
  102 + rtpPushed = true;
  103 + logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",
  104 + streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());
  105 + zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
  106 + } else {
  107 + logger.info("等待设备推流[{}/{}].......",
  108 + streamInfo.getApp() ,streamInfo.getStreamId());
  109 + Thread.sleep(1000);
  110 + continue;
  111 + }
  112 + } else {
  113 + rtpPushed = true;
  114 + logger.info("设备推流[{}/{}]超时,终止向上级推流",
  115 + streamInfo.getApp() ,streamInfo.getStreamId());
  116 + }
  117 + } catch (InterruptedException e) {
  118 + e.printStackTrace();
  119 + }
  120 + }
  121 + }
  122 + }
  123 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java renamed to src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
1 -package com.genersoft.iot.vmp.gb28181.transmit.request.impl;  
2 -  
3 -import javax.sip.*;  
4 -import javax.sip.address.SipURI;  
5 -import javax.sip.header.FromHeader;  
6 -import javax.sip.header.HeaderAddress;  
7 -import javax.sip.header.ToHeader;  
8 -import javax.sip.message.Response;  
9 -  
10 -import com.genersoft.iot.vmp.common.StreamInfo;  
11 -import com.genersoft.iot.vmp.gb28181.bean.Device;  
12 -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;  
13 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;  
14 -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;  
15 -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;  
16 -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;  
17 -import com.genersoft.iot.vmp.service.IMediaServerService;  
18 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
19 -import com.genersoft.iot.vmp.storager.IVideoManagerStorager;  
20 -import org.slf4j.Logger;  
21 -import org.slf4j.LoggerFactory;  
22 -  
23 -import java.text.ParseException;  
24 -import java.util.HashMap;  
25 -import java.util.Map;  
26 -  
27 -/**  
28 - * @Description: BYE请求处理器  
29 - * @author: lawrencehj  
30 - * @date: 2021年3月9日  
31 - */  
32 -public class ByeRequestProcessor extends SIPRequestAbstractProcessor {  
33 -  
34 - private Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);  
35 -  
36 - private ISIPCommander cmder;  
37 -  
38 - private IRedisCatchStorage redisCatchStorage;  
39 -  
40 - private IVideoManagerStorager storager;  
41 -  
42 - private ZLMRTPServerFactory zlmrtpServerFactory;  
43 -  
44 - private IMediaServerService mediaServerService;  
45 -  
46 - /**  
47 - * 处理BYE请求  
48 - * @param evt  
49 - */  
50 - @Override  
51 - public void process(RequestEvent evt) {  
52 - try {  
53 - responseAck(evt);  
54 - Dialog dialog = evt.getDialog();  
55 - if (dialog == null) return;  
56 - if (dialog.getState().equals(DialogState.TERMINATED)) {  
57 - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();  
58 - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();  
59 - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId);  
60 - logger.info("收到bye, [{}/{}]", platformGbId, channelId);  
61 - if (sendRtpItem != null){  
62 - String streamId = sendRtpItem.getStreamId();  
63 - Map<String, Object> param = new HashMap<>();  
64 - param.put("vhost","__defaultVhost__");  
65 - param.put("app",sendRtpItem.getApp());  
66 - param.put("stream",streamId);  
67 - param.put("ssrc",sendRtpItem.getSsrc());  
68 - logger.info("停止向上级推流:" + streamId);  
69 - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());  
70 - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);  
71 - redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);  
72 - if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {  
73 - logger.info(streamId + "无其它观看者,通知设备停止推流");  
74 - cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);  
75 - }  
76 - }  
77 - // 可能是设备主动停止  
78 - Device device = storager.queryVideoDeviceByChannelId(platformGbId);  
79 - if (device != null) {  
80 - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);  
81 - if (streamInfo != null) {  
82 - redisCatchStorage.stopPlay(streamInfo);  
83 - }  
84 - storager.stopPlay(device.getDeviceId(), channelId);  
85 - mediaServerService.closeRTPServer(device, channelId);  
86 - }  
87 - }  
88 - } catch (SipException e) {  
89 - e.printStackTrace();  
90 - } catch (InvalidArgumentException e) {  
91 - e.printStackTrace();  
92 - } catch (ParseException e) {  
93 - e.printStackTrace();  
94 - }  
95 - }  
96 -  
97 - /***  
98 - * 回复200 OK  
99 - * @param evt  
100 - * @throws SipException  
101 - * @throws InvalidArgumentException  
102 - * @throws ParseException  
103 - */  
104 - private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {  
105 - Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());  
106 - ServerTransaction serverTransaction = getServerTransaction(evt);  
107 - serverTransaction.sendResponse(response);  
108 - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();  
109 - }  
110 -  
111 - public IRedisCatchStorage getRedisCatchStorage() {  
112 - return redisCatchStorage;  
113 - }  
114 -  
115 - public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {  
116 - this.redisCatchStorage = redisCatchStorage;  
117 - }  
118 -  
119 - public ZLMRTPServerFactory getZlmrtpServerFactory() {  
120 - return zlmrtpServerFactory;  
121 - }  
122 -  
123 - public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {  
124 - this.zlmrtpServerFactory = zlmrtpServerFactory;  
125 - }  
126 -  
127 - public ISIPCommander getSIPCommander() {  
128 - return cmder;  
129 - }  
130 -  
131 - public void setSIPCommander(ISIPCommander cmder) {  
132 - this.cmder = cmder;  
133 - }  
134 -  
135 - public IMediaServerService getMediaServerService() {  
136 - return mediaServerService;  
137 - }  
138 -  
139 - public void setMediaServerService(IMediaServerService mediaServerService) {  
140 - this.mediaServerService = mediaServerService;  
141 - }  
142 -  
143 - public IVideoManagerStorager getStorager() {  
144 - return storager;  
145 - }  
146 -  
147 - public void setStorager(IVideoManagerStorager storager) {  
148 - this.storager = storager;  
149 - }  
150 -} 1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
  9 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  10 +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  11 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  12 +import com.genersoft.iot.vmp.service.IMediaServerService;
  13 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  14 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  15 +import org.slf4j.Logger;
  16 +import org.slf4j.LoggerFactory;
  17 +import org.springframework.beans.factory.InitializingBean;
  18 +import org.springframework.beans.factory.annotation.Autowired;
  19 +import org.springframework.stereotype.Component;
  20 +
  21 +import javax.sip.*;
  22 +import javax.sip.address.SipURI;
  23 +import javax.sip.header.FromHeader;
  24 +import javax.sip.header.HeaderAddress;
  25 +import javax.sip.header.ToHeader;
  26 +import javax.sip.message.Response;
  27 +import java.text.ParseException;
  28 +import java.util.HashMap;
  29 +import java.util.Map;
  30 +
  31 +/**
  32 + * SIP命令类型: BYE请求
  33 + */
  34 +@Component
  35 +public class ByeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
  36 +
  37 + private final Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);
  38 + private final String method = "BYE";
  39 +
  40 + @Autowired
  41 + private ISIPCommander cmder;
  42 +
  43 + @Autowired
  44 + private IRedisCatchStorage redisCatchStorage;
  45 +
  46 + @Autowired
  47 + private IVideoManagerStorager storager;
  48 +
  49 + @Autowired
  50 + private ZLMRTPServerFactory zlmrtpServerFactory;
  51 +
  52 + @Autowired
  53 + private IMediaServerService mediaServerService;
  54 +
  55 + @Autowired
  56 + private SIPProcessorObserver sipProcessorObserver;
  57 +
  58 + @Override
  59 + public void afterPropertiesSet() throws Exception {
  60 + // 添加消息处理的订阅
  61 + sipProcessorObserver.addRequestProcessor(method, this);
  62 + }
  63 +
  64 + /**
  65 + * 处理BYE请求
  66 + * @param evt
  67 + */
  68 + @Override
  69 + public void process(RequestEvent evt) {
  70 + try {
  71 + responseAck(evt, Response.OK);
  72 + Dialog dialog = evt.getDialog();
  73 + if (dialog == null) return;
  74 + if (dialog.getState().equals(DialogState.TERMINATED)) {
  75 + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
  76 + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
  77 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId);
  78 + logger.info("收到bye, [{}/{}]", platformGbId, channelId);
  79 + if (sendRtpItem != null){
  80 + String streamId = sendRtpItem.getStreamId();
  81 + Map<String, Object> param = new HashMap<>();
  82 + param.put("vhost","__defaultVhost__");
  83 + param.put("app",sendRtpItem.getApp());
  84 + param.put("stream",streamId);
  85 + param.put("ssrc",sendRtpItem.getSsrc());
  86 + logger.info("停止向上级推流:" + streamId);
  87 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  88 + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
  89 + redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
  90 + if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
  91 + logger.info(streamId + "无其它观看者,通知设备停止推流");
  92 + cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);
  93 + }
  94 + }
  95 + // 可能是设备主动停止
  96 + Device device = storager.queryVideoDeviceByChannelId(platformGbId);
  97 + if (device != null) {
  98 + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
  99 + if (streamInfo != null) {
  100 + redisCatchStorage.stopPlay(streamInfo);
  101 + }
  102 + storager.stopPlay(device.getDeviceId(), channelId);
  103 + mediaServerService.closeRTPServer(device, channelId);
  104 + }
  105 + }
  106 + } catch (SipException e) {
  107 + e.printStackTrace();
  108 + } catch (InvalidArgumentException e) {
  109 + e.printStackTrace();
  110 + } catch (ParseException e) {
  111 + e.printStackTrace();
  112 + }
  113 + }
  114 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  4 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  6 +import org.springframework.beans.factory.InitializingBean;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.sip.RequestEvent;
  11 +
  12 +/**
  13 + * SIP命令类型: CANCEL请求
  14 + */
  15 +@Component
  16 +public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
  17 +
  18 + private String method = "CANCEL";
  19 +
  20 + @Autowired
  21 + private SIPProcessorObserver sipProcessorObserver;
  22 +
  23 + @Override
  24 + public void afterPropertiesSet() throws Exception {
  25 + // 添加消息处理的订阅
  26 + sipProcessorObserver.addRequestProcessor(method, this);
  27 + }
  28 +
  29 + /**
  30 + * 处理CANCEL请求
  31 + *
  32 + * @param evt 事件
  33 + */
  34 + @Override
  35 + public void process(RequestEvent evt) {
  36 + // TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令
  37 +
  38 + }
  39 +
  40 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java renamed to src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
1 -package com.genersoft.iot.vmp.gb28181.transmit.request.impl;  
2 -  
3 -import javax.sdp.*;  
4 -import javax.sip.*;  
5 -import javax.sip.address.Address;  
6 -import javax.sip.address.SipURI;  
7 -import javax.sip.header.*;  
8 -import javax.sip.message.Request;  
9 -import javax.sip.message.Response;  
10 -  
11 -import com.genersoft.iot.vmp.gb28181.bean.*;  
12 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;  
13 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;  
14 -import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;  
15 -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;  
16 -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;  
17 -import com.genersoft.iot.vmp.service.IMediaServerService;  
18 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
19 -import com.genersoft.iot.vmp.storager.IVideoManagerStorager;  
20 -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;  
21 -import com.genersoft.iot.vmp.service.IPlayService;  
22 -import gov.nist.javax.sip.address.AddressImpl;  
23 -import gov.nist.javax.sip.address.SipUri;  
24 -import org.slf4j.Logger;  
25 -import org.slf4j.LoggerFactory;  
26 -  
27 -import java.text.ParseException;  
28 -import java.util.Vector;  
29 -  
30 -/**  
31 - * @Description:处理INVITE请求  
32 - * @author: panll  
33 - * @date: 2021年1月14日  
34 - */  
35 -@SuppressWarnings("rawtypes")  
36 -public class InviteRequestProcessor extends SIPRequestAbstractProcessor {  
37 -  
38 - private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);  
39 -  
40 - private SIPCommanderFroPlatform cmderFroPlatform;  
41 -  
42 - private IVideoManagerStorager storager;  
43 -  
44 - private IRedisCatchStorage redisCatchStorage;  
45 -  
46 - private SIPCommander cmder;  
47 -  
48 - private IPlayService playService;  
49 -  
50 - private ZLMRTPServerFactory zlmrtpServerFactory;  
51 -  
52 - private IMediaServerService mediaServerService;  
53 -  
54 - public ZLMRTPServerFactory getZlmrtpServerFactory() {  
55 - return zlmrtpServerFactory;  
56 - }  
57 -  
58 - public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {  
59 - this.zlmrtpServerFactory = zlmrtpServerFactory;  
60 - }  
61 -  
62 - /**  
63 - * 处理invite请求  
64 - *  
65 - * @param evt  
66 - * 请求消息  
67 - */  
68 - @Override  
69 - public void process(RequestEvent evt) {  
70 - // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令  
71 - try {  
72 - Request request = evt.getRequest();  
73 - SipURI sipURI = (SipURI) request.getRequestURI();  
74 - String channelId = sipURI.getUser();  
75 - String requesterId = null;  
76 -  
77 - FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);  
78 - AddressImpl address = (AddressImpl) fromHeader.getAddress();  
79 - SipUri uri = (SipUri) address.getURI();  
80 - requesterId = uri.getUser();  
81 -  
82 - if (requesterId == null || channelId == null) {  
83 - logger.info("无法从FromHeader的Address中获取到平台id,返回400");  
84 - responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误  
85 - return;  
86 - }  
87 -  
88 - // 查询请求方是否上级平台  
89 - ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);  
90 - if (platform != null) {  
91 - // 查询平台下是否有该通道  
92 - DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);  
93 - GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);  
94 - MediaServerItem mediaServerItem = null;  
95 - // 不是通道可能是直播流  
96 - if (channel != null && gbStream == null ) {  
97 - if (channel.getStatus() == 0) {  
98 - logger.info("通道离线,返回400");  
99 - responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");  
100 - return;  
101 - }  
102 - responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中  
103 - }else if(channel == null && gbStream != null){  
104 - String mediaServerId = gbStream.getMediaServerId();  
105 - mediaServerItem = mediaServerService.getOne(mediaServerId);  
106 - if (mediaServerItem == null) {  
107 - logger.info("[ app={}, stream={} ]zlm找不到,返回410",gbStream.getApp(), gbStream.getStream());  
108 - responseAck(evt, Response.GONE, "media server not found");  
109 - return;  
110 - }  
111 - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());  
112 - if (!streamReady ) {  
113 - logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream());  
114 - responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");  
115 - return;  
116 - }  
117 - responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中  
118 - }else {  
119 - logger.info("通道不存在,返回404");  
120 - responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在  
121 - return;  
122 - }  
123 - // 解析sdp消息, 使用jainsip 自带的sdp解析方式  
124 - String contentString = new String(request.getRawContent());  
125 -  
126 - // jainSip不支持y=字段, 移除移除以解析。  
127 - int ssrcIndex = contentString.indexOf("y=");  
128 - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段  
129 - String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
130 - String substring = contentString.substring(0, contentString.indexOf("y="));  
131 - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);  
132 -  
133 - // 获取支持的格式  
134 - Vector mediaDescriptions = sdp.getMediaDescriptions(true);  
135 - // 查看是否支持PS 负载96  
136 - //String ip = null;  
137 - int port = -1;  
138 - //boolean recvonly = false;  
139 - boolean mediaTransmissionTCP = false;  
140 - Boolean tcpActive = null;  
141 - for (Object description : mediaDescriptions) {  
142 - MediaDescription mediaDescription = (MediaDescription) description;  
143 - Media media = mediaDescription.getMedia();  
144 -  
145 - Vector mediaFormats = media.getMediaFormats(false);  
146 - if (mediaFormats.contains("96")) {  
147 - port = media.getMediaPort();  
148 - //String mediaType = media.getMediaType();  
149 - String protocol = media.getProtocol();  
150 -  
151 - // 区分TCP发流还是udp, 当前默认udp  
152 - if ("TCP/RTP/AVP".equals(protocol)) {  
153 - String setup = mediaDescription.getAttribute("setup");  
154 - if (setup != null) {  
155 - mediaTransmissionTCP = true;  
156 - if ("active".equals(setup)) {  
157 - tcpActive = true;  
158 - } else if ("passive".equals(setup)) {  
159 - tcpActive = false;  
160 - }  
161 - }  
162 - }  
163 - break;  
164 - }  
165 - }  
166 - if (port == -1) {  
167 - logger.info("不支持的媒体格式,返回415");  
168 - // 回复不支持的格式  
169 - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415  
170 - return;  
171 - }  
172 - String username = sdp.getOrigin().getUsername();  
173 - String addressStr = sdp.getOrigin().getAddress();  
174 - //String sessionName = sdp.getSessionName().getValue();  
175 - logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc);  
176 - Device device = null;  
177 - // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标  
178 - if (channel != null) {  
179 - device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);  
180 - if (device == null) {  
181 - logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);  
182 - responseAck(evt, Response.SERVER_INTERNAL_ERROR);  
183 - return;  
184 - }  
185 - mediaServerItem = playService.getNewMediaServerItem(device);  
186 - if (mediaServerItem == null) {  
187 - logger.warn("未找到可用的zlm");  
188 - responseAck(evt, Response.BUSY_HERE);  
189 - return;  
190 - }  
191 - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,  
192 - device.getDeviceId(), channelId,  
193 - mediaTransmissionTCP);  
194 - if (tcpActive != null) {  
195 - sendRtpItem.setTcpActive(tcpActive);  
196 - }  
197 - if (sendRtpItem == null) {  
198 - logger.warn("服务器端口资源不足");  
199 - responseAck(evt, Response.BUSY_HERE);  
200 - return;  
201 - }  
202 -  
203 - // 写入redis, 超时时回复  
204 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
205 - // 通知下级推流,  
206 - PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{  
207 - // 收到推流, 回复200OK, 等待ack  
208 - // if (sendRtpItem == null) return;  
209 - sendRtpItem.setStatus(1);  
210 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
211 - // TODO 添加对tcp的支持  
212 -  
213 - StringBuffer content = new StringBuffer(200);  
214 - content.append("v=0\r\n");  
215 - content.append("o="+"00000"+" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");  
216 - content.append("s=Play\r\n");  
217 - content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");  
218 - content.append("t=0 0\r\n");  
219 - content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");  
220 - content.append("a=sendonly\r\n");  
221 - content.append("a=rtpmap:96 PS/90000\r\n");  
222 - content.append("y="+ ssrc + "\r\n");  
223 - content.append("f=\r\n");  
224 -  
225 - try {  
226 - responseAck(evt, content.toString());  
227 - } catch (SipException e) {  
228 - e.printStackTrace();  
229 - } catch (InvalidArgumentException e) {  
230 - e.printStackTrace();  
231 - } catch (ParseException e) {  
232 - e.printStackTrace();  
233 - }  
234 - } ,((event) -> {  
235 - // 未知错误。直接转发设备点播的错误  
236 - Response response = null;  
237 - try {  
238 - response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest());  
239 - ServerTransaction serverTransaction = getServerTransaction(evt);  
240 - serverTransaction.sendResponse(response);  
241 - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();  
242 - } catch (ParseException | SipException | InvalidArgumentException e) {  
243 - e.printStackTrace();  
244 - }  
245 - }));  
246 - if (logger.isDebugEnabled()) {  
247 - logger.debug(playResult.getResult().toString());  
248 - }  
249 -  
250 - }else if (gbStream != null) {  
251 - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,  
252 - gbStream.getApp(), gbStream.getStream(), channelId,  
253 - mediaTransmissionTCP);  
254 -  
255 - if (tcpActive != null) {  
256 - sendRtpItem.setTcpActive(tcpActive);  
257 - }  
258 - if (sendRtpItem == null) {  
259 - logger.warn("服务器端口资源不足");  
260 - responseAck(evt, Response.BUSY_HERE);  
261 - return;  
262 - }  
263 -  
264 - // 写入redis, 超时时回复  
265 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
266 -  
267 - sendRtpItem.setStatus(1);  
268 - redisCatchStorage.updateSendRTPSever(sendRtpItem);  
269 - // TODO 添加对tcp的支持  
270 - StringBuffer content = new StringBuffer(200);  
271 - content.append("v=0\r\n");  
272 - content.append("o="+"00000"+" 0 0 IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");  
273 - content.append("s=Play\r\n");  
274 - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");  
275 - content.append("t=0 0\r\n");  
276 - content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");  
277 - content.append("a=sendonly\r\n");  
278 - content.append("a=rtpmap:96 PS/90000\r\n");  
279 - content.append("y="+ ssrc + "\r\n");  
280 - content.append("f=\r\n");  
281 -  
282 - try {  
283 - responseAck(evt, content.toString());  
284 - } catch (SipException e) {  
285 - e.printStackTrace();  
286 - } catch (InvalidArgumentException e) {  
287 - e.printStackTrace();  
288 - } catch (ParseException e) {  
289 - e.printStackTrace();  
290 - }  
291 - }  
292 -  
293 - } else {  
294 - // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)  
295 - Device device = storager.queryVideoDevice(requesterId);  
296 - if (device != null) {  
297 - logger.info("收到设备" + requesterId + "的语音广播Invite请求");  
298 - responseAck(evt, Response.TRYING);  
299 -  
300 - String contentString = new String(request.getRawContent());  
301 - // jainSip不支持y=字段, 移除移除以解析。  
302 - String substring = contentString;  
303 - String ssrc = "0000000404";  
304 - int ssrcIndex = contentString.indexOf("y=");  
305 - if (ssrcIndex > 0) {  
306 - substring = contentString.substring(0, ssrcIndex);  
307 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);  
308 - }  
309 - ssrcIndex = substring.indexOf("f=");  
310 - if (ssrcIndex > 0) {  
311 - substring = contentString.substring(0, ssrcIndex);  
312 - }  
313 - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);  
314 -  
315 - // 获取支持的格式  
316 - Vector mediaDescriptions = sdp.getMediaDescriptions(true);  
317 - // 查看是否支持PS 负载96  
318 - int port = -1;  
319 - //boolean recvonly = false;  
320 - boolean mediaTransmissionTCP = false;  
321 - Boolean tcpActive = null;  
322 - for (int i = 0; i < mediaDescriptions.size(); i++) {  
323 - MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);  
324 - Media media = mediaDescription.getMedia();  
325 -  
326 - Vector mediaFormats = media.getMediaFormats(false);  
327 - if (mediaFormats.contains("8")) {  
328 - port = media.getMediaPort();  
329 - String protocol = media.getProtocol();  
330 - // 区分TCP发流还是udp, 当前默认udp  
331 - if ("TCP/RTP/AVP".equals(protocol)) {  
332 - String setup = mediaDescription.getAttribute("setup");  
333 - if (setup != null) {  
334 - mediaTransmissionTCP = true;  
335 - if ("active".equals(setup)) {  
336 - tcpActive = true;  
337 - } else if ("passive".equals(setup)) {  
338 - tcpActive = false;  
339 - }  
340 - }  
341 - }  
342 - break;  
343 - }  
344 - }  
345 - if (port == -1) {  
346 - logger.info("不支持的媒体格式,返回415");  
347 - // 回复不支持的格式  
348 - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415  
349 - return;  
350 - }  
351 - String username = sdp.getOrigin().getUsername();  
352 - String addressStr = sdp.getOrigin().getAddress();  
353 - logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);  
354 -  
355 - } else {  
356 - logger.warn("来自无效设备/平台的请求");  
357 - responseAck(evt, Response.BAD_REQUEST);  
358 - }  
359 - }  
360 -  
361 - } catch (SipException | InvalidArgumentException | ParseException e) {  
362 - e.printStackTrace();  
363 - logger.warn("sdp解析错误");  
364 - e.printStackTrace();  
365 - } catch (SdpParseException e) {  
366 - e.printStackTrace();  
367 - } catch (SdpException e) {  
368 - e.printStackTrace();  
369 - }  
370 - }  
371 -  
372 -  
373 - /***  
374 - * 回复状态码  
375 - * 100 trying  
376 - * 200 OK  
377 - * 400  
378 - * 404  
379 - * @param evt  
380 - * @throws SipException  
381 - * @throws InvalidArgumentException  
382 - * @throws ParseException  
383 - */  
384 - private void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException {  
385 - Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());  
386 - ServerTransaction serverTransaction = getServerTransaction(evt);  
387 - serverTransaction.sendResponse(response);  
388 - if (statusCode >= 200) {  
389 - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();  
390 - }  
391 - }  
392 -  
393 - private void responseAck(RequestEvent evt, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {  
394 - Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());  
395 - response.setReasonPhrase(msg);  
396 - ServerTransaction serverTransaction = getServerTransaction(evt);  
397 - serverTransaction.sendResponse(response);  
398 - if (statusCode >= 200) {  
399 - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();  
400 - }  
401 - }  
402 -  
403 - /**  
404 - * 回复带sdp的200  
405 - * @param evt  
406 - * @param sdp  
407 - * @throws SipException  
408 - * @throws InvalidArgumentException  
409 - * @throws ParseException  
410 - */  
411 - private void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException {  
412 - Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());  
413 - SipFactory sipFactory = SipFactory.getInstance();  
414 - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");  
415 - response.setContent(sdp, contentTypeHeader);  
416 -  
417 - SipURI sipURI = (SipURI)evt.getRequest().getRequestURI();  
418 -  
419 - Address concatAddress = sipFactory.createAddressFactory().createAddress(  
420 - sipFactory.createAddressFactory().createSipURI(sipURI.getUser(), sipURI.getHost()+":"+sipURI.getPort()  
421 - ));  
422 - response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));  
423 - getServerTransaction(evt).sendResponse(response);  
424 - }  
425 -  
426 -  
427 -  
428 -  
429 -  
430 -  
431 - public SIPCommanderFroPlatform getCmderFroPlatform() {  
432 - return cmderFroPlatform;  
433 - }  
434 -  
435 - public void setCmderFroPlatform(SIPCommanderFroPlatform cmderFroPlatform) {  
436 - this.cmderFroPlatform = cmderFroPlatform;  
437 - }  
438 -  
439 - public IVideoManagerStorager getStorager() {  
440 - return storager;  
441 - }  
442 -  
443 - public void setStorager(IVideoManagerStorager storager) {  
444 - this.storager = storager;  
445 - }  
446 -  
447 - public SIPCommander getCmder() {  
448 - return cmder;  
449 - }  
450 -  
451 - public void setCmder(SIPCommander cmder) {  
452 - this.cmder = cmder;  
453 - }  
454 -  
455 - public IPlayService getPlayService() {  
456 - return playService;  
457 - }  
458 -  
459 - public void setPlayService(IPlayService playService) {  
460 - this.playService = playService;  
461 - }  
462 -  
463 - public IRedisCatchStorage getRedisCatchStorage() {  
464 - return redisCatchStorage;  
465 - }  
466 -  
467 - public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {  
468 - this.redisCatchStorage = redisCatchStorage;  
469 - }  
470 -  
471 - public IMediaServerService getMediaServerService() {  
472 - return mediaServerService;  
473 - }  
474 -  
475 - public void setMediaServerService(IMediaServerService mediaServerService) {  
476 - this.mediaServerService = mediaServerService;  
477 - }  
478 -} 1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.gb28181.bean.*;
  4 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  5 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  9 +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  10 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  11 +import com.genersoft.iot.vmp.service.IMediaServerService;
  12 +import com.genersoft.iot.vmp.service.IPlayService;
  13 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  14 +import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  15 +import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
  16 +import gov.nist.javax.sip.address.AddressImpl;
  17 +import gov.nist.javax.sip.address.SipUri;
  18 +import org.slf4j.Logger;
  19 +import org.slf4j.LoggerFactory;
  20 +import org.springframework.beans.factory.InitializingBean;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.stereotype.Component;
  23 +
  24 +import javax.sdp.*;
  25 +import javax.sip.InvalidArgumentException;
  26 +import javax.sip.RequestEvent;
  27 +import javax.sip.ServerTransaction;
  28 +import javax.sip.SipException;
  29 +import javax.sip.address.SipURI;
  30 +import javax.sip.header.FromHeader;
  31 +import javax.sip.message.Request;
  32 +import javax.sip.message.Response;
  33 +import java.text.ParseException;
  34 +import java.util.Vector;
  35 +
  36 +/**
  37 + * SIP命令类型: INVITE请求
  38 + */
  39 +@SuppressWarnings("rawtypes")
  40 +@Component
  41 +public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
  42 +
  43 + private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class);
  44 +
  45 + private String method = "INVITE";
  46 +
  47 + @Autowired
  48 + private SIPCommanderFroPlatform cmderFroPlatform;
  49 +
  50 + @Autowired
  51 + private IVideoManagerStorager storager;
  52 +
  53 + @Autowired
  54 + private IRedisCatchStorage redisCatchStorage;
  55 +
  56 + @Autowired
  57 + private SIPCommander cmder;
  58 +
  59 + @Autowired
  60 + private IPlayService playService;
  61 +
  62 + @Autowired
  63 + private ZLMRTPServerFactory zlmrtpServerFactory;
  64 +
  65 + @Autowired
  66 + private IMediaServerService mediaServerService;
  67 +
  68 + @Autowired
  69 + private SIPProcessorObserver sipProcessorObserver;
  70 +
  71 + @Override
  72 + public void afterPropertiesSet() throws Exception {
  73 + // 添加消息处理的订阅
  74 + sipProcessorObserver.addRequestProcessor(method, this);
  75 + }
  76 +
  77 + /**
  78 + * 处理invite请求
  79 + *
  80 + * @param evt
  81 + * 请求消息
  82 + */
  83 + @Override
  84 + public void process(RequestEvent evt) {
  85 + // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
  86 + try {
  87 + Request request = evt.getRequest();
  88 + SipURI sipURI = (SipURI) request.getRequestURI();
  89 + String channelId = sipURI.getUser();
  90 + String requesterId = null;
  91 +
  92 + FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
  93 + AddressImpl address = (AddressImpl) fromHeader.getAddress();
  94 + SipUri uri = (SipUri) address.getURI();
  95 + requesterId = uri.getUser();
  96 +
  97 + if (requesterId == null || channelId == null) {
  98 + logger.info("无法从FromHeader的Address中获取到平台id,返回400");
  99 + responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误
  100 + return;
  101 + }
  102 +
  103 + // 查询请求方是否上级平台
  104 + ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
  105 + if (platform != null) {
  106 + // 查询平台下是否有该通道
  107 + DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
  108 + GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
  109 + MediaServerItem mediaServerItem = null;
  110 + // 不是通道可能是直播流
  111 + if (channel != null && gbStream == null ) {
  112 + if (channel.getStatus() == 0) {
  113 + logger.info("通道离线,返回400");
  114 + responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
  115 + return;
  116 + }
  117 + responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
  118 + }else if(channel == null && gbStream != null){
  119 + String mediaServerId = gbStream.getMediaServerId();
  120 + mediaServerItem = mediaServerService.getOne(mediaServerId);
  121 + if (mediaServerItem == null) {
  122 + logger.info("[ app={}, stream={} ]找不到zlm {},返回410",gbStream.getApp(), gbStream.getStream(), mediaServerId);
  123 + responseAck(evt, Response.GONE, "media server not found");
  124 + return;
  125 + }
  126 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
  127 + if (!streamReady ) {
  128 + logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream());
  129 + responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
  130 + return;
  131 + }
  132 + responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
  133 + }else {
  134 + logger.info("通道不存在,返回404");
  135 + responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在
  136 + return;
  137 + }
  138 + // 解析sdp消息, 使用jainsip 自带的sdp解析方式
  139 + String contentString = new String(request.getRawContent());
  140 +
  141 + // jainSip不支持y=字段, 移除以解析。
  142 + int ssrcIndex = contentString.indexOf("y=");
  143 + // 检查是否有y字段
  144 + String ssrcDefault = "0000000000";
  145 + String ssrc;
  146 + SessionDescription sdp;
  147 + if (ssrcIndex >= 0) {
  148 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
  149 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  150 + String substring = contentString.substring(0, contentString.indexOf("y="));
  151 + sdp = SdpFactory.getInstance().createSessionDescription(substring);
  152 + }else {
  153 + ssrc = ssrcDefault;
  154 + sdp = SdpFactory.getInstance().createSessionDescription(contentString);
  155 + }
  156 +
  157 + // 获取支持的格式
  158 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  159 + // 查看是否支持PS 负载96
  160 + //String ip = null;
  161 + int port = -1;
  162 + //boolean recvonly = false;
  163 + boolean mediaTransmissionTCP = false;
  164 + Boolean tcpActive = null;
  165 + for (Object description : mediaDescriptions) {
  166 + MediaDescription mediaDescription = (MediaDescription) description;
  167 + Media media = mediaDescription.getMedia();
  168 +
  169 + Vector mediaFormats = media.getMediaFormats(false);
  170 + if (mediaFormats.contains("96")) {
  171 + port = media.getMediaPort();
  172 + //String mediaType = media.getMediaType();
  173 + String protocol = media.getProtocol();
  174 +
  175 + // 区分TCP发流还是udp, 当前默认udp
  176 + if ("TCP/RTP/AVP".equals(protocol)) {
  177 + String setup = mediaDescription.getAttribute("setup");
  178 + if (setup != null) {
  179 + mediaTransmissionTCP = true;
  180 + if ("active".equals(setup)) {
  181 + tcpActive = true;
  182 + } else if ("passive".equals(setup)) {
  183 + tcpActive = false;
  184 + }
  185 + }
  186 + }
  187 + break;
  188 + }
  189 + }
  190 + if (port == -1) {
  191 + logger.info("不支持的媒体格式,返回415");
  192 + // 回复不支持的格式
  193 + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
  194 + return;
  195 + }
  196 + String username = sdp.getOrigin().getUsername();
  197 + String addressStr = sdp.getOrigin().getAddress();
  198 + //String sessionName = sdp.getSessionName().getValue();
  199 + logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc);
  200 + Device device = null;
  201 + // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
  202 + if (channel != null) {
  203 + device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
  204 + if (device == null) {
  205 + logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
  206 + responseAck(evt, Response.SERVER_INTERNAL_ERROR);
  207 + return;
  208 + }
  209 + mediaServerItem = playService.getNewMediaServerItem(device);
  210 + if (mediaServerItem == null) {
  211 + logger.warn("未找到可用的zlm");
  212 + responseAck(evt, Response.BUSY_HERE);
  213 + return;
  214 + }
  215 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  216 + device.getDeviceId(), channelId,
  217 + mediaTransmissionTCP);
  218 + if (tcpActive != null) {
  219 + sendRtpItem.setTcpActive(tcpActive);
  220 + }
  221 + if (sendRtpItem == null) {
  222 + logger.warn("服务器端口资源不足");
  223 + responseAck(evt, Response.BUSY_HERE);
  224 + return;
  225 + }
  226 +
  227 + // 写入redis, 超时时回复
  228 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  229 + // 通知下级推流,
  230 + PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{
  231 + // 收到推流, 回复200OK, 等待ack
  232 + // if (sendRtpItem == null) return;
  233 + sendRtpItem.setStatus(1);
  234 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  235 + // TODO 添加对tcp的支持
  236 +
  237 + StringBuffer content = new StringBuffer(200);
  238 + content.append("v=0\r\n");
  239 + content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
  240 + content.append("s=Play\r\n");
  241 + content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
  242 + content.append("t=0 0\r\n");
  243 + content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
  244 + content.append("a=sendonly\r\n");
  245 + content.append("a=rtpmap:96 PS/90000\r\n");
  246 + content.append("y="+ ssrc + "\r\n");
  247 + content.append("f=\r\n");
  248 +
  249 + try {
  250 + responseAck(evt, content.toString());
  251 + } catch (SipException e) {
  252 + e.printStackTrace();
  253 + } catch (InvalidArgumentException e) {
  254 + e.printStackTrace();
  255 + } catch (ParseException e) {
  256 + e.printStackTrace();
  257 + }
  258 + } ,((event) -> {
  259 + // 未知错误。直接转发设备点播的错误
  260 + Response response = null;
  261 + try {
  262 + response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
  263 + ServerTransaction serverTransaction = getServerTransaction(evt);
  264 + serverTransaction.sendResponse(response);
  265 + if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
  266 + } catch (ParseException | SipException | InvalidArgumentException e) {
  267 + e.printStackTrace();
  268 + }
  269 + }));
  270 + if (logger.isDebugEnabled()) {
  271 + logger.debug(playResult.getResult().toString());
  272 + }
  273 +
  274 + }else if (gbStream != null) {
  275 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  276 + gbStream.getApp(), gbStream.getStream(), channelId,
  277 + mediaTransmissionTCP);
  278 +
  279 + if (tcpActive != null) {
  280 + sendRtpItem.setTcpActive(tcpActive);
  281 + }
  282 + if (sendRtpItem == null) {
  283 + logger.warn("服务器端口资源不足");
  284 + responseAck(evt, Response.BUSY_HERE);
  285 + return;
  286 + }
  287 +
  288 + // 写入redis, 超时时回复
  289 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  290 +
  291 + sendRtpItem.setStatus(1);
  292 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  293 + // TODO 添加对tcp的支持
  294 + StringBuffer content = new StringBuffer(200);
  295 + content.append("v=0\r\n");
  296 + content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  297 + content.append("s=Play\r\n");
  298 + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  299 + content.append("t=0 0\r\n");
  300 + content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
  301 + content.append("a=sendonly\r\n");
  302 + content.append("a=rtpmap:96 PS/90000\r\n");
  303 + content.append("y="+ ssrc + "\r\n");
  304 + content.append("f=\r\n");
  305 +
  306 + try {
  307 + responseAck(evt, content.toString());
  308 + } catch (SipException e) {
  309 + e.printStackTrace();
  310 + } catch (InvalidArgumentException e) {
  311 + e.printStackTrace();
  312 + } catch (ParseException e) {
  313 + e.printStackTrace();
  314 + }
  315 + }
  316 +
  317 + } else {
  318 + // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
  319 + Device device = storager.queryVideoDevice(requesterId);
  320 + if (device != null) {
  321 + logger.info("收到设备" + requesterId + "的语音广播Invite请求");
  322 + responseAck(evt, Response.TRYING);
  323 +
  324 + String contentString = new String(request.getRawContent());
  325 + // jainSip不支持y=字段, 移除移除以解析。
  326 + String substring = contentString;
  327 + String ssrc = "0000000404";
  328 + int ssrcIndex = contentString.indexOf("y=");
  329 + if (ssrcIndex > 0) {
  330 + substring = contentString.substring(0, ssrcIndex);
  331 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  332 + }
  333 + ssrcIndex = substring.indexOf("f=");
  334 + if (ssrcIndex > 0) {
  335 + substring = contentString.substring(0, ssrcIndex);
  336 + }
  337 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  338 +
  339 + // 获取支持的格式
  340 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  341 + // 查看是否支持PS 负载96
  342 + int port = -1;
  343 + //boolean recvonly = false;
  344 + boolean mediaTransmissionTCP = false;
  345 + Boolean tcpActive = null;
  346 + for (int i = 0; i < mediaDescriptions.size(); i++) {
  347 + MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
  348 + Media media = mediaDescription.getMedia();
  349 +
  350 + Vector mediaFormats = media.getMediaFormats(false);
  351 + if (mediaFormats.contains("8")) {
  352 + port = media.getMediaPort();
  353 + String protocol = media.getProtocol();
  354 + // 区分TCP发流还是udp, 当前默认udp
  355 + if ("TCP/RTP/AVP".equals(protocol)) {
  356 + String setup = mediaDescription.getAttribute("setup");
  357 + if (setup != null) {
  358 + mediaTransmissionTCP = true;
  359 + if ("active".equals(setup)) {
  360 + tcpActive = true;
  361 + } else if ("passive".equals(setup)) {
  362 + tcpActive = false;
  363 + }
  364 + }
  365 + }
  366 + break;
  367 + }
  368 + }
  369 + if (port == -1) {
  370 + logger.info("不支持的媒体格式,返回415");
  371 + // 回复不支持的格式
  372 + responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
  373 + return;
  374 + }
  375 + String username = sdp.getOrigin().getUsername();
  376 + String addressStr = sdp.getOrigin().getAddress();
  377 + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);
  378 +
  379 + } else {
  380 + logger.warn("来自无效设备/平台的请求");
  381 + responseAck(evt, Response.BAD_REQUEST);
  382 + }
  383 + }
  384 +
  385 + } catch (SipException | InvalidArgumentException | ParseException e) {
  386 + e.printStackTrace();
  387 + logger.warn("sdp解析错误");
  388 + e.printStackTrace();
  389 + } catch (SdpParseException e) {
  390 + e.printStackTrace();
  391 + } catch (SdpException e) {
  392 + e.printStackTrace();
  393 + }
  394 + }
  395 +}