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 1 [submodule "be.teletask.onvif-java"]
2 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 2 # 撘蝞勗28181悅閫像
3 3  
4 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 瘚鈭LMediaKit-https://github.com/xiongziliang/ZLMediaKit
14 14  
15 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 19 像VR蝑挽憭
... ... @@ -34,7 +28,7 @@ docker雿輻霂行https://hub.docker.com/r/648540858/wvp_pro](https://
34 28 [https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki)
35 29  
36 30 # gitee郊隞
37   -https://gitee.com/18010473990/wvp-GB28181.git
  31 +https://gitee.com/pan648540858/wvp-GB28181-pro.git
38 32  
39 33 #
40 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 100 - [ ] 瘛餃NVIF瘚挽憭
107 101 - [X] 瘛餃TMP閫
108 102 - [X] 鈭垢敶閬蝵脣蝙嚗
  103 +- [X] 憭嚗韐蝸雿雿輻
109 104 - [X] 蝙mysql雿蛹摨恕sqlite3,撘蝞勗
110 105 - [ ] 瘛餃頂蝏蔭
111 106 - [ ] 瘛餃蝞∠
112 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 117 # gitee郊隞
117   -https://gitee.com/18010473990/wvp-GB28181.git
  118 +https://gitee.com/pan648540858/wvp-GB28181-pro.git
118 119  
119 120 # 雿輻撣桀
120 121 QQ蝢: 901799015, 690854210(ZLM憭抒黎)
121 122 QQ蝘縑銝銝, 蝎曉.甈Z之摰嗅蝢日悄霈.閫★撖嫣葬嚗洽餈tar漱pr
122 123  
  124 +
123 125 # 靚
124 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 212 <!-- <version>1.0.8</version>-->
213 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 215 <dependency>
227 216 <groupId>org.springframework.boot</groupId>
228 217 <artifactId>spring-boot-starter-test</artifactId>
... ...
sql/mysql.sql
... ... @@ -23,6 +23,7 @@ create table device
23 23 updateTime varchar(50) not null,
24 24 port int not null,
25 25 expires int not null,
  26 + subscribeCycleForCatalog int not null,
26 27 hostAddress varchar(50) not null,
27 28 charset varchar(50) not null
28 29 );
... ... @@ -207,6 +208,7 @@ create table stream_proxy
207 208 enable_hls bit null,
208 209 enable_mp4 bit null,
209 210 enable bit not null,
  211 + enable_remove_none_reader bit not null,
210 212 createTime varchar(50) not null,
211 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 import org.springframework.boot.autoconfigure.SpringBootApplication;
7 7 import org.springframework.boot.web.servlet.ServletComponentScan;
8 8 import org.springframework.context.ConfigurableApplicationContext;
  9 +import org.springframework.scheduling.annotation.EnableAsync;
9 10 import org.springframework.scheduling.annotation.EnableScheduling;
10 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 30 private String rtsps;
31 31 private String rtc;
32 32 private String mediaServerId;
33   - private JSONArray tracks;
  33 + private Object tracks;
