Commit 6ac131bcf6ec034ffc9e5813c68c56c6e5c32b70
Merge remote-tracking branch 'origin/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
.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
README.md
| 1 | - | |
| 1 | + | |
| 2 | 2 | # 撘蝞勗28181悅閫像 |
| 3 | 3 | |
| 4 | 4 | [](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 |  |
| ... | ... | @@ -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
pom.xml
| ... | ... | @@ -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
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
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
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
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<OfflineEvent> { |
| 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
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<OnlineEvent> { |
| 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<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<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<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
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
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 | +} | ... | ... |