34 34  
35 35 public static class TransactionInfo{
36 36 public String callId;
... ... @@ -105,11 +105,11 @@ public class StreamInfo {
105 105 this.rtsp = rtsp;
106 106 }
107 107  
108   - public JSONArray getTracks() {
  108 + public Object getTracks() {
109 109 return tracks;
110 110 }
111 111  
112   - public void setTracks(JSONArray tracks) {
  112 + public void setTracks(Object tracks) {
113 113 this.tracks = tracks;
114 114 }
115 115  
... ...
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
1 1 package com.genersoft.iot.vmp.common;
2 2  
3 3 /**
4   - * @Description: 定义常量
  4 + * @description: 定义常量
5 5 * @author: swwheihei
6 6 * @date: 2019年5月30日 下午3:04:04
7 7 *
8 8 */
9 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 15 public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
14 16  
... ... @@ -25,6 +27,7 @@ public class VideoManagerConstants {
25 27 public static final String PLAYER_PREFIX = "VMP_PLAYER_";
26 28  
27 29 public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
  30 + public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
28 31  
29 32 public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_";
30 33  
... ... @@ -51,4 +54,7 @@ public class VideoManagerConstants {
51 54 public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
52 55  
53 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 163 * 异常处理
164 164 */
165 165 @Override
166   - protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
  166 + protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e){
167 167 try {
168   - super.handleRequestException(proxyRequest, proxyResonse, e);
  168 + super.handleRequestException(proxyRequest, proxyResponse, e);
169 169 } catch (ServletException servletException) {
170 170 logger.error("录像服务 代理失败: ", e);
171 171 } catch (IOException ioException) {
... ...
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
... ... @@ -16,7 +16,7 @@ import redis.clients.jedis.JedisPool;
16 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 20 * @author: swwheihei
21 21 * @date: 2019年5月30日 上午10:58:25
22 22 *
... ...
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
1 1 package com.genersoft.iot.vmp.conf;
2 2  
3 3  
4   -import org.springframework.beans.factory.annotation.Value;
5 4 import org.springframework.boot.context.properties.ConfigurationProperties;
6   -import org.springframework.context.annotation.Configuration;
7 5 import org.springframework.stereotype.Component;
8 6  
9 7 @Component
... ... @@ -27,7 +25,7 @@ public class SipConfig {
27 25  
28 26 Integer ptzSpeed = 50;
29 27  
30   - Integer keepaliveTimeOut = 180;
  28 + Integer keepaliveTimeOut = 255;
31 29  
32 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 32 for (String deviceId : onlineForAll) {
33 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 27  
28 28 private Boolean logInDatebase = Boolean.TRUE;
29 29  
  30 + private String serverId = "000000";
  31 +
30 32 private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
31 33  
32 34 public Boolean getSavePositionHistory() {
... ... @@ -104,4 +106,12 @@ public class UserSetup {
104 106 public void setLogInDatebase(Boolean logInDatebase) {
105 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 1 package com.genersoft.iot.vmp.conf;
2 2  
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.service.IMediaServerService;
3 5 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
4 6 import org.springframework.beans.factory.annotation.Autowired;
  7 +import org.springframework.beans.factory.annotation.Value;
5 8 import org.springframework.scheduling.annotation.Scheduled;
6 9 import org.springframework.stereotype.Component;
7 10  
... ... @@ -12,13 +15,22 @@ public class WVPTimerTask {
12 15 private IRedisCatchStorage redisCatchStorage;
13 16  
14 17 @Autowired
15   - private SipConfig sipConfig;
  18 + private IMediaServerService mediaServerService;
16 19  
17 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 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 34  
35 35 } else if (e instanceof BadCredentialsException) {
36 36 // 密码错误
37   - logger.info("[登录失败] - 用户[{}]密码错误", username);
  37 + logger.info("[登录失败] - 用户[{}]密码/SIP服务器ID 错误", username);
38 38  
39 39 } else if (e instanceof CredentialsExpiredException) {
40 40 // 密码过期
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
1 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 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 7 import gov.nist.javax.sip.SipProviderImpl;
  8 +import gov.nist.javax.sip.SipStackImpl;
16 9 import org.slf4j.Logger;
17 10 import org.slf4j.LoggerFactory;
18 11 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -20,14 +13,15 @@ import org.springframework.context.annotation.Bean;
20 13 import org.springframework.context.annotation.DependsOn;
21 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 23 @Component
30   -public class SipLayer implements SipListener {
  24 +public class SipLayer{
31 25  
32 26 private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
33 27  
... ... @@ -35,33 +29,14 @@ public class SipLayer implements SipListener {
35 29 private SipConfig sipConfig;
36 30  
37 31 @Autowired
38   - private SIPProcessorFactory processorFactory;
39   -
40   - @Autowired
41   - private SipSubscribe sipSubscribe;
  32 + private ISIPProcessorObserver sipProcessorObserver;
42 33  
43 34 private SipStackImpl sipStack;
44 35  
45 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 39 @Bean("sipFactory")
64   - @DependsOn("initSipServer")
65 40 private SipFactory createSipFactory() {
66 41 sipFactory = SipFactory.getInstance();
67 42 sipFactory.setPathName("gov.nist");
... ... @@ -69,7 +44,7 @@ public class SipLayer implements SipListener {
69 44 }
70 45  
71 46 @Bean("sipStack")
72   - @DependsOn({"initSipServer", "sipFactory"})
  47 + @DependsOn({"sipFactory"})
73 48 private SipStack createSipStack() throws PeerUnavailableException {
74 49 Properties properties = new Properties();
75 50 properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
... ... @@ -87,7 +62,7 @@ public class SipLayer implements SipListener {
87 62 return sipStack;
88 63 }
89 64  
90   - @Bean("tcpSipProvider")
  65 + @Bean(name = "tcpSipProvider")
91 66 @DependsOn("sipStack")
92 67 private SipProviderImpl startTcpListener() {
93 68 ListeningPoint tcpListeningPoint = null;
... ... @@ -95,7 +70,7 @@ public class SipLayer implements SipListener {
95 70 try {
96 71 tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP");
97 72 tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
98   - tcpSipProvider.addSipListener(this);
  73 + tcpSipProvider.addSipListener(sipProcessorObserver);
99 74 logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}");
100 75 } catch (TransportNotSupportedException e) {
101 76 e.printStackTrace();
... ... @@ -110,7 +85,7 @@ public class SipLayer implements SipListener {
110 85 return tcpSipProvider;
111 86 }
112 87  
113   - @Bean("udpSipProvider")
  88 + @Bean(name = "udpSipProvider")
114 89 @DependsOn("sipStack")
115 90 private SipProviderImpl startUdpListener() {
116 91 ListeningPoint udpListeningPoint = null;
... ... @@ -118,8 +93,7 @@ public class SipLayer implements SipListener {
118 93 try {
119 94 udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
120 95 udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
121   - udpSipProvider.addSipListener(this);
122   -// udpSipProvider.setAutomaticDialogSupportEnabled(false);
  96 + udpSipProvider.addSipListener(sipProcessorObserver);
123 97 } catch (TransportNotSupportedException e) {
124 98 e.printStackTrace();
125 99 } catch (InvalidArgumentException e) {
... ... @@ -134,123 +108,4 @@ public class SipLayer implements SipListener {
134 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 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
10 10  
11 11 /**
12   - * @Description:注册逻辑处理,当设备注册后触发逻辑。
  12 + * @description:注册逻辑处理,当设备注册后触发逻辑。
13 13 * @author: swwheihei
14 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 109 */
110 110 private String charset ;
111 111  
  112 + /**
  113 + * 目录订阅周期,0为不订阅
  114 + */
  115 + private int subscribeCycleForCatalog ;
  116 +
112 117  
113 118  
114 119 public String getDeviceId() {
... ... @@ -270,4 +275,12 @@ public class Device {
270 275 public void setCharset(String charset) {
271 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 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3 3 /**
4   - * @Description: 移动位置bean
  4 + * @description: 移动位置bean
5 5 * @author: lawrencehj
6 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 6 import java.util.List;
7 7  
8 8 /**
9   - * @Description:设备录像信息bean
  9 + * @description:设备录像信息bean
10 10 * @author: swwheihei
11 11 * @date: 2020年5月8日 下午2:05:56
12 12 */
13 13 public class RecordInfo {
14 14  
15 15 private String deviceId;
16   -
  16 +
  17 + private String channelId;
  18 +
  19 + private String sn;
  20 +
17 21 private String name;
18 22  
19 23 private int sumNum;
... ... @@ -52,4 +56,19 @@ public class RecordInfo {
52 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 8 import java.util.Date;
9 9  
10 10 /**
11   - * @Description:设备录像bean
  11 + * @description:设备录像bean
12 12 * @author: swwheihei
13 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 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
8 8  
9 9 /**
10   - * @Description:设备离在线状态检测器,用于检测设备状态
  10 + * @description:设备离在线状态检测器,用于检测设备状态
11 11 * @author: swwheihei
12 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 13 import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent;
14 14  
15 15 /**
16   - * @Description:Event事件通知推送器,支持推送在线事件、离线事件
  16 + * @description:Event事件通知推送器,支持推送在线事件、离线事件
17 17 * @author: swwheihei
18 18 * @date: 2020年5月6日 上午11:30:50
19 19 */
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
1 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 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 13 import java.util.Map;
7 14 import java.util.concurrent.ConcurrentHashMap;
8 15  
9 16 @Component
10 17 public class SipSubscribe {
11 18  
  19 + private final Logger logger = LoggerFactory.getLogger(SipSubscribe.class);
  20 +
12 21 private Map<String, SipSubscribe.Event> errorSubscribes = new ConcurrentHashMap<>();
13 22  
14 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 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 98 public void addErrorSubscribe(String key, SipSubscribe.Event event) {
21 99 errorSubscribes.put(key, event);
  100 + timeSubscribes.put(key, new Date());
22 101 }
23 102  
24 103 public void addOkSubscribe(String key, SipSubscribe.Event event) {
25 104 okSubscribes.put(key, event);
  105 + timeSubscribes.put(key, new Date());
26 106 }
27 107  
28 108 public SipSubscribe.Event getErrorSubscribe(String key) {
29 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 117 public SipSubscribe.Event getOkSubscribe(String key) {
33 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 125 public int getErrorSubscribesSize(){
37 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 12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
13 13  
14 14 /**
15   - * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
  15 + * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
16 16 * @author: swwheihei
17 17 * @date: 2020年5月6日 上午11:35:46
18 18 */
... ... @@ -39,10 +39,6 @@ public class KeepaliveTimeoutListenerForPlatform extends KeyExpirationEventMessa
39 39 // 获取失效的key
40 40 String expiredKey = message.toString();
41 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 43 if (expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)) {
48 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 12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
13 13  
14 14 /**
15   - * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
  15 + * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
16 16 * @author: swwheihei
17 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 3 import org.springframework.context.ApplicationEvent;
4 4  
5 5 /**
6   - * @Description: 离线事件类
  6 + * @description: 离线事件类
7 7 * @author: swwheihei
8 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 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 16 * 2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.event.offline.OfflineEventListener}
17 17 * @author: swwheihei
18 18 * @date: 2020年5月6日 下午1:51:23
... ... @@ -54,5 +54,8 @@ public class OfflineEventListener implements ApplicationListener&lt;OfflineEvent&gt; {
54 54  
55 55 // 处理离线监听
56 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 4 import org.springframework.context.ApplicationEvent;
5 5  
6 6 /**
7   - * @Description: 在线事件类
  7 + * @description: 在线事件类
8 8 * @author: swwheihei
9 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 13 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
14 14  
15 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 21 * @author: swwheihei
23 22 * @date: 2020年5月6日 下午1:51:23
24 23 */
... ... @@ -74,5 +73,8 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
74 73 device.setOnline(1);
75 74 // 处理上线监听
76 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 18 import javax.sip.message.Response;
19 19  
20 20 /**
21   - * @Description: 平台心跳超时事件
  21 + * @description: 平台心跳超时事件
22 22 * @author: panll
23 23 * @date: 2020年11月5日 10:00
24 24 */
... ... @@ -66,6 +66,7 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener&lt;P
66 66 storager.updateParentPlatformStatus(event.getPlatformGbID(), false);
67 67 publisher.platformNotRegisterEventPublish(event.getPlatformGbID());
68 68 parentPlatformCatch.setKeepAliveReply(0);
  69 + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
69 70 }else {
70 71 // 再次发送心跳
71 72 String callId = sipCommanderForPlatform.keepalive(parentPlatform);
... ... @@ -75,8 +76,8 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener&lt;P
75 76 redisCatchStorage.updatePlatformKeepalive(parentPlatform);
76 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 82 parentPlatformCatch.setKeepAliveReply(0);
82 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 19 import java.util.*;
20 20  
21 21 /**
22   - * @Description: 平台未注册事件,来源有二:
  22 + * @description: 平台未注册事件,来源有二:
23 23 * 1、平台新添加
24 24 * 2、平台心跳超时
25 25 * @author: panll
... ... @@ -100,6 +100,6 @@ public class PlatformNotRegisterEventLister implements ApplicationListener&lt;Platf
100 100 logger.info("再次向平台注册,平台国标ID:" + event.getPlatformGbID());
101 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 15 import org.springframework.stereotype.Component;
16 16  
17 17 /**
18   - * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄
  18 + * @description:视频流session管理器,管理视频预览、预览回放的通信句柄
19 19 * @author: swwheihei
20 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 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 3 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
9 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 6 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
12   -
13 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 14 @SuppressWarnings("unchecked")
16 15 public class CheckForAllRecordsThread extends Thread {
17 16  
... ... @@ -54,13 +53,11 @@ public class CheckForAllRecordsThread extends Thread {
54 53 // 自然顺序排序, 元素进行升序排列
55 54 this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
56 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 57 msg.setData(recordInfo);
61   - deferredResultHolder.invokeResult(msg);
  58 + deferredResultHolder.invokeAllResult(msg);
62 59 logger.info("处理完成,返回结果");
63   - MessageRequestProcessor.threadNameList.remove(cacheKey);
  60 + RecordInfoResponseMessageHandler.threadNameList.remove(cacheKey);
64 61 }
65 62  
66 63 public void setRedis(RedisUtil redis) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.callback;
2 2  
  3 +import java.util.HashMap;
3 4 import java.util.Map;
  5 +import java.util.Set;
4 6 import java.util.concurrent.ConcurrentHashMap;
5 7  
6 8 import org.springframework.http.HttpStatus;
... ... @@ -9,7 +11,7 @@ import org.springframework.stereotype.Component;
9 11 import org.springframework.web.context.request.async.DeferredResult;
10 12  
11 13 /**
12   - * @Description: 异步请求处理
  14 + * @description: 异步请求处理
13 15 * @author: swwheihei
14 16 * @date: 2020年5月8日 下午7:59:05
15 17 */
... ... @@ -31,11 +33,13 @@ public class DeferredResultHolder {
31 33  
32 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 44 public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
41 45  
... ... @@ -45,21 +49,72 @@ public class DeferredResultHolder {
45 49  
46 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 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 90 if (result == null) {
61 91 return;
62 92 }
63 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 1 package com.genersoft.iot.vmp.gb28181.transmit.callback;
2 2  
3 3 /**
4   - * @Description: 请求信息定义
  4 + * @description: 请求信息定义
5 5 * @author: swwheihei
6 6 * @date: 2020年5月8日 下午1:09:18
7 7 */
... ... @@ -9,12 +9,10 @@ public class RequestMessage {
9 9  
10 10 private String id;
11 11  
12   - private String deviceId;
13   -
14   - private String type;
15   -
  12 + private String key;
  13 +
16 14 private Object data;
17   -
  15 +
18 16 public String getId() {
19 17 return id;
20 18 }
... ... @@ -23,22 +21,12 @@ public class RequestMessage {
23 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 32 public Object getData() {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
2 2  
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
3 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
4 5 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
5 6 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
... ... @@ -7,7 +8,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
7 8 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
8 9  
9 10 /**
10   - * @Description:设备能力接口,用于定义设备的控制、查询能力
  11 + * @description:设备能力接口,用于定义设备的控制、查询能力
11 12 * @author: swwheihei
12 13 * @date: 2020年5月3日 下午9:16:34
13 14 */
... ... @@ -122,6 +123,26 @@ public interface ISIPCommander {
122 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 148 * @param device 视频设备
... ... @@ -235,8 +256,9 @@ public interface ISIPCommander {
235 256 * @param device 视频设备
236 257 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
237 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 321 * @return true = 命令发送成功
300 322 */
301 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 19 import java.util.UUID;
20 20  
21 21 /**
22   - * @Description: 平台命令request创造器 TODO 冗余代码太多待优化
  22 + * @description: 平台命令request创造器 TODO 冗余代码太多待优化
23 23 * @author: panll
24 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 3 import java.text.ParseException;
4 4 import java.util.ArrayList;
5 5  
  6 +import javax.sip.Dialog;
6 7 import javax.sip.InvalidArgumentException;
7 8 import javax.sip.PeerUnavailableException;
8 9 import javax.sip.SipFactory;
... ... @@ -11,6 +12,9 @@ import javax.sip.address.SipURI;
11 12 import javax.sip.header.*;
12 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 18 import org.springframework.beans.factory.annotation.Autowired;
15 19 import org.springframework.stereotype.Component;
16 20  
... ... @@ -18,7 +22,7 @@ import com.genersoft.iot.vmp.conf.SipConfig;
18 22 import com.genersoft.iot.vmp.gb28181.bean.Device;
19 23  
20 24 /**
21   - * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化
  25 + * @description:摄像头命令request创造器 TODO 冗余代码太多待优化
22 26 * @author: swwheihei
23 27 * @date: 2020年5月6日 上午9:29:02
24 28 */
... ... @@ -30,6 +34,9 @@ public class SIPRequestHeaderProvider {
30 34  
31 35 @Autowired
32 36 private SipFactory sipFactory;
  37 +
  38 + @Autowired
  39 + private VideoStreamSessionManager streamSession;
33 40  
34 41 public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
35 42 Request request = null;
... ... @@ -99,13 +106,13 @@ public class SIPRequestHeaderProvider {
99 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 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 113 // via
107 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 116 viaHeader.setRPort();
110 117 viaHeaders.add(viaHeader);
111 118 //from
... ... @@ -113,7 +120,7 @@ public class SIPRequestHeaderProvider {
113 120 Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
114 121 FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
115 122 //to
116   - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain());
  123 + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain());
117 124 Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
118 125 ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
119 126  
... ... @@ -127,7 +134,10 @@ public class SIPRequestHeaderProvider {
127 134 Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
128 135 // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
129 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 141 ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
132 142 request.setContent(content, contentTypeHeader);
133 143 return request;
... ... @@ -207,4 +217,50 @@ public class SIPRequestHeaderProvider {
207 217 request.setContent(content, contentTypeHeader);
208 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 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 3 import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.conf.SipConfig;
14 6 import com.genersoft.iot.vmp.conf.UserSetup;
  7 +import com.genersoft.iot.vmp.gb28181.bean.Device;
15 8 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
16   -import com.genersoft.iot.vmp.media.zlm.*;
17 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 16 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
19 17 import com.genersoft.iot.vmp.service.IMediaServerService;
20 18 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
21 19 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
22 20 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  21 +import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
23 22 import gov.nist.javax.sip.SipProviderImpl;
24 23 import gov.nist.javax.sip.SipStackImpl;
25 24 import gov.nist.javax.sip.message.SIPRequest;
... ... @@ -29,20 +28,20 @@ import org.slf4j.LoggerFactory;
29 28 import org.springframework.beans.factory.annotation.Autowired;
30 29 import org.springframework.beans.factory.annotation.Qualifier;
31 30 import org.springframework.context.annotation.DependsOn;
32   -import org.springframework.context.annotation.Lazy;
33 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 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 45 * @author: swwheihei
47 46 * @date: 2020年5月3日 下午9:22:48
48 47 */
... ... @@ -55,12 +54,10 @@ public class SIPCommander implements ISIPCommander {
55 54 @Autowired
56 55 private SipConfig sipConfig;
57 56  
58   - @Lazy
59 57 @Autowired
60 58 @Qualifier(value="tcpSipProvider")
61 59 private SipProviderImpl tcpSipProvider;
62 60  
63   - @Lazy
64 61 @Autowired
65 62 @Qualifier(value="udpSipProvider")
66 63 private SipProviderImpl udpSipProvider;
... ... @@ -89,11 +86,6 @@ public class SIPCommander implements ISIPCommander {
89 86 @Autowired
90 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 353 //
362 354 StringBuffer content = new StringBuffer(200);
363 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 357 content.append("s=Play\r\n");
366 358 content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
367 359 content.append("t=0 0\r\n");
... ... @@ -427,8 +419,8 @@ public class SIPCommander implements ISIPCommander {
427 419 mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc());
428 420 errorEvent.response(e);
429 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 460  
469 461 StringBuffer content = new StringBuffer(200);
470 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 464 content.append("s=Playback\r\n");
473 465 content.append("u="+channelId+":0\r\n");
474 466 content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
... ... @@ -532,12 +524,12 @@ public class SIPCommander implements ISIPCommander {
532 524 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
533 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 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 534 } catch ( SipException | ParseException | InvalidArgumentException e) {
543 535 e.printStackTrace();
... ... @@ -575,7 +567,7 @@ public class SIPCommander implements ISIPCommander {
575 567  
576 568 StringBuffer content = new StringBuffer(200);
577 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 571 content.append("s=Download\r\n");
580 572 content.append("u="+channelId+":0\r\n");
581 573 content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
... ... @@ -640,7 +632,7 @@ public class SIPCommander implements ISIPCommander {
640 632 CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
641 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 637 ClientTransaction transaction = transmitRequest(device, request, errorEvent);
646 638 streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction);
... ... @@ -667,6 +659,10 @@ public class SIPCommander implements ISIPCommander {
667 659 ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
668 660 if (transaction == null) {
669 661 logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
  662 + SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
  663 + if (okEvent != null) {
  664 + okEvent.response(eventResult);
  665 + }
670 666 return;
671 667 }
672 668 SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
... ... @@ -1200,14 +1196,15 @@ public class SIPCommander implements ISIPCommander {
1200 1196 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
1201 1197 */
1202 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 1202 try {
1206 1203 StringBuffer recordInfoXml = new StringBuffer(200);
1207 1204 recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
1208 1205 recordInfoXml.append("<Query>\r\n");
1209 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 1208 recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
1212 1209 recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
1213 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 1221 Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
1225 1222 "z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader);
1226 1223  
1227   - transmitRequest(device, request);
  1224 + transmitRequest(device, request, errorEvent);
1228 1225 } catch (SipException | ParseException | InvalidArgumentException e) {
1229 1226 e.printStackTrace();
1230 1227 return false;
... ... @@ -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 1514 private ClientTransaction transmitRequest(Device device, Request request) throws SipException {
1491 1515 return transmitRequest(device, request, null, null);
... ... @@ -1506,14 +1530,127 @@ public class SIPCommander implements ISIPCommander {
1506 1530 CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
1507 1531 // 添加错误订阅
1508 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 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 1546 clientTransaction.sendRequest();
1517 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 100 if (event != null) {
101 101 logger.info("向上级平台 [ {} ] 注册发上错误: {} ",
102 102 parentPlatform.getServerGBId(),
103   - event.getResponse().getReasonPhrase());
  103 + event.msg);
104 104 }
105 105 if (errorEvent != null ) {
106 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 +}
... ...