Commit 30ae9e929fad80f624ab632c53081db3d2dc9aec

Authored by 648540858
2 parents d295f3c5 512b0cc8

合并主线

Showing 128 changed files with 5910 additions and 2400 deletions
.github/ISSUE_TEMPLATE/--bug---.md renamed to .github/ISSUE_TEMPLATE/bug.md
1 1 ---
2 2 name: "[ BUG ] "
3   -about: Create a report to help us improve
4   -title: ''
5   -labels: ''
  3 +about: 关于wvp的bug,与zlm有关的建议直接在zlm的issue中提问
  4 +title: 'BUG'
  5 +labels: 'wvp的bug'
6 6 assignees: ''
7 7  
8 8 ---
9 9  
  10 +**环境信息:**
  11 +
  12 + - 1. 部署方式 wvp-pro docker / zlm(docker) + 编译wvp-pro/ wvp-prp + zlm都是编译部署/
  13 + - 2. 部署环境 windows / ubuntu/ centos ...
  14 + - 3. 端口开放情况
  15 + - 4. 是否是公网部署
  16 + - 5. 是否使用https
  17 + - 6. 方便的话提供下使用的设备品牌或平台
  18 + - 7. 你做过哪些尝试
  19 + - 8. 代码更新时间
  20 +
10 21 **描述错误**
11 22 描述下您遇到的问题
12 23  
13 24 **如何复现**
14 25 有明确复现步骤的问题会很容易被解决
15 26  
16   -**预期行为**
17   -清晰简洁的描述您期望发生的事情
  27 +**截图**
18 28  
19   -**截图**
  29 +**抓包文件**
  30 +
  31 +**日志**
  32 +```
  33 +日志内容放这里, 文件的话请直接上传
  34 +```
20 35  
21 36  
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. 你做过哪些尝试
... ...
.github/ISSUE_TEMPLATE/-------.md renamed to .github/ISSUE_TEMPLATE/new.md
1 1 ---
2 2 name: "[ 新功能 ]"
3 3 about: 新功能
4   -title: ''
  4 +title: '希望wVP实现的新功能,此功能应与你的具体业务无关'
5 5 labels: ''
6 6 assignees: ''
7 7  
8 8 ---
9 9  
  10 +**项目的详细需求**
  11 +
  12 +**这样的实现什么作用**
10 13  
... ...
.github/ISSUE_TEMPLATE/solve.md 0 → 100644
  1 +---
  2 +name: "[ 技术咨询 ] "
  3 +about: 对于使用中遇到问题
  4 +title: '技术咨询'
  5 +labels: '技术咨询'
  6 +assignees: ''
  7 +
  8 +---
  9 +
  10 +**环境信息:**
  11 +
  12 + - 1. 部署方式 wvp-pro docker / zlm(docker) + 编译wvp-pro/ wvp-prp + zlm都是编译部署/
  13 + - 2. 部署环境 windows / ubuntu/ centos ...
  14 + - 3. 端口开放情况
  15 + - 4. 是否是公网部署
  16 + - 5. 是否使用https
  17 + - 6. 方便的话提供下使用的设备品牌或平台
  18 + - 7. 你做过哪些尝试
  19 + - 8. 代码更新时间
  20 +
  21 +
  22 +**内容描述:**
  23 +
  24 +**截图**
  25 +
  26 +**抓包文件**
  27 +
  28 +**日志**
  29 +```
  30 +日志内容放这里, 文件的话请直接上传
  31 +```
... ...
README.md
... ... @@ -15,17 +15,21 @@ WEB VIDEO PLATFORM銝銝芸鈭B28181-2016蝞勗
15 15 垢憿菟鈭Kyle MediaServerUI [https://gitee.com/kkkkk5G/MediaServerUI](https://gitee.com/kkkkk5G/MediaServerUI) 餈耨.
16 16  
17 17 # 摨嚗
18   -辣仍閫
19   -像VR蝑挽憭
  18 +辣仍閫
  19 +挽憭(像VR蝑)霈曉
  20 +(onvif, rtsp, rtmp嚗霈曉)霈曉嚗
20 21 漣像蝥扯楊蝵
21   -tsp/rtmp蝑蓮像
22   -tsp/rtmp蝑瘚蓮像
  22 +楊蝵撟喳鈭
23 23  
24   -# 憿寧
25   -銝芣蔭,蝙,靘蹂輕28181縑隞斤頂蝏, 靘皞獢ZLMediaKit, 摰銝銝芸GB28181撟喳.
26 24  
27   -# 蝵脫﹝
28   -[doc.wvp-pro.cn](https://doc.wvp-pro.cn)
  25 +# ﹝
  26 +wvp雿輻﹝ [https://doc.wvp-pro.cn](https://doc.wvp-pro.cn)
  27 +ZLM雿輻﹝ [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
  28 +> wvp﹝gitee嚗霂瑕活
  29 +
  30 +# 蝷曄黎
  31 +[![蝷曄黎](doc/_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
  32 +> 韐寞銝箔憟賜嚗撖嫣憭抒銝予隞亦縑凝靽∪嚗之摰嗅蝢扎銝予說隞亦甈橘之摰嗡閬▽憳予銋銝隞乓
29 33  
30 34 # gitee郊隞
31 35 https://gitee.com/pan648540858/wvp-GB28181-pro.git
... ... @@ -100,23 +104,17 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
100 104 - [X] 鈭垢敶瘚/隞/隞亙鈭垢嚗蝸
101 105 - [X] 銵arar
102 106 - [X] 楊窈瘙垢氖蝵
103   -
104   -
105   -# 憸圾
106   -暻餌鈭挽憭摰寞改隞仿閬之挽憭瘚挽憭偌撟單隞仿憸
107   -1. ﹝蝵粉隞亙葬雿憸
108   -2. 揣issues嚗之
109   -3. Q蝢歹901799015嚗之敹撈嚗撣歇蝏粉鈭iki揣鈭ssues
110   -4. 雿隞亥窈雿蛹雿圾蝑晶
111   -5. 雿隞交憸挽憭隞交摰寞摰寡挽憭圾憸
112   -
113   -# 雿輻撣桀
114   -QQ蝢: 901799015, ZLM雿輻﹝[https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
115   -QQ蝘縑銝銝, 蝎曉.甈Z之摰嗅蝢日悄霈.閫★撖嫣葬嚗洽餈tar漱pr
116 107  
117 108 # 悅
118 109 憿寧誨蝙摰賣IT悅嚗靽縑銝隞亥摨鈭★ 雿憿寧銋蝣蝙鈭鈭隞皞誨銝窈銵隞嚗 鈭蝙憿寧漣熒噩蛹銝璁憿寧嚗窈銵 雿輻憿寧隞嚗砲悅銝剖銵冽憿寧靘洵銝摨悅
119 110  
  111 +#
  112 +
  113 +[霂(https://t.zsxq.com/0d8VAD3Dm)銝”嚗
  114 +- [雿輻蝟餃嚗VP-PRO銋(https://t.zsxq.com/0dLguVoSp)
  115 +
  116 +窈隞嗅648540858@qq.com
  117 +
120 118 # 靚
121 119 陝雿憭(https://github.com/xia-chu) 皞獢,撟嗅撘葉蝏葬
122 120 陝雿dexter langhuihui](https://github.com/langhuihui) 撘皞末EB
... ... @@ -128,7 +126,6 @@ QQ蝘縑銝銝, 蝎曉.甈Z之摰嗅蝢日悄霈.閫★撖嫣
128 126 [ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666)
129 127 [mk1990](https://github.com/mk1990) [SaltFish001](https://github.com/SaltFish001)
130 128  
131   -ps: 葵摰鈭之雿穿洽餈之雿祈頂溶
132 129  
133 130 ffmpeg -re -i 123.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp rtsp://192.168.1.3:30554/broadcast/34020000001320000101_34020000001310000001
134 131  
... ...
doc/README.md
... ... @@ -14,6 +14,10 @@
14 14 - 完全开源,且使用MIT许可协议。保留版权的情况下可以用于商业项目。
15 15 - 支持多流媒体节点负载均衡。
16 16  
  17 +# 社群
  18 +[![社群](_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
  19 +> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。
  20 +
17 21 # 我们实现了哪些国标功能
18 22 **作为上级平台**
19 23 - [X] 注册
... ...
doc/_content/ability/_media/img_16.png

41.5 KB | W: | H:

112 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
doc/_content/qa/bug.md
... ... @@ -2,18 +2,11 @@
2 2 # 反馈bug
3 3 代码是在不断的完善的,不断修改会修复旧的问题也有可能引入新的问题,所以遇到BUG是很正常的一件事。所以遇到问题不要烦燥,咱们就事论事就好了。
4 4 ## 如何反馈
5   -1. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试;
6   -2. 可以在github提ISSUE,我几乎每天都会去看issue,你的问题我会尽快给予答复;
7   -3. 你可以来我的QQ群里,询问群友看看是否遇到了同样的问题;
8   -4. 你可以私聊我的QQ,如果我有时间我会给你答复,但是除非你有明确的复现步骤或者修复方案,否则你可能等不到我的答复。
9   -
10   -## 如何快速解决BUG
11   -目前解决BUG有三种方式:
12   -1. 作者验证以及修复;
13   -2. 热心开发者提来的PR;
14   -3. 使用运维手段屏蔽BUG的影响。
15   -
16   -- 对于第一种:详细的复现步骤,完整的抓包文件,有条理的错误分析都可以帮助作者复现问题,进而解决问题。解决问题往往不是最难的,复现才是。
17   -- 对于第二种:如果你是开发者,你已经发现了造成BUG的原因以及知道如何正确的修复,那么我很希望你PR,SRS的大佬经常说的,开源不是一个人的事。所以你的参与就是最大的鼓励。
18   -- 对于第三种:如果你有一个有经验的运维伙伴,那么部分问题是可以通过运维的手段暂时屏蔽的,在等待修复的这段时间了以保证项目的运行。
  5 +1. 在知识星球提问。
  6 +2. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试;
  7 +3. 可以在github提ISSUE,我几乎每天都会去看issue,你的问题我会尽快给予答复;
  8 +> 有偿支持可以给我发邮件, 648540858@qq.com
19 9  
  10 +## 社群
  11 +[![社群](../../_media/shequ.png "shequ")](https://t.zsxq.com/0d8VAD3Dm)
  12 +> 收费是为了提供更好的服务,也是对作者更大的激励。加入星球的用户三天后可以私信我留下微信号,我会拉大家入群。加入三天内不满意可以直接退款,大家不需要有顾虑,来白嫖三天也不是不可以。
20 13 \ No newline at end of file
... ...
doc/_media/shequ.png 0 → 100644

35.3 KB

src/main/java/com/genersoft/iot/vmp/common/CommonCallback.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common;
  2 +
  3 +public interface CommonCallback<T>{
  4 + public void run(T t);
  5 +}
... ...
src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common;
  2 +
  3 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  4 +
  5 +/**
  6 + * 记录每次发送invite消息的状态
  7 + */
  8 +public class InviteInfo {
  9 +
  10 + private String deviceId;
  11 +
  12 + private String channelId;
  13 +
  14 + private String stream;
  15 +
  16 + private SSRCInfo ssrcInfo;
  17 +
  18 + private String receiveIp;
  19 +
  20 + private Integer receivePort;
  21 +
  22 + private String streamMode;
  23 +
  24 + private InviteSessionType type;
  25 +
  26 + private InviteSessionStatus status;
  27 +
  28 + private StreamInfo streamInfo;
  29 +
  30 +
  31 + public static InviteInfo getinviteInfo(String deviceId, String channelId, String stream, SSRCInfo ssrcInfo,
  32 + String receiveIp, Integer receivePort, String streamMode,
  33 + InviteSessionType type, InviteSessionStatus status) {
  34 + InviteInfo inviteInfo = new InviteInfo();
  35 + inviteInfo.setDeviceId(deviceId);
  36 + inviteInfo.setChannelId(channelId);
  37 + inviteInfo.setStream(stream);
  38 + inviteInfo.setSsrcInfo(ssrcInfo);
  39 + inviteInfo.setReceiveIp(receiveIp);
  40 + inviteInfo.setReceivePort(receivePort);
  41 + inviteInfo.setStreamMode(streamMode);
  42 + inviteInfo.setType(type);
  43 + inviteInfo.setStatus(status);
  44 + return inviteInfo;
  45 + }
  46 +
  47 + public String getDeviceId() {
  48 + return deviceId;
  49 + }
  50 +
  51 + public void setDeviceId(String deviceId) {
  52 + this.deviceId = deviceId;
  53 + }
  54 +
  55 + public String getChannelId() {
  56 + return channelId;
  57 + }
  58 +
  59 + public void setChannelId(String channelId) {
  60 + this.channelId = channelId;
  61 + }
  62 +
  63 + public InviteSessionType getType() {
  64 + return type;
  65 + }
  66 +
  67 + public void setType(InviteSessionType type) {
  68 + this.type = type;
  69 + }
  70 +
  71 + public InviteSessionStatus getStatus() {
  72 + return status;
  73 + }
  74 +
  75 + public void setStatus(InviteSessionStatus status) {
  76 + this.status = status;
  77 + }
  78 +
  79 + public StreamInfo getStreamInfo() {
  80 + return streamInfo;
  81 + }
  82 +
  83 + public void setStreamInfo(StreamInfo streamInfo) {
  84 + this.streamInfo = streamInfo;
  85 + }
  86 +
  87 + public String getStream() {
  88 + return stream;
  89 + }
  90 +
  91 + public void setStream(String stream) {
  92 + this.stream = stream;
  93 + }
  94 +
  95 + public SSRCInfo getSsrcInfo() {
  96 + return ssrcInfo;
  97 + }
  98 +
  99 + public void setSsrcInfo(SSRCInfo ssrcInfo) {
  100 + this.ssrcInfo = ssrcInfo;
  101 + }
  102 +
  103 + public String getReceiveIp() {
  104 + return receiveIp;
  105 + }
  106 +
  107 + public void setReceiveIp(String receiveIp) {
  108 + this.receiveIp = receiveIp;
  109 + }
  110 +
  111 + public Integer getReceivePort() {
  112 + return receivePort;
  113 + }
  114 +
  115 + public void setReceivePort(Integer receivePort) {
  116 + this.receivePort = receivePort;
  117 + }
  118 +
  119 + public String getStreamMode() {
  120 + return streamMode;
  121 + }
  122 +
  123 + public void setStreamMode(String streamMode) {
  124 + this.streamMode = streamMode;
  125 + }
  126 +}
... ...
src/main/java/com/genersoft/iot/vmp/common/InviteSessionStatus.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common;
  2 +
  3 +/**
  4 + * 标识invite消息发出后的各个状态,
  5 + * 收到ok钱停止invite发送cancel,
  6 + * 收到200ok后发送BYE停止invite
  7 + */
  8 +public enum InviteSessionStatus {
  9 + ready,
  10 + ok,
  11 +}
... ...
src/main/java/com/genersoft/iot/vmp/common/InviteSessionType.java 0 → 100644
  1 +package com.genersoft.iot.vmp.common;
  2 +
  3 +public enum InviteSessionType {
  4 + PLAY,
  5 + PLAYBACK,
  6 + DOWNLOAD,
  7 + BROADCAST,
  8 + TALK
  9 +}
... ...
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
... ... @@ -16,8 +16,6 @@ public class VideoManagerConstants {
16 16  
17 17 public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS_";
18 18  
19   - public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM";
20   -
21 19 public static final String DEVICE_PREFIX = "VMP_DEVICE_";
22 20  
23 21 // 设备同步完成
... ... @@ -28,9 +26,10 @@ public class VideoManagerConstants {
28 26 public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_";
29 27  
30 28 // TODO 此处多了一个_,暂不修改
31   - public static final String PLAYER_PREFIX = "VMP_PLAYER_";
32   - public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
33   - public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
  29 + public static final String INVITE_PREFIX = "VMP_INVITE";
  30 + public static final String PLAYER_PREFIX = "VMP_INVITE_PLAY_";
  31 + public static final String PLAY_BLACK_PREFIX = "VMP_INVITE_PLAYBACK_";
  32 + public static final String DOWNLOAD_PREFIX = "VMP_INVITE_DOWNLOAD_";
34 33  
35 34 public static final String PLATFORM_KEEPALIVE_PREFIX = "VMP_PLATFORM_KEEPALIVE_";
36 35  
... ... @@ -123,6 +122,7 @@ public class VideoManagerConstants {
123 122 */
124 123 public static final String VM_MSG_SUBSCRIBE_ALARM = "alarm";
125 124  
  125 +
126 126 /**
127 127 * 报警通知的发送 (收到redis发出的通知,转发给其他平台)
128 128 */
... ...
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
... ... @@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.conf;
2 2  
3 3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
4 4 import com.genersoft.iot.vmp.service.IMediaServerService;
5   -import org.apache.catalina.connector.ClientAbortException;
6 5 import org.apache.http.HttpHost;
7 6 import org.apache.http.HttpRequest;
8 7 import org.apache.http.HttpResponse;
... ... @@ -194,11 +193,11 @@ public class ProxyServletConfig {
194 193 } catch (IOException ioException) {
195 194 if (ioException instanceof ConnectException) {
196 195 logger.error("录像服务 连接失败");
197   - }else if (ioException instanceof ClientAbortException) {
198   - /**
199   - * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常,
200   - * TODO 暂时去除异常处理。后续使用其他代理框架修改测试
201   - */
  196 +// }else if (ioException instanceof ClientAbortException) {
  197 +// /**
  198 +// * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常,
  199 +// * TODO 暂时去除异常处理。后续使用其他代理框架修改测试
  200 +// */
202 201  
203 202 }else {
204 203 logger.error("录像服务 代理失败: ", e);
... ...
src/main/java/com/genersoft/iot/vmp/conf/ScheduleConfig.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +import org.apache.commons.lang3.concurrent.BasicThreadFactory;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.scheduling.annotation.SchedulingConfigurer;
  6 +import org.springframework.scheduling.config.ScheduledTaskRegistrar;
  7 +
  8 +import java.util.concurrent.ScheduledThreadPoolExecutor;
  9 +import java.util.concurrent.ThreadPoolExecutor;
  10 +
  11 +/**
  12 + * "@Scheduled"是Spring框架提供的一种定时任务执行机制,默认情况下它是单线程的,在同时执行多个定时任务时可能会出现阻塞和性能问题。
  13 + * 为了解决这种单线程瓶颈问题,可以将定时任务的执行机制改为支持多线程
  14 + */
  15 +@Configuration
  16 +public class ScheduleConfig implements SchedulingConfigurer {
  17 +
  18 + public static final int cpuNum = Runtime.getRuntime().availableProcessors();
  19 +
  20 + private static final int corePoolSize = cpuNum;
  21 +
  22 + private static final String threadNamePrefix = "scheduled-task-pool-%d";
  23 +
  24 + @Override
  25 + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
  26 + taskRegistrar.setScheduler(new ScheduledThreadPoolExecutor(corePoolSize,
  27 + new BasicThreadFactory.Builder().namingPattern(threadNamePrefix).daemon(true).build(),
  28 + new ThreadPoolExecutor.CallerRunsPolicy()));
  29 + }
  30 +}
... ...
src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
... ... @@ -48,10 +48,13 @@ public class SipPlatformRunner implements CommandLineRunner {
48 48 parentPlatformCatch.setParentPlatform(parentPlatform);
49 49 parentPlatformCatch.setId(parentPlatform.getServerGBId());
50 50 redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
51   - // 取消订阅
52   - sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
53   - platformService.login(parentPlatform);
54   - });
  51 + if (parentPlatformCatchOld != null) {
  52 + // 取消订阅
  53 + sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
  54 + platformService.login(parentPlatform);
  55 + });
  56 + }
  57 +
55 58 // 设置所有平台离线
56 59 platformService.offline(parentPlatform, true);
57 60 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
... ... @@ -54,6 +54,9 @@ public class UserSetting {
54 54  
55 55 private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
56 56  
  57 + private Boolean deviceStatusNotify = Boolean.FALSE;
  58 + private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
  59 +
57 60 private String serverId = "000000";
58 61  
59 62 private String recordPath = null;
... ... @@ -66,6 +69,8 @@ public class UserSetting {
66 69  
67 70 private List<String> allowedOrigins = new ArrayList<>();
68 71  
  72 + private int maxNotifyCountQueue = 10000;
  73 +
69 74 public Boolean getSavePositionHistory() {
70 75 return savePositionHistory;
71 76 }
... ... @@ -277,4 +282,28 @@ public class UserSetting {
277 282 public void setRecordPath(String recordPath) {
278 283 this.recordPath = recordPath;
279 284 }
  285 +
  286 + public int getMaxNotifyCountQueue() {
  287 + return maxNotifyCountQueue;
  288 + }
  289 +
  290 + public void setMaxNotifyCountQueue(int maxNotifyCountQueue) {
  291 + this.maxNotifyCountQueue = maxNotifyCountQueue;
  292 + }
  293 +
  294 + public Boolean getDeviceStatusNotify() {
  295 + return deviceStatusNotify;
  296 + }
  297 +
  298 + public void setDeviceStatusNotify(Boolean deviceStatusNotify) {
  299 + this.deviceStatusNotify = deviceStatusNotify;
  300 + }
  301 +
  302 + public Boolean getUseCustomSsrcForParentInvite() {
  303 + return useCustomSsrcForParentInvite;
  304 + }
  305 +
  306 + public void setUseCustomSsrcForParentInvite(Boolean useCustomSsrcForParentInvite) {
  307 + this.useCustomSsrcForParentInvite = useCustomSsrcForParentInvite;
  308 + }
280 309 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java
1   -package com.genersoft.iot.vmp.conf.redis;
2   -
3   -
4   -import com.genersoft.iot.vmp.common.VideoManagerConstants;
5   -import com.genersoft.iot.vmp.service.redisMsg.*;
6   -import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
7   -import org.springframework.beans.factory.annotation.Autowired;
8   -import org.springframework.cache.annotation.CachingConfigurerSupport;
9   -import com.alibaba.fastjson2.support.spring.data.redis.GenericFastJsonRedisSerializer;
10   -import org.springframework.context.annotation.Bean;
11   -import org.springframework.context.annotation.Configuration;
12   -import org.springframework.core.annotation.Order;
13   -import org.springframework.data.redis.connection.RedisConnectionFactory;
14   -import org.springframework.data.redis.core.RedisTemplate;
15   -import org.springframework.data.redis.serializer.StringRedisSerializer;
16   -
17   -
18   -/**
19   - * Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置
20   - * swwheihei
21   - * 2019年5月30日 上午10:58:25
22   - *
23   - */
24   -@Configuration
25   -@Order(value=1)
26   -public class RedisConfig {
27   -
28   -
29   - @Bean
30   - public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
31   -
32   - RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
33   - // 使用fastJson序列化
34   - GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
35   - // value值的序列化采用fastJsonRedisSerializer
36   - redisTemplate.setValueSerializer(fastJsonRedisSerializer);
37   - redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
38   -
39   - // key的序列化采用StringRedisSerializer
40   - redisTemplate.setKeySerializer(new StringRedisSerializer());
41   - redisTemplate.setHashKeySerializer(new StringRedisSerializer());
42   - redisTemplate.setConnectionFactory(redisConnectionFactory);
43   - return redisTemplate;
44   - }
45   -}
src/main/java/com/genersoft/iot/vmp/conf/redis/RedisTemplateConfig.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf.redis;
  2 +
  3 +import com.alibaba.fastjson2.support.spring.data.redis.GenericFastJsonRedisSerializer;
  4 +import org.springframework.context.annotation.Bean;
  5 +import org.springframework.context.annotation.Configuration;
  6 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  7 +import org.springframework.data.redis.core.RedisTemplate;
  8 +import org.springframework.data.redis.serializer.StringRedisSerializer;
  9 +
  10 +@Configuration
  11 +public class RedisTemplateConfig {
  12 +
  13 + @Bean
  14 + public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  15 + RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
  16 + // 使用fastJson序列化
  17 + GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
  18 + // value值的序列化采用fastJsonRedisSerializer
  19 + redisTemplate.setValueSerializer(fastJsonRedisSerializer);
  20 + redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
  21 +
  22 + // key的序列化采用StringRedisSerializer
  23 + redisTemplate.setKeySerializer(new StringRedisSerializer());
  24 + redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  25 + redisTemplate.setConnectionFactory(redisConnectionFactory);
  26 + return redisTemplate;
  27 + }
  28 +}
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
... ... @@ -2,6 +2,8 @@ package com.genersoft.iot.vmp.conf.security;
2 2  
3 3 import com.genersoft.iot.vmp.conf.UserSetting;
4 4 import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
  5 +import com.genersoft.iot.vmp.storager.dao.dto.Role;
  6 +import com.genersoft.iot.vmp.storager.dao.dto.User;
5 7 import org.apache.commons.lang3.StringUtils;
6 8 import org.springframework.beans.factory.annotation.Autowired;
7 9 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
... ... @@ -38,7 +40,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
38 40 return;
39 41 }
40 42 if (!userSetting.isInterfaceAuthentication()) {
41   - // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
42 43 UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
43 44 SecurityContextHolder.getContext().setAuthentication(token);
44 45 chain.doFilter(request, response);
... ... @@ -76,7 +77,13 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
76 77 }
77 78  
78 79 // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
79   - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() );
  80 + User user = new User();
  81 + user.setUsername(jwtUser.getUserName());
  82 + user.setPassword(jwtUser.getPassword());
  83 + Role role = new Role();
  84 + role.setId(jwtUser.getRoleId());
  85 + user.setRole(role);
  86 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, jwtUser.getPassword(), new ArrayList<>() );
80 87 SecurityContextHolder.getContext().setAuthentication(token);
81 88 chain.doFilter(request, response);
82 89 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
... ... @@ -37,7 +37,7 @@ public class JwtUtils {
37 37 */
38 38 public static final long expirationTime = 30;
39 39  
40   - public static String createToken(String username, String password) {
  40 + public static String createToken(String username, String password, Integer roleId) {
41 41 try {
42 42 /**
43 43 * “iss” (issuer) 发行人
... ... @@ -64,6 +64,7 @@ public class JwtUtils {
64 64 //添加自定义参数,必须是字符串类型
65 65 claims.setClaim("username", username);
66 66 claims.setClaim("password", password);
  67 + claims.setClaim("roleId", roleId);
67 68  
68 69 //jws
69 70 JsonWebSignature jws = new JsonWebSignature();
... ... @@ -118,8 +119,10 @@ public class JwtUtils {
118 119  
119 120 String username = (String) claims.getClaimValue("username");
120 121 String password = (String) claims.getClaimValue("password");
  122 + Long roleId = (Long) claims.getClaimValue("roleId");
121 123 jwtUser.setUserName(username);
122 124 jwtUser.setPassword(password);
  125 + jwtUser.setRoleId(roleId.intValue());
123 126  
124 127 return jwtUser;
125 128 } catch (InvalidJwtException e) {
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.conf.security;
2   -
3   -import com.fasterxml.jackson.databind.ObjectMapper;
4   -import org.slf4j.Logger;
5   -import org.slf4j.LoggerFactory;
6   -import org.springframework.beans.factory.annotation.Autowired;
7   -import org.springframework.security.authentication.*;
8   -import org.springframework.security.core.AuthenticationException;
9   -import org.springframework.security.web.authentication.AuthenticationFailureHandler;
10   -import org.springframework.stereotype.Component;
11   -
12   -import javax.servlet.ServletException;
13   -import javax.servlet.http.HttpServletRequest;
14   -import javax.servlet.http.HttpServletResponse;
15   -import java.io.IOException;
16   -import java.util.HashMap;
17   -import java.util.Map;
18   -
19   -@Component
20   -public class LoginFailureHandler implements AuthenticationFailureHandler {
21   -
22   - private final static Logger logger = LoggerFactory.getLogger(LoginFailureHandler.class);
23   -
24   - @Autowired
25   - private ObjectMapper objectMapper;
26   -
27   - @Override
28   - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
29   -
30   - String username = request.getParameter("username");
31   - if (e instanceof AccountExpiredException) {
32   - // 账号过期
33   - logger.info("[登录失败] - 用户[{}]账号过期", username);
34   -
35   - } else if (e instanceof BadCredentialsException) {
36   - // 密码错误
37   - logger.info("[登录失败] - 用户[{}]密码/SIP服务器ID 错误", username);
38   -
39   - } else if (e instanceof CredentialsExpiredException) {
40   - // 密码过期
41   - logger.info("[登录失败] - 用户[{}]密码过期", username);
42   -
43   - } else if (e instanceof DisabledException) {
44   - // 用户被禁用
45   - logger.info("[登录失败] - 用户[{}]被禁用", username);
46   -
47   - } else if (e instanceof LockedException) {
48   - // 用户被锁定
49   - logger.info("[登录失败] - 用户[{}]被锁定", username);
50   -
51   - } else if (e instanceof InternalAuthenticationServiceException) {
52   - // 内部错误
53   - logger.error(String.format("[登录失败] - [%s]内部错误", username), e);
54   -
55   - } else {
56   - // 其他错误
57   - logger.error(String.format("[登录失败] - [%s]其他错误", username), e);
58   - }
59   - Map<String, Object> map = new HashMap<>();
60   - map.put("code","0");
61   - map.put("msg","登录失败");
62   - response.setContentType("application/json;charset=UTF-8");
63   - response.getWriter().write(objectMapper.writeValueAsString(map));
64   - }
65   -}
src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.conf.security;
2   -
3   -import org.slf4j.Logger;
4   -import org.slf4j.LoggerFactory;
5   -import org.springframework.security.core.Authentication;
6   -import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
7   -import org.springframework.stereotype.Component;
8   -
9   -import javax.servlet.ServletException;
10   -import javax.servlet.http.HttpServletRequest;
11   -import javax.servlet.http.HttpServletResponse;
12   -import java.io.IOException;
13   -
14   -/**
15   - * @author lin
16   - */
17   -@Component
18   -public class LoginSuccessHandler implements AuthenticationSuccessHandler {
19   -
20   - private final static Logger logger = LoggerFactory.getLogger(LoginSuccessHandler.class);
21   -
22   - @Override
23   - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
24   -// String username = request.getParameter("username");
25   -// httpServletResponse.setContentType("application/json;charset=UTF-8");
26   -// // 生成JWT,并放置到请求头中
27   -// String jwt = JwtUtils.createToken(authentication.getName(), );
28   -// httpServletResponse.setHeader(JwtUtils.getHeader(), jwt);
29   -// ServletOutputStream outputStream = httpServletResponse.getOutputStream();
30   -// outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8));
31   -// outputStream.flush();
32   -// outputStream.close();
33   -
34   -// logger.info("[登录成功] - [{}]", username);
35   - }
36   -}
src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
... ... @@ -53,14 +53,10 @@ public class SecurityUtils {
53 53 Authentication authentication = getAuthentication();
54 54 if(authentication!=null){
55 55 Object principal = authentication.getPrincipal();
56   - if(principal!=null && !"anonymousUser".equals(principal)){
57   -// LoginUser user = (LoginUser) authentication.getPrincipal();
  56 + if(principal!=null && !"anonymousUser".equals(principal.toString())){
58 57  
59   - String username = (String) principal;
60   - User user = new User();
61   - user.setUsername(username);
62   - LoginUser loginUser = new LoginUser(user, LocalDateTime.now());
63   - return loginUser;
  58 + User user = (User) principal;
  59 + return new LoginUser(user, LocalDateTime.now());
64 60 }
65 61 }
66 62 return null;
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
... ... @@ -47,16 +47,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
47 47 * 登出成功的处理
48 48 */
49 49 @Autowired
50   - private LoginFailureHandler loginFailureHandler;
51   - /**
52   - * 登录成功的处理
53   - */
54   - @Autowired
55   - private LoginSuccessHandler loginSuccessHandler;
56   - /**
57   - * 登出成功的处理
58   - */
59   - @Autowired
60 50 private LogoutHandler logoutHandler;
61 51 /**
62 52 * 未登录的处理
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java
... ... @@ -25,6 +25,8 @@ public class JwtUser {
25 25  
26 26 private String password;
27 27  
  28 + private int roleId;
  29 +
28 30 private TokenStatus status;
29 31  
30 32 public String getUserName() {
... ... @@ -50,4 +52,12 @@ public class JwtUser {
50 52 public void setPassword(String password) {
51 53 this.password = password;
52 54 }
  55 +
  56 + public int getRoleId() {
  57 + return roleId;
  58 + }
  59 +
  60 + public void setRoleId(int roleId) {
  61 + this.roleId = roleId;
  62 + }
53 63 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
... ... @@ -36,8 +36,6 @@ public class SipLayer implements CommandLineRunner {
36 36 private final Map<String, SipProviderImpl> tcpSipProviderMap = new ConcurrentHashMap<>();
37 37 private final Map<String, SipProviderImpl> udpSipProviderMap = new ConcurrentHashMap<>();
38 38  
39   - private SipFactory sipFactory;
40   -
41 39 @Override
42 40 public void run(String... args) {
43 41 List<String> monitorIps = new ArrayList<>();
... ... @@ -50,8 +48,7 @@ public class SipLayer implements CommandLineRunner {
50 48 monitorIps.add(sipConfig.getIp());
51 49 }
52 50  
53   - sipFactory = SipFactory.getInstance();
54   - sipFactory.setPathName("gov.nist");
  51 + SipFactory.getInstance().setPathName("gov.nist");
55 52 if (monitorIps.size() > 0) {
56 53 for (String monitorIp : monitorIps) {
57 54 addListeningPoint(monitorIp, sipConfig.getPort());
... ... @@ -65,7 +62,7 @@ public class SipLayer implements CommandLineRunner {
65 62 private void addListeningPoint(String monitorIp, int port){
66 63 SipStackImpl sipStack;
67 64 try {
68   - sipStack = (SipStackImpl)sipFactory.createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog()));
  65 + sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog()));
69 66 } catch (PeerUnavailableException e) {
70 67 logger.error("[Sip Server] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp);
71 68 return;
... ... @@ -106,10 +103,6 @@ public class SipLayer implements CommandLineRunner {
106 103 }
107 104 }
108 105  
109   - public SipFactory getSipFactory() {
110   - return sipFactory;
111   - }
112   -
113 106 public SipProviderImpl getUdpSipProvider(String ip) {
114 107 if (ObjectUtils.isEmpty(ip)) {
115 108 return null;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdSendFailEvent.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.gb28181.bean;
2   -
3   -import javax.sip.Dialog;
4   -import java.util.EventObject;
5   -
6   -public class CmdSendFailEvent extends EventObject {
7   -
8   - private String callId;
9   -
10   - /**
11   - * Constructs a prototypical Event.
12   - *
13   - * @param dialog
14   - * @throws IllegalArgumentException if source is null.
15   - */
16   - public CmdSendFailEvent(Dialog dialog) {
17   - super(dialog);
18   - }
19   -
20   - public String getCallId() {
21   - return callId;
22   - }
23   -
24   - public void setCallId(String callId) {
25   - this.callId = callId;
26   - }
27   -}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
... ... @@ -247,6 +247,17 @@ public class Device {
247 247 return streamMode;
248 248 }
249 249  
  250 + public Integer getStreamModeForParam() {
  251 + if (streamMode.equalsIgnoreCase("UDP")) {
  252 + return 0;
  253 + }else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) {
  254 + return 1;
  255 + }else if (streamMode.equalsIgnoreCase("TCP-ACTIVE")) {
  256 + return 2;
  257 + }
  258 + return 0;
  259 + }
  260 +
250 261 public void setStreamMode(String streamMode) {
251 262 this.streamMode = streamMode;
252 263 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java
... ... @@ -2,7 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3 3 public enum InviteStreamType {
4 4  
5   - PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY,BROADCAST,TALK
  5 + PLAY,PLAYBACK,DOWNLOAD,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY,BROADCAST,TALK
6 6  
7 7  
8 8 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
1 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3   -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  3 +import com.genersoft.iot.vmp.common.InviteSessionType;
4 4  
5 5 public class SsrcTransaction {
6 6  
... ... @@ -13,7 +13,7 @@ public class SsrcTransaction {
13 13  
14 14 private SipTransactionInfo sipTransactionInfo;
15 15  
16   - private VideoStreamSessionManager.SessionType type;
  16 + private InviteSessionType type;
17 17  
18 18 public String getDeviceId() {
19 19 return deviceId;
... ... @@ -63,11 +63,11 @@ public class SsrcTransaction {
63 63 this.ssrc = ssrc;
64 64 }
65 65  
66   - public VideoStreamSessionManager.SessionType getType() {
  66 + public InviteSessionType getType() {
67 67 return type;
68 68 }
69 69  
70   - public void setType(VideoStreamSessionManager.SessionType type) {
  70 + public void setType(InviteSessionType type) {
71 71 this.type = type;
72 72 }
73 73  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/conf/ServerLoggerImpl.java
... ... @@ -27,7 +27,7 @@ public class ServerLoggerImpl implements ServerLogger {
27 27 return;
28 28 }
29 29 StringBuilder stringBuilder = new StringBuilder();
30   - stringBuilder.append(!sender? "发送:目标--->" + from:"接收:来自--->" + to)
  30 + stringBuilder.append(sender? "发送:目标--->" + from:"接收:来自--->" + to)
31 31 .append("\r\n")
32 32 .append(message);
33 33 this.stackLogger.logInfo(stringBuilder.toString());
... ... @@ -40,7 +40,7 @@ public class ServerLoggerImpl implements ServerLogger {
40 40 return;
41 41 }
42 42 StringBuilder stringBuilder = new StringBuilder();
43   - stringBuilder.append(!sender? "发送: 目标->" + from :"接收:来自->" + to)
  43 + stringBuilder.append(sender? "发送: 目标->" + from :"接收:来自->" + to)
44 44 .append("\r\n")
45 45 .append(message);
46 46 this.stackLogger.logInfo(stringBuilder.toString());
... ... @@ -52,7 +52,7 @@ public class ServerLoggerImpl implements ServerLogger {
52 52 return;
53 53 }
54 54 StringBuilder stringBuilder = new StringBuilder();
55   - stringBuilder.append(!sender? "发送: 目标->" + from :"接收:来自->" + to)
  55 + stringBuilder.append(sender? "发送: 目标->" + from :"接收:来自->" + to)
56 56 .append("\r\n")
57 57 .append(message);
58 58 this.stackLogger.logInfo(stringBuilder.toString());
... ... @@ -87,6 +87,4 @@ public class ServerLoggerImpl implements ServerLogger {
87 87 this.stackLogger = this.sipStack.getStackLogger();
88 88 }
89 89 }
90   -
91   -
92 90 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
... ... @@ -76,7 +76,11 @@ public class SipSubscribe {
76 76 // 会话已结束
77 77 dialogTerminated,
78 78 // 设备未找到
79   - deviceNotFoundEvent
  79 + deviceNotFoundEvent,
  80 + // 消息发送失败
  81 + cmdSendFailEvent,
  82 + // 消息发送失败
  83 + failedToGetPort
80 84 }
81 85  
82 86 public static class EventResult<EventObject>{
... ... @@ -86,9 +90,7 @@ public class SipSubscribe {
86 90 public String callId;
87 91 public EventObject event;
88 92  
89   - public EventResult(int statusCode, String msg) {
90   - this.statusCode = statusCode;
91   - this.msg = msg;
  93 + public EventResult() {
92 94 }
93 95  
94 96 public EventResult(EventObject event) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
1 1 package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog;
2 2  
3   -import com.genersoft.iot.vmp.common.VideoManagerConstants;
4   -import com.genersoft.iot.vmp.conf.SipConfig;
5 3 import com.genersoft.iot.vmp.conf.UserSetting;
6 4 import com.genersoft.iot.vmp.gb28181.bean.*;
7 5 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
8   -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
9 6 import com.genersoft.iot.vmp.service.IGbStreamService;
10   -import com.genersoft.iot.vmp.service.IMediaServerService;
11   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
12 7 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
13 8 import org.slf4j.Logger;
14 9 import org.slf4j.LoggerFactory;
... ... @@ -16,12 +11,14 @@ import org.springframework.beans.factory.annotation.Autowired;
16 11 import org.springframework.context.ApplicationListener;
17 12 import org.springframework.stereotype.Component;
18 13 import org.springframework.util.ObjectUtils;
19   -import org.springframework.util.StringUtils;
20 14  
21 15 import javax.sip.InvalidArgumentException;
22 16 import javax.sip.SipException;
23 17 import java.text.ParseException;
24   -import java.util.*;
  18 +import java.util.ArrayList;
  19 +import java.util.HashMap;
  20 +import java.util.List;
  21 +import java.util.Map;
25 22  
26 23 /**
27 24 * catalog事件
... ... @@ -43,6 +40,9 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
43 40 @Autowired
44 41 private SubscribeHolder subscribeHolder;
45 42  
  43 + @Autowired
  44 + private UserSetting userSetting;
  45 +
46 46 @Override
47 47 public void onApplicationEvent(CatalogEvent event) {
48 48 SubscribeInfo subscribe = null;
... ... @@ -93,6 +93,9 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
93 93 }
94 94 if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
95 95 for (GbStream gbStream : event.getGbStreams()) {
  96 + if (gbStream.getStreamType().equals("push") && !userSetting.isUsePushingAsStatus()) {
  97 + continue;
  98 + }
96 99 DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform);
97 100 deviceChannelList.add(deviceChannelByStream);
98 101 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/SSRCFactory.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.session;
  2 +
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import org.springframework.beans.factory.annotation.Autowired;
  5 +import org.springframework.data.redis.core.StringRedisTemplate;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +import java.util.ArrayList;
  9 +import java.util.List;
  10 +import java.util.Set;
  11 +
  12 +/**
  13 + * ssrc使用
  14 + */
  15 +@Component
  16 +public class SSRCFactory {
  17 +
  18 + /**
  19 + * 播流最大并发个数
  20 + */
  21 + private static final Integer MAX_STREAM_COUNT = 10000;
  22 +
  23 + /**
  24 + * 播流最大并发个数
  25 + */
  26 + private static final String SSRC_INFO_KEY = "VMP_SSRC_INFO_";
  27 +
  28 + @Autowired
  29 + private StringRedisTemplate redisTemplate;
  30 +
  31 + @Autowired
  32 + private SipConfig sipConfig;
  33 +
  34 +
  35 + public void initMediaServerSSRC(String mediaServerId, Set<String> usedSet) {
  36 + String ssrcPrefix = sipConfig.getDomain().substring(3, 8);
  37 + String redisKey = SSRC_INFO_KEY + mediaServerId;
  38 + List<String> ssrcList = new ArrayList<>();
  39 + for (int i = 1; i < MAX_STREAM_COUNT; i++) {
  40 + String ssrc = String.format("%s%04d", ssrcPrefix, i);
  41 +
  42 + if (null == usedSet || !usedSet.contains(ssrc)) {
  43 + ssrcList.add(ssrc);
  44 +
  45 + }
  46 + }
  47 + if (redisTemplate.opsForSet().size(redisKey) != null) {
  48 + redisTemplate.delete(redisKey);
  49 + }
  50 + redisTemplate.opsForSet().add(redisKey, ssrcList.toArray(new String[0]));
  51 + }
  52 +
  53 +
  54 + /**
  55 + * 获取视频预览的SSRC值,第一位固定为0
  56 + *
  57 + * @return ssrc
  58 + */
  59 + public String getPlaySsrc(String mediaServerId) {
  60 + return "0" + getSN(mediaServerId);
  61 + }
  62 +
  63 + /**
  64 + * 获取录像回放的SSRC值,第一位固定为1
  65 + */
  66 + public String getPlayBackSsrc(String mediaServerId) {
  67 + return "1" + getSN(mediaServerId);
  68 + }
  69 +
  70 + /**
  71 + * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
  72 + *
  73 + * @param ssrc 需要重置的ssrc
  74 + */
  75 + public void releaseSsrc(String mediaServerId, String ssrc) {
  76 + if (ssrc == null) {
  77 + return;
  78 + }
  79 + String sn = ssrc.substring(1);
  80 + String redisKey = SSRC_INFO_KEY + mediaServerId;
  81 + redisTemplate.opsForSet().add(redisKey, sn);
  82 + }
  83 +
  84 + /**
  85 + * 获取后四位数SN,随机数
  86 + */
  87 + private String getSN(String mediaServerId) {
  88 + String sn = null;
  89 + String redisKey = SSRC_INFO_KEY + mediaServerId;
  90 + Long size = redisTemplate.opsForSet().size(redisKey);
  91 + if (size == null || size == 0) {
  92 + throw new RuntimeException("ssrc已经用完");
  93 + } else {
  94 + // 在集合中移除并返回一个随机成员。
  95 + sn = (String) redisTemplate.opsForSet().pop(redisKey);
  96 + redisTemplate.opsForSet().remove(redisKey, sn);
  97 + }
  98 + return sn;
  99 + }
  100 +
  101 + /**
  102 + * 重置一个流媒体服务的所有ssrc
  103 + *
  104 + * @param mediaServerId 流媒体服务ID
  105 + */
  106 + public void reset(String mediaServerId) {
  107 + this.initMediaServerSSRC(mediaServerId, null);
  108 + }
  109 +
  110 + /**
  111 + * 是否已经存在了某个MediaServer的SSRC信息
  112 + *
  113 + * @param mediaServerId 流媒体服务ID
  114 + */
  115 + public boolean hasMediaServerSSRC(String mediaServerId) {
  116 + String redisKey = SSRC_INFO_KEY + mediaServerId;
  117 + return redisTemplate.opsForSet().members(redisKey) != null;
  118 + }
  119 +
  120 + /**
  121 + * 查询ssrc是否可用
  122 + *
  123 + * @param mediaServerId
  124 + * @param ssrc
  125 + * @return
  126 + */
  127 + public boolean checkSsrc(String mediaServerId, String ssrc) {
  128 + String sn = ssrc.substring(1);
  129 + String redisKey = SSRC_INFO_KEY + mediaServerId;
  130 + return redisTemplate.opsForSet().isMember(redisKey, sn) != null;
  131 + }
  132 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.gb28181.session;
2   -
3   -import com.genersoft.iot.vmp.utils.ConfigConst;
4   -import io.swagger.v3.oas.annotations.media.Schema;
5   -
6   -import java.util.ArrayList;
7   -import java.util.List;
8   -import java.util.Random;
9   -import java.util.Set;
10   -
11   -@Schema(description = "ssrc信息")
12   -public class SsrcConfig {
13   -
14   - /**
15   - * zlm流媒体服务器Id
16   - */
17   - @Schema(description = "流媒体服务器Id")
18   - private String mediaServerId;
19   -
20   - @Schema(description = "SSRC前缀")
21   - private String ssrcPrefix;
22   -
23   - /**
24   - * zlm流媒体服务器已用会话句柄
25   - */
26   - @Schema(description = "zlm流媒体服务器已用会话句柄")
27   - private List<String> isUsed;
28   -
29   - /**
30   - * zlm流媒体服务器可用会话句柄
31   - */
32   - @Schema(description = "zlm流媒体服务器可用会话句柄")
33   - private List<String> notUsed;
34   -
35   - public SsrcConfig() {
36   - }
37   -
38   - public SsrcConfig(String mediaServerId, Set<String> usedSet, String sipDomain) {
39   - this.mediaServerId = mediaServerId;
40   - this.isUsed = new ArrayList<>();
41   - this.ssrcPrefix = sipDomain.substring(3, 8);
42   - this.notUsed = new ArrayList<>();
43   - for (int i = 1; i < ConfigConst.MAX_STRTEAM_COUNT; i++) {
44   - String ssrc;
45   - if (i < 10) {
46   - ssrc = "000" + i;
47   - } else if (i < 100) {
48   - ssrc = "00" + i;
49   - } else if (i < 1000) {
50   - ssrc = "0" + i;
51   - } else {
52   - ssrc = String.valueOf(i);
53   - }
54   - if (null == usedSet || !usedSet.contains(ssrc)) {
55   - this.notUsed.add(ssrc);
56   - } else {
57   - this.isUsed.add(ssrc);
58   - }
59   - }
60   - }
61   -
62   -
63   - /**
64   - * 获取视频预览的SSRC值,第一位固定为0
65   - * @return ssrc
66   - */
67   - public String getPlaySsrc() {
68   - return "0" + getSsrcPrefix() + getSN();
69   - }
70   -
71   - /**
72   - * 获取录像回放的SSRC值,第一位固定为1
73   - *
74   - */
75   - public String getPlayBackSsrc() {
76   - return "1" + getSsrcPrefix() + getSN();
77   - }
78   -
79   - /**
80   - * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
81   - * @param ssrc 需要重置的ssrc
82   - */
83   - public void releaseSsrc(String ssrc) {
84   - if (ssrc == null) {
85   - return;
86   - }
87   - String sn = ssrc.substring(6);
88   - try {
89   - isUsed.remove(sn);
90   - notUsed.add(sn);
91   - }catch (NullPointerException e){
92   - }
93   - }
94   -
95   - /**
96   - * 获取后四位数SN,随机数
97   - *
98   - */
99   - private String getSN() {
100   - String sn = null;
101   - int index = 0;
102   - if (notUsed.size() == 0) {
103   - throw new RuntimeException("ssrc已经用完");
104   - } else if (notUsed.size() == 1) {
105   - sn = notUsed.get(0);
106   - } else {
107   - index = new Random().nextInt(notUsed.size() - 1);
108   - sn = notUsed.get(index);
109   - }
110   - notUsed.remove(index);
111   - isUsed.add(sn);
112   - return sn;
113   - }
114   -
115   - public String getSsrcPrefix() {
116   - return ssrcPrefix;
117   - }
118   -
119   - public String getMediaServerId() {
120   - return mediaServerId;
121   - }
122   -
123   - public void setMediaServerId(String mediaServerId) {
124   - this.mediaServerId = mediaServerId;
125   - }
126   -
127   - public void setSsrcPrefix(String ssrcPrefix) {
128   - this.ssrcPrefix = ssrcPrefix;
129   - }
130   -
131   - public List<String> getIsUsed() {
132   - return isUsed;
133   - }
134   -
135   - public void setIsUsed(List<String> isUsed) {
136   - this.isUsed = isUsed;
137   - }
138   -
139   - public List<String> getNotUsed() {
140   - return notUsed;
141   - }
142   -
143   - public void setNotUsed(List<String> notUsed) {
144   - this.notUsed = notUsed;
145   - }
146   -
147   - public boolean checkSsrc(String ssrcInResponse) {
148   - return !isUsed.contains(ssrcInResponse);
149   - }
150   -}
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
1 1 package com.genersoft.iot.vmp.gb28181.session;
2 2  
  3 +import com.genersoft.iot.vmp.common.InviteSessionType;
3 4 import com.genersoft.iot.vmp.common.VideoManagerConstants;
4 5 import com.genersoft.iot.vmp.conf.UserSetting;
5 6 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
... ... @@ -27,14 +28,6 @@ public class VideoStreamSessionManager {
27 28 @Autowired
28 29 private RedisTemplate<Object, Object> redisTemplate;
29 30  
30   - public enum SessionType {
31   - play,
32   - playback,
33   - download,
34   - broadcast,
35   - talk
36   - }
37   -
38 31 /**
39 32 * 添加一个点播/回放的事务信息
40 33 * 后续可以通过流Id/callID
... ... @@ -45,7 +38,7 @@ public class VideoStreamSessionManager {
45 38 * @param mediaServerId 所使用的流媒体ID
46 39 * @param response 回复
47 40 */
48   - public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, SIPResponse response, SessionType type){
  41 + public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, SIPResponse response, InviteSessionType type){
49 42 SsrcTransaction ssrcTransaction = new SsrcTransaction();
50 43 ssrcTransaction.setDeviceId(deviceId);
51 44 ssrcTransaction.setChannelId(channelId);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
... ... @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
5 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
6 6 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
7 7 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  8 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
8 9 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
9 10 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
10 11 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
... ... @@ -38,6 +39,9 @@ public class SipRunner implements CommandLineRunner {
38 39 private IRedisCatchStorage redisCatchStorage;
39 40  
40 41 @Autowired
  42 + private SSRCFactory ssrcFactory;
  43 +
  44 + @Autowired
41 45 private UserSetting userSetting;
42 46  
43 47 @Autowired
... ... @@ -96,6 +100,7 @@ public class SipRunner implements CommandLineRunner {
96 100 MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
97 101 redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStream());
98 102 if (mediaServerItem != null) {
  103 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
99 104 Map<String, Object> param = new HashMap<>();
100 105 param.put("vhost","__defaultVhost__");
101 106 param.put("app",sendRtpItem.getApp());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
... ... @@ -46,8 +46,7 @@ public class SIPSender {
46 46 transmitRequest(ip, message, errorEvent, null);
47 47 }
48 48  
49   - public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
50   - try {
  49 + public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException {
51 50 ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
52 51 String transport = "UDP";
53 52 if (viaHeader == null) {
... ... @@ -57,7 +56,7 @@ public class SIPSender {
57 56 }
58 57 if (message.getHeader(UserAgentHeader.NAME) == null) {
59 58 try {
60   - message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  59 + message.addHeader(SipUtils.createUserAgentHeader(gitUtil));
61 60 } catch (ParseException e) {
62 61 logger.error("添加UserAgentHeader失败", e);
63 62 }
... ... @@ -104,9 +103,6 @@ public class SIPSender {
104 103 sipProvider.sendResponse((Response)message);
105 104 }
106 105 }
107   - } finally {
108   -// logger.info("[SEND]:SUCCESS:{}", message);
109   - }
110 106 }
111 107  
112 108 public CallIdHeader getNewCallIdHeader(String ip, String transport){
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
... ... @@ -3,6 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
4 4 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
5 5 import com.genersoft.iot.vmp.gb28181.bean.*;
  6 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  7 +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
6 8 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
7 9 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
8 10 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
... ... @@ -107,7 +109,7 @@ public interface ISIPCommander {
107 109 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
108 110 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
109 111 */
110   - void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,InviteStreamCallback inviteStreamCallback, InviteStreamCallback event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
  112 + void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
111 113  
112 114 /**
113 115 * 请求历史媒体下载
... ... @@ -119,7 +121,7 @@ public interface ISIPCommander {
119 121 * @param downloadSpeed 下载倍速参数
120 122 */
121 123 void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
122   - String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  124 + String startTime, String endTime, int downloadSpeed, ZlmHttpHookSubscribe.Event hookEvent,
123 125 SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
124 126  
125 127  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
... ... @@ -17,6 +17,7 @@ import org.springframework.util.DigestUtils;
17 17  
18 18 import javax.sip.InvalidArgumentException;
19 19 import javax.sip.PeerUnavailableException;
  20 +import javax.sip.SipFactory;
20 21 import javax.sip.address.Address;
21 22 import javax.sip.address.SipURI;
22 23 import javax.sip.header.*;
... ... @@ -50,39 +51,39 @@ public class SIPRequestHeaderPlarformProvider {
50 51 Request request = null;
51 52 String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
52 53 //请求行
53   - SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(),
  54 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(),
54 55 parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
55 56 //via
56 57 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
57   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(),
  58 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(),
58 59 parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag());
59 60 viaHeader.setRPort();
60 61 viaHeaders.add(viaHeader);
61 62 //from
62   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
63   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
64   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag);
  63 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
  64 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  65 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag);
65 66 //to
66   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
67   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
68   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,toTag);
  67 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
  68 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  69 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,toTag);
69 70  
70 71 //Forwards
71   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  72 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
72 73  
73 74 //ceq
74   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER);
75   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader,
  75 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER);
  76 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader,
76 77 cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
77 78  
78   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory()
  79 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory()
79 80 .createSipURI(parentPlatform.getDeviceGBId(), sipAddress));
80   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  81 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
81 82  
82   - ExpiresHeader expiresHeader = sipLayer.getSipFactory().createHeaderFactory().createExpiresHeader(expires);
  83 + ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires);
83 84 request.addHeader(expiresHeader);
84 85  
85   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  86 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
86 87  
87 88 return request;
88 89 }
... ... @@ -92,9 +93,9 @@ public class SIPRequestHeaderPlarformProvider {
92 93  
93 94  
94 95 Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, expires);
95   - SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
  96 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
96 97 if (www == null) {
97   - AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest");
  98 + AuthorizationHeader authorizationHeader = SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader("Digest");
98 99 String username = parentPlatform.getUsername();
99 100 if ( username == null || username == "" )
100 101 {
... ... @@ -147,7 +148,7 @@ public class SIPRequestHeaderPlarformProvider {
147 148  
148 149 String RESPONSE = DigestUtils.md5DigestAsHex(reStr.toString().getBytes());
149 150  
150   - AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader(scheme);
  151 + AuthorizationHeader authorizationHeader = SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader(scheme);
151 152 authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
152 153 authorizationHeader.setRealm(realm);
153 154 authorizationHeader.setNonce(nonce);
... ... @@ -165,7 +166,7 @@ public class SIPRequestHeaderPlarformProvider {
165 166 }
166 167  
167 168 public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException {
168   - CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
  169 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
169 170 return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader);
170 171 }
171 172  
... ... @@ -178,36 +179,36 @@ public class SIPRequestHeaderPlarformProvider {
178 179 Request request = null;
179 180 String serverAddress = parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort();
180 181 // sipuri
181   - SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
  182 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
182 183 // via
183 184 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
184   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
  185 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
185 186 parentPlatform.getTransport(), viaTag);
186 187 viaHeader.setRPort();
187 188 viaHeaders.add(viaHeader);
188 189 // from
189   - // SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp());
190   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
191   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
192   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag);
  190 + // SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp());
  191 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
  192 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  193 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag);
193 194 // to
194   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
195   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
196   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag);
  195 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
  196 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  197 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag);
197 198  
198 199 // Forwards
199   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  200 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
200 201 // ceq
201   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
202   - MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
  202 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
  203 + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory();
203 204 // 设置编码, 防止中文乱码
204 205 messageFactory.setDefaultContentEncodingCharset(parentPlatform.getCharacterSet());
205 206 request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
206 207 toHeader, viaHeaders, maxForwards);
207 208  
208   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  209 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
209 210  
210   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  211 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
211 212 request.setContent(content, contentTypeHeader);
212 213 return request;
213 214 }
... ... @@ -215,54 +216,54 @@ public class SIPRequestHeaderPlarformProvider {
215 216 public SIPRequest createNotifyRequest(ParentPlatform parentPlatform, String content, SubscribeInfo subscribeInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException {
216 217 SIPRequest request = null;
217 218 // sipuri
218   - SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
  219 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
219 220 // via
220 221 ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
221   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
  222 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
222 223 parentPlatform.getTransport(), SipUtils.getNewViaTag());
223 224 viaHeader.setRPort();
224 225 viaHeaders.add(viaHeader);
225 226 // from
226   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
  227 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
227 228 parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort());
228   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
229   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag());
  229 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  230 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag());
230 231 // to
231   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
232   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
233   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag());
  232 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
  233 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  234 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag());
234 235  
235 236 // Forwards
236   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  237 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
237 238 // ceq
238   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY);
239   - MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
  239 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY);
  240 + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory();
240 241 // 设置编码, 防止中文乱码
241 242 messageFactory.setDefaultContentEncodingCharset("gb2312");
242 243  
243   - CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId());
  244 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId());
244 245  
245 246 request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader,
246 247 toHeader, viaHeaders, maxForwards);
247 248  
248   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  249 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
249 250  
250   - EventHeader event = sipLayer.getSipFactory().createHeaderFactory().createEventHeader(subscribeInfo.getEventType());
  251 + EventHeader event = SipFactory.getInstance().createHeaderFactory().createEventHeader(subscribeInfo.getEventType());
251 252 if (subscribeInfo.getEventId() != null) {
252 253 event.setEventId(subscribeInfo.getEventId());
253 254 }
254 255  
255 256 request.addHeader(event);
256 257  
257   - SubscriptionStateHeader active = sipLayer.getSipFactory().createHeaderFactory().createSubscriptionStateHeader("active");
  258 + SubscriptionStateHeader active = SipFactory.getInstance().createHeaderFactory().createSubscriptionStateHeader("active");
258 259 request.setHeader(active);
259 260  
260 261 String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
261   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory()
  262 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory()
262 263 .createSipURI(parentPlatform.getDeviceGBId(), sipAddress));
263   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  264 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
264 265  
265   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  266 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
266 267 request.setContent(content, contentTypeHeader);
267 268 return request;
268 269 }
... ... @@ -275,42 +276,42 @@ public class SIPRequestHeaderPlarformProvider {
275 276  
276 277 SIPRequest request = null;
277 278 // sipuri
278   - SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort());
  279 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort());
279 280 // via
280 281 ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
281   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(platform.getDeviceIp(), platform.getDevicePort(),
  282 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(platform.getDeviceIp(), platform.getDevicePort(),
282 283 platform.getTransport(), SipUtils.getNewViaTag());
283 284 viaHeader.setRPort();
284 285 viaHeaders.add(viaHeader);
285 286 // from
286   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getDeviceGBId(),
  287 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getDeviceGBId(),
287 288 platform.getDeviceIp() + ":" + platform.getDevicePort());
288   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
289   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag());
  289 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  290 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag());
290 291 // to
291   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerGBDomain());
292   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
293   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, sendRtpItem.getFromTag());
  292 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerGBDomain());
  293 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  294 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, sendRtpItem.getFromTag());
294 295  
295 296 // Forwards
296   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  297 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
297 298 // ceq
298   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
299   - MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
  299 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
  300 + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory();
300 301 // 设置编码, 防止中文乱码
301 302 messageFactory.setDefaultContentEncodingCharset("gb2312");
302 303  
303   - CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
  304 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
304 305  
305 306 request = (SIPRequest) messageFactory.createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader,
306 307 toHeader, viaHeaders, maxForwards);
307 308  
308   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  309 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
309 310  
310 311 String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
311   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory()
  312 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory()
312 313 .createSipURI(platform.getDeviceGBId(), sipAddress));
313   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  314 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
314 315  
315 316 return request;
316 317 }
... ... @@ -320,37 +321,37 @@ public class SIPRequestHeaderPlarformProvider {
320 321 //请求行
321 322 String platformHostAddress = platform.getServerIP() + ":" + platform.getServerPort();
322 323 String localHostAddress = sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort();
323   - SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, platformHostAddress);
  324 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, platformHostAddress);
324 325 //via
325 326 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
326   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), viaTag);
  327 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), viaTag);
327 328 viaHeader.setRPort();
328 329 viaHeaders.add(viaHeader);
329 330  
330 331 //from
331   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getDeviceGBId(), sipConfig.getDomain());
332   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
333   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
  332 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getDeviceGBId(), sipConfig.getDomain());
  333 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  334 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
334 335 //to
335   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, platformHostAddress);
336   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
337   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
  336 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, platformHostAddress);
  337 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  338 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null);
338 339  
339 340 //Forwards
340   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  341 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
341 342  
342 343 //ceq
343   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
344   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  344 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
  345 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
345 346  
346   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  347 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
347 348  
348   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),localHostAddress));
349   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  349 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),localHostAddress));
  350 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
350 351 // Subject
351   - SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
  352 + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
352 353 request.addHeader(subjectHeader);
353   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  354 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
354 355 request.setContent(content, contentTypeHeader);
355 356 return request;
356 357 }
... ... @@ -358,35 +359,35 @@ public class SIPRequestHeaderPlarformProvider {
358 359 public Request createByteRequest(ParentPlatform platform, String channelId, SipTransactionInfo transactionInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException {
359 360 String deviceHostAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
360 361 Request request = null;
361   - SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
  362 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, deviceHostAddress);
362 363  
363 364 // via
364 365 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
365   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), SipUtils.getNewViaTag());
  366 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), SipUtils.getNewViaTag());
366 367 viaHeaders.add(viaHeader);
367 368 //from
368   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
369   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
370   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.isAsSender()?transactionInfo.getFromTag():transactionInfo.getToTag());
  369 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
  370 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  371 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.isAsSender()?transactionInfo.getFromTag():transactionInfo.getToTag());
371 372 //to
372   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, deviceHostAddress);
373   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
374   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,transactionInfo.isAsSender()?transactionInfo.getToTag():transactionInfo.getFromTag());
  373 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, deviceHostAddress);
  374 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  375 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,transactionInfo.isAsSender()?transactionInfo.getToTag():transactionInfo.getFromTag());
375 376  
376 377 //Forwards
377   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  378 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
378 379  
379 380 //ceq
380   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
381   - CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
382   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  381 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
  382 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
  383 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
383 384  
384   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  385 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
385 386  
386   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort()));
387   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  387 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort()));
  388 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
388 389  
389   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  390 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
390 391  
391 392 return request;
392 393 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
... ... @@ -16,6 +16,7 @@ import org.springframework.stereotype.Component;
16 16 import javax.sip.InvalidArgumentException;
17 17 import javax.sip.PeerUnavailableException;
18 18 import javax.sip.SipException;
  19 +import javax.sip.SipFactory;
19 20 import javax.sip.address.Address;
20 21 import javax.sip.address.SipURI;
21 22 import javax.sip.header.*;
... ... @@ -49,32 +50,32 @@ public class SIPRequestHeaderProvider {
49 50 public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
50 51 Request request = null;
51 52 // sipuri
52   - SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  53 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
53 54 // via
54 55 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
55   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
  56 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
56 57 viaHeader.setRPort();
57 58 viaHeaders.add(viaHeader);
58 59 // from
59   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
60   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
61   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag);
  60 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
  61 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  62 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag);
62 63 // to
63   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
64   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
65   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag);
  64 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  65 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  66 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag);
66 67  
67 68 // Forwards
68   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  69 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
69 70 // ceq
70   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
  71 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
71 72  
72   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
  73 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
73 74 toHeader, viaHeaders, maxForwards);
74 75  
75   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  76 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
76 77  
77   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  78 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
78 79 request.setContent(content, contentTypeHeader);
79 80 return request;
80 81 }
... ... @@ -82,39 +83,39 @@ public class SIPRequestHeaderProvider {
82 83 public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
83 84 Request request = null;
84 85 //请求行
85   - SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  86 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
86 87 //via
87 88 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
88   - HeaderFactory headerFactory = sipLayer.getSipFactory().createHeaderFactory();
89   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
  89 + HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory();
  90 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
90 91 viaHeader.setRPort();
91 92 viaHeaders.add(viaHeader);
92 93  
93 94 //from
94   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
95   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
96   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
  95 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
  96 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  97 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
97 98 //to
98   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
99   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
100   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
  99 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  100 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  101 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null);
101 102  
102 103 //Forwards
103   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  104 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
104 105  
105 106 //ceq
106   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
107   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  107 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
  108 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
108 109  
109   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  110 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
110 111  
111   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
112   - // Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
113   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  112 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  113 + // Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
  114 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
114 115 // Subject
115   - SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
  116 + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
116 117 request.addHeader(subjectHeader);
117   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  118 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
118 119 request.setContent(content, contentTypeHeader);
119 120 return request;
120 121 }
... ... @@ -122,69 +123,74 @@ public class SIPRequestHeaderProvider {
122 123 public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException {
123 124 Request request = null;
124 125 //请求行
125   - SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  126 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
126 127 // via
127 128 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
128   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
  129 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
129 130 viaHeader.setRPort();
130 131 viaHeaders.add(viaHeader);
131 132 //from
132   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
133   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
134   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
  133 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
  134 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  135 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
135 136 //to
136   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
137   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
138   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
  137 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  138 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  139 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null);
139 140  
140 141 //Forwards
141   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  142 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
142 143  
143 144 //ceq
144   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
145   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  145 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
  146 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
146 147  
147   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
148   - // Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
149   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  148 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  149 + // Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
  150 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
150 151  
151   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  152 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
152 153  
153 154 // Subject
154   - SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
  155 + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
155 156 request.addHeader(subjectHeader);
156 157  
157   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  158 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
158 159 request.setContent(content, contentTypeHeader);
159 160 return request;
160 161 }
161 162  
162 163 public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException {
163 164 Request request = null;
164   - SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
165   -
  165 + //请求行
  166 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
166 167 // via
167 168 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
168   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
  169 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
169 170 viaHeaders.add(viaHeader);
170 171 //from
171   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
172   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
173   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, !transactionInfo.isAsSender()? transactionInfo.getToTag():transactionInfo.getFromTag());
  172 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
  173 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  174 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
174 175 //to
175   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId,device.getHostAddress());
176   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
177   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, !transactionInfo.isAsSender()? transactionInfo.getToTag(): transactionInfo.getFromTag());
  176 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
  177 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  178 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
178 179  
179 180 //Forwards
180   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  181 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
181 182  
182 183 //ceq
183   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
184   - CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
185   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  184 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
  185 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
  186 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  187 +
  188 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
186 189  
187   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  190 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  191 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
  192 +
  193 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
188 194  
189 195 return request;
190 196 }
... ... @@ -192,50 +198,50 @@ public class SIPRequestHeaderProvider {
192 198 public Request createSubscribeRequest(Device device, String content, SIPRequest requestOld, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
193 199 Request request = null;
194 200 // sipuri
195   - SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  201 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
196 202 // via
197 203 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
198   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(),
  204 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(),
199 205 device.getTransport(), SipUtils.getNewViaTag());
200 206 viaHeader.setRPort();
201 207 viaHeaders.add(viaHeader);
202 208 // from
203   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
204   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
205   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag());
  209 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
  210 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  211 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag());
206 212 // to
207   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
208   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
209   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag());
  213 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  214 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  215 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag());
210 216  
211 217 // Forwards
212   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  218 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
213 219  
214 220 // ceq
215   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE);
  221 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE);
216 222  
217   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
  223 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
218 224 toHeader, viaHeaders, maxForwards);
219 225  
220 226  
221   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
222   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  227 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  228 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
223 229  
224 230 // Expires
225   - ExpiresHeader expireHeader = sipLayer.getSipFactory().createHeaderFactory().createExpiresHeader(expires);
  231 + ExpiresHeader expireHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires);
226 232 request.addHeader(expireHeader);
227 233  
228 234 // Event
229   - EventHeader eventHeader = sipLayer.getSipFactory().createHeaderFactory().createEventHeader(event);
  235 + EventHeader eventHeader = SipFactory.getInstance().createHeaderFactory().createEventHeader(event);
230 236  
231 237 int random = (int) Math.floor(Math.random() * 10000);
232 238 eventHeader.setEventId(random + "");
233 239 request.addHeader(eventHeader);
234 240  
235   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  241 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
236 242 request.setContent(content, contentTypeHeader);
237 243  
238   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  244 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
239 245  
240 246 return request;
241 247 }
... ... @@ -247,37 +253,37 @@ public class SIPRequestHeaderProvider {
247 253 }
248 254 SIPRequest request = null;
249 255 //请求行
250   - SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  256 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
251 257 // via
252 258 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
253   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
  259 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
254 260 viaHeaders.add(viaHeader);
255 261 //from
256   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
257   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
258   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
  262 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
  263 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  264 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
259 265 //to
260   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId,device.getHostAddress());
261   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
262   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
  266 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
  267 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  268 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
263 269  
264 270 //Forwards
265   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  271 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
266 272  
267 273 //ceq
268   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO);
269   - CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
270   - request = (SIPRequest)sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  274 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO);
  275 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
  276 + request = (SIPRequest)SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
271 277  
272   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  278 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
273 279  
274   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
275   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  280 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  281 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
276 282  
277   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  283 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
278 284  
279 285 if (content != null) {
280   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application",
  286 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application",
281 287 "MANSRTSP");
282 288 request.setContent(content, contentTypeHeader);
283 289 }
... ... @@ -289,56 +295,55 @@ public class SIPRequestHeaderProvider {
289 295  
290 296 // via
291 297 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
292   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag());
  298 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag());
293 299 viaHeaders.add(viaHeader);
294 300  
295 301 //Forwards
296   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  302 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
297 303  
298 304 //ceq
299   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK);
  305 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK);
300 306  
301   - Request request = sipLayer.getSipFactory().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards);
  307 + Request request = SipFactory.getInstance().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards);
302 308  
303   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  309 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
304 310  
305   - Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort()));
306   - request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
  311 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort()));
  312 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
307 313  
308   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  314 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
309 315  
310 316 return request;
311 317 }
312   -
313 318 public Request createBroadcastMessageRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
314 319 Request request = null;
315 320 // sipuri
316   - SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  321 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
317 322 // via
318 323 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
319   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
  324 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
320 325 viaHeader.setRPort();
321 326 viaHeaders.add(viaHeader);
322 327 // from
323   - SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
324   - Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
325   - FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag);
  328 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
  329 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  330 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag);
326 331 // to
327   - SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
328   - Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
329   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag);
  332 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  333 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  334 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag);
330 335  
331 336 // Forwards
332   - MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
  337 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
333 338 // ceq
334   - CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
  339 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
335 340  
336   - ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  341 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
337 342  
338   - request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
  343 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
339 344 toHeader, viaHeaders, maxForwards, contentTypeHeader, content);
340 345  
341   - request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
  346 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
342 347  
343 348 return request;
344 349 }
... ...
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 3 import com.alibaba.fastjson2.JSONObject;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
4 5 import com.genersoft.iot.vmp.common.StreamInfo;
5 6 import com.genersoft.iot.vmp.conf.SipConfig;
6 7 import com.genersoft.iot.vmp.conf.UserSetting;
... ... @@ -36,6 +37,7 @@ import org.springframework.util.ObjectUtils;
36 37 import javax.sip.InvalidArgumentException;
37 38 import javax.sip.ResponseEvent;
38 39 import javax.sip.SipException;
  40 +import javax.sip.SipFactory;
39 41 import javax.sip.header.CallIdHeader;
40 42 import javax.sip.message.Request;
41 43 import java.text.ParseException;
... ... @@ -358,7 +360,7 @@ public class SIPCommander implements ISIPCommander {
358 360 // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
359 361 ResponseEvent responseEvent = (ResponseEvent) e.event;
360 362 SIPResponse response = (SIPResponse) responseEvent.getResponse();
361   - streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play);
  363 + streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY);
362 364 okEvent.response(e);
363 365 });
364 366 }
... ... @@ -373,11 +375,11 @@ public class SIPCommander implements ISIPCommander {
373 375 */
374 376 @Override
375 377 public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
376   - String startTime, String endTime, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  378 + String startTime, String endTime, ZlmHttpHookSubscribe.Event hookEvent,
377 379 SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
378 380  
379 381  
380   - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
  382 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
381 383 String sdpIp;
382 384 if (!ObjectUtils.isEmpty(device.getSdpIp())) {
383 385 sdpIp = device.getSdpIp();
... ... @@ -450,8 +452,7 @@ public class SIPCommander implements ISIPCommander {
450 452 // 添加订阅
451 453 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
452 454 if (hookEvent != null) {
453   - InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream());
454   - hookEvent.call(inviteStreamInfo);
  455 + hookEvent.response(mediaServerItemInUse, json);
455 456 }
456 457 subscribe.removeSubscribe(hookSubscribe);
457 458 });
... ... @@ -460,12 +461,9 @@ public class SIPCommander implements ISIPCommander {
460 461 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
461 462 ResponseEvent responseEvent = (ResponseEvent) event.event;
462 463 SIPResponse response = (SIPResponse) responseEvent.getResponse();
463   - streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.playback);
  464 + streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAYBACK);
464 465 okEvent.response(event);
465 466 });
466   - if (inviteStreamCallback != null) {
467   - inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
468   - }
469 467 }
470 468  
471 469 /**
... ... @@ -480,10 +478,10 @@ public class SIPCommander implements ISIPCommander {
480 478 @Override
481 479 public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
482 480 String startTime, String endTime, int downloadSpeed,
483   - InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  481 + ZlmHttpHookSubscribe.Event hookEvent,
484 482 SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
485 483  
486   - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
  484 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
487 485 String sdpIp;
488 486 if (!ObjectUtils.isEmpty(device.getSdpIp())) {
489 487 sdpIp = device.getSdpIp();
... ... @@ -551,13 +549,13 @@ public class SIPCommander implements ISIPCommander {
551 549  
552 550 content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
553 551 logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
554   - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
  552 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
555 553 // 添加订阅
556 554 CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
557   - String callId=newCallIdHeader.getCallId();
  555 + String callId= newCallIdHeader.getCallId();
558 556 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
559 557 logger.debug("sipc 添加订阅===callId {}",callId);
560   - hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream()));
  558 + hookEvent.response(mediaServerItemInUse, json);
561 559 subscribe.removeSubscribe(hookSubscribe);
562 560 hookSubscribe.getContent().put("regist", false);
563 561 hookSubscribe.getContent().put("schema", "rtsp");
... ... @@ -566,7 +564,7 @@ public class SIPCommander implements ISIPCommander {
566 564 (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
567 565 logger.info("[录像]下载结束, 发送BYE");
568 566 try {
569   - streamByeCmd(device, channelId, ssrcInfo.getStream(),callId);
  567 + streamByeCmd(device, channelId, ssrcInfo.getStream(), callId);
570 568 } catch (InvalidArgumentException | ParseException | SipException |
571 569 SsrcTransactionNotFoundException e) {
572 570 logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
... ... @@ -575,9 +573,6 @@ public class SIPCommander implements ISIPCommander {
575 573 });
576 574  
577 575 Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
578   - if (inviteStreamCallback != null) {
579   - inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,callId, "rtp", ssrcInfo.getStream()));
580   - }
581 576  
582 577 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
583 578 ResponseEvent responseEvent = (ResponseEvent) event.event;
... ... @@ -588,9 +583,7 @@ public class SIPCommander implements ISIPCommander {
588 583 if (ssrcIndex >= 0) {
589 584 ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
590 585 }
591   - logger.debug("接收到的下载响应ssrc====>{}",ssrcInfo.getSsrc());
592   - logger.debug("接收到的下载响应ssrc====>{}",ssrc);
593   - streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
  586 + streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD);
594 587 okEvent.response(event);
595 588 });
596 589 }
... ... @@ -654,7 +647,7 @@ public class SIPCommander implements ISIPCommander {
654 647 // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
655 648 ResponseEvent responseEvent = (ResponseEvent) e.event;
656 649 SIPResponse response = (SIPResponse) responseEvent.getResponse();
657   - streamSession.put(device.getDeviceId(), channelId, "talk", stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.talk);
  650 + streamSession.put(device.getDeviceId(), channelId, "talk", stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.TALK);
658 651 okEvent.response(e);
659 652 });
660 653 }
... ... @@ -1247,7 +1240,7 @@ public class SIPCommander implements ISIPCommander {
1247 1240 CallIdHeader callIdHeader;
1248 1241  
1249 1242 if (requestOld != null) {
1250   - callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
  1243 + callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
1251 1244 } else {
1252 1245 callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
1253 1246 }
... ... @@ -1322,7 +1315,7 @@ public class SIPCommander implements ISIPCommander {
1322 1315 CallIdHeader callIdHeader;
1323 1316  
1324 1317 if (requestOld != null) {
1325   - callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
  1318 + callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
1326 1319 } else {
1327 1320 callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
1328 1321 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2 2  
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.common.InviteSessionType;
5 6 import com.genersoft.iot.vmp.conf.DynamicTask;
6 7 import com.genersoft.iot.vmp.conf.UserSetting;
7 8 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
... ... @@ -39,6 +40,7 @@ import org.springframework.util.ObjectUtils;
39 40 import javax.sip.InvalidArgumentException;
40 41 import javax.sip.ResponseEvent;
41 42 import javax.sip.SipException;
  43 +import javax.sip.SipFactory;
42 44 import javax.sip.header.CallIdHeader;
43 45 import javax.sip.header.WWWAuthenticateHeader;
44 46 import javax.sip.message.Request;
... ... @@ -518,7 +520,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
518 520 private void sendNotify(ParentPlatform parentPlatform, String catalogXmlContent,
519 521 SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent )
520 522 throws SipException, ParseException, InvalidArgumentException {
521   - MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
  523 + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory();
522 524 String characterSet = parentPlatform.getCharacterSet();
523 525 // 设置编码, 防止中文乱码
524 526 messageFactory.setDefaultContentEncodingCharset(characterSet);
... ... @@ -854,7 +856,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
854 856 }), e -> {
855 857 ResponseEvent responseEvent = (ResponseEvent) e.event;
856 858 SIPResponse response = (SIPResponse) responseEvent.getResponse();
857   - streamSession.put(platform.getServerGBId(), channelId, callIdHeader.getCallId(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.broadcast);
  859 + streamSession.put(platform.getServerGBId(), channelId, callIdHeader.getCallId(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.BROADCAST);
858 860 okEvent.response(e);
859 861 });
860 862 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2  
3   -import com.genersoft.iot.vmp.common.StreamInfo;
  3 +import com.genersoft.iot.vmp.common.InviteInfo;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
4 5 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
5 6 import com.genersoft.iot.vmp.gb28181.bean.*;
6 7 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
  8 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
7 9 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
8 10 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
9 11 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
... ... @@ -13,6 +15,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
13 15 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
14 16 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
15 17 import com.genersoft.iot.vmp.service.IDeviceService;
  18 +import com.genersoft.iot.vmp.service.IInviteStreamService;
16 19 import com.genersoft.iot.vmp.service.IMediaServerService;
17 20 import com.genersoft.iot.vmp.service.IPlayService;
18 21 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
... ... @@ -29,6 +32,7 @@ import javax.sip.InvalidArgumentException;
29 32 import javax.sip.RequestEvent;
30 33 import javax.sip.SipException;
31 34 import javax.sip.address.SipURI;
  35 +import javax.sip.header.CallIdHeader;
32 36 import javax.sip.header.FromHeader;
33 37 import javax.sip.header.HeaderAddress;
34 38 import javax.sip.header.ToHeader;
... ... @@ -57,6 +61,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
57 61 private IRedisCatchStorage redisCatchStorage;
58 62  
59 63 @Autowired
  64 + private IInviteStreamService inviteStreamService;
  65 +
  66 + @Autowired
60 67 private IDeviceService deviceService;
61 68  
62 69 @Autowired
... ... @@ -69,6 +76,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
69 76 private ZLMRTPServerFactory zlmrtpServerFactory;
70 77  
71 78 @Autowired
  79 + private SSRCFactory ssrcFactory;
  80 +
  81 + @Autowired
72 82 private IMediaServerService mediaServerService;
73 83  
74 84 @Autowired
... ... @@ -100,92 +110,89 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
100 110 } catch (SipException | InvalidArgumentException | ParseException e) {
101 111 logger.error("[回复BYE信息失败],{}", e.getMessage());
102 112 }
103   -
104   - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, request.getCallIdHeader().getCallId());
105   -
106   - if (sendRtpItem != null){
107   - logger.info("[收到bye] {}/{}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId());
108   - String streamId = sendRtpItem.getStream();
109   - MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
110   - if (mediaServerItem == null) {
111   - return;
112   - }
113   -
114   - Boolean ready = zlmrtpServerFactory.isStreamReady(mediaServerItem, sendRtpItem.getApp(), streamId);
115   - if (!ready) {
116   - logger.info("[收到bye] 发现流{}/{}已经结束,不需处理", sendRtpItem.getApp(), sendRtpItem.getStream());
117   - return;
118   - }
119   - Map<String, Object> param = new HashMap<>();
120   - param.put("vhost","__defaultVhost__");
121   - param.put("app",sendRtpItem.getApp());
122   - param.put("stream",streamId);
123   - param.put("ssrc",sendRtpItem.getSsrc());
124   - logger.info("[收到bye] 停止推流:{}", streamId);
125   - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
126   - redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), request.getCallIdHeader().getCallId(), null);
127   - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
128   -
129   - int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
130   - if (totalReaderCount <= 0) {
131   - logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
132   - if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
133   - Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
134   - if (device == null) {
135   - logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId);
  113 + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
  114 + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
  115 + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
  116 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId());
  117 + logger.info("[收到bye] {}/{}", platformGbId, channelId);
  118 + if (sendRtpItem != null){
  119 + String streamId = sendRtpItem.getStream();
  120 + Map<String, Object> param = new HashMap<>();
  121 + param.put("vhost","__defaultVhost__");
  122 + param.put("app",sendRtpItem.getApp());
  123 + param.put("stream",streamId);
  124 + param.put("ssrc",sendRtpItem.getSsrc());
  125 + logger.info("[收到bye] 停止向上级推流:{}", streamId);
  126 + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
  127 + redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null);
  128 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
  129 + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
  130 + int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId);
  131 + if (totalReaderCount <= 0) {
  132 + logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
  133 + if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
  134 + Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
  135 + if (device == null) {
  136 + logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId);
  137 + }
  138 + try {
  139 + logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), channelId);
  140 + cmder.streamByeCmd(device, channelId, streamId, null);
  141 + } catch (InvalidArgumentException | ParseException | SipException |
  142 + SsrcTransactionNotFoundException e) {
  143 + logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage());
  144 + }
136 145 }
137   - try {
138   - logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
139   - cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null);
140   - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
141   - logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage());
  146 + if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
  147 + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
  148 + sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
  149 + sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
  150 + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
142 151 }
143 152 }
144   -
145   - if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) {
146   - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0,
147   - sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getChannelId(),
148   - sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId());
149   - redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
150   - }
151 153 }
  154 + // 可能是设备主动停止
  155 + Device device = storager.queryVideoDeviceByChannelId(platformGbId);
  156 + if (device != null) {
  157 + storager.stopPlay(device.getDeviceId(), channelId);
  158 + SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
  159 + if (ssrcTransactionForPlay != null){
  160 + if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){
  161 + // 释放ssrc
  162 + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId());
  163 + if (mediaServerItem != null) {
  164 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc());
  165 + }
  166 + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream());
  167 + }
  168 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
152 169  
153   - playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
154   - }
155   -
156   - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
157   - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
158   -
159   - // 可能是设备主动停止
160   - Device device = storager.queryVideoDeviceByChannelId(platformGbId);
161   - if (device != null) {
162   - storager.stopPlay(device.getDeviceId(), channelId);
163   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
164   - if (streamInfo != null) {
165   - redisCatchStorage.stopPlay(streamInfo);
166   - mediaServerService.closeRTPServer(streamInfo.getMediaServerId(), streamInfo.getStream());
167   - }
168   - SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
169   - if (ssrcTransactionForPlay != null){
170   - if (ssrcTransactionForPlay.getCallId().equals(request.getCallIdHeader().getCallId())){
  170 + if (inviteInfo != null) {
  171 + inviteStreamService.removeInviteInfo(inviteInfo);
  172 + if (inviteInfo.getStreamInfo() != null) {
  173 + mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream());
  174 + }
  175 + }
  176 + }
  177 + SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callIdHeader.getCallId(), null);
  178 + if (ssrcTransactionForPlayBack != null) {
171 179 // 释放ssrc
172   - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId());
  180 + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId());
173 181 if (mediaServerItem != null) {
174   - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc());
  182 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc());
  183 + }
  184 + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream());
  185 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, device.getDeviceId(), channelId);
  186 +
  187 + if (inviteInfo != null) {
  188 + inviteStreamService.removeInviteInfo(inviteInfo);
  189 + if (inviteInfo.getStreamInfo() != null) {
  190 + mediaServerService.closeRTPServer(inviteInfo.getStreamInfo().getMediaServerId(), inviteInfo.getStream());
  191 + }
175 192 }
176   - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream());
177   - }
178   - }
179   - SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, request.getCallIdHeader().getCallId(), null);
180   - if (ssrcTransactionForPlayBack != null) {
181   - // 释放ssrc
182   - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId());
183   - if (mediaServerItem != null) {
184   - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc());
185 193 }
186   - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream());
187 194 }
188   - }
  195 +
189 196 SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, request.getCallIdHeader().getCallId(), null);
190 197 if (ssrcTransaction != null) {
191 198 // 释放ssrc
... ... @@ -203,7 +210,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
203 210 // break;
204 211 // case download:
205 212 // break;
206   - case broadcast:
  213 + case BROADCAST:
207 214 String channelId1 = ssrcTransaction.getChannelId();
208 215  
209 216 Device deviceFromTransaction = storager.queryVideoDevice(ssrcTransaction.getDeviceId());
... ... @@ -255,7 +262,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
255 262 List<SsrcTransaction> ssrcTransactions = streamSession.getSsrcTransactionForAll(null, channelId1, null, null);
256 263 if (ssrcTransactions.size() > 0) {
257 264 for (SsrcTransaction transaction : ssrcTransactions) {
258   - if (transaction.getType().equals(VideoStreamSessionManager.SessionType.broadcast)) {
  265 + if (transaction.getType().equals(InviteSessionType.BROADCAST)) {
259 266 ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(transaction.getDeviceId());
260 267 if (parentPlatform != null) {
261 268 try {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2  
3   -import com.alibaba.fastjson2.JSONObject;
4 3 import com.genersoft.iot.vmp.common.VideoManagerConstants;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
5 5 import com.genersoft.iot.vmp.conf.DynamicTask;
6 6 import com.genersoft.iot.vmp.conf.SipConfig;
7 7 import com.genersoft.iot.vmp.conf.UserSetting;
8 8 import com.genersoft.iot.vmp.gb28181.bean.*;
9   -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
10 9 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
11   -import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
12 10 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  11 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
13 12 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
14 13 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
15 14 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
... ... @@ -23,7 +22,12 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
23 22 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
24 23 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
25 24 import com.genersoft.iot.vmp.media.zlm.dto.*;
26   -import com.genersoft.iot.vmp.service.*;
  25 +import com.genersoft.iot.vmp.service.IMediaServerService;
  26 +import com.genersoft.iot.vmp.service.IPlayService;
  27 +import com.genersoft.iot.vmp.service.IStreamProxyService;
  28 +import com.genersoft.iot.vmp.service.IStreamPushService;
  29 +import com.genersoft.iot.vmp.service.bean.InviteErrorCallback;
  30 +import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
27 31 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
28 32 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
29 33 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
... ... @@ -79,6 +83,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
79 83 private IRedisCatchStorage redisCatchStorage;
80 84  
81 85 @Autowired
  86 + private SSRCFactory ssrcFactory;
  87 +
  88 + @Autowired
82 89 private DynamicTask dynamicTask;
83 90  
84 91 @Autowired
... ... @@ -90,8 +97,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
90 97 @Autowired
91 98 private SIPSender sipSender;
92 99  
93   - @Autowired
94   - private AudioBroadcastManager audioBroadcastManager;
  100 + @Autowired
  101 + private AudioBroadcastManager audioBroadcastManager;
95 102  
96 103 @Autowired
97 104 private ZLMRTPServerFactory zlmrtpServerFactory;
... ... @@ -102,8 +109,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
102 109 @Autowired
103 110 private ISIPCommander commander;
104 111  
105   - @Autowired
106   - private ZLMRESTfulUtils zlmresTfulUtils;
  112 + @Autowired
  113 + private ZLMRESTfulUtils zlmresTfulUtils;
107 114  
108 115 @Autowired
109 116 private ZlmHttpHookSubscribe zlmHttpHookSubscribe;
... ... @@ -112,28 +119,24 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
112 119 private SIPProcessorObserver sipProcessorObserver;
113 120  
114 121 @Autowired
115   - private VideoStreamSessionManager sessionManager;
116   -
117   - @Autowired
118 122 private UserSetting userSetting;
119 123  
120 124 @Autowired
121 125 private ZLMMediaListManager mediaListManager;
122 126  
123   - @Autowired
124   - private DeferredResultHolder resultHolder;
  127 + @Autowired
  128 + private DeferredResultHolder resultHolder;
125 129  
126   - @Autowired
127   - private ZlmHttpHookSubscribe subscribe;
  130 + @Autowired
  131 + private ZlmHttpHookSubscribe subscribe;
128 132  
129   - @Autowired
130   - private SipConfig config;
  133 + @Autowired
  134 + private SipConfig config;
131 135  
132 136 @Autowired
133 137 private VideoStreamSessionManager streamSession;
134 138  
135 139  
136   -
137 140 @Autowired
138 141 private RedisGbPlayMsgListener redisGbPlayMsgListener;
139 142  
... ... @@ -153,7 +156,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
153 156 public void process(RequestEvent evt) {
154 157 // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
155 158 try {
156   - SIPRequest request = (SIPRequest)evt.getRequest();
  159 + SIPRequest request = (SIPRequest) evt.getRequest();
157 160 String channelId = SipUtils.getChannelIdFromRequest(request);
158 161 String requesterId = SipUtils.getUserIdFromFromHeader(request);
159 162 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
... ... @@ -167,27 +170,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
167 170 }
168 171 return;
169 172 }
170   - String ssrc = null;
171   - SessionDescription sdp = null;
172   - String ssrcDefault = "0000000000";
173   - if (channelId == null) {
174   - // 解析sdp消息, 使用jainsip 自带的sdp解析方式
175   - String contentString = new String(request.getRawContent());
176   -
177   - // jainSip不支持y=字段, 移除以解析。
178   - int ssrcIndex = contentString.indexOf("y=");
179   -
180   - if (ssrcIndex >= 0) {
181   - //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段
182   - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
183   - String substring = contentString.substring(0, contentString.indexOf("y="));
184   - sdp = SdpFactory.getInstance().createSessionDescription(substring);
185   - } else {
186   - ssrc = ssrcDefault;
187   - sdp = SdpFactory.getInstance().createSessionDescription(contentString);
188   - }
189   - channelId = sdp.getOrigin().getUsername();
190   - }
191 173  
192 174  
193 175 // 查询请求是否来自上级平台\设备
... ... @@ -249,7 +231,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
249 231 }
250 232 return;
251 233 }
252   - }else if("proxy".equals(gbStream.getStreamType())){
  234 + } else if ("proxy".equals(gbStream.getStreamType())) {
253 235 proxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(gbStream.getApp(), gbStream.getStream());
254 236 if (proxyByAppAndStream == null) {
255 237 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
... ... @@ -285,23 +267,21 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
285 267 }
286 268 return;
287 269 }
288   - if (sdp == null || ssrc == null) {
289   - // 解析sdp消息, 使用jainsip 自带的sdp解析方式
290   - String contentString = new String(request.getRawContent());
291   -
292   - // jainSip不支持y=字段, 移除以解析。
293   - int ssrcIndex = contentString.indexOf("y=");
294   - if (ssrcIndex >= 0) {
295   - //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段
296   - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
297   - String substring = contentString.substring(0, contentString.indexOf("y="));
298   - sdp = SdpFactory.getInstance().createSessionDescription(substring);
299   - } else {
300   - ssrc = ssrcDefault;
301   - sdp = SdpFactory.getInstance().createSessionDescription(contentString);
302   - }
303   - }
  270 + // 解析sdp消息, 使用jainsip 自带的sdp解析方式
  271 + String contentString = new String(request.getRawContent());
304 272  
  273 + // jainSip不支持y=字段, 移除以解析。
  274 + // 检查是否有y字段
  275 + int ssrcIndex = contentString.indexOf("y=");
  276 +
  277 + SessionDescription sdp;
  278 + if (ssrcIndex >= 0) {
  279 + //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段
  280 + String substring = contentString.substring(0, ssrcIndex);
  281 + sdp = SdpFactory.getInstance().createSessionDescription(substring);
  282 + } else {
  283 + sdp = SdpFactory.getInstance().createSessionDescription(contentString);
  284 + }
305 285 String sessionName = sdp.getSessionName().getValue();
306 286  
307 287 Long startTime = null;
... ... @@ -363,7 +343,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
363 343 String username = sdp.getOrigin().getUsername();
364 344 String addressStr = sdp.getConnection().getAddress();
365 345  
366   - logger.info("[上级点播]用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc);
  346 +
367 347 Device device = null;
368 348 // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
369 349 if (channel != null) {
... ... @@ -387,6 +367,25 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
387 367 }
388 368 return;
389 369 }
  370 +
  371 + String ssrc;
  372 + if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) {
  373 + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  374 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
  375 + } else {
  376 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  377 + }
  378 + String streamTypeStr = null;
  379 + if (mediaTransmissionTCP) {
  380 + if (tcpActive) {
  381 + streamTypeStr = "TCP-ACTIVE";
  382 + } else {
  383 + streamTypeStr = "TCP-PASSIVE";
  384 + }
  385 + } else {
  386 + streamTypeStr = "UDP";
  387 + }
  388 + logger.info("[上级Invite] {}, 平台:{}, 通道:{}, 收流地址:{}:{},收流方式:{}, ssrc:{}", sessionName, username, channelId, addressStr, port, streamTypeStr, ssrc);
390 389 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
391 390 device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp());
392 391  
... ... @@ -407,11 +406,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
407 406  
408 407 Long finalStartTime = startTime;
409 408 Long finalStopTime = stopTime;
410   - String finalChannelId = channelId;
411   - ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON) -> {
412   - String app = responseJSON.getString("app");
413   - String stream = responseJSON.getString("stream");
414   - logger.info("[上级点播]下级已经开始推流。 回复200OK(SDP), {}/{}", app, stream);
  409 + InviteErrorCallback<Object> hookEvent = (code, msg, data) -> {
  410 + StreamInfo streamInfo = (StreamInfo) data;
  411 + MediaServerItem mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId());
  412 + logger.info("[上级Invite]下级已经开始推流。 回复200OK(SDP), {}/{}", streamInfo.getApp(), streamInfo.getStream());
415 413 // * 0 等待设备推流上来
416 414 // * 1 下级已经推流,等待上级平台回复ack
417 415 // * 2 推流中
... ... @@ -420,7 +418,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
420 418  
421 419 StringBuffer content = new StringBuffer(200);
422 420 content.append("v=0\r\n");
423   - content.append("o=" + finalChannelId + " 0 0 IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n");
  421 + content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n");
424 422 content.append("s=" + sessionName + "\r\n");
425 423 content.append("c=IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n");
426 424 if ("Playback".equalsIgnoreCase(sessionName)) {
... ... @@ -462,111 +460,118 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
462 460 logger.error("[命令发送失败] 国标级联 回复SdpAck", e);
463 461 }
464 462 };
465   - SipSubscribe.Event errorEvent = ((event) -> {
  463 + InviteErrorCallback<Object> errorEvent = ((statusCode, msg, data) -> {
466 464 // 未知错误。直接转发设备点播的错误
467 465 try {
468   - Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
469   - sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
470   - } catch (ParseException | SipException e) {
  466 + if (statusCode > 0) {
  467 + Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
  468 + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
  469 + }
  470 + } catch (ParseException | SipException e) {
471 471 logger.error("未处理的异常 ", e);
472 472 }
473 473 });
474 474 sendRtpItem.setApp("rtp");
475 475 if ("Playback".equalsIgnoreCase(sessionName)) {
476 476 sendRtpItem.setPlayType(InviteStreamType.PLAYBACK);
477   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, device.isSsrcCheck(), true);
  477 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
478 478 sendRtpItem.setStream(ssrcInfo.getStream());
479 479 // 写入redis, 超时时回复
480 480 redisCatchStorage.updateSendRTPSever(sendRtpItem);
481 481 playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),
482   - DateUtil.formatter.format(end), null, result -> {
483   - if (result.getCode() != 0) {
484   - logger.warn("录像回放失败");
485   - if (result.getEvent() != null) {
486   - errorEvent.response(result.getEvent());
487   - }
488   - redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), finalChannelId, callIdHeader.getCallId(), null);
489   - try {
490   - responseAck(request, Response.REQUEST_TIMEOUT);
491   - } catch (SipException | InvalidArgumentException | ParseException e) {
492   - logger.error("[命令发送失败] 国标级联 录像回放 发送REQUEST_TIMEOUT: {}", e.getMessage());
493   - }
  482 + DateUtil.formatter.format(end),
  483 + (code, msg, data) -> {
  484 + if (code == InviteErrorCode.SUCCESS.getCode()) {
  485 + hookEvent.run(code, msg, data);
  486 + } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) {
  487 + logger.info("[录像回放]超时, 用户:{}, 通道:{}", username, channelId);
  488 + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
  489 + errorEvent.run(code, msg, data);
  490 + } else {
  491 + errorEvent.run(code, msg, data);
  492 + }
  493 + });
  494 + } else if ("Download".equalsIgnoreCase(sessionName)) {
  495 + // 获取指定的下载速度
  496 + Vector sdpMediaDescriptions = sdp.getMediaDescriptions(true);
  497 + MediaDescription mediaDescription = null;
  498 + String downloadSpeed = "1";
  499 + if (sdpMediaDescriptions.size() > 0) {
  500 + mediaDescription = (MediaDescription) sdpMediaDescriptions.get(0);
  501 + }
  502 + if (mediaDescription != null) {
  503 + downloadSpeed = mediaDescription.getAttribute("downloadspeed");
  504 + }
  505 +
  506 + sendRtpItem.setPlayType(InviteStreamType.DOWNLOAD);
  507 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, false, device.getStreamModeForParam());
  508 + sendRtpItem.setStream(ssrcInfo.getStream());
  509 + // 写入redis, 超时时回复
  510 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  511 + playService.download(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start),
  512 + DateUtil.formatter.format(end), Integer.parseInt(downloadSpeed),
  513 + (code, msg, data) -> {
  514 + if (code == InviteErrorCode.SUCCESS.getCode()) {
  515 + hookEvent.run(code, msg, data);
  516 + } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) {
  517 + logger.info("[录像下载]超时, 用户:{}, 通道:{}", username, channelId);
  518 + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
  519 + errorEvent.run(code, msg, data);
494 520 } else {
495   - if (result.getMediaServerItem() != null) {
496   - hookEvent.response(result.getMediaServerItem(), result.getResponse());
497   - }
  521 + errorEvent.run(code, msg, data);
498 522 }
499 523 });
500 524 } else {
501 525 sendRtpItem.setPlayType(InviteStreamType.PLAY);
502   - SsrcTransaction playTransaction = sessionManager.getSsrcTransaction(device.getDeviceId(), channelId, "play", null);
503   - if (playTransaction != null) {
504   - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, "rtp", playTransaction.getStream());
505   - if (!streamReady) {
506   - boolean hasRtpServer = mediaServerService.checkRtpServer(mediaServerItem, "rtp", playTransaction.getStream());
507   - if (hasRtpServer) {
508   - logger.info("[上级点播]已经开启rtpServer但是尚未收到流,开启监听流的到来");
509   - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", playTransaction.getStream(), true, "rtsp", mediaServerItem.getId());
510   - zlmHttpHookSubscribe.addSubscribe(hookSubscribe, hookEvent);
511   - }else {
512   - playTransaction = null;
513   - }
514   - }
515   - }
516   - if (playTransaction == null) {
517   - String streamId = null;
518   - if (mediaServerItem.isRtpEnable()) {
519   - streamId = String.format("%s_%s", device.getDeviceId(), channelId);
520   - }
521   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false);
522   - logger.info(JSONObject.toJSONString(ssrcInfo));
523   - sendRtpItem.setStream(ssrcInfo.getStream());
524   - sendRtpItem.setSsrc(ssrc.equals(ssrcDefault) ? ssrcInfo.getSsrc() : ssrc);
525   -
526   - // 写入redis, 超时时回复
527   - redisCatchStorage.updateSendRTPSever(sendRtpItem);
528   - playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
529   - logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, finalChannelId);
530   - redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), finalChannelId, callIdHeader.getCallId(), null);
531   - });
  526 + String streamId = null;
  527 + if (mediaServerItem.isRtpEnable()) {
  528 + streamId = String.format("%s_%s", device.getDeviceId(), channelId);
532 529 } else {
533   - sendRtpItem.setStream(playTransaction.getStream());
534   - // 写入redis, 超时时回复
535   - redisCatchStorage.updateSendRTPSever(sendRtpItem);
536   - JSONObject jsonObject = new JSONObject();
537   - jsonObject.put("app", sendRtpItem.getApp());
538   - jsonObject.put("stream", sendRtpItem.getStream());
539   - hookEvent.response(mediaServerItem, jsonObject);
  530 + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
540 531 }
  532 + sendRtpItem.setStream(streamId);
  533 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
  534 + playService.play(mediaServerItem, device.getDeviceId(), channelId, ((code, msg, data) -> {
  535 + if (code == InviteErrorCode.SUCCESS.getCode()) {
  536 + hookEvent.run(code, msg, data);
  537 + } else if (code == InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode() || code == InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode()) {
  538 + logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId);
  539 + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
  540 + errorEvent.run(code, msg, data);
  541 + } else {
  542 + errorEvent.run(code, msg, data);
  543 + }
  544 + }));
  545 +
541 546 }
542 547 } else if (gbStream != null) {
543   - if(ssrc.equals(ssrcDefault))
544   - {
545   - SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
546   - if(ssrcConfig != null)
547   - {
548   - ssrc = ssrcConfig.getPlaySsrc();
549   - ssrcConfig.releaseSsrc(ssrc);
550   - }
  548 +
  549 + String ssrc;
  550 + if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) {
  551 + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  552 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
  553 + } else {
  554 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
551 555 }
552   - if("push".equals(gbStream.getStreamType())) {
  556 +
  557 + if ("push".equals(gbStream.getStreamType())) {
553 558 if (streamPushItem != null && streamPushItem.isPushIng()) {
554 559 // 推流状态
555 560 pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
556 561 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
557 562 } else {
558 563 // 未推流 拉起
559   - notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  564 + notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
560 565 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
561 566 }
562   - }else if ("proxy".equals(gbStream.getStreamType())){
  567 + } else if ("proxy".equals(gbStream.getStreamType())) {
563 568 if (null != proxyByAppAndStream) {
564   - if(proxyByAppAndStream.isStatus()){
565   - pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
  569 + if (proxyByAppAndStream.isStatus()) {
  570 + pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
566 571 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
567   - }else{
  572 + } else {
568 573 //开启代理拉流
569   - notifyStreamOnline(evt, request,gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive,
  574 + notifyStreamOnline(evt, request, gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive,
570 575 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
571 576 }
572 577 }
... ... @@ -586,42 +591,43 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
586 591 * 安排推流
587 592 */
588 593 private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform,
589   - CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
590   - int port, Boolean tcpActive, boolean mediaTransmissionTCP,
591   - String channelId, String addressStr, String ssrc, String requesterId) {
592   - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
593   - if (streamReady) {
594   - // 自平台内容
595   - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
596   - gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
597   -
598   - if (sendRtpItem == null) {
599   - logger.warn("服务器端口资源不足");
600   - try {
601   - responseAck(request, Response.BUSY_HERE);
602   - } catch (SipException | InvalidArgumentException | ParseException e) {
603   - logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
604   - }
605   - return;
606   - }
607   - if (tcpActive != null) {
608   - sendRtpItem.setTcpActive(tcpActive);
609   - }
610   - sendRtpItem.setPlayType(InviteStreamType.PUSH);
611   - // 写入redis, 超时时回复
612   - sendRtpItem.setStatus(1);
613   - sendRtpItem.setCallId(callIdHeader.getCallId());
614   - sendRtpItem.setFromTag(request.getFromTag());
615   -
616   - SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
617   - if (response != null) {
618   - sendRtpItem.setToTag(response.getToTag());
  594 + CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
  595 + int port, Boolean tcpActive, boolean mediaTransmissionTCP,
  596 + String channelId, String addressStr, String ssrc, String requesterId) {
  597 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
  598 + if (streamReady != null && streamReady) {
  599 + // 自平台内容
  600 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
  601 + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
  602 +
  603 + if (sendRtpItem == null) {
  604 + logger.warn("服务器端口资源不足");
  605 + try {
  606 + responseAck(request, Response.BUSY_HERE);
  607 + } catch (SipException | InvalidArgumentException | ParseException e) {
  608 + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
619 609 }
620   - redisCatchStorage.updateSendRTPSever(sendRtpItem);
  610 + return;
  611 + }
  612 + if (tcpActive != null) {
  613 + sendRtpItem.setTcpActive(tcpActive);
  614 + }
  615 + sendRtpItem.setPlayType(InviteStreamType.PUSH);
  616 + // 写入redis, 超时时回复
  617 + sendRtpItem.setStatus(1);
  618 + sendRtpItem.setCallId(callIdHeader.getCallId());
  619 + sendRtpItem.setFromTag(request.getFromTag());
  620 +
  621 + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
  622 + if (response != null) {
  623 + sendRtpItem.setToTag(response.getToTag());
  624 + }
  625 + redisCatchStorage.updateSendRTPSever(sendRtpItem);
621 626  
622 627 }
623 628  
624 629 }
  630 +
625 631 private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
626 632 CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
627 633 int port, Boolean tcpActive, boolean mediaTransmissionTCP,
... ... @@ -629,7 +635,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
629 635 // 推流
630 636 if (streamPushItem.isSelf()) {
631 637 Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
632   - if (streamReady) {
  638 + if (streamReady != null && streamReady) {
633 639 // 自平台内容
634 640 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
635 641 gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp());
... ... @@ -661,7 +667,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
661 667  
662 668 } else {
663 669 // 不在线 拉起
664   - notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
  670 + notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
665 671 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
666 672 }
667 673  
... ... @@ -671,6 +677,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
671 677 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
672 678 }
673 679 }
  680 +
674 681 /**
675 682 * 通知流上线
676 683 */
... ... @@ -688,7 +695,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
688 695 String stream = responseJSON.getString("stream");
689 696 logger.info("[上级点播]拉流代理已经就绪, {}/{}", app, stream);
690 697 dynamicTask.stop(callIdHeader.getCallId());
691   - pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
  698 + pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive,
692 699 mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
693 700 });
694 701 dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
... ... @@ -707,7 +714,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
707 714 }
708 715  
709 716  
710   -
711 717 } else if ("push".equals(gbStream.getStreamType())) {
712 718 if (!platform.isStartOfflinePush()) {
713 719 // 平台设置中关闭了拉起离线的推流则直接回复
... ... @@ -811,7 +817,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
811 817 // 发送redis消息
812 818 redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(),
813 819 streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId,
814   - channelId, mediaTransmissionTCP, platform.isRtcp(),null, responseSendItemMsg -> {
  820 + channelId, mediaTransmissionTCP, platform.isRtcp(), null, responseSendItemMsg -> {
815 821 SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem();
816 822 if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) {
817 823 logger.warn("服务器端口资源不足");
... ... @@ -836,7 +842,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
836 842 sendRtpItem.setCallId(callIdHeader.getCallId());
837 843  
838 844 sendRtpItem.setFromTag(request.getFromTag());
839   - SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), request,sendRtpItem, platform, evt);
  845 + SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), request, sendRtpItem, platform, evt);
840 846 if (response != null) {
841 847 sendRtpItem.setToTag(response.getToTag());
842 848 }
... ... @@ -877,8 +883,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
877 883 content.append("t=0 0\r\n");
878 884 // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
879 885 int localPort = sendRtpItem.getLocalPort();
880   - if(localPort == 0)
881   - {
  886 + if (localPort == 0) {
882 887 localPort = new Random().nextInt(65535) + 1;
883 888 }
884 889 content.append("m=video " + localPort + " RTP/AVP 96\r\n");
... ... @@ -953,7 +958,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
953 958 }
954 959 if (device != null) {
955 960 logger.info("收到设备" + requesterId + "的语音广播Invite请求");
956   - String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + broadcastCatch.getChannelId();
  961 + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId() + broadcastCatch.getChannelId();
957 962 dynamicTask.stop(key);
958 963 try {
959 964 responseAck(request, Response.TRYING);
... ... @@ -986,7 +991,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
986 991 boolean mediaTransmissionTCP = false;
987 992 Boolean tcpActive = null;
988 993 for (int i = 0; i < mediaDescriptions.size(); i++) {
989   - MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
  994 + MediaDescription mediaDescription = (MediaDescription) mediaDescriptions.get(i);
990 995 Media media = mediaDescription.getMedia();
991 996  
992 997 Vector mediaFormats = media.getMediaFormats(false);
... ... @@ -1022,7 +1027,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1022 1027 }
1023 1028 String addressStr = sdp.getOrigin().getAddress();
1024 1029 logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, ssrc,
1025   - mediaTransmissionTCP ? (tcpActive? "TCP主动":"TCP被动") : "UDP");
  1030 + mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP");
1026 1031  
1027 1032 MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem();
1028 1033 if (mediaServerItem == null) {
... ... @@ -1036,7 +1041,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1036 1041 return;
1037 1042 }
1038 1043 logger.info("设备{}请求语音流, 收流地址:{}:{},ssrc:{}, {}, 对讲方式:{}", requesterId, addressStr, port, ssrc,
1039   - mediaTransmissionTCP ? (tcpActive? "TCP主动":"TCP被动") : "UDP", sdp.getSessionName().getValue());
  1044 + mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP", sdp.getSessionName().getValue());
1040 1045  
1041 1046 SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
1042 1047 device.getDeviceId(), broadcastCatch.getChannelId(),
... ... @@ -1076,7 +1081,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1076 1081 Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream());
1077 1082 if (streamReady) {
1078 1083 sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc);
1079   - }else {
  1084 + } else {
1080 1085 logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream());
1081 1086 try {
1082 1087 responseAck(request, Response.GONE);
... ... @@ -1093,29 +1098,30 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1093 1098 } else {
1094 1099 logger.warn("来自无效设备/平台的请求");
1095 1100 try {
1096   - responseAck(request, Response.BAD_REQUEST);; // 不支持的格式,发415
  1101 + responseAck(request, Response.BAD_REQUEST);
  1102 + ; // 不支持的格式,发415
1097 1103 } catch (SipException | InvalidArgumentException | ParseException e) {
1098 1104 logger.error("[命令发送失败] invite 来自无效设备/平台的请求, {}", e.getMessage());
1099 1105 }
1100 1106 }
1101 1107 }
1102 1108  
1103   - SIPResponse sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServerItem mediaServerItem, boolean mediaTransmissionTCP, String ssrc){
  1109 + SIPResponse sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, SIPRequest request, MediaServerItem mediaServerItem, boolean mediaTransmissionTCP, String ssrc) {
1104 1110 SIPResponse sipResponse = null;
1105 1111 try {
1106 1112 sendRtpItem.setStatus(2);
1107 1113 redisCatchStorage.updateSendRTPSever(sendRtpItem);
1108 1114 StringBuffer content = new StringBuffer(200);
1109 1115 content.append("v=0\r\n");
1110   - content.append("o="+ config.getId() +" "+ sdp.getOrigin().getSessionId() +" " + sdp.getOrigin().getSessionVersion() + " IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  1116 + content.append("o=" + config.getId() + " " + sdp.getOrigin().getSessionId() + " " + sdp.getOrigin().getSessionVersion() + " IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
1111 1117 content.append("s=Play\r\n");
1112   - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  1118 + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
1113 1119 content.append("t=0 0\r\n");
1114 1120  
1115 1121 if (mediaTransmissionTCP) {
1116   - content.append("m=audio "+ sendRtpItem.getLocalPort()+" TCP/RTP/AVP 8\r\n");
1117   - }else {
1118   - content.append("m=audio "+ sendRtpItem.getLocalPort()+" RTP/AVP 8\r\n");
  1122 + content.append("m=audio " + sendRtpItem.getLocalPort() + " TCP/RTP/AVP 8\r\n");
  1123 + } else {
  1124 + content.append("m=audio " + sendRtpItem.getLocalPort() + " RTP/AVP 8\r\n");
1119 1125 }
1120 1126  
1121 1127 content.append("a=rtpmap:8 PCMA/8000/1\r\n");
... ... @@ -1125,11 +1131,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
1125 1131 content.append("a=connection:new\r\n");
1126 1132 if (!sendRtpItem.isTcpActive()) {
1127 1133 content.append("a=setup:active\r\n");
1128   - }else {
  1134 + } else {
1129 1135 content.append("a=setup:passive\r\n");
1130 1136 }
1131 1137 }
1132   - content.append("y="+ ssrc + "\r\n");
  1138 + content.append("y=" + ssrc + "\r\n");
1133 1139 content.append("f=v/////a/1/8/1\r\n");
1134 1140  
1135 1141 ParentPlatform parentPlatform = new ParentPlatform();
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
  2 +
  3 +import com.genersoft.iot.vmp.conf.DynamicTask;
  4 +import com.genersoft.iot.vmp.conf.UserSetting;
  5 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  6 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  7 +import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
  8 +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
  9 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  10 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  11 +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
  12 +import com.genersoft.iot.vmp.service.IDeviceChannelService;
  13 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  14 +import org.dom4j.DocumentException;
  15 +import org.dom4j.Element;
  16 +import org.slf4j.Logger;
  17 +import org.slf4j.LoggerFactory;
  18 +import org.springframework.beans.factory.annotation.Autowired;
  19 +import org.springframework.stereotype.Component;
  20 +
  21 +import javax.sip.RequestEvent;
  22 +import javax.sip.header.FromHeader;
  23 +import java.util.*;
  24 +import java.util.concurrent.ConcurrentHashMap;
  25 +import java.util.concurrent.CopyOnWriteArrayList;
  26 +
  27 +/**
  28 + * SIP命令类型: NOTIFY请求中的目录请求处理
  29 + */
  30 +@Component
  31 +public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent {
  32 +
  33 +
  34 + private final static Logger logger = LoggerFactory.getLogger(NotifyRequestForCatalogProcessor.class);
  35 +
  36 + private final List<DeviceChannel> updateChannelOnlineList = new CopyOnWriteArrayList<>();
  37 + private final List<DeviceChannel> updateChannelOfflineList = new CopyOnWriteArrayList<>();
  38 + private final Map<String, DeviceChannel> updateChannelMap = new ConcurrentHashMap<>();
  39 +
  40 + private final Map<String, DeviceChannel> addChannelMap = new ConcurrentHashMap<>();
  41 + private final List<DeviceChannel> deleteChannelList = new CopyOnWriteArrayList<>();
  42 +
  43 +
  44 + @Autowired
  45 + private UserSetting userSetting;
  46 +
  47 + @Autowired
  48 + private EventPublisher eventPublisher;
  49 +
  50 + @Autowired
  51 + private IRedisCatchStorage redisCatchStorage;
  52 +
  53 + @Autowired
  54 + private IDeviceChannelService deviceChannelService;
  55 +
  56 + @Autowired
  57 + private DynamicTask dynamicTask;
  58 +
  59 + private final static String talkKey = "notify-request-for-catalog-task";
  60 +
  61 + public void process(RequestEvent evt) {
  62 + try {
  63 + long start = System.currentTimeMillis();
  64 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
  65 + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
  66 +
  67 + Device device = redisCatchStorage.getDevice(deviceId);
  68 + if (device == null || device.getOnline() == 0) {
  69 + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" ));
  70 + return;
  71 + }
  72 + Element rootElement = getRootElement(evt, device.getCharset());
  73 + if (rootElement == null) {
  74 + logger.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest());
  75 + return;
  76 + }
  77 + Element deviceListElement = rootElement.element("DeviceList");
  78 + if (deviceListElement == null) {
  79 + return;
  80 + }
  81 + Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
  82 + if (deviceListIterator != null) {
  83 +
  84 + // 遍历DeviceList
  85 + while (deviceListIterator.hasNext()) {
  86 + Element itemDevice = deviceListIterator.next();
  87 + Element channelDeviceElement = itemDevice.element("DeviceID");
  88 + if (channelDeviceElement == null) {
  89 + continue;
  90 + }
  91 + Element eventElement = itemDevice.element("Event");
  92 + String event;
  93 + if (eventElement == null) {
  94 + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
  95 + event = CatalogEvent.ADD;
  96 + }else {
  97 + event = eventElement.getText().toUpperCase();
  98 + }
  99 + DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device, event);
  100 +
  101 + channel.setDeviceId(device.getDeviceId());
  102 + logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId());
  103 + switch (event) {
  104 + case CatalogEvent.ON:
  105 + // 上线
  106 + logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  107 + updateChannelOnlineList.add(channel);
  108 + if (updateChannelOnlineList.size() > 300) {
  109 + executeSaveForOnline();
  110 + }
  111 + if (userSetting.getDeviceStatusNotify()) {
  112 + // 发送redis消息
  113 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), true);
  114 + }
  115 +
  116 + break;
  117 + case CatalogEvent.OFF :
  118 + // 离线
  119 + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  120 + if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
  121 + logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  122 + }else {
  123 + updateChannelOfflineList.add(channel);
  124 + if (updateChannelOfflineList.size() > 300) {
  125 + executeSaveForOffline();
  126 + }
  127 + if (userSetting.getDeviceStatusNotify()) {
  128 + // 发送redis消息
  129 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false);
  130 + }
  131 + }
  132 + break;
  133 + case CatalogEvent.VLOST:
  134 + // 视频丢失
  135 + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  136 + if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
  137 + logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  138 + }else {
  139 + updateChannelOfflineList.add(channel);
  140 + if (updateChannelOfflineList.size() > 300) {
  141 + executeSaveForOffline();
  142 + }
  143 + if (userSetting.getDeviceStatusNotify()) {
  144 + // 发送redis消息
  145 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false);
  146 + }
  147 + }
  148 + break;
  149 + case CatalogEvent.DEFECT:
  150 + // 故障
  151 + logger.info("[收到通道视频故障通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  152 + if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
  153 + logger.info("[收到通道视频故障通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  154 + }else {
  155 + updateChannelOfflineList.add(channel);
  156 + if (updateChannelOfflineList.size() > 300) {
  157 + executeSaveForOffline();
  158 + }
  159 + if (userSetting.getDeviceStatusNotify()) {
  160 + // 发送redis消息
  161 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false);
  162 + }
  163 + }
  164 + break;
  165 + case CatalogEvent.ADD:
  166 + // 增加
  167 + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  168 + // 判断此通道是否存在
  169 + DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId());
  170 + if (deviceChannel != null) {
  171 + channel.setId(deviceChannel.getId());
  172 + updateChannelMap.put(channel.getChannelId(), channel);
  173 + if (updateChannelMap.keySet().size() > 300) {
  174 + executeSaveForUpdate();
  175 + }
  176 + }else {
  177 + addChannelMap.put(channel.getChannelId(), channel);
  178 + if (addChannelMap.keySet().size() > 300) {
  179 + executeSaveForAdd();
  180 + }
  181 + }
  182 +
  183 + break;
  184 + case CatalogEvent.DEL:
  185 + // 删除
  186 + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  187 + deleteChannelList.add(channel);
  188 + if (deleteChannelList.size() > 300) {
  189 + executeSaveForDelete();
  190 + }
  191 + break;
  192 + case CatalogEvent.UPDATE:
  193 + // 更新
  194 + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  195 + // 判断此通道是否存在
  196 + DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId());
  197 + if (deviceChannelForUpdate != null) {
  198 + channel.setId(deviceChannelForUpdate.getId());
  199 + updateChannelMap.put(channel.getChannelId(), channel);
  200 + if (updateChannelMap.keySet().size() > 300) {
  201 + executeSaveForUpdate();
  202 + }
  203 + }else {
  204 + addChannelMap.put(channel.getChannelId(), channel);
  205 + if (addChannelMap.keySet().size() > 300) {
  206 + executeSaveForAdd();
  207 + }
  208 + }
  209 + break;
  210 + default:
  211 + logger.warn("[ NotifyCatalog ] event not found : {}", event );
  212 +
  213 + }
  214 + // 转发变化信息
  215 + eventPublisher.catalogEventPublish(null, channel, event);
  216 +
  217 + if (updateChannelMap.keySet().size() > 0
  218 + || addChannelMap.keySet().size() > 0
  219 + || updateChannelOnlineList.size() > 0
  220 + || updateChannelOfflineList.size() > 0
  221 + || deleteChannelList.size() > 0) {
  222 +
  223 + if (!dynamicTask.contains(talkKey)) {
  224 + dynamicTask.startDelay(talkKey, this::executeSave, 1000);
  225 + }
  226 + }
  227 + }
  228 + }
  229 + } catch (DocumentException e) {
  230 + logger.error("未处理的异常 ", e);
  231 + }
  232 + }
  233 +
  234 + private void executeSave(){
  235 + System.out.println("定时存储数据");
  236 + executeSaveForUpdate();
  237 + executeSaveForDelete();
  238 + executeSaveForOnline();
  239 + executeSaveForOffline();
  240 + dynamicTask.stop(talkKey);
  241 + }
  242 +
  243 + private void executeSaveForUpdate(){
  244 + if (updateChannelMap.values().size() > 0) {
  245 + ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values());
  246 + updateChannelMap.clear();
  247 + deviceChannelService.batchUpdateChannel(deviceChannels);
  248 + }
  249 +
  250 + }
  251 +
  252 + private void executeSaveForAdd(){
  253 + if (addChannelMap.values().size() > 0) {
  254 + ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(addChannelMap.values());
  255 + addChannelMap.clear();
  256 + deviceChannelService.batchAddChannel(deviceChannels);
  257 + }
  258 + }
  259 +
  260 + private void executeSaveForDelete(){
  261 + if (deleteChannelList.size() > 0) {
  262 + deviceChannelService.deleteChannels(deleteChannelList);
  263 + deleteChannelList.clear();
  264 + }
  265 + }
  266 +
  267 + private void executeSaveForOnline(){
  268 + if (updateChannelOnlineList.size() > 0) {
  269 + deviceChannelService.channelsOnline(updateChannelOnlineList);
  270 + updateChannelOnlineList.clear();
  271 + }
  272 + }
  273 +
  274 + private void executeSaveForOffline(){
  275 + if (updateChannelOfflineList.size() > 0) {
  276 + deviceChannelService.channelsOffline(updateChannelOfflineList);
  277 + updateChannelOfflineList.clear();
  278 + }
  279 + }
  280 +
  281 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
... ... @@ -76,12 +76,17 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
76 76 @Autowired
77 77 private IDeviceChannelService deviceChannelService;
78 78  
  79 + @Autowired
  80 + private NotifyRequestForCatalogProcessor notifyRequestForCatalogProcessor;
  81 +
79 82 private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
80 83  
81 84 @Qualifier("taskExecutor")
82 85 @Autowired
83 86 private ThreadPoolTaskExecutor taskExecutor;
84 87  
  88 + private int maxQueueCount = 30000;
  89 +
85 90 @Override
86 91 public void afterPropertiesSet() throws Exception {
87 92 // 添加消息处理的订阅
... ... @@ -91,43 +96,52 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
91 96 @Override
92 97 public void process(RequestEvent evt) {
93 98 try {
94   - responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
  99 +
  100 + if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) {
  101 + responseAck((SIPRequest) evt.getRequest(), Response.BUSY_HERE, null, null);
  102 + logger.error("[notify] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue());
  103 + return;
  104 + }else {
  105 + responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
  106 + }
  107 +
95 108 }catch (SipException | InvalidArgumentException | ParseException e) {
96 109 logger.error("未处理的异常 ", e);
97 110 }
98 111 boolean runed = !taskQueue.isEmpty();
  112 + logger.info("[notify] 待处理消息数量: {}", taskQueue.size());
99 113 taskQueue.offer(new HandlerCatchData(evt, null, null));
100 114 if (!runed) {
101 115 taskExecutor.execute(()-> {
102   - try {
103   - while (!taskQueue.isEmpty()) {
104   - try {
105   - HandlerCatchData take = taskQueue.poll();
106   - Element rootElement = getRootElement(take.getEvt());
107   - if (rootElement == null) {
108   - logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest());
109   - continue;
110   - }
111   - String cmd = XmlUtil.getText(rootElement, "CmdType");
112   -
113   - if (CmdType.CATALOG.equals(cmd)) {
114   - logger.info("接收到Catalog通知");
115   - processNotifyCatalogList(take.getEvt());
116   - } else if (CmdType.ALARM.equals(cmd)) {
117   - logger.info("接收到Alarm通知");
118   - processNotifyAlarm(take.getEvt());
119   - } else if (CmdType.MOBILE_POSITION.equals(cmd)) {
120   - logger.info("接收到MobilePosition通知");
121   - processNotifyMobilePosition(take.getEvt());
122   - } else {
123   - logger.info("接收到消息:" + cmd);
124   - }
125   - } catch (DocumentException e) {
126   - logger.error("处理NOTIFY消息时错误", e);
  116 + while (!taskQueue.isEmpty()) {
  117 + try {
  118 + HandlerCatchData take = taskQueue.poll();
  119 + if (take == null) {
  120 + continue;
  121 + }
  122 + Element rootElement = getRootElement(take.getEvt());
  123 + if (rootElement == null) {
  124 + logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest());
  125 + continue;
  126 + }
  127 + String cmd = XmlUtil.getText(rootElement, "CmdType");
  128 +
  129 + if (CmdType.CATALOG.equals(cmd)) {
  130 + logger.info("接收到Catalog通知");
  131 +// processNotifyCatalogList(take.getEvt());
  132 + notifyRequestForCatalogProcessor.process(take.getEvt());
  133 + } else if (CmdType.ALARM.equals(cmd)) {
  134 + logger.info("接收到Alarm通知");
  135 + processNotifyAlarm(take.getEvt());
  136 + } else if (CmdType.MOBILE_POSITION.equals(cmd)) {
  137 + logger.info("接收到MobilePosition通知");
  138 + processNotifyMobilePosition(take.getEvt());
  139 + } else {
  140 + logger.info("接收到消息:" + cmd);
127 141 }
  142 + } catch (DocumentException e) {
  143 + logger.error("处理NOTIFY消息时错误", e);
128 144 }
129   - }catch (Exception e) {
130   - logger.error("处理NOTIFY消息时错误", e);
131 145 }
132 146 });
133 147 }
... ... @@ -135,7 +149,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
135 149  
136 150 /**
137 151 * 处理MobilePosition移动位置Notify
138   - *
  152 + *
139 153 * @param evt
140 154 */
141 155 private void processNotifyMobilePosition(RequestEvent evt) {
... ... @@ -239,7 +253,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
239 253  
240 254 /***
241 255 * 处理alarm设备报警Notify
242   - *
  256 + *
243 257 * @param evt
244 258 */
245 259 private void processNotifyAlarm(RequestEvent evt) {
... ... @@ -349,7 +363,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
349 363  
350 364 /***
351 365 * 处理catalog设备目录列表Notify
352   - *
  366 + *
353 367 * @param evt
354 368 */
355 369 private void processNotifyCatalogList(RequestEvent evt) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
... ... @@ -83,21 +83,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
83 83 public void process(RequestEvent evt) {
84 84 try {
85 85 RequestEventExt evtExt = (RequestEventExt) evt;
86   - String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
87   -
88   -// MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
89   -// QueryExp protocol = Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"));
90   -//// ObjectName name = new ObjectName("*:type=Connector,*");
91   -// ObjectName name = new ObjectName("*:*");
92   -// Set<ObjectName> objectNames = beanServer.queryNames(name, protocol);
93   -// for (ObjectName objectName : objectNames) {
94   -// String catalina = objectName.getDomain();
95   -// if ("Catalina".equals(catalina)) {
96   -// System.out.println(objectName.getKeyProperty("port"));
97   -// }
98   -// }
99   -
100   -// System.out.println(ServiceInfo.getServerPort());
  86 +
101 87 SIPRequest request = (SIPRequest)evt.getRequest();
102 88 Response response = null;
103 89 boolean passwordCorrect = false;
... ... @@ -107,12 +93,13 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
107 93 AddressImpl address = (AddressImpl) fromHeader.getAddress();
108 94 SipUri uri = (SipUri) address.getURI();
109 95 String deviceId = uri.getUser();
110   - logger.info("[注册请求] 设备:{}, 开始处理: {}", deviceId, requestAddress);
  96 +
111 97 Device device = deviceService.getDevice(deviceId);
112 98  
113 99 RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
114 100 userSetting.getSipUseSourceIpAsRemoteAddress());
115   -
  101 + String requestAddress = remoteAddressInfo.getIp() + ":" + remoteAddressInfo.getPort();
  102 + logger.info("[注册请求] 设备:{}, 开始处理: {}", deviceId, requestAddress);
116 103 if (device != null &&
117 104 device.getSipTransactionInfo() != null &&
118 105 request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info;
2 2  
3   -import com.genersoft.iot.vmp.common.StreamInfo;
  3 +import com.genersoft.iot.vmp.common.InviteInfo;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
4 5 import com.genersoft.iot.vmp.gb28181.bean.*;
5 6 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
6 7 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
... ... @@ -9,6 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
9 10 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
10 11 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
11 12 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  13 +import com.genersoft.iot.vmp.service.IInviteStreamService;
12 14 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
13 15 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
14 16 import gov.nist.javax.sip.message.SIPRequest;
... ... @@ -17,10 +19,12 @@ import org.slf4j.LoggerFactory;
17 19 import org.springframework.beans.factory.InitializingBean;
18 20 import org.springframework.beans.factory.annotation.Autowired;
19 21 import org.springframework.stereotype.Component;
  22 +
20 23 import javax.sip.InvalidArgumentException;
21 24 import javax.sip.RequestEvent;
22 25 import javax.sip.SipException;
23   -import javax.sip.header.*;
  26 +import javax.sip.header.CallIdHeader;
  27 +import javax.sip.header.ContentTypeHeader;
24 28 import javax.sip.message.Response;
25 29 import java.text.ParseException;
26 30  
... ... @@ -44,6 +48,9 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
44 48 private IRedisCatchStorage redisCatchStorage;
45 49  
46 50 @Autowired
  51 + private IInviteStreamService inviteStreamService;
  52 +
  53 + @Autowired
47 54 private IVideoManagerStorage storager;
48 55  
49 56 @Autowired
... ... @@ -103,27 +110,30 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
103 110 if ("Application".equalsIgnoreCase(contentType) && "MANSRTSP".equalsIgnoreCase(contentSubType)) {
104 111 SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
105 112 String streamId = sendRtpItem.getStream();
106   - StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
107   - if (null == streamInfo) {
  113 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId);
  114 + if (null == inviteInfo) {
108 115 responseAck(request, Response.NOT_FOUND, "stream " + streamId + " not found");
109 116 return;
110 117 }
111   - Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID());
112   - cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()),eventResult -> {
113   - // 失败的回复
114   - try {
115   - responseAck(request, eventResult.statusCode, eventResult.msg);
116   - } catch (SipException | InvalidArgumentException | ParseException e) {
117   - logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage());
118   - }
119   - }, eventResult -> {
120   - // 成功的回复
121   - try {
122   - responseAck(request, eventResult.statusCode);
123   - } catch (SipException | InvalidArgumentException | ParseException e) {
124   - logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage());
125   - }
126   - });
  118 + Device device1 = storager.queryVideoDevice(inviteInfo.getDeviceId());
  119 + if (inviteInfo.getStreamInfo() != null) {
  120 + cmder.playbackControlCmd(device1,inviteInfo.getStreamInfo(),new String(evt.getRequest().getRawContent()),eventResult -> {
  121 + // 失败的回复
  122 + try {
  123 + responseAck(request, eventResult.statusCode, eventResult.msg);
  124 + } catch (SipException | InvalidArgumentException | ParseException e) {
  125 + logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage());
  126 + }
  127 + }, eventResult -> {
  128 + // 成功的回复
  129 + try {
  130 + responseAck(request, eventResult.statusCode);
  131 + } catch (SipException | InvalidArgumentException | ParseException e) {
  132 + logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage());
  133 + }
  134 + });
  135 + }
  136 +
127 137 }
128 138 }
129 139 } catch (SipException e) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
... ... @@ -69,6 +69,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
69 69  
70 70 RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
71 71 if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
  72 + logger.info("[心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
72 73 device.setPort(remoteAddressInfo.getPort());
73 74 device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
74 75 device.setIp(remoteAddressInfo.getIp());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
2 2  
3   -import com.genersoft.iot.vmp.common.StreamInfo;
  3 +import com.genersoft.iot.vmp.common.InviteInfo;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
4 5 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
5 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
6 7 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
... ... @@ -12,6 +13,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
12 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
13 14 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
14 15 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
  16 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  17 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  18 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
  19 +import com.genersoft.iot.vmp.service.IInviteStreamService;
15 20 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
16 21 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
17 22 import gov.nist.javax.sip.message.SIPRequest;
... ... @@ -58,6 +63,15 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
58 63 @Autowired
59 64 private VideoStreamSessionManager sessionManager;
60 65  
  66 + @Autowired
  67 + private ZlmHttpHookSubscribe subscribe;
  68 +
  69 + @Autowired
  70 + private IInviteStreamService inviteStreamService;
  71 +
  72 + @Autowired
  73 + private VideoStreamSessionManager streamSession;
  74 +
61 75 @Override
62 76 public void afterPropertiesSet() throws Exception {
63 77 notifyMessageHandler.addHandler(cmdType, this);
... ... @@ -76,23 +90,24 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
76 90 String NotifyType =getText(rootElement, "NotifyType");
77 91 if ("121".equals(NotifyType)){
78 92 logger.info("[录像流]推送完毕,收到关流通知");
79   - // 查询是设备
80   - StreamInfo streamInfo = redisCatchStorage.queryDownload(null, null, null, callIdHeader.getCallId());
81   - if (streamInfo != null) {
82   - // 设置进度100%
83   - streamInfo.setProgress(1);
84   - redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
85   - }
86 93  
87   - // 先从会话内查找
88   - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
89   - if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
  94 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
  95 + if (ssrcTransaction != null) {
  96 + logger.info("[录像流]推送完毕,关流通知, device: {}, channelId: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
  97 + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  98 + if (inviteInfo.getStreamInfo() != null) {
  99 + inviteInfo.getStreamInfo().setProgress(1);
  100 + inviteStreamService.updateInviteInfo(inviteInfo);
  101 + }
90 102  
91 103 try {
92 104 cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), null, callIdHeader.getCallId());
93 105 } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
94 106 logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage());
95 107 }
  108 + // 去除监听流注销自动停止下载的监听
  109 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcTransaction.getStream(), false, "rtsp", ssrcTransaction.getMediaServerId());
  110 + subscribe.removeSubscribe(hookSubscribe);
96 111  
97 112 // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定
98 113 SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null);
... ... @@ -108,6 +123,8 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
108 123 logger.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage());
109 124 }
110 125 }
  126 + }else {
  127 + logger.info("[录像流]推送完毕,关流通知, 但是未找到对应的下载信息");
111 128 }
112 129 }
113 130 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
... ... @@ -18,6 +18,10 @@ import javax.sdp.SessionDescription;
18 18 import javax.sip.InvalidArgumentException;
19 19 import javax.sip.ResponseEvent;
20 20 import javax.sip.SipException;
  21 +import javax.sip.InvalidArgumentException;
  22 +import javax.sip.ResponseEvent;
  23 +import javax.sip.SipException;
  24 +import javax.sip.SipFactory;
21 25 import javax.sip.address.SipURI;
22 26 import javax.sip.message.Request;
23 27 import javax.sip.message.Response;
... ... @@ -91,7 +95,7 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
91 95 }
92 96 // 查看是否是来自设备的,此是回复
93 97  
94   - SipURI requestUri = sipLayer.getSipFactory().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
  98 + SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
95 99 Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);
96 100  
97 101 logger.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
... ... @@ -54,7 +54,7 @@ public class SipUtils {
54 54 return "z9hG4bK" + System.currentTimeMillis();
55 55 }
56 56  
57   - public static UserAgentHeader createUserAgentHeader(SipFactory sipFactory, GitUtil gitUtil) throws PeerUnavailableException, ParseException {
  57 + public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException {
58 58 List<String> agentParam = new ArrayList<>();
59 59 agentParam.add("WVP-Pro ");
60 60 if (gitUtil != null ) {
... ... @@ -66,7 +66,7 @@ public class SipUtils {
66 66 agentParam.add(gitUtil.getCommitTime());
67 67 }
68 68 }
69   - return sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);
  69 + return SipFactory.getInstance().createHeaderFactory().createUserAgentHeader(agentParam);
70 70 }
71 71  
72 72 public static String getNewFromTag(){
... ... @@ -153,8 +153,9 @@ public class SipUtils {
153 153 String remoteAddress;
154 154 int remotePort;
155 155 if (sipUseSourceIpAsRemoteAddress) {
156   - remoteAddress = request.getRemoteAddress().getHostAddress();
157   - remotePort = request.getRemotePort();
  156 + remoteAddress = request.getPeerPacketSourceAddress().getHostAddress();
  157 + remotePort = request.getPeerPacketSourcePort();
  158 +
158 159 }else {
159 160 // 判断RPort是否改变,改变则说明路由nat信息变化,修改设备信息
160 161 // 获取到通信地址等信息
... ... @@ -162,8 +163,8 @@ public class SipUtils {
162 163 remotePort = request.getTopmostViaHeader().getRPort();
163 164 // 解析本地地址替代
164 165 if (ObjectUtils.isEmpty(remoteAddress) || remotePort == -1) {
165   - remoteAddress = request.getTopmostViaHeader().getHost();
166   - remotePort = request.getTopmostViaHeader().getPort();
  166 + remoteAddress = request.getPeerPacketSourceAddress().getHostAddress();
  167 + remotePort = request.getPeerPacketSourcePort();
167 168 }
168 169 }
169 170  
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/annotation/MsgId.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.annotation;
  2 +
  3 +import java.lang.annotation.*;
  4 +
  5 +/**
  6 + * @author QingtaiJiang
  7 + * @date 2023/4/27 18:31
  8 + * @email qingtaij@163.com
  9 + */
  10 +@Target(ElementType.TYPE)
  11 +@Retention(RetentionPolicy.RUNTIME)
  12 +@Documented
  13 +public @interface MsgId {
  14 + String id();
  15 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/cmd/JT1078Template.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.cmd;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd;
  4 +import com.genersoft.iot.vmp.jt1078.proc.response.*;
  5 +import com.genersoft.iot.vmp.jt1078.session.SessionManager;
  6 +
  7 +import java.util.Random;
  8 +
  9 +/**
  10 + * @author QingtaiJiang
  11 + * @date 2023/4/27 18:58
  12 + * @email qingtaij@163.com
  13 + */
  14 +public class JT1078Template {
  15 +
  16 + private final Random random = new Random();
  17 +
  18 + private static final String H9101 = "9101";
  19 + private static final String H9102 = "9102";
  20 + private static final String H9201 = "9201";
  21 + private static final String H9202 = "9202";
  22 + private static final String H9205 = "9205";
  23 +
  24 + private static final String H0001 = "0001";
  25 + private static final String H1205 = "1205";
  26 +
  27 + /**
  28 + * 开启直播视频
  29 + *
  30 + * @param devId 设备号
  31 + * @param j9101 开启视频参数
  32 + */
  33 + public String startLive(String devId, J9101 j9101, Integer timeOut) {
  34 + Cmd cmd = new Cmd.Builder()
  35 + .setDevId(devId)
  36 + .setPackageNo(randomInt())
  37 + .setMsgId(H9101)
  38 + .setRespId(H0001)
  39 + .setRs(j9101)
  40 + .build();
  41 + return SessionManager.INSTANCE.request(cmd, timeOut);
  42 + }
  43 +
  44 + /**
  45 + * 关闭直播视频
  46 + *
  47 + * @param devId 设备号
  48 + * @param j9102 关闭视频参数
  49 + */
  50 + public String stopLive(String devId, J9102 j9102, Integer timeOut) {
  51 + Cmd cmd = new Cmd.Builder()
  52 + .setDevId(devId)
  53 + .setPackageNo(randomInt())
  54 + .setMsgId(H9102)
  55 + .setRespId(H0001)
  56 + .setRs(j9102)
  57 + .build();
  58 + return SessionManager.INSTANCE.request(cmd, timeOut);
  59 + }
  60 +
  61 + /**
  62 + * 查询音视频列表
  63 + *
  64 + * @param devId 设备号
  65 + * @param j9205 查询音视频列表
  66 + */
  67 + public String queryBackTime(String devId, J9205 j9205, Integer timeOut) {
  68 + Cmd cmd = new Cmd.Builder()
  69 + .setDevId(devId)
  70 + .setPackageNo(randomInt())
  71 + .setMsgId(H9205)
  72 + .setRespId(H1205)
  73 + .setRs(j9205)
  74 + .build();
  75 + return SessionManager.INSTANCE.request(cmd, timeOut);
  76 + }
  77 +
  78 + /**
  79 + * 开启视频回放
  80 + *
  81 + * @param devId 设备号
  82 + * @param j9201 视频回放参数
  83 + */
  84 + public String startBackLive(String devId, J9201 j9201, Integer timeOut) {
  85 + Cmd cmd = new Cmd.Builder()
  86 + .setDevId(devId)
  87 + .setPackageNo(randomInt())
  88 + .setMsgId(H9201)
  89 + .setRespId(H1205)
  90 + .setRs(j9201)
  91 + .build();
  92 + return SessionManager.INSTANCE.request(cmd, timeOut);
  93 + }
  94 +
  95 + /**
  96 + * 视频回放控制
  97 + *
  98 + * @param devId 设备号
  99 + * @param j9202 控制视频回放参数
  100 + */
  101 + public String controlBackLive(String devId, J9202 j9202, Integer timeOut) {
  102 + Cmd cmd = new Cmd.Builder()
  103 + .setDevId(devId)
  104 + .setPackageNo(randomInt())
  105 + .setMsgId(H9202)
  106 + .setRespId(H0001)
  107 + .setRs(j9202)
  108 + .build();
  109 + return SessionManager.INSTANCE.request(cmd, timeOut);
  110 + }
  111 +
  112 + private Long randomInt() {
  113 + return (long) random.nextInt(1000) + 1;
  114 + }
  115 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/codec/decode/Jt808Decoder.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.codec.decode;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  4 +import com.genersoft.iot.vmp.jt1078.proc.factory.CodecFactory;
  5 +import com.genersoft.iot.vmp.jt1078.proc.request.Re;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  7 +import com.genersoft.iot.vmp.jt1078.session.Session;
  8 +import io.netty.buffer.ByteBuf;
  9 +import io.netty.buffer.ByteBufUtil;
  10 +import io.netty.buffer.CompositeByteBuf;
  11 +import io.netty.buffer.UnpooledByteBufAllocator;
  12 +import io.netty.channel.ChannelHandlerContext;
  13 +import io.netty.handler.codec.ByteToMessageDecoder;
  14 +import org.slf4j.Logger;
  15 +import org.slf4j.LoggerFactory;
  16 +
  17 +import java.util.ArrayList;
  18 +import java.util.List;
  19 +
  20 +/**
  21 + * @author QingtaiJiang
  22 + * @date 2023/4/27 18:10
  23 + * @email qingtaij@163.com
  24 + */
  25 +public class Jt808Decoder extends ByteToMessageDecoder {
  26 + private final static Logger log = LoggerFactory.getLogger(Jt808Decoder.class);
  27 +
  28 + @Override
  29 + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
  30 + Session session = ctx.channel().attr(Session.KEY).get();
  31 + log.info("> {} hex:{}", session, ByteBufUtil.hexDump(in));
  32 +
  33 + try {
  34 + ByteBuf buf = unEscapeAndCheck(in);
  35 +
  36 + Header header = new Header();
  37 + header.setMsgId(ByteBufUtil.hexDump(buf.readSlice(2)));
  38 + header.setMsgPro(buf.readUnsignedShort());
  39 + if (header.is2019Version()) {
  40 + header.setVersion(buf.readUnsignedByte());
  41 + String devId = ByteBufUtil.hexDump(buf.readSlice(10));
  42 + header.setDevId(devId.replaceFirst("^0*", ""));
  43 + } else {
  44 + header.setDevId(ByteBufUtil.hexDump(buf.readSlice(6)).replaceFirst("^0*", ""));
  45 + }
  46 + header.setSn(buf.readUnsignedShort());
  47 +
  48 + Re handler = CodecFactory.getHandler(header.getMsgId());
  49 + if (handler == null) {
  50 + log.error("get msgId is null {}", header.getMsgId());
  51 + return;
  52 + }
  53 + Rs decode = handler.decode(buf, header, session);
  54 + if (decode != null) {
  55 + out.add(decode);
  56 + }
  57 + } finally {
  58 + in.skipBytes(in.readableBytes());
  59 + }
  60 +
  61 +
  62 + }
  63 +
  64 +
  65 + /**
  66 + * 转义与验证校验码
  67 + *
  68 + * @param byteBuf 转义Buf
  69 + * @return 转义好的数据
  70 + */
  71 + public ByteBuf unEscapeAndCheck(ByteBuf byteBuf) throws Exception {
  72 + int low = byteBuf.readerIndex();
  73 + int high = byteBuf.writerIndex();
  74 + byte checkSum = 0;
  75 + int calculationCheckSum = 0;
  76 +
  77 + byte aByte = byteBuf.getByte(high - 2);
  78 + byte protocolEscapeFlag7d = 0x7d;
  79 + //0x7d转义
  80 + byte protocolEscapeFlag01 = 0x01;
  81 + //0x7e转义
  82 + byte protocolEscapeFlag02 = 0x02;
  83 + if (aByte == protocolEscapeFlag7d) {
  84 + byte b2 = byteBuf.getByte(high - 1);
  85 + if (b2 == protocolEscapeFlag01) {
  86 + checkSum = protocolEscapeFlag7d;
  87 + } else if (b2 == protocolEscapeFlag02) {
  88 + checkSum = 0x7e;
  89 + } else {
  90 + log.error("转义1异常:{}", ByteBufUtil.hexDump(byteBuf));
  91 + throw new Exception("转义错误");
  92 + }
  93 + high = high - 2;
  94 + } else {
  95 + high = high - 1;
  96 + checkSum = byteBuf.getByte(high);
  97 + }
  98 + List<ByteBuf> bufList = new ArrayList<>();
  99 + int index = low;
  100 + while (index < high) {
  101 + byte b = byteBuf.getByte(index);
  102 + if (b == protocolEscapeFlag7d) {
  103 + byte c = byteBuf.getByte(index + 1);
  104 + if (c == protocolEscapeFlag01) {
  105 + ByteBuf slice = slice0x01(byteBuf, low, index);
  106 + bufList.add(slice);
  107 + b = protocolEscapeFlag7d;
  108 + } else if (c == protocolEscapeFlag02) {
  109 + ByteBuf slice = slice0x02(byteBuf, low, index);
  110 + bufList.add(slice);
  111 + b = 0x7e;
  112 + } else {
  113 + log.error("转义2异常:{}", ByteBufUtil.hexDump(byteBuf));
  114 + throw new Exception("转义错误");
  115 + }
  116 + index += 2;
  117 + low = index;
  118 + } else {
  119 + index += 1;
  120 + }
  121 + calculationCheckSum = calculationCheckSum ^ b;
  122 + }
  123 +
  124 + if (calculationCheckSum == checkSum) {
  125 + if (bufList.size() == 0) {
  126 + return byteBuf.slice(low, high);
  127 + } else {
  128 + bufList.add(byteBuf.slice(low, high - low));
  129 + return new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, bufList.size(), bufList);
  130 + }
  131 + } else {
  132 + log.info("{} 解析校验码:{}--计算校验码:{}", ByteBufUtil.hexDump(byteBuf), checkSum, calculationCheckSum);
  133 + throw new Exception("校验码错误!");
  134 + }
  135 + }
  136 +
  137 +
  138 + private ByteBuf slice0x01(ByteBuf buf, int low, int sign) {
  139 + return buf.slice(low, sign - low + 1);
  140 + }
  141 +
  142 + private ByteBuf slice0x02(ByteBuf buf, int low, int sign) {
  143 + buf.setByte(sign, 0x7e);
  144 + return buf.slice(low, sign - low + 1);
  145 + }
  146 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808Encoder.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.codec.encode;
  2 +
  3 +
  4 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  5 +import com.genersoft.iot.vmp.jt1078.session.Session;
  6 +import io.netty.buffer.ByteBuf;
  7 +import io.netty.buffer.ByteBufUtil;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.handler.codec.MessageToByteEncoder;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +
  13 +/**
  14 + * @author QingtaiJiang
  15 + * @date 2023/4/27 18:10
  16 + * @email qingtaij@163.com
  17 + */
  18 +public class Jt808Encoder extends MessageToByteEncoder<Rs> {
  19 + private final static Logger log = LoggerFactory.getLogger(Jt808Encoder.class);
  20 +
  21 + @Override
  22 + protected void encode(ChannelHandlerContext ctx, Rs msg, ByteBuf out) throws Exception {
  23 + Session session = ctx.channel().attr(Session.KEY).get();
  24 +
  25 + ByteBuf encode = Jt808EncoderCmd.encode(msg, session, session.nextSerialNo());
  26 + if(encode!=null){
  27 + log.info("< {} hex:{}", session, ByteBufUtil.hexDump(encode));
  28 + out.writeBytes(encode);
  29 + }
  30 + }
  31 +
  32 +
  33 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/codec/encode/Jt808EncoderCmd.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.codec.encode;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  5 +import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  7 +import com.genersoft.iot.vmp.jt1078.session.Session;
  8 +import com.genersoft.iot.vmp.jt1078.util.Bin;
  9 +import io.netty.buffer.ByteBuf;
  10 +import io.netty.buffer.ByteBufUtil;
  11 +import io.netty.buffer.CompositeByteBuf;
  12 +import io.netty.buffer.Unpooled;
  13 +import io.netty.channel.ChannelHandlerContext;
  14 +import io.netty.handler.codec.MessageToByteEncoder;
  15 +import io.netty.util.ByteProcessor;
  16 +import org.slf4j.Logger;
  17 +import org.slf4j.LoggerFactory;
  18 +import org.springframework.util.StringUtils;
  19 +
  20 +import java.util.LinkedList;
  21 +
  22 +/**
  23 + * @author QingtaiJiang
  24 + * @date 2023/4/27 18:25
  25 + * @email qingtaij@163.com
  26 + */
  27 +public class Jt808EncoderCmd extends MessageToByteEncoder<Cmd> {
  28 + private final static Logger log = LoggerFactory.getLogger(Jt808EncoderCmd.class);
  29 +
  30 + @Override
  31 + protected void encode(ChannelHandlerContext ctx, Cmd cmd, ByteBuf out) throws Exception {
  32 + Session session = ctx.channel().attr(Session.KEY).get();
  33 + Rs msg = cmd.getRs();
  34 + ByteBuf encode = encode(msg, session, cmd.getPackageNo().intValue());
  35 + if (encode != null) {
  36 + log.info("< {} hex:{}", session, ByteBufUtil.hexDump(encode));
  37 + out.writeBytes(encode);
  38 + }
  39 + }
  40 +
  41 +
  42 + public static ByteBuf encode(Rs msg, Session session, Integer packageNo) {
  43 + String id = msg.getClass().getAnnotation(MsgId.class).id();
  44 + if (!StringUtils.hasLength(id)) {
  45 + log.error("Not find msgId");
  46 + return null;
  47 + }
  48 +
  49 + ByteBuf byteBuf = Unpooled.buffer();
  50 +
  51 + byteBuf.writeBytes(ByteBufUtil.decodeHexDump(id));
  52 +
  53 + ByteBuf encode = msg.encode();
  54 +
  55 + Header header = msg.getHeader();
  56 + if (header == null) {
  57 + header = session.getHeader();
  58 + }
  59 +
  60 + if (header.is2019Version()) {
  61 + // 消息体属性
  62 + byteBuf.writeShort(encode.readableBytes() | 1 << 14);
  63 +
  64 + // 版本号
  65 + byteBuf.writeByte(header.getVersion());
  66 +
  67 + // 终端手机号
  68 + byteBuf.writeBytes(ByteBufUtil.decodeHexDump(Bin.strHexPaddingLeft(header.getDevId(), 20)));
  69 + } else {
  70 + // 消息体属性
  71 + byteBuf.writeShort(encode.readableBytes());
  72 +
  73 + byteBuf.writeBytes(ByteBufUtil.decodeHexDump(Bin.strHexPaddingLeft(header.getDevId(), 12)));
  74 + }
  75 +
  76 + // 消息体流水号
  77 + byteBuf.writeShort(packageNo);
  78 +
  79 + // 写入消息体
  80 + byteBuf.writeBytes(encode);
  81 +
  82 + // 计算校验码,并反转义
  83 + byteBuf = escapeAndCheck0(byteBuf);
  84 + return byteBuf;
  85 + }
  86 +
  87 +
  88 + private static final ByteProcessor searcher = value -> !(value == 0x7d || value == 0x7e);
  89 +
  90 + //转义与校验
  91 + public static ByteBuf escapeAndCheck0(ByteBuf source) {
  92 +
  93 + sign(source);
  94 +
  95 + int low = source.readerIndex();
  96 + int high = source.writerIndex();
  97 +
  98 + LinkedList<ByteBuf> bufList = new LinkedList<>();
  99 + int mark, len;
  100 + while ((mark = source.forEachByte(low, high - low, searcher)) > 0) {
  101 +
  102 + len = mark + 1 - low;
  103 + ByteBuf[] slice = slice(source, low, len);
  104 + bufList.add(slice[0]);
  105 + bufList.add(slice[1]);
  106 + low += len;
  107 + }
  108 +
  109 + if (bufList.size() > 0) {
  110 + bufList.add(source.slice(low, high - low));
  111 + } else {
  112 + bufList.add(source);
  113 + }
  114 +
  115 + ByteBuf delimiter = Unpooled.buffer(1, 1).writeByte(0x7e).retain();
  116 + bufList.addFirst(delimiter);
  117 + bufList.addLast(delimiter);
  118 +
  119 + CompositeByteBuf byteBufLs = Unpooled.compositeBuffer(bufList.size());
  120 + byteBufLs.addComponents(true, bufList);
  121 + return byteBufLs;
  122 + }
  123 +
  124 + public static void sign(ByteBuf buf) {
  125 + byte checkCode = bcc(buf);
  126 + buf.writeByte(checkCode);
  127 + }
  128 +
  129 + public static byte bcc(ByteBuf byteBuf) {
  130 + byte cs = 0;
  131 + while (byteBuf.isReadable())
  132 + cs ^= byteBuf.readByte();
  133 + byteBuf.resetReaderIndex();
  134 + return cs;
  135 + }
  136 +
  137 + protected static ByteBuf[] slice(ByteBuf byteBuf, int index, int length) {
  138 + byte first = byteBuf.getByte(index + length - 1);
  139 +
  140 + ByteBuf[] byteBufList = new ByteBuf[2];
  141 + byteBufList[0] = byteBuf.retainedSlice(index, length);
  142 +
  143 + if (first == 0x7d) {
  144 + byteBufList[1] = Unpooled.buffer(1, 1).writeByte(0x01);
  145 + } else {
  146 + byteBuf.setByte(index + length - 1, 0x7d);
  147 + byteBufList[1] = Unpooled.buffer(1, 1).writeByte(0x02);
  148 + }
  149 + return byteBufList;
  150 + }
  151 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/Jt808Handler.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.codec.netty;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  4 +import com.genersoft.iot.vmp.jt1078.session.Session;
  5 +import com.genersoft.iot.vmp.jt1078.session.SessionManager;
  6 +import io.netty.channel.Channel;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.channel.ChannelInboundHandlerAdapter;
  9 +import io.netty.handler.timeout.IdleState;
  10 +import io.netty.handler.timeout.IdleStateEvent;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +
  14 +/**
  15 + * @author QingtaiJiang
  16 + * @date 2023/4/27 18:14
  17 + * @email qingtaij@163.com
  18 + */
  19 +public class Jt808Handler extends ChannelInboundHandlerAdapter {
  20 +
  21 + private final static Logger log = LoggerFactory.getLogger(Jt808Handler.class);
  22 +
  23 + @Override
  24 + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  25 + if (msg instanceof Rs) {
  26 + ctx.writeAndFlush(msg);
  27 + } else {
  28 + ctx.fireChannelRead(msg);
  29 + }
  30 + }
  31 +
  32 + @Override
  33 + public void channelActive(ChannelHandlerContext ctx) {
  34 + Channel channel = ctx.channel();
  35 + Session session = SessionManager.INSTANCE.newSession(channel);
  36 + channel.attr(Session.KEY).set(session);
  37 + log.info("> Tcp connect {}", session);
  38 + }
  39 +
  40 + @Override
  41 + public void channelInactive(ChannelHandlerContext ctx) {
  42 + Session session = ctx.channel().attr(Session.KEY).get();
  43 + log.info("< Tcp disconnect {}", session);
  44 + ctx.close();
  45 + }
  46 +
  47 + @Override
  48 + public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
  49 + Session session = ctx.channel().attr(Session.KEY).get();
  50 + String message = e.getMessage();
  51 + if (message.toLowerCase().contains("Connection reset by peer".toLowerCase())) {
  52 + log.info("< exception{} {}", session, e.getMessage());
  53 + } else {
  54 + log.info("< exception{} {}", session, e.getMessage(), e);
  55 + }
  56 +
  57 + }
  58 +
  59 + @Override
  60 + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
  61 + if (evt instanceof IdleStateEvent) {
  62 + IdleStateEvent event = (IdleStateEvent) evt;
  63 + IdleState state = event.state();
  64 + if (state == IdleState.READER_IDLE || state == IdleState.WRITER_IDLE) {
  65 + Session session = ctx.channel().attr(Session.KEY).get();
  66 + log.warn("< Proactively disconnect{}", session);
  67 + ctx.close();
  68 + }
  69 + }
  70 + }
  71 +
  72 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/codec/netty/TcpServer.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.codec.netty;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.codec.decode.Jt808Decoder;
  4 +import com.genersoft.iot.vmp.jt1078.codec.encode.Jt808Encoder;
  5 +import com.genersoft.iot.vmp.jt1078.codec.encode.Jt808EncoderCmd;
  6 +import com.genersoft.iot.vmp.jt1078.proc.factory.CodecFactory;
  7 +import io.netty.bootstrap.ServerBootstrap;
  8 +import io.netty.buffer.ByteBuf;
  9 +import io.netty.buffer.Unpooled;
  10 +import io.netty.channel.ChannelFuture;
  11 +import io.netty.channel.ChannelInitializer;
  12 +import io.netty.channel.EventLoopGroup;
  13 +import io.netty.channel.nio.NioEventLoopGroup;
  14 +import io.netty.channel.socket.nio.NioChannelOption;
  15 +import io.netty.channel.socket.nio.NioServerSocketChannel;
  16 +import io.netty.channel.socket.nio.NioSocketChannel;
  17 +import io.netty.handler.codec.DelimiterBasedFrameDecoder;
  18 +import io.netty.handler.timeout.IdleStateHandler;
  19 +import io.netty.util.concurrent.Future;
  20 +import org.slf4j.Logger;
  21 +import org.slf4j.LoggerFactory;
  22 +
  23 +import java.util.concurrent.TimeUnit;
  24 +
  25 +/**
  26 + * @author QingtaiJiang
  27 + * @date 2023/4/27 18:01
  28 + * @email qingtaij@163.com
  29 + */
  30 +
  31 +public class TcpServer {
  32 + private final static Logger log = LoggerFactory.getLogger(TcpServer.class);
  33 +
  34 + private final Integer port;
  35 + private boolean isRunning = false;
  36 + private EventLoopGroup bossGroup = null;
  37 + private EventLoopGroup workerGroup = null;
  38 +
  39 + private final ByteBuf DECODER_JT808 = Unpooled.wrappedBuffer(new byte[]{0x7e});
  40 +
  41 + public TcpServer(Integer port) {
  42 + this.port = port;
  43 + }
  44 +
  45 + private void startTcpServer() {
  46 + try {
  47 + CodecFactory.init();
  48 + this.bossGroup = new NioEventLoopGroup();
  49 + this.workerGroup = new NioEventLoopGroup();
  50 + ServerBootstrap bootstrap = new ServerBootstrap();
  51 + bootstrap.channel(NioServerSocketChannel.class);
  52 + bootstrap.group(bossGroup, workerGroup);
  53 +
  54 + bootstrap.option(NioChannelOption.SO_BACKLOG, 1024)
  55 + .option(NioChannelOption.SO_REUSEADDR, true)
  56 + .childOption(NioChannelOption.TCP_NODELAY, true)
  57 + .childHandler(new ChannelInitializer<NioSocketChannel>() {
  58 + @Override
  59 + public void initChannel(NioSocketChannel channel) {
  60 + channel.pipeline()
  61 + .addLast(new IdleStateHandler(10, 0, 0, TimeUnit.MINUTES))
  62 + .addLast(new DelimiterBasedFrameDecoder(1024 * 2, DECODER_JT808))
  63 + .addLast(new Jt808Decoder())
  64 + .addLast(new Jt808Encoder())
  65 + .addLast(new Jt808EncoderCmd())
  66 + .addLast(new Jt808Handler());
  67 + }
  68 + });
  69 + ChannelFuture channelFuture = bootstrap.bind(port).sync();
  70 + // 监听设备TCP端口是否启动成功
  71 + channelFuture.addListener(future -> {
  72 + if (!future.isSuccess()) {
  73 + log.error("Binding port:{} fail! cause: {}", port, future.cause().getCause(), future.cause());
  74 + }
  75 + });
  76 + log.info("服务:JT808 Server 启动成功, port:{}", port);
  77 + channelFuture.channel().closeFuture().sync();
  78 + } catch (Exception e) {
  79 + log.warn("服务:JT808 Server 启动异常, port:{},{}", port, e.getMessage(), e);
  80 + } finally {
  81 + stop();
  82 + }
  83 + }
  84 +
  85 + /**
  86 + * 开启一个新的线程,拉起来Netty
  87 + */
  88 + public synchronized void start() {
  89 + if (this.isRunning) {
  90 + log.warn("服务:JT808 Server 已经启动, port:{}", port);
  91 + return;
  92 + }
  93 + this.isRunning = true;
  94 + new Thread(this::startTcpServer).start();
  95 + }
  96 +
  97 + public synchronized void stop() {
  98 + if (!this.isRunning) {
  99 + log.warn("服务:JT808 Server 已经停止, port:{}", port);
  100 + }
  101 + this.isRunning = false;
  102 + Future<?> future = this.bossGroup.shutdownGracefully();
  103 + if (!future.isSuccess()) {
  104 + log.warn("bossGroup 无法正常停止", future.cause());
  105 + }
  106 + future = this.workerGroup.shutdownGracefully();
  107 + if (!future.isSuccess()) {
  108 + log.warn("workerGroup 无法正常停止", future.cause());
  109 + }
  110 + log.warn("服务:JT808 Server 已经停止, port:{}", port);
  111 + }
  112 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078AutoConfiguration.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.config;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template;
  4 +import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer;
  5 +import org.springframework.beans.factory.annotation.Value;
  6 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  7 +import org.springframework.context.annotation.Bean;
  8 +import org.springframework.context.annotation.Configuration;
  9 +import org.springframework.core.annotation.Order;
  10 +
  11 +/**
  12 + * @author QingtaiJiang
  13 + * @date 2023/4/27 19:35
  14 + * @email qingtaij@163.com
  15 + */
  16 +@Order(Integer.MIN_VALUE)
  17 +@Configuration
  18 +@ConditionalOnProperty(value = "jt1078.enable", havingValue = "true")
  19 +public class JT1078AutoConfiguration {
  20 +
  21 + @Bean(initMethod = "start", destroyMethod = "stop")
  22 + public TcpServer jt1078Server(@Value("${jt1078.port}") Integer port) {
  23 + return new TcpServer(port);
  24 + }
  25 +
  26 + @Bean
  27 + public JT1078Template jt1078Template() {
  28 + return new JT1078Template();
  29 + }
  30 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078Controller.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.config;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template;
  4 +import com.genersoft.iot.vmp.jt1078.proc.response.*;
  5 +import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
  6 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  7 +import org.springframework.web.bind.annotation.GetMapping;
  8 +import org.springframework.web.bind.annotation.PathVariable;
  9 +import org.springframework.web.bind.annotation.RequestMapping;
  10 +import org.springframework.web.bind.annotation.RestController;
  11 +
  12 +import javax.annotation.Resource;
  13 +
  14 +/**
  15 + * curl http://localhost:18080/api/jt1078/start/live/18864197066/1
  16 + *
  17 + * @author QingtaiJiang
  18 + * @date 2023/4/27 18:12
  19 + * @email qingtaij@163.com
  20 + */
  21 +@ConditionalOnProperty(value = "jt1078.enable", havingValue = "true")
  22 +@RestController
  23 +@RequestMapping("/api/jt1078")
  24 +public class JT1078Controller {
  25 +
  26 + @Resource
  27 + JT1078Template jt1078Template;
  28 +
  29 + /**
  30 + * jt1078Template 调用示例
  31 + */
  32 + @GetMapping("/start/live/{deviceId}/{channelId}")
  33 + public WVPResult<?> startLive(@PathVariable String deviceId, @PathVariable String channelId) {
  34 + J9101 j9101 = new J9101();
  35 + j9101.setChannel(Integer.valueOf(channelId));
  36 + j9101.setIp("192.168.1.1");
  37 + j9101.setRate(1);
  38 + j9101.setTcpPort(7618);
  39 + j9101.setUdpPort(7618);
  40 + j9101.setType(0);
  41 + // TODO 分配ZLM,获取IP、端口
  42 + String s = jt1078Template.startLive(deviceId, j9101, 6);
  43 + // TODO 设备响应成功后,封装拉流结果集
  44 + WVPResult<String> wvpResult = new WVPResult<>();
  45 + wvpResult.setCode(200);
  46 + wvpResult.setData(String.format("http://192.168.1.1/rtp/%s_%s.live.mp4", deviceId, channelId));
  47 + return wvpResult;
  48 + }
  49 +
  50 +}
  51 +
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/Header.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.util.Bin;
  4 +
  5 +/**
  6 + * @author QingtaiJiang
  7 + * @date 2023/4/27 18:22
  8 + * @email qingtaij@163.com
  9 + */
  10 +public class Header {
  11 + // 消息ID
  12 + String msgId;
  13 +
  14 + // 消息体属性
  15 + Integer msgPro;
  16 +
  17 + // 标识
  18 + String devId;
  19 +
  20 + // 消息体流水号
  21 + Integer sn;
  22 +
  23 + // 协议版本号
  24 + Short version = -1;
  25 +
  26 +
  27 + public String getMsgId() {
  28 + return msgId;
  29 + }
  30 +
  31 + public void setMsgId(String msgId) {
  32 + this.msgId = msgId;
  33 + }
  34 +
  35 + public Integer getMsgPro() {
  36 + return msgPro;
  37 + }
  38 +
  39 + public void setMsgPro(Integer msgPro) {
  40 + this.msgPro = msgPro;
  41 + }
  42 +
  43 + public String getDevId() {
  44 + return devId;
  45 + }
  46 +
  47 + public void setDevId(String devId) {
  48 + this.devId = devId;
  49 + }
  50 +
  51 + public Integer getSn() {
  52 + return sn;
  53 + }
  54 +
  55 + public void setSn(Integer sn) {
  56 + this.sn = sn;
  57 + }
  58 +
  59 + public Short getVersion() {
  60 + return version;
  61 + }
  62 +
  63 + public void setVersion(Short version) {
  64 + this.version = version;
  65 + }
  66 +
  67 + /**
  68 + * 判断是否是2019的版本
  69 + *
  70 + * @return true 2019后的版本。false 2013
  71 + */
  72 + public boolean is2019Version() {
  73 + return Bin.get(msgPro, 14);
  74 + }
  75 +
  76 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/entity/Cmd.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.entity;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  4 +
  5 +/**
  6 + * @author QingtaiJiang
  7 + * @date 2023/4/27 18:23
  8 + * @email qingtaij@163.com
  9 + */
  10 +public class Cmd {
  11 + String devId;
  12 + Long packageNo;
  13 + String msgId;
  14 + String respId;
  15 + Rs rs;
  16 +
  17 + public Cmd() {
  18 + }
  19 +
  20 + public Cmd(Builder builder) {
  21 + this.devId = builder.devId;
  22 + this.packageNo = builder.packageNo;
  23 + this.msgId = builder.msgId;
  24 + this.respId = builder.respId;
  25 + this.rs = builder.rs;
  26 + }
  27 +
  28 + public String getDevId() {
  29 + return devId;
  30 + }
  31 +
  32 + public void setDevId(String devId) {
  33 + this.devId = devId;
  34 + }
  35 +
  36 + public Long getPackageNo() {
  37 + return packageNo;
  38 + }
  39 +
  40 + public void setPackageNo(Long packageNo) {
  41 + this.packageNo = packageNo;
  42 + }
  43 +
  44 + public String getMsgId() {
  45 + return msgId;
  46 + }
  47 +
  48 + public void setMsgId(String msgId) {
  49 + this.msgId = msgId;
  50 + }
  51 +
  52 + public String getRespId() {
  53 + return respId;
  54 + }
  55 +
  56 + public void setRespId(String respId) {
  57 + this.respId = respId;
  58 + }
  59 +
  60 + public Rs getRs() {
  61 + return rs;
  62 + }
  63 +
  64 + public void setRs(Rs rs) {
  65 + this.rs = rs;
  66 + }
  67 +
  68 + public static class Builder {
  69 + String devId;
  70 + Long packageNo;
  71 + String msgId;
  72 + String respId;
  73 + Rs rs;
  74 +
  75 + public Builder setDevId(String devId) {
  76 + this.devId = devId.replaceFirst("^0*", "");
  77 + return this;
  78 + }
  79 +
  80 + public Builder setPackageNo(Long packageNo) {
  81 + this.packageNo = packageNo;
  82 + return this;
  83 + }
  84 +
  85 + public Builder setMsgId(String msgId) {
  86 + this.msgId = msgId;
  87 + return this;
  88 + }
  89 +
  90 + public Builder setRespId(String respId) {
  91 + this.respId = respId;
  92 + return this;
  93 + }
  94 +
  95 + public Builder setRs(Rs re) {
  96 + this.rs = re;
  97 + return this;
  98 + }
  99 +
  100 + public Cmd build() {
  101 + return new Cmd(this);
  102 + }
  103 + }
  104 +
  105 +
  106 + @Override
  107 + public String toString() {
  108 + return "Cmd{" +
  109 + "devId='" + devId + '\'' +
  110 + ", packageNo=" + packageNo +
  111 + ", msgId='" + msgId + '\'' +
  112 + ", respId='" + respId + '\'' +
  113 + ", rs=" + rs +
  114 + '}';
  115 + }
  116 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/factory/CodecFactory.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.factory;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import com.genersoft.iot.vmp.jt1078.proc.request.Re;
  5 +import com.genersoft.iot.vmp.jt1078.util.ClassUtil;
  6 +import org.slf4j.Logger;
  7 +import org.slf4j.LoggerFactory;
  8 +
  9 +import java.util.HashMap;
  10 +import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +/**
  14 + * @author QingtaiJiang
  15 + * @date 2023/4/27 18:29
  16 + * @email qingtaij@163.com
  17 + */
  18 +
  19 +public class CodecFactory {
  20 + private final static Logger log = LoggerFactory.getLogger(CodecFactory.class);
  21 +
  22 + private static Map<String, Class<?>> protocolHash;
  23 +
  24 + public static void init() {
  25 + protocolHash = new HashMap<>();
  26 + List<Class<?>> classList = ClassUtil.getClassList("com.genersoft.iot.vmp.jt1078.proc", MsgId.class);
  27 + for (Class<?> handlerClass : classList) {
  28 + String id = handlerClass.getAnnotation(MsgId.class).id();
  29 + protocolHash.put(id, handlerClass);
  30 + }
  31 + if (log.isDebugEnabled()) {
  32 + log.debug("消息ID缓存表 protocolHash:{}", protocolHash);
  33 + }
  34 + }
  35 +
  36 + public static Re getHandler(String msgId) {
  37 + Class<?> aClass = protocolHash.get(msgId);
  38 + Object bean = ClassUtil.getBean(aClass);
  39 + if (bean instanceof Re) {
  40 + return (Re) bean;
  41 + }
  42 + return null;
  43 + }
  44 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0001.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  5 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  7 +import com.genersoft.iot.vmp.jt1078.session.Session;
  8 +import com.genersoft.iot.vmp.jt1078.session.SessionManager;
  9 +import io.netty.buffer.ByteBuf;
  10 +import io.netty.buffer.ByteBufUtil;
  11 +
  12 +/**
  13 + * 终端通用应答
  14 + *
  15 + * @author QingtaiJiang
  16 + * @date 2023/4/27 18:04
  17 + * @email qingtaij@163.com
  18 + */
  19 +@MsgId(id = "0001")
  20 +public class J0001 extends Re {
  21 + int respNo;
  22 + String respId;
  23 + int result;
  24 +
  25 + @Override
  26 + protected Rs decode0(ByteBuf buf, Header header, Session session) {
  27 + respNo = buf.readUnsignedShort();
  28 + respId = ByteBufUtil.hexDump(buf.readSlice(2));
  29 + result = buf.readUnsignedByte();
  30 + return null;
  31 + }
  32 +
  33 + @Override
  34 + protected Rs handler(Header header, Session session) {
  35 + SessionManager.INSTANCE.response(header.getDevId(), "0001", (long) respNo, JSON.toJSONString(this));
  36 + return null;
  37 + }
  38 +
  39 + public int getRespNo() {
  40 + return respNo;
  41 + }
  42 +
  43 + public String getRespId() {
  44 + return respId;
  45 + }
  46 +
  47 + public int getResult() {
  48 + return result;
  49 + }
  50 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0002.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  5 +import com.genersoft.iot.vmp.jt1078.proc.response.J8001;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  7 +import com.genersoft.iot.vmp.jt1078.session.Session;
  8 +import io.netty.buffer.ByteBuf;
  9 +
  10 +/**
  11 + * 终端心跳
  12 + *
  13 + * @author QingtaiJiang
  14 + * @date 2023/4/27 18:04
  15 + * @email qingtaij@163.com
  16 + */
  17 +@MsgId(id = "0002")
  18 +public class J0002 extends Re {
  19 + @Override
  20 + protected Rs decode0(ByteBuf buf, Header header, Session session) {
  21 + return null;
  22 + }
  23 +
  24 + @Override
  25 + protected Rs handler(Header header, Session session) {
  26 + J8001 j8001 = new J8001();
  27 + j8001.setRespNo(header.getSn());
  28 + j8001.setRespId(header.getMsgId());
  29 + j8001.setResult(J8001.SUCCESS);
  30 + return j8001;
  31 + }
  32 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0004.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  5 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  6 +import com.genersoft.iot.vmp.jt1078.session.Session;
  7 +import io.netty.buffer.ByteBuf;
  8 +
  9 +/**
  10 + * 查询服务器时间
  11 + *
  12 + * @author QingtaiJiang
  13 + * @date 2023/4/27 18:06
  14 + * @email qingtaij@163.com
  15 + */
  16 +@MsgId(id = "0004")
  17 +public class J0004 extends Re {
  18 + @Override
  19 + protected Rs decode0(ByteBuf buf, Header header, Session session) {
  20 + return null;
  21 + }
  22 +
  23 + @Override
  24 + protected Rs handler(Header header, Session session) {
  25 + return null;
  26 + }
  27 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0100.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  5 +import com.genersoft.iot.vmp.jt1078.proc.response.J8100;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  7 +import com.genersoft.iot.vmp.jt1078.session.Session;
  8 +import io.netty.buffer.ByteBuf;
  9 +
  10 +/**
  11 + * 终端注册
  12 + *
  13 + * @author QingtaiJiang
  14 + * @date 2023/4/27 18:06
  15 + * @email qingtaij@163.com
  16 + */
  17 +@MsgId(id = "0100")
  18 +public class J0100 extends Re {
  19 +
  20 + private int provinceId;
  21 +
  22 + private int cityId;
  23 +
  24 + private String makerId;
  25 +
  26 + private String deviceModel;
  27 +
  28 + private String deviceId;
  29 +
  30 + private int plateColor;
  31 +
  32 + private String plateNo;
  33 +
  34 + @Override
  35 + protected Rs decode0(ByteBuf buf, Header header, Session session) {
  36 + Short version = header.getVersion();
  37 + provinceId = buf.readUnsignedShort();
  38 + if (version > 1) {
  39 + cityId = buf.readUnsignedShort();
  40 + // decode as 2019
  41 + } else {
  42 + int i = buf.readUnsignedShort();
  43 + // decode as 2013
  44 + }
  45 + return null;
  46 + }
  47 +
  48 + @Override
  49 + protected Rs handler(Header header, Session session) {
  50 + J8100 j8100 = new J8100();
  51 + j8100.setRespNo(header.getSn());
  52 + j8100.setResult(J8100.SUCCESS);
  53 + j8100.setCode("WVP_YYDS");
  54 + return j8100;
  55 + }
  56 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0102.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  5 +import com.genersoft.iot.vmp.jt1078.proc.response.J8001;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  7 +import com.genersoft.iot.vmp.jt1078.session.Session;
  8 +import io.netty.buffer.ByteBuf;
  9 +
  10 +/**
  11 + * 终端鉴权
  12 + *
  13 + * @author QingtaiJiang
  14 + * @date 2023/4/27 18:06
  15 + * @email qingtaij@163.com
  16 + */
  17 +@MsgId(id = "0102")
  18 +public class J0102 extends Re {
  19 + @Override
  20 + protected Rs decode0(ByteBuf buf, Header header, Session session) {
  21 + int lenCode = buf.readUnsignedByte();
  22 +// String code = buf.readCharSequence(lenCode, CharsetUtil.UTF_8).toString();
  23 + // if 2019 to decode next
  24 + return null;
  25 + }
  26 +
  27 + @Override
  28 + protected Rs handler(Header header, Session session) {
  29 + J8001 j8001 = new J8001();
  30 + j8001.setRespNo(header.getSn());
  31 + j8001.setRespId(header.getMsgId());
  32 + j8001.setResult(J8001.SUCCESS);
  33 + return j8001;
  34 + }
  35 +
  36 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J0200.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  5 +import com.genersoft.iot.vmp.jt1078.proc.response.J8001;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  7 +import com.genersoft.iot.vmp.jt1078.session.Session;
  8 +import io.netty.buffer.ByteBuf;
  9 +
  10 +/**
  11 + * 实时消息上报
  12 + *
  13 + * @author QingtaiJiang
  14 + * @date 2023/4/27 18:06
  15 + * @email qingtaij@163.com
  16 + */
  17 +@MsgId(id = "0200")
  18 +public class J0200 extends Re {
  19 + @Override
  20 + protected Rs decode0(ByteBuf buf, Header header, Session session) {
  21 + return null;
  22 + }
  23 +
  24 + @Override
  25 + protected Rs handler(Header header, Session session) {
  26 + J8001 j8001 = new J8001();
  27 + j8001.setRespNo(header.getSn());
  28 + j8001.setRespId(header.getMsgId());
  29 + j8001.setResult(J8001.SUCCESS);
  30 + return j8001;
  31 + }
  32 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/J1205.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  5 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.J8001;
  7 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  8 +import com.genersoft.iot.vmp.jt1078.session.Session;
  9 +import com.genersoft.iot.vmp.jt1078.session.SessionManager;
  10 +import io.netty.buffer.ByteBuf;
  11 +import io.netty.buffer.ByteBufUtil;
  12 +
  13 +import java.util.ArrayList;
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * 终端上传音视频资源列表
  18 + *
  19 + * @author QingtaiJiang
  20 + * @date 2023/4/28 10:36
  21 + * @email qingtaij@163.com
  22 + */
  23 +@MsgId(id = "1205")
  24 +public class J1205 extends Re {
  25 + Integer respNo;
  26 +
  27 + private List<JRecordItem> recordList = new ArrayList<JRecordItem>();
  28 +
  29 + @Override
  30 + protected Rs decode0(ByteBuf buf, Header header, Session session) {
  31 + respNo = buf.readUnsignedShort();
  32 + long size = buf.readUnsignedInt();
  33 +
  34 + for (int i = 0; i < size; i++) {
  35 + JRecordItem item = new JRecordItem();
  36 + item.setChannelId(buf.readUnsignedByte());
  37 + item.setStartTime(ByteBufUtil.hexDump(buf.readSlice(6)));
  38 + item.setEndTime(ByteBufUtil.hexDump(buf.readSlice(6)));
  39 + item.setWarn(buf.readLong());
  40 + item.setMediaType(buf.readUnsignedByte());
  41 + item.setStreamType(buf.readUnsignedByte());
  42 + item.setStorageType(buf.readUnsignedByte());
  43 + item.setSize(buf.readUnsignedInt());
  44 + recordList.add(item);
  45 + }
  46 +
  47 + return null;
  48 + }
  49 +
  50 + @Override
  51 + protected Rs handler(Header header, Session session) {
  52 + SessionManager.INSTANCE.response(header.getDevId(), "1205", (long) respNo, JSON.toJSONString(this));
  53 +
  54 + J8001 j8001 = new J8001();
  55 + j8001.setRespNo(header.getSn());
  56 + j8001.setRespId(header.getMsgId());
  57 + j8001.setResult(J8001.SUCCESS);
  58 + return j8001;
  59 + }
  60 +
  61 +
  62 + public Integer getRespNo() {
  63 + return respNo;
  64 + }
  65 +
  66 + public void setRespNo(Integer respNo) {
  67 + this.respNo = respNo;
  68 + }
  69 +
  70 + public List<JRecordItem> getRecordList() {
  71 + return recordList;
  72 + }
  73 +
  74 + public void setRecordList(List<JRecordItem> recordList) {
  75 + this.recordList = recordList;
  76 + }
  77 +
  78 + public static class JRecordItem {
  79 +
  80 + // 逻辑通道号
  81 + private int channelId;
  82 +
  83 + // 开始时间
  84 + private String startTime;
  85 +
  86 + // 结束时间
  87 + private String endTime;
  88 +
  89 + // 报警标志
  90 + private long warn;
  91 +
  92 + // 音视频资源类型
  93 + private int mediaType;
  94 +
  95 + // 码流类型
  96 + private int streamType = 1;
  97 +
  98 + // 存储器类型
  99 + private int storageType;
  100 +
  101 + // 文件大小
  102 + private long size;
  103 +
  104 + public int getChannelId() {
  105 + return channelId;
  106 + }
  107 +
  108 + public void setChannelId(int channelId) {
  109 + this.channelId = channelId;
  110 + }
  111 +
  112 + public String getStartTime() {
  113 + return startTime;
  114 + }
  115 +
  116 + public void setStartTime(String startTime) {
  117 + this.startTime = startTime;
  118 + }
  119 +
  120 + public String getEndTime() {
  121 + return endTime;
  122 + }
  123 +
  124 + public void setEndTime(String endTime) {
  125 + this.endTime = endTime;
  126 + }
  127 +
  128 + public long getWarn() {
  129 + return warn;
  130 + }
  131 +
  132 + public void setWarn(long warn) {
  133 + this.warn = warn;
  134 + }
  135 +
  136 + public int getMediaType() {
  137 + return mediaType;
  138 + }
  139 +
  140 + public void setMediaType(int mediaType) {
  141 + this.mediaType = mediaType;
  142 + }
  143 +
  144 + public int getStreamType() {
  145 + return streamType;
  146 + }
  147 +
  148 + public void setStreamType(int streamType) {
  149 + this.streamType = streamType;
  150 + }
  151 +
  152 + public int getStorageType() {
  153 + return storageType;
  154 + }
  155 +
  156 + public void setStorageType(int storageType) {
  157 + this.storageType = storageType;
  158 + }
  159 +
  160 + public long getSize() {
  161 + return size;
  162 + }
  163 +
  164 + public void setSize(long size) {
  165 + this.size = size;
  166 + }
  167 +
  168 + @Override
  169 + public String toString() {
  170 + return "JRecordItem{" +
  171 + "channelId=" + channelId +
  172 + ", startTime='" + startTime + '\'' +
  173 + ", endTime='" + endTime + '\'' +
  174 + ", warn=" + warn +
  175 + ", mediaType=" + mediaType +
  176 + ", streamType=" + streamType +
  177 + ", storageType=" + storageType +
  178 + ", size=" + size +
  179 + '}';
  180 + }
  181 + }
  182 +
  183 + @Override
  184 + public String toString() {
  185 + return "J1205{" +
  186 + "respNo=" + respNo +
  187 + ", recordList=" + recordList +
  188 + '}';
  189 + }
  190 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/request/Re.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.request;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  4 +import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
  5 +import com.genersoft.iot.vmp.jt1078.session.Session;
  6 +import io.netty.buffer.ByteBuf;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +import org.springframework.util.StringUtils;
  10 +
  11 +/**
  12 + * @author QingtaiJiang
  13 + * @date 2023/4/27 18:50
  14 + * @email qingtaij@163.com
  15 + */
  16 +public abstract class Re {
  17 + private final static Logger log = LoggerFactory.getLogger(Re.class);
  18 +
  19 + protected abstract Rs decode0(ByteBuf buf, Header header, Session session);
  20 +
  21 + protected abstract Rs handler(Header header, Session session);
  22 +
  23 + public Rs decode(ByteBuf buf, Header header, Session session) {
  24 + if (session != null && !StringUtils.hasLength(session.getDevId())) {
  25 + session.register(header.getDevId(), (int) header.getVersion(), header);
  26 + }
  27 + Rs rs = decode0(buf, header, session);
  28 + Rs rsHand = handler(header, session);
  29 + if (rs == null && rsHand != null) {
  30 + rs = rsHand;
  31 + } else if (rs != null && rsHand != null) {
  32 + log.warn("decode0:{} 与 handler:{} 返回值冲突,采用decode0返回值", rs, rsHand);
  33 + }
  34 + if (rs != null) {
  35 + rs.setHeader(header);
  36 + }
  37 +
  38 + return rs;
  39 + }
  40 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8001.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.ByteBufUtil;
  6 +import io.netty.buffer.Unpooled;
  7 +
  8 +/**
  9 + * @author QingtaiJiang
  10 + * @date 2023/4/27 18:48
  11 + * @email qingtaij@163.com
  12 + */
  13 +@MsgId(id = "8001")
  14 +public class J8001 extends Rs {
  15 + public static final Integer SUCCESS = 0;
  16 +
  17 + Integer respNo;
  18 + String respId;
  19 + Integer result;
  20 +
  21 + @Override
  22 + public ByteBuf encode() {
  23 + ByteBuf buffer = Unpooled.buffer();
  24 + buffer.writeShort(respNo);
  25 + buffer.writeBytes(ByteBufUtil.decodeHexDump(respId));
  26 + buffer.writeByte(result);
  27 +
  28 + return buffer;
  29 + }
  30 +
  31 +
  32 + public void setRespNo(Integer respNo) {
  33 + this.respNo = respNo;
  34 + }
  35 +
  36 + public void setRespId(String respId) {
  37 + this.respId = respId;
  38 + }
  39 +
  40 + public void setResult(Integer result) {
  41 + this.result = result;
  42 + }
  43 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J8100.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.Unpooled;
  6 +import io.netty.util.CharsetUtil;
  7 +
  8 +/**
  9 + * @author QingtaiJiang
  10 + * @date 2023/4/27 18:40
  11 + * @email qingtaij@163.com
  12 + */
  13 +@MsgId(id = "8100")
  14 +public class J8100 extends Rs {
  15 + public static final Integer SUCCESS = 0;
  16 +
  17 + Integer respNo;
  18 + Integer result;
  19 + String code;
  20 +
  21 + @Override
  22 + public ByteBuf encode() {
  23 + ByteBuf buffer = Unpooled.buffer();
  24 + buffer.writeShort(respNo);
  25 + buffer.writeByte(result);
  26 + buffer.writeCharSequence(code, CharsetUtil.UTF_8);
  27 + return buffer;
  28 + }
  29 +
  30 + public void setRespNo(Integer respNo) {
  31 + this.respNo = respNo;
  32 + }
  33 +
  34 + public void setResult(Integer result) {
  35 + this.result = result;
  36 + }
  37 +
  38 + public void setCode(String code) {
  39 + this.code = code;
  40 + }
  41 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9101.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.Unpooled;
  6 +import io.netty.util.CharsetUtil;
  7 +
  8 +/**
  9 + * 实时音视频传输请求
  10 + *
  11 + * @author QingtaiJiang
  12 + * @date 2023/4/27 18:25
  13 + * @email qingtaij@163.com
  14 + */
  15 +@MsgId(id = "9101")
  16 +public class J9101 extends Rs {
  17 + String ip;
  18 +
  19 + // TCP端口
  20 + Integer tcpPort;
  21 +
  22 + // UDP端口
  23 + Integer udpPort;
  24 +
  25 + // 逻辑通道号
  26 + Integer channel;
  27 +
  28 + // 数据类型
  29 + /**
  30 + * 0:音视频,1:视频,2:双向对讲,3:监听,4:中心广播,5:透传
  31 + */
  32 + Integer type;
  33 +
  34 + // 码流类型
  35 + /**
  36 + * 0:主码流,1:子码流
  37 + */
  38 + Integer rate;
  39 +
  40 + @Override
  41 + public ByteBuf encode() {
  42 + ByteBuf buffer = Unpooled.buffer();
  43 + buffer.writeByte(ip.getBytes().length);
  44 + buffer.writeCharSequence(ip, CharsetUtil.UTF_8);
  45 + buffer.writeShort(tcpPort);
  46 + buffer.writeShort(udpPort);
  47 + buffer.writeByte(channel);
  48 + buffer.writeByte(type);
  49 + buffer.writeByte(rate);
  50 + return buffer;
  51 + }
  52 +
  53 + public String getIp() {
  54 + return ip;
  55 + }
  56 +
  57 + public void setIp(String ip) {
  58 + this.ip = ip;
  59 + }
  60 +
  61 + public Integer getTcpPort() {
  62 + return tcpPort;
  63 + }
  64 +
  65 + public void setTcpPort(Integer tcpPort) {
  66 + this.tcpPort = tcpPort;
  67 + }
  68 +
  69 + public Integer getUdpPort() {
  70 + return udpPort;
  71 + }
  72 +
  73 + public void setUdpPort(Integer udpPort) {
  74 + this.udpPort = udpPort;
  75 + }
  76 +
  77 + public Integer getChannel() {
  78 + return channel;
  79 + }
  80 +
  81 + public void setChannel(Integer channel) {
  82 + this.channel = channel;
  83 + }
  84 +
  85 + public Integer getType() {
  86 + return type;
  87 + }
  88 +
  89 + public void setType(Integer type) {
  90 + this.type = type;
  91 + }
  92 +
  93 + public Integer getRate() {
  94 + return rate;
  95 + }
  96 +
  97 + public void setRate(Integer rate) {
  98 + this.rate = rate;
  99 + }
  100 +
  101 + @Override
  102 + public String toString() {
  103 + return "J9101{" +
  104 + "ip='" + ip + '\'' +
  105 + ", tcpPort=" + tcpPort +
  106 + ", udpPort=" + udpPort +
  107 + ", channel=" + channel +
  108 + ", type=" + type +
  109 + ", rate=" + rate +
  110 + '}';
  111 + }
  112 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9102.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.Unpooled;
  6 +
  7 +/**
  8 + * 音视频实时传输控制
  9 + *
  10 + * @author QingtaiJiang
  11 + * @date 2023/4/27 18:49
  12 + * @email qingtaij@163.com
  13 + */
  14 +@MsgId(id = "9102")
  15 +public class J9102 extends Rs {
  16 +
  17 + // 通道号
  18 + Integer channel;
  19 +
  20 + // 控制指令
  21 + /**
  22 + * 0:关闭音视频传输指令;
  23 + * 1:切换码流(增加暂停和继续);
  24 + * 2:暂停该通道所有流的发送;
  25 + * 3:恢复暂停前流的发送,与暂停前的流类型一致;
  26 + * 4:关闭双向对讲
  27 + */
  28 + Integer command;
  29 +
  30 + // 数据类型
  31 + /**
  32 + * 0:关闭该通道有关的音视频数据;
  33 + * 1:只关闭该通道有关的音频,保留该通道
  34 + * 有关的视频;
  35 + * 2:只关闭该通道有关的视频,保留该通道
  36 + * 有关的音频
  37 + */
  38 + Integer closeType;
  39 +
  40 + // 数据类型
  41 + /**
  42 + * 0:主码流;
  43 + * 1:子码流
  44 + */
  45 + Integer streamType;
  46 +
  47 + @Override
  48 + public ByteBuf encode() {
  49 + ByteBuf buffer = Unpooled.buffer();
  50 + buffer.writeByte(channel);
  51 + buffer.writeByte(command);
  52 + buffer.writeByte(closeType);
  53 + buffer.writeByte(streamType);
  54 + return buffer;
  55 + }
  56 +
  57 +
  58 + public Integer getChannel() {
  59 + return channel;
  60 + }
  61 +
  62 + public void setChannel(Integer channel) {
  63 + this.channel = channel;
  64 + }
  65 +
  66 + public Integer getCommand() {
  67 + return command;
  68 + }
  69 +
  70 + public void setCommand(Integer command) {
  71 + this.command = command;
  72 + }
  73 +
  74 + public Integer getCloseType() {
  75 + return closeType;
  76 + }
  77 +
  78 + public void setCloseType(Integer closeType) {
  79 + this.closeType = closeType;
  80 + }
  81 +
  82 + public Integer getStreamType() {
  83 + return streamType;
  84 + }
  85 +
  86 + public void setStreamType(Integer streamType) {
  87 + this.streamType = streamType;
  88 + }
  89 +
  90 + @Override
  91 + public String toString() {
  92 + return "J9102{" +
  93 + "channel=" + channel +
  94 + ", command=" + command +
  95 + ", closeType=" + closeType +
  96 + ", streamType=" + streamType +
  97 + '}';
  98 + }
  99 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9201.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.ByteBufUtil;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.util.CharsetUtil;
  8 +
  9 +/**
  10 + * 回放请求
  11 + *
  12 + * @author QingtaiJiang
  13 + * @date 2023/4/28 10:37
  14 + * @email qingtaij@163.com
  15 + */
  16 +@MsgId(id = "9201")
  17 +public class J9201 extends Rs {
  18 + // 服务器IP地址
  19 + private String ip;
  20 +
  21 + // 实时视频服务器TCP端口号
  22 + private int tcpPort;
  23 +
  24 + // 实时视频服务器UDP端口号
  25 + private int udpPort;
  26 +
  27 + // 逻辑通道号
  28 + private int channel;
  29 +
  30 + // 音视频资源类型:0.音视频 1.音频 2.视频 3.视频或音视频
  31 + private int type;
  32 +
  33 + // 码流类型:0.所有码流 1.主码流 2.子码流(如果此通道只传输音频,此字段置0)
  34 + private int rate;
  35 +
  36 + // 存储器类型:0.所有存储器 1.主存储器 2.灾备存储器"
  37 + private int storageType;
  38 +
  39 + // 回放方式:0.正常回放 1.快进回放 2.关键帧快退回放 3.关键帧播放 4.单帧上传
  40 + private int playbackType;
  41 +
  42 + // 快进或快退倍数:0.无效 1.1倍 2.2倍 3.4倍 4.8倍 5.16倍 (回放控制为1和2时,此字段内容有效,否则置0)
  43 + private int playbackSpeed;
  44 +
  45 + // 开始时间YYMMDDHHMMSS,回放方式为4时,该字段表示单帧上传时间
  46 + private String startTime;
  47 +
  48 + // 结束时间YYMMDDHHMMSS,回放方式为4时,该字段无效,为0表示一直回放
  49 + private String endTime;
  50 +
  51 + @Override
  52 + public ByteBuf encode() {
  53 + ByteBuf buffer = Unpooled.buffer();
  54 + buffer.writeByte(ip.getBytes().length);
  55 + buffer.writeCharSequence(ip, CharsetUtil.UTF_8);
  56 + buffer.writeShort(tcpPort);
  57 + buffer.writeShort(udpPort);
  58 + buffer.writeByte(channel);
  59 + buffer.writeByte(type);
  60 + buffer.writeByte(rate);
  61 + buffer.writeByte(storageType);
  62 + buffer.writeByte(playbackType);
  63 + buffer.writeByte(playbackSpeed);
  64 + buffer.writeBytes(ByteBufUtil.decodeHexDump(startTime));
  65 + buffer.writeBytes(ByteBufUtil.decodeHexDump(endTime));
  66 + return buffer;
  67 + }
  68 +
  69 + public String getIp() {
  70 + return ip;
  71 + }
  72 +
  73 + public void setIp(String ip) {
  74 + this.ip = ip;
  75 + }
  76 +
  77 + public int getTcpPort() {
  78 + return tcpPort;
  79 + }
  80 +
  81 + public void setTcpPort(int tcpPort) {
  82 + this.tcpPort = tcpPort;
  83 + }
  84 +
  85 + public int getUdpPort() {
  86 + return udpPort;
  87 + }
  88 +
  89 + public void setUdpPort(int udpPort) {
  90 + this.udpPort = udpPort;
  91 + }
  92 +
  93 + public int getChannel() {
  94 + return channel;
  95 + }
  96 +
  97 + public void setChannel(int channel) {
  98 + this.channel = channel;
  99 + }
  100 +
  101 + public int getType() {
  102 + return type;
  103 + }
  104 +
  105 + public void setType(int type) {
  106 + this.type = type;
  107 + }
  108 +
  109 + public int getRate() {
  110 + return rate;
  111 + }
  112 +
  113 + public void setRate(int rate) {
  114 + this.rate = rate;
  115 + }
  116 +
  117 + public int getStorageType() {
  118 + return storageType;
  119 + }
  120 +
  121 + public void setStorageType(int storageType) {
  122 + this.storageType = storageType;
  123 + }
  124 +
  125 + public int getPlaybackType() {
  126 + return playbackType;
  127 + }
  128 +
  129 + public void setPlaybackType(int playbackType) {
  130 + this.playbackType = playbackType;
  131 + }
  132 +
  133 + public int getPlaybackSpeed() {
  134 + return playbackSpeed;
  135 + }
  136 +
  137 + public void setPlaybackSpeed(int playbackSpeed) {
  138 + this.playbackSpeed = playbackSpeed;
  139 + }
  140 +
  141 + public String getStartTime() {
  142 + return startTime;
  143 + }
  144 +
  145 + public void setStartTime(String startTime) {
  146 + this.startTime = startTime;
  147 + }
  148 +
  149 + public String getEndTime() {
  150 + return endTime;
  151 + }
  152 +
  153 + public void setEndTime(String endTime) {
  154 + this.endTime = endTime;
  155 + }
  156 +
  157 + @Override
  158 + public String toString() {
  159 + return "J9201{" +
  160 + "ip='" + ip + '\'' +
  161 + ", tcpPort=" + tcpPort +
  162 + ", udpPort=" + udpPort +
  163 + ", channel=" + channel +
  164 + ", type=" + type +
  165 + ", rate=" + rate +
  166 + ", storageType=" + storageType +
  167 + ", playbackType=" + playbackType +
  168 + ", playbackSpeed=" + playbackSpeed +
  169 + ", startTime='" + startTime + '\'' +
  170 + ", endTime='" + endTime + '\'' +
  171 + '}';
  172 + }
  173 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9202.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.ByteBufUtil;
  6 +import io.netty.buffer.Unpooled;
  7 +
  8 +/**
  9 + * 平台下发远程录像回放控制
  10 + *
  11 + * @author QingtaiJiang
  12 + * @date 2023/4/28 10:37
  13 + * @email qingtaij@163.com
  14 + */
  15 +@MsgId(id = "9202")
  16 +public class J9202 extends Rs {
  17 + // 逻辑通道号
  18 + private int channel;
  19 +
  20 + // 回放控制:0.开始回放 1.暂停回放 2.结束回放 3.快进回放 4.关键帧快退回放 5.拖动回放 6.关键帧播放
  21 + private int playbackType;
  22 +
  23 + // 快进或快退倍数:0.无效 1.1倍 2.2倍 3.4倍 4.8倍 5.16倍 (回放控制为3和4时,此字段内容有效,否则置0)
  24 + private int playbackSpeed;
  25 +
  26 + // 拖动回放位置(YYMMDDHHMMSS,回放控制为5时,此字段有效)
  27 + private String playbackTime;
  28 +
  29 + @Override
  30 + public ByteBuf encode() {
  31 + ByteBuf buffer = Unpooled.buffer();
  32 + buffer.writeByte(channel);
  33 + buffer.writeByte(playbackType);
  34 + buffer.writeByte(playbackSpeed);
  35 + buffer.writeBytes(ByteBufUtil.decodeHexDump(playbackTime));
  36 + return buffer;
  37 + }
  38 +
  39 + public int getChannel() {
  40 + return channel;
  41 + }
  42 +
  43 + public void setChannel(int channel) {
  44 + this.channel = channel;
  45 + }
  46 +
  47 + public int getPlaybackType() {
  48 + return playbackType;
  49 + }
  50 +
  51 + public void setPlaybackType(int playbackType) {
  52 + this.playbackType = playbackType;
  53 + }
  54 +
  55 + public int getPlaybackSpeed() {
  56 + return playbackSpeed;
  57 + }
  58 +
  59 + public void setPlaybackSpeed(int playbackSpeed) {
  60 + this.playbackSpeed = playbackSpeed;
  61 + }
  62 +
  63 + public String getPlaybackTime() {
  64 + return playbackTime;
  65 + }
  66 +
  67 + public void setPlaybackTime(String playbackTime) {
  68 + this.playbackTime = playbackTime;
  69 + }
  70 +
  71 + @Override
  72 + public String toString() {
  73 + return "J9202{" +
  74 + "channel=" + channel +
  75 + ", playbackType=" + playbackType +
  76 + ", playbackSpeed=" + playbackSpeed +
  77 + ", playbackTime='" + playbackTime + '\'' +
  78 + '}';
  79 + }
  80 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/J9205.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
  4 +import io.netty.buffer.ByteBuf;
  5 +import io.netty.buffer.ByteBufUtil;
  6 +import io.netty.buffer.Unpooled;
  7 +
  8 +/**
  9 + * 查询资源列表
  10 + *
  11 + * @author QingtaiJiang
  12 + * @date 2023/4/28 10:36
  13 + * @email qingtaij@163.com
  14 + */
  15 +@MsgId(id = "9205")
  16 +public class J9205 extends Rs {
  17 + // 逻辑通道号
  18 + private int channelId;
  19 +
  20 + // 开始时间YYMMDDHHMMSS,全0表示无起始时间
  21 + private String startTime;
  22 +
  23 + // 结束时间YYMMDDHHMMSS,全0表示无终止时间
  24 + private String endTime;
  25 +
  26 + // 报警标志
  27 + private final int warnType = 0;
  28 +
  29 + // 音视频资源类型:0.音视频 1.音频 2.视频 3.视频或音视频
  30 + private int mediaType;
  31 +
  32 + // 码流类型:0.所有码流 1.主码流 2.子码流
  33 + private int streamType = 0;
  34 +
  35 + // 存储器类型:0.所有存储器 1.主存储器 2.灾备存储器
  36 + private int storageType = 0;
  37 +
  38 + @Override
  39 + public ByteBuf encode() {
  40 + ByteBuf buffer = Unpooled.buffer();
  41 +
  42 + buffer.writeByte(channelId);
  43 + buffer.writeBytes(ByteBufUtil.decodeHexDump(startTime));
  44 + buffer.writeBytes(ByteBufUtil.decodeHexDump(endTime));
  45 + buffer.writeLong(warnType);
  46 + buffer.writeByte(mediaType);
  47 + buffer.writeByte(streamType);
  48 + buffer.writeByte(storageType);
  49 +
  50 + return buffer;
  51 + }
  52 +
  53 +
  54 + public void setChannelId(int channelId) {
  55 + this.channelId = channelId;
  56 + }
  57 +
  58 + public void setStartTime(String startTime) {
  59 + this.startTime = startTime;
  60 + }
  61 +
  62 + public void setEndTime(String endTime) {
  63 + this.endTime = endTime;
  64 + }
  65 +
  66 + public void setMediaType(int mediaType) {
  67 + this.mediaType = mediaType;
  68 + }
  69 +
  70 + public void setStreamType(int streamType) {
  71 + this.streamType = streamType;
  72 + }
  73 +
  74 + public void setStorageType(int storageType) {
  75 + this.storageType = storageType;
  76 + }
  77 +
  78 + public int getWarnType() {
  79 + return warnType;
  80 + }
  81 +
  82 + @Override
  83 + public String toString() {
  84 + return "J9205{" +
  85 + "channelId=" + channelId +
  86 + ", startTime='" + startTime + '\'' +
  87 + ", endTime='" + endTime + '\'' +
  88 + ", warnType=" + warnType +
  89 + ", mediaType=" + mediaType +
  90 + ", streamType=" + streamType +
  91 + ", storageType=" + storageType +
  92 + '}';
  93 + }
  94 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/proc/response/Rs.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.proc.response;
  2 +
  3 +
  4 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  5 +import io.netty.buffer.ByteBuf;
  6 +
  7 +
  8 +/**
  9 + * @author QingtaiJiang
  10 + * @date 2021/8/30 18:54
  11 + * @email qingtaij@163.com
  12 + */
  13 +
  14 +public abstract class Rs {
  15 + private Header header;
  16 +
  17 + public abstract ByteBuf encode();
  18 +
  19 +
  20 + public Header getHeader() {
  21 + return header;
  22 + }
  23 +
  24 + public void setHeader(Header header) {
  25 + this.header = header;
  26 + }
  27 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/session/Session.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.session;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.proc.Header;
  4 +import io.netty.channel.Channel;
  5 +import io.netty.util.AttributeKey;
  6 +import org.slf4j.Logger;
  7 +import org.slf4j.LoggerFactory;
  8 +
  9 +import java.util.concurrent.atomic.AtomicInteger;
  10 +
  11 +/**
  12 + * @author QingtaiJiang
  13 + * @date 2023/4/27 18:54
  14 + * @email qingtaij@163.com
  15 + */
  16 +public class Session {
  17 + private final static Logger log = LoggerFactory.getLogger(Session.class);
  18 +
  19 + public static final AttributeKey<Session> KEY = AttributeKey.newInstance(Session.class.getName());
  20 +
  21 + // Netty的channel
  22 + protected final Channel channel;
  23 +
  24 + // 原子类的自增ID
  25 + private final AtomicInteger serialNo = new AtomicInteger(0);
  26 +
  27 + // 是否注册成功
  28 + private boolean registered = false;
  29 +
  30 + // 设备ID
  31 + private String devId;
  32 +
  33 + // 创建时间
  34 + private final long creationTime;
  35 +
  36 + // 协议版本号
  37 + private Integer protocolVersion;
  38 +
  39 + private Header header;
  40 +
  41 + protected Session(Channel channel) {
  42 + this.channel = channel;
  43 + this.creationTime = System.currentTimeMillis();
  44 + }
  45 +
  46 + public void writeObject(Object message) {
  47 + log.info("<<<<<<<<<< cmd{},{}", this, message);
  48 + channel.writeAndFlush(message);
  49 + }
  50 +
  51 + /**
  52 + * 获得下一个流水号
  53 + *
  54 + * @return 流水号
  55 + */
  56 + public int nextSerialNo() {
  57 + int current;
  58 + int next;
  59 + do {
  60 + current = serialNo.get();
  61 + next = current > 0xffff ? 0 : current;
  62 + } while (!serialNo.compareAndSet(current, next + 1));
  63 + return next;
  64 + }
  65 +
  66 + /**
  67 + * 注册session
  68 + *
  69 + * @param devId 设备ID
  70 + */
  71 + public void register(String devId, Integer version, Header header) {
  72 + this.devId = devId;
  73 + this.registered = true;
  74 + this.protocolVersion = version;
  75 + this.header = header;
  76 + SessionManager.INSTANCE.put(devId, this);
  77 + }
  78 +
  79 + /**
  80 + * 获取设备号
  81 + *
  82 + * @return 设备号
  83 + */
  84 + public String getDevId() {
  85 + return devId;
  86 + }
  87 +
  88 +
  89 + public boolean isRegistered() {
  90 + return registered;
  91 + }
  92 +
  93 + public long getCreationTime() {
  94 + return creationTime;
  95 + }
  96 +
  97 + public Integer getProtocolVersion() {
  98 + return protocolVersion;
  99 + }
  100 +
  101 + public Header getHeader() {
  102 + return header;
  103 + }
  104 +
  105 + @Override
  106 + public String toString() {
  107 + return "[" +
  108 + "devId=" + devId +
  109 + ", reg=" + registered +
  110 + ", version=" + protocolVersion +
  111 + ",ip=" + channel.remoteAddress() +
  112 + ']';
  113 + }
  114 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/session/SessionManager.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.session;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd;
  4 +import io.netty.channel.Channel;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +
  8 +import java.util.Map;
  9 +import java.util.concurrent.ConcurrentHashMap;
  10 +import java.util.concurrent.SynchronousQueue;
  11 +import java.util.concurrent.TimeUnit;
  12 +
  13 +
  14 +/**
  15 + * @author QingtaiJiang
  16 + * @date 2023/4/27 19:54
  17 + * @email qingtaij@163.com
  18 + */
  19 +public enum SessionManager {
  20 + INSTANCE;
  21 + private final static Logger log = LoggerFactory.getLogger(SessionManager.class);
  22 +
  23 + // 用与消息的缓存
  24 + private final Map<String, SynchronousQueue<String>> topicSubscribers = new ConcurrentHashMap<>();
  25 +
  26 + // session的缓存
  27 + private final Map<Object, Session> sessionMap;
  28 +
  29 + SessionManager() {
  30 + this.sessionMap = new ConcurrentHashMap<>();
  31 + }
  32 +
  33 + /**
  34 + * 创建新的Session
  35 + *
  36 + * @param channel netty通道
  37 + * @return 创建的session对象
  38 + */
  39 + public Session newSession(Channel channel) {
  40 + return new Session(channel);
  41 + }
  42 +
  43 +
  44 + /**
  45 + * 获取指定设备的Session
  46 + *
  47 + * @param clientId 设备Id
  48 + * @return Session
  49 + */
  50 + public Session get(Object clientId) {
  51 + return sessionMap.get(clientId);
  52 + }
  53 +
  54 + /**
  55 + * 放入新设备连接的session
  56 + *
  57 + * @param clientId 设备ID
  58 + * @param newSession session
  59 + */
  60 + protected void put(Object clientId, Session newSession) {
  61 + sessionMap.put(clientId, newSession);
  62 + }
  63 +
  64 +
  65 + /**
  66 + * 发送同步消息,接收响应
  67 + * 默认超时时间6秒
  68 + */
  69 + public String request(Cmd cmd) {
  70 + // 默认6秒
  71 + int timeOut = 6000;
  72 + return request(cmd, timeOut);
  73 + }
  74 +
  75 + public String request(Cmd cmd, Integer timeOut) {
  76 + Session session = this.get(cmd.getDevId());
  77 + if (session == null) {
  78 + log.error("DevId: {} not online!", cmd.getDevId());
  79 + return null;
  80 + }
  81 + String requestKey = requestKey(cmd.getDevId(), cmd.getRespId(), cmd.getPackageNo());
  82 + SynchronousQueue<String> subscribe = subscribe(requestKey);
  83 + if (subscribe == null) {
  84 + log.error("DevId: {} key:{} send repaid", cmd.getDevId(), requestKey);
  85 + return null;
  86 + }
  87 + session.writeObject(cmd);
  88 + try {
  89 + return subscribe.poll(timeOut, TimeUnit.SECONDS);
  90 + } catch (InterruptedException e) {
  91 + log.warn("<<<<<<<<<< timeout" + session, e);
  92 + } finally {
  93 + this.unsubscribe(requestKey);
  94 + }
  95 + return null;
  96 + }
  97 +
  98 + public Boolean response(String devId, String respId, Long responseNo, String data) {
  99 + String requestKey = requestKey(devId, respId, responseNo);
  100 + SynchronousQueue<String> queue = topicSubscribers.get(requestKey);
  101 + if (queue != null) {
  102 + try {
  103 + return queue.offer(data, 2, TimeUnit.SECONDS);
  104 + } catch (InterruptedException e) {
  105 + log.error("{}", e.getMessage(), e);
  106 + }
  107 + }
  108 + log.warn("Not find response,key:{} data:{} ", requestKey, data);
  109 + return false;
  110 + }
  111 +
  112 + private void unsubscribe(String key) {
  113 + topicSubscribers.remove(key);
  114 + }
  115 +
  116 + private SynchronousQueue<String> subscribe(String key) {
  117 + SynchronousQueue<String> queue = null;
  118 + if (!topicSubscribers.containsKey(key))
  119 + topicSubscribers.put(key, queue = new SynchronousQueue<String>());
  120 + return queue;
  121 + }
  122 +
  123 + private String requestKey(String devId, String respId, Long requestNo) {
  124 + return String.join("_", devId.replaceFirst("^0*", ""), respId, requestNo.toString());
  125 + }
  126 +
  127 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/util/Bin.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.util;
  2 +
  3 +/**
  4 + * 32位整型的二进制读写
  5 + */
  6 +public class Bin {
  7 +
  8 + private static final int[] bits = new int[32];
  9 +
  10 + static {
  11 + bits[0] = 1;
  12 + for (int i = 1; i < bits.length; i++) {
  13 + bits[i] = bits[i - 1] << 1;
  14 + }
  15 + }
  16 +
  17 + /**
  18 + * 读取n的第i位
  19 + *
  20 + * @param n int32
  21 + * @param i 取值范围0-31
  22 + */
  23 + public static boolean get(int n, int i) {
  24 + return (n & bits[i]) == bits[i];
  25 + }
  26 +
  27 + /**
  28 + * 不足位数从左边加0
  29 + */
  30 + public static String strHexPaddingLeft(String data, int length) {
  31 + int dataLength = data.length();
  32 + if (dataLength < length) {
  33 + StringBuilder dataBuilder = new StringBuilder(data);
  34 + for (int i = dataLength; i < length; i++) {
  35 + dataBuilder.insert(0, "0");
  36 + }
  37 + data = dataBuilder.toString();
  38 + }
  39 + return data;
  40 + }
  41 +}
... ...
src/main/java/com/genersoft/iot/vmp/jt1078/util/ClassUtil.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078.util;
  2 +
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +import org.springframework.core.io.Resource;
  6 +import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  7 +import org.springframework.core.io.support.ResourcePatternResolver;
  8 +
  9 +import java.lang.annotation.Annotation;
  10 +import java.util.LinkedList;
  11 +import java.util.List;
  12 +
  13 +public class ClassUtil {
  14 +
  15 + private static final Logger logger = LoggerFactory.getLogger(ClassUtil.class);
  16 +
  17 +
  18 + public static Object getBean(Class<?> clazz) {
  19 + if (clazz != null) {
  20 + try {
  21 + return clazz.getDeclaredConstructor().newInstance();
  22 + } catch (Exception ex) {
  23 + logger.error("ClassUtil:找不到指定的类", ex);
  24 + }
  25 + }
  26 + return null;
  27 + }
  28 +
  29 +
  30 + public static Object getBean(String className) {
  31 + Class<?> clazz = null;
  32 + try {
  33 + clazz = Class.forName(className);
  34 + } catch (Exception ex) {
  35 + logger.error("ClassUtil:找不到指定的类");
  36 + }
  37 + if (clazz != null) {
  38 + try {
  39 + //获取声明的构造器--》创建实例
  40 + return clazz.getDeclaredConstructor().newInstance();
  41 + } catch (Exception ex) {
  42 + logger.error("ClassUtil:找不到指定的类", ex);
  43 + }
  44 + }
  45 + return null;
  46 + }
  47 +
  48 +
  49 + /**
  50 + * 获取包下所有带注解的class
  51 + *
  52 + * @param packageName 包名
  53 + * @param annotationClass 注解类型
  54 + * @return list
  55 + */
  56 + public static List<Class<?>> getClassList(String packageName, Class<? extends Annotation> annotationClass) {
  57 + List<Class<?>> classList = getClassList(packageName);
  58 + classList.removeIf(next -> !next.isAnnotationPresent(annotationClass));
  59 + return classList;
  60 + }
  61 +
  62 + public static List<Class<?>> getClassList(String... packageName) {
  63 + List<Class<?>> classList = new LinkedList<>();
  64 + for (String s : packageName) {
  65 + List<Class<?>> c = getClassList(s);
  66 + classList.addAll(c);
  67 + }
  68 + return classList;
  69 + }
  70 +
  71 + public static List<Class<?>> getClassList(String packageName) {
  72 + List<Class<?>> classList = new LinkedList<>();
  73 + try {
  74 + ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
  75 + Resource[] resources = resourcePatternResolver.getResources(packageName.replace(".", "/") + "/**/*.class");
  76 + for (Resource resource : resources) {
  77 + String url = resource.getURL().toString();
  78 +
  79 + String[] split = url.split(packageName.replace(".", "/"));
  80 + String s = split[split.length - 1];
  81 + String className = s.replace("/", ".");
  82 + className = className.substring(0, className.lastIndexOf("."));
  83 + doAddClass(classList, packageName + className);
  84 + }
  85 +
  86 + } catch (Exception e) {
  87 + throw new RuntimeException(e);
  88 + }
  89 + return classList;
  90 + }
  91 +
  92 + private static void doAddClass(List<Class<?>> classList, String className) {
  93 + Class<?> cls = loadClass(className, false);
  94 + classList.add(cls);
  95 + }
  96 +
  97 + public static Class<?> loadClass(String className, boolean isInitialized) {
  98 + Class<?> cls;
  99 + try {
  100 + cls = Class.forName(className, isInitialized, getClassLoader());
  101 + } catch (ClassNotFoundException e) {
  102 + throw new RuntimeException(e);
  103 + }
  104 + return cls;
  105 + }
  106 +
  107 +
  108 + public static ClassLoader getClassLoader() {
  109 + return Thread.currentThread().getContextClassLoader();
  110 + }
  111 +
  112 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -2,12 +2,16 @@ package com.genersoft.iot.vmp.media.zlm;
2 2  
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.common.InviteInfo;
  6 +import com.genersoft.iot.vmp.common.InviteSessionType;
5 7 import com.genersoft.iot.vmp.common.StreamInfo;
6 8 import com.genersoft.iot.vmp.conf.UserSetting;
7 9 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
8 10 import com.genersoft.iot.vmp.gb28181.bean.*;
9 11 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
10 12 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
  13 +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
  14 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
11 15 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
12 16 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
13 17 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
... ... @@ -21,10 +25,8 @@ import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
21 25 import com.genersoft.iot.vmp.service.*;
22 26 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
23 27 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
24   -import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;
25 28 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
26 29 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
27   -import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
28 30 import org.slf4j.Logger;
29 31 import org.slf4j.LoggerFactory;
30 32 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -57,14 +59,14 @@ public class ZLMHttpHookListener {
57 59 @Autowired
58 60 private SIPCommander cmder;
59 61  
60   - @Autowired
61   - private ISIPCommanderForPlatform commanderFroPlatform;
  62 + @Autowired
  63 + private ISIPCommanderForPlatform commanderFroPlatform;
62 64  
63   - @Autowired
64   - private AudioBroadcastManager audioBroadcastManager;
  65 + @Autowired
  66 + private AudioBroadcastManager audioBroadcastManager;
65 67  
66   - @Autowired
67   - private ZLMRTPServerFactory zlmrtpServerFactory;
  68 + @Autowired
  69 + private ZLMRTPServerFactory zlmrtpServerFactory;
68 70  
69 71 @Autowired
70 72 private IPlayService playService;
... ... @@ -76,6 +78,9 @@ public class ZLMHttpHookListener {
76 78 private IRedisCatchStorage redisCatchStorage;
77 79  
78 80 @Autowired
  81 + private IInviteStreamService inviteStreamService;
  82 +
  83 + @Autowired
79 84 private IDeviceService deviceService;
80 85  
81 86 @Autowired
... ... @@ -111,6 +116,9 @@ public class ZLMHttpHookListener {
111 116 @Autowired
112 117 private AssistRESTfulUtils assistRESTfulUtils;
113 118  
  119 + @Autowired
  120 + private SSRCFactory ssrcFactory;
  121 +
114 122 @Qualifier("taskExecutor")
115 123 @Autowired
116 124 private ThreadPoolTaskExecutor taskExecutor;
... ... @@ -255,13 +263,13 @@ public class ZLMHttpHookListener {
255 263 result.setEnable_audio(deviceChannel.isHasAudio());
256 264 }
257 265 // 如果是录像下载就设置视频间隔十秒
258   - if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {
  266 + if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
259 267 result.setMp4_max_second(10);
260 268 result.setEnable_audio(true);
261 269 result.setEnable_mp4(true);
262 270 }
263 271 // 如果是talk对讲,则默认获取声音
264   - if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.talk) {
  272 + if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
265 273 result.setEnable_audio(true);
266 274 }
267 275  
... ... @@ -269,7 +277,7 @@ public class ZLMHttpHookListener {
269 277 if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
270 278 logger.info("推流时发现尚未设置录像路径,从assist服务中读取");
271 279 JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
272   - if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
  280 + if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0) {
273 281 JSONObject dataJson = info.getJSONObject("data");
274 282 if (dataJson != null) {
275 283 String recordPath = dataJson.getString("record");
... ... @@ -301,29 +309,30 @@ public class ZLMHttpHookListener {
301 309 logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
302 310 }
303 311  
304   - JSONObject ret = new JSONObject();
305   - ret.put("code", 0);
306   - ret.put("msg", "success");
  312 + JSONObject ret = new JSONObject();
  313 + ret.put("code", 0);
  314 + ret.put("msg", "success");
307 315 MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
308 316 JSONObject json = (JSONObject) JSON.toJSON(param);
309 317 taskExecutor.execute(() -> {
310 318 ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
  319 + if (mediaInfo == null) {
  320 + logger.info("[ZLM HOOK] 流变化未找到ZLM, {}", param.getMediaServerId());
  321 + return;
  322 + }
311 323 if (subscribe != null) {
312   -
313   - if (mediaInfo != null) {
314   - subscribe.response(mediaInfo, json);
315   - }
  324 + subscribe.response(mediaInfo, json);
316 325 }
317 326  
318 327 List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
319 328 // TODO 重构此处逻辑
320   -
  329 + boolean isPush = false;
321 330 if (param.isRegist()) {
322 331 // 处理流注册的鉴权信息
323 332 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
324 333 || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
325 334 || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
326   -
  335 + isPush = true;
327 336 StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
328 337 if (streamAuthorityInfo == null) {
329 338 streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
... ... @@ -337,123 +346,122 @@ public class ZLMHttpHookListener {
337 346 redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
338 347 }
339 348  
340   - if ("rtsp".equals(param.getSchema())){
341   - logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
342   - if (param.isRegist()) {
343   - mediaServerService.addCount(param.getMediaServerId());
344   - }else {
345   - mediaServerService.removeCount(param.getMediaServerId());
346   - }
347   - if (param.getOriginType() == OriginType.PULL.ordinal()
348   - || param.getOriginType() == OriginType.FFMPEG_PULL.ordinal()) {
349   - // 设置拉流代理上线/离线
350   - streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream());
351   - }
352   - if ("rtp".equals(param.getApp()) && !param.isRegist() ) {
353   - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(param.getStream());
354   - if (streamInfo!=null){
355   - redisCatchStorage.stopPlay(streamInfo);
356   - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
357   - }else{
358   - streamInfo = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
359   - if (streamInfo != null) {
360   - redisCatchStorage.stopPlayback(streamInfo.getDeviceID(), streamInfo.getChannelId(),
361   - streamInfo.getStream(), null);
362   - }
363   - }
364   - }else if ("broadcast".equals(param.getApp())){
365   - // 语音对讲推流 stream需要满足格式deviceId_channelId
366   - if (param.getStream().indexOf("_") > 0) {
367   - String[] streamArray = param.getStream().split("_");
368   - if (streamArray.length == 2) {
369   - String deviceId = streamArray[0];
370   - String channelId = streamArray[1];
371   - Device device = deviceService.getDevice(deviceId);
372   - if (device != null) {
373   - if (param.isRegist()) {
374   - if (audioBroadcastManager.exit(deviceId, channelId)) {
  349 + if ("rtsp".equals(param.getSchema())) {
  350 + logger.info("流变化:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
  351 + if (param.isRegist()) {
  352 + mediaServerService.addCount(param.getMediaServerId());
  353 + } else {
  354 + mediaServerService.removeCount(param.getMediaServerId());
  355 + }
  356 +
  357 + int updateStatusResult = streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream());
  358 + if (updateStatusResult > 0) {
  359 +
  360 + }
  361 +
  362 + if ("rtp".equals(param.getApp()) && !param.isRegist()) {
  363 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
  364 + if (inviteInfo != null && (inviteInfo.getType() == InviteSessionType.PLAY || inviteInfo.getType() == InviteSessionType.PLAYBACK)) {
  365 + inviteStreamService.removeInviteInfo(inviteInfo);
  366 + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
  367 + }
  368 + } else if ("broadcast".equals(param.getApp())) {
  369 + // 语音对讲推流 stream需要满足格式deviceId_channelId
  370 + if (param.getStream().indexOf("_") > 0) {
  371 + String[] streamArray = param.getStream().split("_");
  372 + if (streamArray.length == 2) {
  373 + String deviceId = streamArray[0];
  374 + String channelId = streamArray[1];
  375 + Device device = deviceService.getDevice(deviceId);
  376 + if (device != null) {
  377 + if (param.isRegist()) {
  378 + if (audioBroadcastManager.exit(deviceId, channelId)) {
  379 + playService.stopAudioBroadcast(deviceId, channelId);
  380 + }
  381 + // 开启语音对讲通道
  382 + try {
  383 + playService.audioBroadcastCmd(device, channelId, mediaInfo, param.getApp(), param.getStream(), 60, false, (msg) -> {
  384 + logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
  385 + });
  386 + } catch (InvalidArgumentException | ParseException | SipException e) {
  387 + logger.error("[命令发送失败] 语音对讲: {}", e.getMessage());
  388 + }
  389 + } else {
  390 + // 流注销
375 391 playService.stopAudioBroadcast(deviceId, channelId);
376 392 }
377   - // 开启语音对讲通道
378   - try {
379   - playService.audioBroadcastCmd(device, channelId, mediaInfo, param.getApp(), param.getStream(), 60, false, (msg)->{
380   - logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
381   - });
382   - } catch (InvalidArgumentException | ParseException | SipException e) {
383   - logger.error("[命令发送失败] 语音对讲: {}", e.getMessage());
384   - }
385   - }else {
386   - // 流注销
387   - playService.stopAudioBroadcast(deviceId, channelId);
  393 + } else {
  394 + logger.info("[语音对讲] 未找到设备:{}", deviceId);
388 395 }
389   - } else{
390   - logger.info("[语音对讲] 未找到设备:{}", deviceId);
391 396 }
392 397 }
393   - }
394   - }else if ("talk".equals(param.getApp())){
395   - // 语音对讲推流 stream需要满足格式deviceId_channelId
396   - if (param.getStream().indexOf("_") > 0) {
397   - String[] streamArray = param.getStream().split("_");
398   - if (streamArray.length == 2) {
399   - String deviceId = streamArray[0];
400   - String channelId = streamArray[1];
401   - Device device = deviceService.getDevice(deviceId);
402   - if (device != null) {
403   - if (param.isRegist()) {
404   - if (audioBroadcastManager.exit(deviceId, channelId)) {
405   - playService.stopAudioBroadcast(deviceId, channelId);
  398 + } else if ("talk".equals(param.getApp())) {
  399 + // 语音对讲推流 stream需要满足格式deviceId_channelId
  400 + if (param.getStream().indexOf("_") > 0) {
  401 + String[] streamArray = param.getStream().split("_");
  402 + if (streamArray.length == 2) {
  403 + String deviceId = streamArray[0];
  404 + String channelId = streamArray[1];
  405 + Device device = deviceService.getDevice(deviceId);
  406 + if (device != null) {
  407 + if (param.isRegist()) {
  408 + if (audioBroadcastManager.exit(deviceId, channelId)) {
  409 + playService.stopAudioBroadcast(deviceId, channelId);
  410 + }
  411 + // 开启语音对讲通道
  412 + playService.talkCmd(device, channelId, mediaInfo, param.getStream(), (msg) -> {
  413 + logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
  414 + });
  415 + } else {
  416 + // 流注销
  417 + playService.stopTalk(device, channelId, param.isRegist());
406 418 }
407   - // 开启语音对讲通道
408   - playService.talkCmd(device, channelId, mediaInfo, param.getStream(), (msg)->{
409   - logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId);
410   - });
411   - }else {
412   - // 流注销
413   - playService.stopTalk(device, channelId, param.isRegist());
  419 + } else {
  420 + logger.info("[语音对讲] 未找到设备:{}", deviceId);
414 421 }
415   - } else{
416   - logger.info("[语音对讲] 未找到设备:{}", deviceId);
417 422 }
418 423 }
419   - }
420 424  
421   - }else{
422   - if (!"rtp".equals(param.getApp())){
423   - String type = OriginType.values()[param.getOriginType()].getType();
424   - MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
425   -
426   - if (mediaServerItem != null){
427   - if (param.isRegist()) {
428   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
429   - String callId = null;
430   - if (streamAuthorityInfo != null) {
431   - callId = streamAuthorityInfo.getCallId();
432   - }
433   - StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
434   - param.getApp(), param.getStream(), param.getTracks(), callId);
435   - param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
436   - redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param);
437   - if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
438   - || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
439   - || param.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
440   - param.setSeverId(userSetting.getServerId());
441   - zlmMediaListManager.addPush(param);
442   - }
443   - }else {
444   - // 兼容流注销时类型从redis记录获取
445   - OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
446   - param.getApp(), param.getStream(), param.getMediaServerId());
447   - if (onStreamChangedHookParam != null) {
448   - type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
449   - redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream());
450   - }
451   - GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
452   - if (gbStream != null) {
  425 + } else {
  426 + if (!"rtp".equals(param.getApp())) {
  427 + String type = OriginType.values()[param.getOriginType()].getType();
  428 + MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
  429 +
  430 + if (mediaServerItem != null) {
  431 + if (param.isRegist()) {
  432 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  433 + String callId = null;
  434 + if (streamAuthorityInfo != null) {
  435 + callId = streamAuthorityInfo.getCallId();
  436 + }
  437 + StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
  438 + param.getApp(), param.getStream(), param.getTracks(), callId);
  439 + param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
  440 + redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param);
  441 + if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  442 + || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  443 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  444 + param.setSeverId(userSetting.getServerId());
  445 + zlmMediaListManager.addPush(param);
  446 + }
  447 + } else {
  448 + // 兼容流注销时类型从redis记录获取
  449 + OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
  450 + param.getApp(), param.getStream(), param.getMediaServerId());
  451 + if (onStreamChangedHookParam != null) {
  452 + type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
  453 + redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream());
  454 + }
  455 + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
  456 + if (gbStream != null) {
453 457 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
454 458 }
455 459 zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
456 460 }
  461 + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
  462 + if (gbStream != null) {
  463 + eventPublisher.catalogEventPublishForStream(null, gbStream, param.isRegist() ? CatalogEvent.ON : CatalogEvent.OFF);
  464 + }
457 465 if (type != null) {
458 466 // 发送流变化redis消息
459 467 JSONObject jsonObject = new JSONObject();
... ... @@ -466,38 +474,35 @@ public class ZLMHttpHookListener {
466 474 }
467 475 }
468 476 }
469   - }
470   - if (!param.isRegist()) {
471   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
472   - if (sendRtpItems.size() > 0) {
473   - for (SendRtpItem sendRtpItem : sendRtpItems) {
474   - if (sendRtpItem.getApp().equals(param.getApp())) {
475   - String platformId = sendRtpItem.getPlatformId();
476   - ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
477   - Device device = deviceService.getDevice(platformId);
478   -
479   -
480   - if (platform != null) {
481   - try {
  477 + if (!param.isRegist()) {
  478 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
  479 + if (sendRtpItems.size() > 0) {
  480 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  481 + if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) {
  482 + String platformId = sendRtpItem.getPlatformId();
  483 + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
  484 + Device device = deviceService.getDevice(platformId);
  485 +
  486 + try {
  487 + if (platform != null) {
482 488 commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
483   - } catch (SipException | InvalidArgumentException | ParseException e) {
484   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
485   - }
486   - } else {
487   - try {
  489 + redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(),
  490 + sendRtpItem.getCallId(), sendRtpItem.getStream());
  491 + } else {
488 492 cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
489   - } catch (SipException | InvalidArgumentException | ParseException |
490   - SsrcTransactionNotFoundException e) {
491   - logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
492 493 }
  494 + } catch (SipException | InvalidArgumentException | ParseException |
  495 + SsrcTransactionNotFoundException e) {
  496 + logger.error("[命令发送失败] 发送BYE: {}", e.getMessage());
493 497 }
  498 + }
494 499 }
495 500 }
496 501 }
497 502 }
  503 +
498 504 }
499 505 });
500   -
501 506 return HookResult.SUCCESS();
502 507 }
503 508  
... ... @@ -508,82 +513,65 @@ public class ZLMHttpHookListener {
508 513 @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
509 514 public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
510 515  
511   - logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
512   - JSONObject ret = new JSONObject();
513   - ret.put("code", 0);
514   - // 国标类型的流
515   - if ("rtp".equals(param.getApp())){
516   - ret.put("close", userSetting.getStreamOnDemand());
517   - // 国标流, 点播/录像回放/录像下载
518   - StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(param.getStream());
519   - // 点播
520   - if (streamInfoForPlayCatch != null) {
521   - // 收到无人观看说明流也没有在往上级推送
522   - if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) {
523   - List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId(streamInfoForPlayCatch.getChannelId());
524   - if (sendRtpItems.size() > 0) {
525   - for (SendRtpItem sendRtpItem : sendRtpItems) {
526   - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
527   - if (parentPlatform == null) {
528   - continue;
529   - }
530   - try {
531   - commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
532   - } catch (SipException | InvalidArgumentException | ParseException e) {
533   - logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
534   - }
535   - redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
536   - sendRtpItem.getCallId(), sendRtpItem.getStream());
537   - }
538   - }
539   - }
540   - Device device = deviceService.getDevice(streamInfoForPlayCatch.getDeviceID());
541   - if (device != null) {
542   - try {
543   - cmder.streamByeCmd(device, streamInfoForPlayCatch.getChannelId(),
544   - streamInfoForPlayCatch.getStream(), null);
545   - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
546   - logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
547   - }
548   - }
549   -
550   - redisCatchStorage.stopPlay(streamInfoForPlayCatch);
551   - storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
552   - return ret;
553   - }
554   - // 录像回放
555   - StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
556   - if (streamInfoForPlayBackCatch != null) {
557   - if (streamInfoForPlayBackCatch.isPause()) {
  516 + logger.info("[ZLM HOOK]流无人观看:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(),
  517 + param.getApp(), param.getStream());
  518 + JSONObject ret = new JSONObject();
  519 + ret.put("code", 0);
  520 + // 国标类型的流
  521 + if ("rtp".equals(param.getApp())) {
  522 + ret.put("close", userSetting.getStreamOnDemand());
  523 + // 国标流, 点播/录像回放/录像下载
  524 +// StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(param.getStream());
  525 +
  526 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, param.getStream());
  527 + // 点播
  528 + if (inviteInfo != null) {
  529 + // 录像下载
  530 + if (inviteInfo.getType() == InviteSessionType.DOWNLOAD) {
558 531 ret.put("close", false);
559   - } else {
560   - Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
561   - if (device != null) {
562   - try {
563   - cmder.streamByeCmd(device, streamInfoForPlayBackCatch.getChannelId(),
564   - streamInfoForPlayBackCatch.getStream(), null);
565   - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
566   - logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage());
  532 + return ret;
  533 + }
  534 + // 收到无人观看说明流也没有在往上级推送
  535 + if (redisCatchStorage.isChannelSendingRTP(inviteInfo.getChannelId())) {
  536 + List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId(
  537 + inviteInfo.getChannelId());
  538 + if (sendRtpItems.size() > 0) {
  539 + for (SendRtpItem sendRtpItem : sendRtpItems) {
  540 + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  541 + try {
  542 + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
  543 + } catch (SipException | InvalidArgumentException | ParseException e) {
  544 + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
  545 + }
  546 + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
  547 + sendRtpItem.getCallId(), sendRtpItem.getStream());
567 548 }
568 549 }
569   - redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
570   - streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
571 550 }
572   - return ret;
573   - }
574   - // 录像下载
575   - StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
576   - // 进行录像下载时无人观看不断流
577   - if (streamInfoForDownload != null) {
578   - ret.put("close", false);
  551 + Device device = deviceService.getDevice(inviteInfo.getDeviceId());
  552 + if (device != null) {
  553 + try {
  554 + if (inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()) != null) {
  555 + cmder.streamByeCmd(device, inviteInfo.getChannelId(),
  556 + inviteInfo.getStream(), null);
  557 + }
  558 + } catch (InvalidArgumentException | ParseException | SipException |
  559 + SsrcTransactionNotFoundException e) {
  560 + logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
  561 + }
  562 + }
  563 +
  564 + inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
  565 + inviteInfo.getChannelId(), inviteInfo.getStream());
  566 + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
579 567 return ret;
580 568 }
581 569 SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, param.getStream(), null);
582   - if ("talk".equals(sendRtpItem.getApp())){
  570 + if ("talk".equals(sendRtpItem.getApp())) {
583 571 ret.put("close", false);
584 572 return ret;
585 573 }
586   - }else if ("talk".equals(param.getApp()) || "broadcast".equals(param.getApp())){
  574 + } else if ("talk".equals(param.getApp()) || "broadcast".equals(param.getApp())) {
587 575 ret.put("close", false);
588 576 } else {
589 577 // 非国标流 推流/拉流代理
... ... @@ -652,6 +640,7 @@ public class ZLMHttpHookListener {
652 640 return defaultResult;
653 641 }
654 642 logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
  643 +
655 644 RequestMessage msg = new RequestMessage();
656 645 String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
657 646 boolean exist = resultHolder.exist(key, null);
... ... @@ -659,31 +648,22 @@ public class ZLMHttpHookListener {
659 648 String uuid = UUID.randomUUID().toString();
660 649 msg.setId(uuid);
661 650 DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
662   - DeferredResultEx<HookResult> deferredResultEx = new DeferredResultEx<>(result);
663 651  
664 652 result.onTimeout(() -> {
665   - logger.info("点播接口等待超时");
  653 + logger.info("[ZLM HOOK] 自动点播, 等待超时");
666 654 // 释放rtpserver
667 655 msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时"));
668 656 resultHolder.invokeResult(msg);
669 657 });
670   - // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误
671   - deferredResultEx.setFilter(result1 -> {
672   - WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>) result1;
673   - HookResult resultForEnd = new HookResult();
674   - resultForEnd.setCode(wvpResult1.getCode());
675   - resultForEnd.setMsg(wvpResult1.getMsg());
676   - return resultForEnd;
677   - });
678 658  
679 659 // 录像查询以channelId作为deviceId查询
680   - resultHolder.put(key, uuid, deferredResultEx);
  660 + resultHolder.put(key, uuid, result);
681 661  
682 662 if (!exist) {
683   - playService.play(mediaInfo, deviceId, channelId, null, eventResult -> {
684   - msg.setData(new HookResult(eventResult.statusCode, eventResult.msg));
  663 + playService.play(mediaInfo, deviceId, channelId, (code, message, data) -> {
  664 + msg.setData(new HookResult(code, message));
685 665 resultHolder.invokeResult(msg);
686   - }, null);
  666 + });
687 667 }
688 668 return result;
689 669 } else {
... ... @@ -740,6 +720,7 @@ public class ZLMHttpHookListener {
740 720 if (sendRtpItems.size() > 0) {
741 721 for (SendRtpItem sendRtpItem : sendRtpItems) {
742 722 ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  723 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
743 724 try {
744 725 commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
745 726 } catch (SipException | InvalidArgumentException | ParseException e) {
... ... @@ -759,7 +740,8 @@ public class ZLMHttpHookListener {
759 740 */
760 741 @ResponseBody
761 742 @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
762   - public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param) {
  743 + public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam
  744 + param) {
763 745 logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
764 746  
765 747 taskExecutor.execute(() -> {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
... ... @@ -97,7 +97,8 @@ public class ZLMMediaListManager {
97 97 public void sendStreamEvent(String app, String stream, String mediaServerId) {
98 98 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
99 99 // 查看推流状态
100   - if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) {
  100 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream);
  101 + if (streamReady != null && streamReady) {
101 102 ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream);
102 103 if (channelOnlineEventLister != null) {
103 104 try {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -25,6 +25,8 @@ public class ZLMRESTfulUtils {
25 25  
26 26 private OkHttpClient client;
27 27  
  28 +
  29 +
28 30 public interface RequestCallback{
29 31 void run(JSONObject response);
30 32 }
... ... @@ -279,6 +281,10 @@ public class ZLMRESTfulUtils {
279 281 return sendPost(mediaServerItem, "closeRtpServer",param, null);
280 282 }
281 283  
  284 + public void closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param, RequestCallback callback) {
  285 + sendPost(mediaServerItem, "closeRtpServer",param, callback);
  286 + }
  287 +
282 288 public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
283 289 return sendPost(mediaServerItem, "listRtpServer",null, null);
284 290 }
... ... @@ -353,4 +359,19 @@ public class ZLMRESTfulUtils {
353 359 param.put("stream_id", streamId);
354 360 return sendPost(mediaServerItem, "resumeRtpCheck",param, null);
355 361 }
  362 +
  363 + public JSONObject connectRtpServer(MediaServerItem mediaServerItem, String dst_url, int dst_port, String stream_id) {
  364 + Map<String, Object> param = new HashMap<>(1);
  365 + param.put("dst_url", dst_url);
  366 + param.put("dst_port", dst_port);
  367 + param.put("stream_id", stream_id);
  368 + return sendPost(mediaServerItem, "connectRtpServer",param, null);
  369 + }
  370 +
  371 + public JSONObject updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
  372 + Map<String, Object> param = new HashMap<>(1);
  373 + param.put("ssrc", ssrc);
  374 + param.put("stream_id", streamId);
  375 + return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null);
  376 + }
356 377 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
... ... @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.media.zlm;
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONArray;
5 5 import com.alibaba.fastjson2.JSONObject;
  6 +import com.genersoft.iot.vmp.common.CommonCallback;
6 7 import com.genersoft.iot.vmp.conf.UserSetting;
7 8 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
8 9 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
... ... @@ -92,7 +93,17 @@ public class ZLMRTPServerFactory {
92 93 return result;
93 94 }
94 95  
95   - public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port, Boolean onlyAuto) {
  96 + /**
  97 + * 开启rtpServer
  98 + * @param mediaServerItem zlm服务实例
  99 + * @param streamId 流Id
  100 + * @param ssrc ssrc
  101 + * @param port 端口, 0/null为使用随机
  102 + * @param reUsePort 是否重用端口
  103 + * @param tcpMode 0/null udp 模式,1 tcp 被动模式, 2 tcp 主动模式。
  104 + * @return
  105 + */
  106 + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) {
96 107 int result = -1;
97 108 // 查询此rtp server 是否已经存在
98 109 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId);
... ... @@ -108,7 +119,7 @@ public class ZLMRTPServerFactory {
108 119 JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(mediaServerItem, param);
109 120 if (jsonObject != null ) {
110 121 if (jsonObject.getInteger("code") == 0) {
111   - return createRTPServer(mediaServerItem, streamId, ssrc, port, onlyAuto);
  122 + return createRTPServer(mediaServerItem, streamId, ssrc, port,onlyAuto, reUsePort, tcpMode);
112 123 }else {
113 124 logger.warn("[开启rtpServer], 重启RtpServer错误");
114 125 }
... ... @@ -122,8 +133,14 @@ public class ZLMRTPServerFactory {
122 133  
123 134 Map<String, Object> param = new HashMap<>();
124 135  
125   - param.put("enable_tcp", 1);
  136 + if (tcpMode == null) {
  137 + tcpMode = 0;
  138 + }
  139 + param.put("tcp_mode", tcpMode);
126 140 param.put("stream_id", streamId);
  141 + if (reUsePort != null) {
  142 + param.put("re_use_port", reUsePort?"1":"0");
  143 + }
127 144 // 推流端口设置0则使用随机端口
128 145 if (port == null) {
129 146 param.put("port", 0);
... ... @@ -169,6 +186,31 @@ public class ZLMRTPServerFactory {
169 186 return result;
170 187 }
171 188  
  189 + public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback<Boolean> callback) {
  190 + if (serverItem == null) {
  191 + callback.run(false);
  192 + return;
  193 + }
  194 + Map<String, Object> param = new HashMap<>();
  195 + param.put("stream_id", streamId);
  196 + zlmresTfulUtils.closeRtpServer(serverItem, param, jsonObject -> {
  197 + if (jsonObject != null ) {
  198 + if (jsonObject.getInteger("code") == 0) {
  199 + callback.run(jsonObject.getInteger("hit") == 1);
  200 + return;
  201 + }else {
  202 + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
  203 + }
  204 + }else {
  205 + // 检查ZLM状态
  206 + logger.error("关闭RTP Server 失败: 请检查ZLM服务");
  207 + }
  208 + callback.run(false);
  209 + });
  210 +
  211 +
  212 + }
  213 +
172 214  
173 215 /**
174 216 * 创建一个国标推流
... ... @@ -256,11 +298,14 @@ public class ZLMRTPServerFactory {
256 298 if (jsonObject.getInteger("code") == 0) {
257 299 localPort = jsonObject.getInteger("port");
258 300 HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
259   - // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
260 301 hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
261 302 (MediaServerItem mediaServerItem, JSONObject response)->{
262   - logger.info("[保持端口] {}->监听端口到期继续保持监听", ssrc);
263   - keepPort(serverItem, ssrc);
  303 + logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);
  304 + int port = keepPort(serverItem, ssrc);
  305 + if (port == 0) {
  306 + logger.info("[上级点播] {}->监听端口失败,移除监听", ssrc);
  307 + hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
  308 + }
264 309 });
265 310 logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
266 311 logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
... ... @@ -305,6 +350,9 @@ public class ZLMRTPServerFactory {
305 350 */
306 351 public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) {
307 352 JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtsp", streamId);
  353 + if (mediaInfo.getInteger("code") == -2) {
  354 + return null;
  355 + }
308 356 return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
309 357 }
310 358  
... ... @@ -313,8 +361,10 @@ public class ZLMRTPServerFactory {
313 361 */
314 362 public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
315 363 JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId);
316   - return mediaInfo != null && (mediaInfo.getInteger("code") == 0
317   -
  364 + if (mediaInfo == null || (mediaInfo.getInteger("code") == -2)) {
  365 + return null;
  366 + }
  367 + return (mediaInfo.getInteger("code") == 0
318 368 && mediaInfo.getJSONArray("data") != null
319 369 && mediaInfo.getJSONArray("data").size() > 0);
320 370 }
... ... @@ -406,4 +456,19 @@ public class ZLMRTPServerFactory {
406 456 }
407 457 return startSendRtpStreamResult;
408 458 }
  459 +
  460 + public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
  461 + boolean result = false;
  462 + JSONObject jsonObject = zlmresTfulUtils.updateRtpServerSSRC(mediaServerItem, streamId, ssrc);
  463 + if (jsonObject == null) {
  464 + logger.error("[更新RTPServer] 失败: 请检查ZLM服务");
  465 + } else if (jsonObject.getInteger("code") == 0) {
  466 + result= true;
  467 + logger.info("[更新RTPServer] 成功");
  468 + } else {
  469 + logger.error("[更新RTPServer] 失败: {}, streamId:{},ssrc:{}->\r\n{}",jsonObject.getString("msg"),
  470 + streamId, ssrc, jsonObject);
  471 + }
  472 + return result;
  473 + }
409 474 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java
... ... @@ -100,7 +100,10 @@ public class ZlmHttpHookSubscribe {
100 100  
101 101 if (!CollectionUtils.isEmpty(entriesToRemove)) {
102 102 for (Map.Entry<IHookSubscribe, ZlmHttpHookSubscribe.Event> entry : entriesToRemove) {
103   - entries.remove(entry);
  103 + eventMap.remove(entry.getKey());
  104 + }
  105 + if (eventMap.size() == 0) {
  106 + allSubscribes.remove(hookSubscribe.getHookType());
104 107 }
105 108 }
106 109  
... ... @@ -136,9 +139,9 @@ public class ZlmHttpHookSubscribe {
136 139 /**
137 140 * 对订阅数据进行过期清理
138 141 */
139   - @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次
  142 +// @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次
  143 + @Scheduled(fixedRate = 2 * 1000)
140 144 public void execute(){
141   -
142 145 Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5));
143 146 int total = 0;
144 147 for (HookType hookType : allSubscribes.keySet()) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
1 1 package com.genersoft.iot.vmp.media.zlm.dto;
2 2  
3 3  
4   -import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
5 4 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
6 5 import io.swagger.v3.oas.annotations.media.Schema;
7 6 import org.springframework.util.ObjectUtils;
8 7  
9   -import java.util.HashMap;
10   -
11 8 @Schema(description = "流媒体服务信息")
12 9 public class MediaServerItem{
13 10  
... ... @@ -80,20 +77,10 @@ public class MediaServerItem{
80 77 @Schema(description = "是否是默认ZLM")
81 78 private boolean defaultServer;
82 79  
83   - @Schema(description = "SSRC信息")
84   - private SsrcConfig ssrcConfig;
85   -
86 80 @Schema(description = "当前使用到的端口")
87 81 private int currentPort;
88 82  
89 83  
90   - /**
91   - * 每一台ZLM都有一套独立的SSRC列表
92   - * 在ApplicationCheckRunner里对mediaServerSsrcMap进行初始化
93   - */
94   - @Schema(description = "ID")
95   - private HashMap<String, SsrcConfig> mediaServerSsrcMap;
96   -
97 84 public MediaServerItem() {
98 85 }
99 86  
... ... @@ -279,22 +266,6 @@ public class MediaServerItem{
279 266 this.updateTime = updateTime;
280 267 }
281 268  
282   - public HashMap<String, SsrcConfig> getMediaServerSsrcMap() {
283   - return mediaServerSsrcMap;
284   - }
285   -
286   - public void setMediaServerSsrcMap(HashMap<String, SsrcConfig> mediaServerSsrcMap) {
287   - this.mediaServerSsrcMap = mediaServerSsrcMap;
288   - }
289   -
290   - public SsrcConfig getSsrcConfig() {
291   - return ssrcConfig;
292   - }
293   -
294   - public void setSsrcConfig(SsrcConfig ssrcConfig) {
295   - this.ssrcConfig = ssrcConfig;
296   - }
297   -
298 269 public int getCurrentPort() {
299 270 return currentPort;
300 271 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java
... ... @@ -18,6 +18,10 @@ public class HookResult {
18 18 return new HookResult(0, "success");
19 19 }
20 20  
  21 + public static HookResult Fail(){
  22 + return new HookResult(-1, "fail");
  23 + }
  24 +
21 25 public int getCode() {
22 26 return code;
23 27 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java
... ... @@ -56,4 +56,35 @@ public interface IDeviceChannelService {
56 56 * 查询通道所属的设备
57 57 */
58 58 List<Device> getDeviceByChannelId(String channelId);
  59 +
  60 + /**
  61 + * 批量删除通道
  62 + * @param deleteChannelList 待删除的通道列表
  63 + */
  64 + int deleteChannels(List<DeviceChannel> deleteChannelList);
  65 +
  66 + /**
  67 + * 批量上线
  68 + */
  69 + int channelsOnline(List<DeviceChannel> channels);
  70 +
  71 + /**
  72 + * 批量下线
  73 + */
  74 + int channelsOffline(List<DeviceChannel> channels);
  75 +
  76 + /**
  77 + * 获取一个通道
  78 + */
  79 + DeviceChannel getOne(String deviceId, String channelId);
  80 +
  81 + /**
  82 + * 直接批量更新通道
  83 + */
  84 + void batchUpdateChannel(List<DeviceChannel> channels);
  85 +
  86 + /**
  87 + * 直接批量添加
  88 + */
  89 + void batchAddChannel(List<DeviceChannel> deviceChannels);
59 90 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IInviteStreamService.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service;
  2 +
  3 +import com.genersoft.iot.vmp.common.InviteInfo;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
  5 +import com.genersoft.iot.vmp.service.bean.InviteErrorCallback;
  6 +
  7 +/**
  8 + * 记录国标点播的状态,包括实时预览,下载,录像回放
  9 + */
  10 +public interface IInviteStreamService {
  11 +
  12 + /**
  13 + * 更新点播的状态信息
  14 + */
  15 + void updateInviteInfo(InviteInfo inviteInfo);
  16 +
  17 + /**
  18 + * 获取点播的状态信息
  19 + */
  20 + InviteInfo getInviteInfo(InviteSessionType type,
  21 + String deviceId,
  22 + String channelId,
  23 + String stream);
  24 +
  25 + /**
  26 + * 移除点播的状态信息
  27 + */
  28 + void removeInviteInfo(InviteSessionType type,
  29 + String deviceId,
  30 + String channelId,
  31 + String stream);
  32 + /**
  33 + * 移除点播的状态信息
  34 + */
  35 + void removeInviteInfo(InviteInfo inviteInfo);
  36 + /**
  37 + * 移除点播的状态信息
  38 + */
  39 + void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId);
  40 +
  41 + /**
  42 + * 获取点播的状态信息
  43 + */
  44 + InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type,
  45 + String deviceId,
  46 + String channelId);
  47 +
  48 + /**
  49 + * 获取点播的状态信息
  50 + */
  51 + InviteInfo getInviteInfoByStream(InviteSessionType type, String stream);
  52 +
  53 +
  54 + /**
  55 + * 添加一个invite回调
  56 + */
  57 + void once(InviteSessionType type, String deviceId, String channelId, String stream, InviteErrorCallback<Object> callback);
  58 +
  59 + /**
  60 + * 调用一个invite回调
  61 + */
  62 + void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data);
  63 +
  64 + /**
  65 + * 清空一个设备的所有invite信息
  66 + */
  67 + void clearInviteInfo(String deviceId);
  68 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
1 1 package com.genersoft.iot.vmp.service;
2 2  
  3 +import com.genersoft.iot.vmp.common.CommonCallback;
3 4 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
4 5 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
5 6 import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
... ... @@ -43,14 +44,16 @@ public interface IMediaServerService {
43 44  
44 45 void updateVmServer(List<MediaServerItem> mediaServerItemList);
45 46  
46   - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback);
47   -
48   - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback);
  47 + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
  48 + boolean isPlayback, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode);
49 49  
50 50 SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port, Boolean onlyAuto);
51 51  
52 52 void closeRTPServer(MediaServerItem mediaServerItem, String streamId);
53 53  
  54 + void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback);
  55 + Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc);
  56 +
54 57 void closeRTPServer(String mediaServerId, String streamId);
55 58  
56 59 void clearRTPServer(MediaServerItem mediaServerItem);
... ...
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
... ... @@ -3,12 +3,11 @@ package com.genersoft.iot.vmp.service;
3 3 import com.alibaba.fastjson2.JSONObject;
4 4 import com.genersoft.iot.vmp.common.StreamInfo;
5 5 import com.genersoft.iot.vmp.conf.exception.ServiceException;
6   -import com.genersoft.iot.vmp.gb28181.bean.*;
7   -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
8   -import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  6 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  7 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  8 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
9 9 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
10   -import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
11   -import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
  10 +import com.genersoft.iot.vmp.service.bean.InviteErrorCallback;
12 11 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
13 12 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
14 13 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
... ... @@ -25,12 +24,11 @@ import java.util.Map;
25 24 */
26 25 public interface IPlayService {
27 26  
28   - void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId);
29   -
30 27 void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
31   - ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
32   - InviteTimeOutCallback timeoutCallback);
33   - void play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent, Runnable timeoutCallback);
  28 + InviteErrorCallback<Object> callback);
  29 + SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, InviteErrorCallback<Object> callback);
  30 +
  31 + StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId);
34 32  
35 33 MediaServerItem getNewMediaServerItem(Device device);
36 34  
... ... @@ -39,15 +37,13 @@ public interface IPlayService {
39 37 */
40 38 MediaServerItem getNewMediaServerItemHasAssist(Device device);
41 39  
42   - void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String toString);
43   -
44   - void playBack(String deviceId, String channelId, String startTime, String endTime, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback);
45   - void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack);
  40 + void playBack(String deviceId, String channelId, String startTime, String endTime, InviteErrorCallback<Object> callback);
  41 + void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, InviteErrorCallback<Object> callback);
46 42  
47 43 void zlmServerOffline(String mediaServerId);
48 44  
49   - void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback);
50   - void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack);
  45 + void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteErrorCallback<Object> callback);
  46 + void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteErrorCallback<Object> callback);
51 47  
52 48 StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
53 49  
... ...
src/main/java/com/genersoft/iot/vmp/service/bean/InviteErrorCallback.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +public interface InviteErrorCallback<T> {
  4 +
  5 + void run(int code, String msg, T data);
  6 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/bean/InviteErrorCode.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +/**
  4 + * 全局错误码
  5 + */
  6 +public enum InviteErrorCode {
  7 + SUCCESS(0, "成功"),
  8 + ERROR_FOR_SIGNALLING_TIMEOUT(-1, "信令超时"),
  9 + ERROR_FOR_STREAM_TIMEOUT(-2, "收流超时"),
  10 + ERROR_FOR_RESOURCE_EXHAUSTION(-3, "资源耗尽"),
  11 + ERROR_FOR_CATCH_DATA(-4, "缓存数据异常"),
  12 + ERROR_FOR_SIGNALLING_ERROR(-5, "收到信令错误"),
  13 + ERROR_FOR_STREAM_PARSING_EXCEPTIONS(-6, "流地址解析错误"),
  14 + ERROR_FOR_SDP_PARSING_EXCEPTIONS(-7, "SDP信息解析失败"),
  15 + ERROR_FOR_SSRC_UNAVAILABLE(-8, "SSRC不可用"),
  16 + ERROR_FOR_RESET_SSRC(-9, "重新设置收流信息失败"),
  17 + ERROR_FOR_SIP_SENDING_FAILED(-10, "命令发送失败"),
  18 + ERROR_FOR_ASSIST_NOT_READY(-11, "没有可用的assist服务"),
  19 + ERROR_FOR_PARAMETER_ERROR(-13, "参数异常");
  20 +
  21 + private final int code;
  22 + private final String msg;
  23 +
  24 + InviteErrorCode(int code, String msg) {
  25 + this.code = code;
  26 + this.msg = msg;
  27 + }
  28 +
  29 + public int getCode() {
  30 + return code;
  31 + }
  32 +
  33 + public String getMsg() {
  34 + return msg;
  35 + }
  36 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
3   -import com.genersoft.iot.vmp.common.StreamInfo;
  3 +import com.genersoft.iot.vmp.common.InviteInfo;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
4 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
5 6 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
6 7 import com.genersoft.iot.vmp.gb28181.utils.Coordtransform;
7 8 import com.genersoft.iot.vmp.service.IDeviceChannelService;
  9 +import com.genersoft.iot.vmp.service.IInviteStreamService;
8 10 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
9 11 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
10 12 import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
... ... @@ -33,6 +35,9 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
33 35 private IRedisCatchStorage redisCatchStorage;
34 36  
35 37 @Autowired
  38 + private IInviteStreamService inviteStreamService;
  39 +
  40 + @Autowired
36 41 private DeviceChannelMapper channelMapper;
37 42  
38 43 @Autowired
... ... @@ -45,6 +50,8 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
45 50 device = deviceMapper.getDeviceByDeviceId(deviceChannel.getDeviceId());
46 51 }
47 52  
  53 +
  54 +
48 55 if ("WGS84".equals(device.getGeoCoordSys())) {
49 56 deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude());
50 57 deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude());
... ... @@ -76,9 +83,10 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
76 83 public void updateChannel(String deviceId, DeviceChannel channel) {
77 84 String channelId = channel.getChannelId();
78 85 channel.setDeviceId(deviceId);
79   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
80   - if (streamInfo != null) {
81   - channel.setStreamId(streamInfo.getStream());
  86 +// StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
  87 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
  88 + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
  89 + channel.setStreamId(inviteInfo.getStreamInfo().getStream());
82 90 }
83 91 String now = DateUtil.getNow();
84 92 channel.setUpdateTime(now);
... ... @@ -104,9 +112,9 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
104 112 if (channelList.size() == 0) {
105 113 for (DeviceChannel channel : channels) {
106 114 channel.setDeviceId(deviceId);
107   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
108   - if (streamInfo != null) {
109   - channel.setStreamId(streamInfo.getStream());
  115 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channel.getChannelId());
  116 + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
  117 + channel.setStreamId(inviteInfo.getStreamInfo().getStream());
110 118 }
111 119 String now = DateUtil.getNow();
112 120 channel.setUpdateTime(now);
... ... @@ -120,9 +128,9 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
120 128 }
121 129 for (DeviceChannel channel : channels) {
122 130 channel.setDeviceId(deviceId);
123   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId());
124   - if (streamInfo != null) {
125   - channel.setStreamId(streamInfo.getStream());
  131 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channel.getChannelId());
  132 + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
  133 + channel.setStreamId(inviteInfo.getStreamInfo().getStream());
126 134 }
127 135 String now = DateUtil.getNow();
128 136 channel.setUpdateTime(now);
... ... @@ -207,6 +215,47 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService {
207 215  
208 216 @Override
209 217 public List<Device> getDeviceByChannelId(String channelId) {
  218 +
210 219 return channelMapper.getDeviceByChannelId(channelId);
211 220 }
  221 +
  222 + @Override
  223 + public int deleteChannels(List<DeviceChannel> deleteChannelList) {
  224 + return channelMapper.batchDel(deleteChannelList);
  225 + }
  226 +
  227 + @Override
  228 + public int channelsOnline(List<DeviceChannel> channels) {
  229 + return channelMapper.batchOnline(channels);
  230 + }
  231 +
  232 + @Override
  233 + public int channelsOffline(List<DeviceChannel> channels) {
  234 + return channelMapper.batchOffline(channels);
  235 + }
  236 +
  237 + @Override
  238 + public DeviceChannel getOne(String deviceId, String channelId){
  239 + return channelMapper.queryChannel(deviceId, channelId);
  240 + }
  241 +
  242 + @Override
  243 + public void batchUpdateChannel(List<DeviceChannel> channels) {
  244 + channelMapper.batchUpdate(channels);
  245 + for (DeviceChannel channel : channels) {
  246 + if (channel.getParentId() != null) {
  247 + channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId());
  248 + }
  249 + }
  250 + }
  251 +
  252 + @Override
  253 + public void batchAddChannel(List<DeviceChannel> channels) {
  254 + channelMapper.batchAdd(channels);
  255 + for (DeviceChannel channel : channels) {
  256 + if (channel.getParentId() != null) {
  257 + channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId());
  258 + }
  259 + }
  260 + }
212 261 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
... ... @@ -15,6 +15,7 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
15 15 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
16 16 import com.genersoft.iot.vmp.service.IDeviceChannelService;
17 17 import com.genersoft.iot.vmp.service.IDeviceService;
  18 +import com.genersoft.iot.vmp.service.IInviteStreamService;
18 19 import com.genersoft.iot.vmp.service.IMediaServerService;
19 20 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
20 21 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
... ... @@ -60,6 +61,9 @@ public class DeviceServiceImpl implements IDeviceService {
60 61 private IRedisCatchStorage redisCatchStorage;
61 62  
62 63 @Autowired
  64 + private IInviteStreamService inviteStreamService;
  65 +
  66 + @Autowired
63 67 private DeviceMapper deviceMapper;
64 68  
65 69 @Autowired
... ... @@ -104,7 +108,7 @@ public class DeviceServiceImpl implements IDeviceService {
104 108 String now = DateUtil.getNow();
105 109 if (deviceInRedis != null && deviceInDb == null) {
106 110 // redis 存在脏数据
107   - redisCatchStorage.clearCatchByDeviceId(device.getDeviceId());
  111 + inviteStreamService.clearInviteInfo(device.getDeviceId());
108 112 }
109 113 device.setUpdateTime(now);
110 114 if (device.getKeepaliveIntervalTime() == 0) {
... ... @@ -172,6 +176,11 @@ public class DeviceServiceImpl implements IDeviceService {
172 176 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
173 177 // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
174 178 dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
  179 + if (userSetting.getDeviceStatusNotify()) {
  180 + // 发送redis消息
  181 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);
  182 + }
  183 +
175 184 }
176 185  
177 186 @Override
... ... @@ -200,6 +209,11 @@ public class DeviceServiceImpl implements IDeviceService {
200 209 // 移除订阅
201 210 removeCatalogSubscribe(device);
202 211 removeMobilePositionSubscribe(device);
  212 + if (userSetting.getDeviceStatusNotify()) {
  213 + // 发送redis消息
  214 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, false);
  215 + }
  216 +
203 217 List<AudioBroadcastCatch> audioBroadcastCatches = audioBroadcastManager.get(deviceId);
204 218 if (audioBroadcastCatches.size() > 0) {
205 219 for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatches) {
... ... @@ -512,8 +526,10 @@ public class DeviceServiceImpl implements IDeviceService {
512 526 node.setBasicData(channel);
513 527 node.setParent(false);
514 528 if (channel.getChannelId().length() > 8) {
515   - String gbCodeType = channel.getChannelId().substring(10, 13);
516   - node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) );
  529 + if (channel.getChannelId().length() > 13) {
  530 + String gbCodeType = channel.getChannelId().substring(10, 13);
  531 + node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) );
  532 + }
517 533 }else {
518 534 node.setParent(true);
519 535 }
... ... @@ -669,4 +685,6 @@ public class DeviceServiceImpl implements IDeviceService {
669 685 public List<Device> getAll() {
670 686 return deviceMapper.getAll();
671 687 }
  688 +
  689 +
672 690 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.common.InviteInfo;
  5 +import com.genersoft.iot.vmp.common.InviteSessionStatus;
  6 +import com.genersoft.iot.vmp.common.InviteSessionType;
  7 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
  8 +import com.genersoft.iot.vmp.service.IInviteStreamService;
  9 +import com.genersoft.iot.vmp.service.bean.InviteErrorCallback;
  10 +import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  11 +import org.slf4j.Logger;
  12 +import org.slf4j.LoggerFactory;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.data.redis.core.RedisTemplate;
  15 +import org.springframework.stereotype.Service;
  16 +
  17 +import java.util.List;
  18 +import java.util.Map;
  19 +import java.util.concurrent.ConcurrentHashMap;
  20 +import java.util.concurrent.CopyOnWriteArrayList;
  21 +
  22 +@Service
  23 +public class InviteStreamServiceImpl implements IInviteStreamService {
  24 +
  25 + private final Logger logger = LoggerFactory.getLogger(InviteStreamServiceImpl.class);
  26 +
  27 + private final Map<String, List<InviteErrorCallback<Object>>> inviteErrorCallbackMap = new ConcurrentHashMap<>();
  28 +
  29 + @Autowired
  30 + private RedisTemplate<Object, Object> redisTemplate;
  31 +
  32 + @Override
  33 + public void updateInviteInfo(InviteInfo inviteInfo) {
  34 + if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) {
  35 + logger.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo));
  36 + return;
  37 + }
  38 + InviteInfo inviteInfoForUpdate = null;
  39 +
  40 + if (InviteSessionStatus.ready == inviteInfo.getStatus()) {
  41 + if (inviteInfo.getDeviceId() == null
  42 + || inviteInfo.getChannelId() == null
  43 + || inviteInfo.getType() == null
  44 + || inviteInfo.getStream() == null
  45 + ) {
  46 + return;
  47 + }
  48 + inviteInfoForUpdate = inviteInfo;
  49 + } else {
  50 + InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
  51 + inviteInfo.getChannelId(), inviteInfo.getStream());
  52 + if (inviteInfoInRedis == null) {
  53 + logger.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}",
  54 + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
  55 + return;
  56 + }
  57 + if (inviteInfo.getStreamInfo() != null) {
  58 + inviteInfoInRedis.setStreamInfo(inviteInfo.getStreamInfo());
  59 + }
  60 + if (inviteInfo.getSsrcInfo() != null) {
  61 + inviteInfoInRedis.setSsrcInfo(inviteInfo.getSsrcInfo());
  62 + }
  63 + if (inviteInfo.getStreamMode() != null) {
  64 + inviteInfoInRedis.setStreamMode(inviteInfo.getStreamMode());
  65 + }
  66 + if (inviteInfo.getReceiveIp() != null) {
  67 + inviteInfoInRedis.setReceiveIp(inviteInfo.getReceiveIp());
  68 + }
  69 + if (inviteInfo.getReceivePort() != null) {
  70 + inviteInfoInRedis.setReceivePort(inviteInfo.getReceivePort());
  71 + }
  72 + if (inviteInfo.getStatus() != null) {
  73 + inviteInfoInRedis.setStatus(inviteInfo.getStatus());
  74 + }
  75 +
  76 + inviteInfoForUpdate = inviteInfoInRedis;
  77 +
  78 + }
  79 + String key = VideoManagerConstants.INVITE_PREFIX +
  80 + "_" + inviteInfoForUpdate.getType() +
  81 + "_" + inviteInfoForUpdate.getDeviceId() +
  82 + "_" + inviteInfoForUpdate.getChannelId() +
  83 + "_" + inviteInfoForUpdate.getStream();
  84 + redisTemplate.opsForValue().set(key, inviteInfoForUpdate);
  85 + }
  86 +
  87 + @Override
  88 + public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) {
  89 + String key = VideoManagerConstants.INVITE_PREFIX +
  90 + "_" + (type != null ? type : "*") +
  91 + "_" + (deviceId != null ? deviceId : "*") +
  92 + "_" + (channelId != null ? channelId : "*") +
  93 + "_" + (stream != null ? stream : "*");
  94 + List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
  95 + if (scanResult.size() != 1) {
  96 + return null;
  97 + }
  98 +
  99 + return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
  100 + }
  101 +
  102 + @Override
  103 + public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId) {
  104 + return getInviteInfo(type, deviceId, channelId, null);
  105 + }
  106 +
  107 + @Override
  108 + public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream) {
  109 + return getInviteInfo(type, null, null, stream);
  110 + }
  111 +
  112 + @Override
  113 + public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) {
  114 + String scanKey = VideoManagerConstants.INVITE_PREFIX +
  115 + "_" + (type != null ? type : "*") +
  116 + "_" + (deviceId != null ? deviceId : "*") +
  117 + "_" + (channelId != null ? channelId : "*") +
  118 + "_" + (stream != null ? stream : "*");
  119 + List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey);
  120 + if (scanResult.size() > 0) {
  121 + for (Object keyObj : scanResult) {
  122 + String key = (String) keyObj;
  123 + InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key);
  124 + if (inviteInfo == null) {
  125 + continue;
  126 + }
  127 + redisTemplate.delete(key);
  128 + inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream()));
  129 + }
  130 + }
  131 + }
  132 +
  133 + @Override
  134 + public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId) {
  135 + removeInviteInfo(inviteSessionType, deviceId, channelId, null);
  136 + }
  137 +
  138 + @Override
  139 + public void removeInviteInfo(InviteInfo inviteInfo) {
  140 + removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream());
  141 + }
  142 +
  143 + @Override
  144 + public void once(InviteSessionType type, String deviceId, String channelId, String stream, InviteErrorCallback<Object> callback) {
  145 + String key = buildKey(type, deviceId, channelId, stream);
  146 + List<InviteErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key);
  147 + if (callbacks == null) {
  148 + callbacks = new CopyOnWriteArrayList<>();
  149 + inviteErrorCallbackMap.put(key, callbacks);
  150 + }
  151 + callbacks.add(callback);
  152 +
  153 + }
  154 +
  155 + @Override
  156 + public void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data) {
  157 + String key = buildKey(type, deviceId, channelId, stream);
  158 + List<InviteErrorCallback<Object>> callbacks = inviteErrorCallbackMap.get(key);
  159 + if (callbacks == null) {
  160 + return;
  161 + }
  162 + for (InviteErrorCallback<Object> callback : callbacks) {
  163 + callback.run(code, msg, data);
  164 + }
  165 + inviteErrorCallbackMap.remove(key);
  166 + }
  167 +
  168 + private String buildKey(InviteSessionType type, String deviceId, String channelId, String stream) {
  169 + String key = type + "_" + deviceId + "_" + channelId;
  170 + // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite
  171 + if (stream != null) {
  172 + key += ("_" + stream);
  173 + }
  174 + return key;
  175 + }
  176 +
  177 +
  178 + @Override
  179 + public void clearInviteInfo(String deviceId) {
  180 + removeInviteInfo(null, deviceId, null, null);
  181 + }
  182 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
... ... @@ -3,13 +3,14 @@ package com.genersoft.iot.vmp.service.impl;
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONArray;
5 5 import com.alibaba.fastjson2.JSONObject;
  6 +import com.genersoft.iot.vmp.common.CommonCallback;
6 7 import com.genersoft.iot.vmp.common.VideoManagerConstants;
7 8 import com.genersoft.iot.vmp.conf.DynamicTask;
8 9 import com.genersoft.iot.vmp.conf.SipConfig;
9 10 import com.genersoft.iot.vmp.conf.UserSetting;
10 11 import com.genersoft.iot.vmp.conf.exception.ControllerException;
11 12 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
12   -import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
  13 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
13 14 import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
14 15 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
15 16 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
... ... @@ -56,6 +57,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
56 57 @Autowired
57 58 private SipConfig sipConfig;
58 59  
  60 + @Autowired
  61 + private SSRCFactory ssrcFactory;
  62 +
59 63 @Value("${server.ssl.enabled:false}")
60 64 private boolean sslEnabled;
61 65  
... ... @@ -96,6 +100,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
96 100 @Autowired
97 101 private RedisTemplate<Object, Object> redisTemplate;
98 102  
  103 +
  104 +
  105 +
99 106 /**
100 107 * 初始化
101 108 */
... ... @@ -107,10 +114,8 @@ public class MediaServerServiceImpl implements IMediaServerService {
107 114 continue;
108 115 }
109 116 // 更新
110   - if (mediaServerItem.getSsrcConfig() == null) {
111   - SsrcConfig ssrcConfig = new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain());
112   - mediaServerItem.setSsrcConfig(ssrcConfig);
113   - redisTemplate.opsForValue().set(VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(), mediaServerItem);
  117 + if (ssrcFactory.hasMediaServerSSRC(mediaServerItem.getId())) {
  118 + ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
114 119 }
115 120 // 查询redis是否存在此mediaServer
116 121 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
... ... @@ -122,56 +127,44 @@ public class MediaServerServiceImpl implements IMediaServerService {
122 127 }
123 128 }
124 129  
125   - @Override
126   - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) {
127   - return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,isPlayback);
128   - }
129 130  
130 131 @Override
131 132 public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck,
132   - boolean isPlayback, Integer port, Boolean onlyAuto) {
  133 + boolean isPlayback, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) {
133 134 if (mediaServerItem == null || mediaServerItem.getId() == null) {
134 135 logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null");
135 136 return null;
136 137 }
137 138 // 获取mediaServer可用的ssrc
138   - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
139   -
140   - SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
141   - if (ssrcConfig == null) {
142   - logger.info("media server [ {} ] ssrcConfig is null", mediaServerItem.getId());
143   - return null;
  139 + String ssrc;
  140 + if (presetSsrc != null) {
  141 + ssrc = presetSsrc;
144 142 }else {
145   - String ssrc;
146   - if (presetSsrc != null) {
147   - ssrc = presetSsrc;
  143 + if (isPlayback) {
  144 + ssrc = ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
148 145 }else {
149   - if (isPlayback) {
150   - ssrc = ssrcConfig.getPlayBackSsrc();
151   - }else {
152   - ssrc = ssrcConfig.getPlaySsrc();
153   - }
  146 + ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());
154 147 }
  148 + }
155 149  
156   - if (streamId == null) {
157   - streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
158   - }
159   - int rtpServerPort;
160   - if (mediaServerItem.isRtpEnable()) {
161   - rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, onlyAuto);
162   - } else {
163   - rtpServerPort = mediaServerItem.getRtpProxyPort();
164   - }
165   - redisTemplate.opsForValue().set(key, mediaServerItem);
166   - return new SSRCInfo(rtpServerPort, ssrc, streamId);
  150 + if (streamId == null) {
  151 + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
  152 + }
  153 + int rtpServerPort;
  154 + if (mediaServerItem.isRtpEnable()) {
  155 + rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, onlyAuto, reUsePort, tcpMode);
  156 + } else {
  157 + rtpServerPort = mediaServerItem.getRtpProxyPort();
167 158 }
  159 + return new SSRCInfo(rtpServerPort, ssrc, streamId);
168 160 }
169 161  
170 162 @Override
171   - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback) {
  163 + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port, Boolean onlyAuto) {
172 164 return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null, null);
173 165 }
174 166  
  167 +
175 168 @Override
176 169 public void closeRTPServer(MediaServerItem mediaServerItem, String streamId) {
177 170 if (mediaServerItem == null) {
... ... @@ -181,22 +174,32 @@ public class MediaServerServiceImpl implements IMediaServerService {
181 174 }
182 175  
183 176 @Override
  177 + public void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback<Boolean> callback) {
  178 + if (mediaServerItem == null) {
  179 + callback.run(false);
  180 + return;
  181 + }
  182 + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId, callback);
  183 + }
  184 +
  185 + @Override
184 186 public void closeRTPServer(String mediaServerId, String streamId) {
185 187 MediaServerItem mediaServerItem = this.getOne(mediaServerId);
186 188 closeRTPServer(mediaServerItem, streamId);
187 189 }
188 190  
189 191 @Override
  192 + public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) {
  193 + return zlmrtpServerFactory.updateRtpServerSSRC(mediaServerItem, streamId, ssrc);
  194 + }
  195 +
  196 + @Override
190 197 public void releaseSsrc(String mediaServerItemId, String ssrc) {
191 198 MediaServerItem mediaServerItem = getOne(mediaServerItemId);
192 199 if (mediaServerItem == null || ssrc == null) {
193 200 return;
194 201 }
195   - SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
196   - ssrcConfig.releaseSsrc(ssrc);
197   - mediaServerItem.setSsrcConfig(ssrcConfig);
198   - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
199   - redisTemplate.opsForValue().set(key, mediaServerItem);
  202 + ssrcFactory.releaseSsrc(mediaServerItemId, ssrc);
200 203 }
201 204  
202 205 /**
... ... @@ -204,8 +207,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
204 207 */
205 208 @Override
206 209 public void clearRTPServer(MediaServerItem mediaServerItem) {
207   - mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain()));
208   - redisTemplate.opsForZSet().add(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), mediaServerItem.getId(), 0);
  210 + ssrcFactory.reset(mediaServerItem.getId());
209 211  
210 212 }
211 213  
... ... @@ -215,16 +217,8 @@ public class MediaServerServiceImpl implements IMediaServerService {
215 217 mediaServerMapper.update(mediaSerItem);
216 218 MediaServerItem mediaServerItemInRedis = getOne(mediaSerItem.getId());
217 219 MediaServerItem mediaServerItemInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId());
218   - if (mediaServerItemInRedis != null && mediaServerItemInRedis.getSsrcConfig() != null) {
219   - mediaServerItemInDataBase.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
220   - }else {
221   - mediaServerItemInDataBase.setSsrcConfig(
222   - new SsrcConfig(
223   - mediaServerItemInDataBase.getId(),
224   - null,
225   - sipConfig.getDomain()
226   - )
227   - );
  220 + if (mediaServerItemInRedis == null || ssrcFactory.hasMediaServerSSRC(mediaSerItem.getId())) {
  221 + ssrcFactory.initMediaServerSSRC(mediaServerItemInDataBase.getId(),null);
228 222 }
229 223 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItemInDataBase.getId();
230 224 redisTemplate.opsForValue().set(key, mediaServerItemInDataBase);
... ... @@ -406,14 +400,8 @@ public class MediaServerServiceImpl implements IMediaServerService {
406 400 }
407 401 mediaServerMapper.update(serverItem);
408 402 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId();
409   - if (redisTemplate.opsForValue().get(key) == null) {
410   - SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain());
411   - serverItem.setSsrcConfig(ssrcConfig);
412   - }else {
413   - MediaServerItem mediaServerItemInRedis = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class);
414   - if (Objects.nonNull(mediaServerItemInRedis)) {
415   - serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
416   - }
  403 + if (ssrcFactory.hasMediaServerSSRC(serverItem.getId())) {
  404 + ssrcFactory.initMediaServerSSRC(zlmServerConfig.getGeneralMediaServerId(), null);
417 405 }
418 406 redisTemplate.opsForValue().set(key, serverItem);
419 407 resetOnlineServerItem(serverItem);
... ... @@ -711,8 +699,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
711 699 }
712 700 // zlm连接重试
713 701 logger.warn("[更新ZLM 保活信息]尝试链接zml id {}", mediaServerId);
714   - SsrcConfig ssrcConfig = new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain());
715   - mediaServerItem.setSsrcConfig(ssrcConfig);
  702 + ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
716 703 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
717 704 redisTemplate.opsForValue().set(key, mediaServerItem);
718 705 clearRTPServer(mediaServerItem);
... ... @@ -761,4 +748,5 @@ public class MediaServerServiceImpl implements IMediaServerService {
761 748 result.setGbSend(redisCatchStorage.getGbSendCount(mediaServerItem.getId()));
762 749 return result;
763 750 }
  751 +
764 752 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
3 3 import com.alibaba.fastjson2.JSONObject;
4   -import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.common.InviteInfo;
  5 +import com.genersoft.iot.vmp.common.InviteSessionType;
5 6 import com.genersoft.iot.vmp.conf.DynamicTask;
6 7 import com.genersoft.iot.vmp.conf.UserSetting;
7 8 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
8 9 import com.genersoft.iot.vmp.gb28181.bean.*;
9 10 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  11 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
10 12 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
11 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
12 14 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
... ... @@ -14,6 +16,7 @@ import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
14 16 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
15 17 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
16 18 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  19 +import com.genersoft.iot.vmp.service.IInviteStreamService;
17 20 import com.genersoft.iot.vmp.service.IMediaServerService;
18 21 import com.genersoft.iot.vmp.service.IPlatformService;
19 22 import com.genersoft.iot.vmp.service.IPlayService;
... ... @@ -66,6 +69,9 @@ public class PlatformServiceImpl implements IPlatformService {
66 69 private IRedisCatchStorage redisCatchStorage;
67 70  
68 71 @Autowired
  72 + private SSRCFactory ssrcFactory;
  73 +
  74 + @Autowired
69 75 private IMediaServerService mediaServerService;
70 76  
71 77 @Autowired
... ... @@ -96,6 +102,8 @@ public class PlatformServiceImpl implements IPlatformService {
96 102 @Autowired
97 103 private IPlayService playService;
98 104  
  105 + @Autowired
  106 + private IInviteStreamService inviteStreamService;
99 107  
100 108  
101 109 @Override
... ... @@ -198,6 +206,7 @@ public class PlatformServiceImpl implements IPlatformService {
198 206 // 保存时启用就发送注册
199 207 // 注册成功时由程序直接调用了online方法
200 208 try {
  209 + logger.info("[国标级联] 平台注册 {}", parentPlatform.getDeviceGBId());
201 210 commanderForPlatform.register(parentPlatform, eventResult -> {
202 211 logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId());
203 212 }, null);
... ... @@ -349,6 +358,7 @@ public class PlatformServiceImpl implements IPlatformService {
349 358 List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(platformId);
350 359 if (sendRtpItems != null && sendRtpItems.size() > 0) {
351 360 for (SendRtpItem sendRtpItem : sendRtpItems) {
  361 + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
352 362 redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), null, null);
353 363 MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
354 364 Map<String, Object> param = new HashMap<>(3);
... ... @@ -420,20 +430,22 @@ public class PlatformServiceImpl implements IPlatformService {
420 430 logger.info("[国标级联] 语音喊话未找到可用的zlm. platform: {}", platform.getServerGBId());
421 431 return;
422 432 }
423   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(platform.getServerGBId(), channelId);
424   - if (streamInfo != null) {
  433 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, platform.getServerGBId(), channelId);
  434 +
  435 +
  436 + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
425 437 // 如果zlm不存在这个流,则删除数据即可
426   - MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
  438 + MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId());
427 439 if (mediaServerItemForStreamInfo != null) {
428   - Boolean ready = zlmrtpServerFactory.isStreamReady(mediaServerItemForStreamInfo, streamInfo.getApp(), streamInfo.getStream());
  440 + Boolean ready = zlmrtpServerFactory.isStreamReady(mediaServerItemForStreamInfo, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream());
429 441 if (!ready) {
430 442 // 错误存在于redis中的数据
431   - redisCatchStorage.stopPlay(streamInfo);
  443 + inviteStreamService.removeInviteInfo(inviteInfo);
432 444 }else {
433 445 // 流确实尚在推流,直接回调结果
434 446 JSONObject json = new JSONObject();
435   - json.put("app", streamInfo.getApp());
436   - json.put("stream", streamInfo.getStream());
  447 + json.put("app", inviteInfo.getStreamInfo().getApp());
  448 + json.put("stream", inviteInfo.getStreamInfo().getStream());
437 449 hookEvent.response(mediaServerItemForStreamInfo, json);
438 450 return;
439 451 }
... ... @@ -449,7 +461,11 @@ public class PlatformServiceImpl implements IPlatformService {
449 461 SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, ssrcCheck, false, null, true);
450 462 if (ssrcInfo == null || ssrcInfo.getPort() < 0) {
451 463 logger.info("[国标级联] 发起语音喊话 开启端口监听失败, platform: {}, channel: {}", platform.getServerGBId(), channelId);
452   - errorEvent.response(new SipSubscribe.EventResult(-1, "端口监听失败"));
  464 + SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
  465 + eventResult.statusCode = -1;
  466 + eventResult.msg = "端口监听失败";
  467 + eventResult.type = SipSubscribe.EventResultType.failedToGetPort;
  468 + errorEvent.response(eventResult);
453 469 return;
454 470 }
455 471 logger.info("[国标级联] 语音喊话,发起Invite消息 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}",
... ... @@ -458,7 +474,8 @@ public class PlatformServiceImpl implements IPlatformService {
458 474 String timeOutTaskKey = UUID.randomUUID().toString();
459 475 dynamicTask.startDelay(timeOutTaskKey, () -> {
460 476 // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况
461   - if (redisCatchStorage.queryPlayByDevice(platform.getServerGBId(), channelId) == null) {
  477 + InviteInfo inviteInfoForBroadcast = inviteStreamService.getInviteInfo(InviteSessionType.BROADCAST, platform.getServerGBId(), channelId, null);
  478 + if (inviteInfoForBroadcast == null) {
462 479 logger.info("[国标级联] 发起语音喊话 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", platform.getServerGBId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc());
463 480 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
464 481 try {
... ... @@ -501,7 +518,7 @@ public class PlatformServiceImpl implements IPlatformService {
501 518 if (!mediaServerItem.isRtpEnable()) {
502 519 logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
503 520  
504   - if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
  521 + if (!ssrcFactory.checkSsrc(mediaServerItem.getId(), ssrcInResponse)) {
505 522 // ssrc 不可用
506 523 // 释放ssrc
507 524 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
3   -import com.alibaba.fastjson2.JSON;
4 3 import com.alibaba.fastjson2.JSONArray;
5 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.common.InviteInfo;
  6 +import com.genersoft.iot.vmp.common.InviteSessionStatus;
  7 +import com.genersoft.iot.vmp.common.InviteSessionType;
6 8 import com.genersoft.iot.vmp.common.StreamInfo;
7 9 import com.genersoft.iot.vmp.conf.DynamicTask;
8 10 import com.genersoft.iot.vmp.conf.SipConfig;
... ... @@ -13,9 +15,9 @@ import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
13 15 import com.genersoft.iot.vmp.gb28181.bean.*;
14 16 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
15 17 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
  18 +import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
16 19 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
17 20 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
18   -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
19 21 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
20 22 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
21 23 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
... ... @@ -27,11 +29,11 @@ import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
27 29 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForRtpServerTimeout;
28 30 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
29 31 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
30   -import com.genersoft.iot.vmp.service.IDeviceService;
31   -import com.genersoft.iot.vmp.service.IMediaServerService;
32   -import com.genersoft.iot.vmp.service.IMediaService;
33   -import com.genersoft.iot.vmp.service.IPlayService;
34   -import com.genersoft.iot.vmp.service.bean.*;
  32 +import com.genersoft.iot.vmp.service.*;
  33 +import com.genersoft.iot.vmp.service.bean.InviteErrorCallback;
  34 +import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
  35 +import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
  36 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
35 37 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
36 38 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
37 39 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
... ... @@ -39,7 +41,6 @@ import com.genersoft.iot.vmp.utils.DateUtil;
39 41 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
40 42 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
41 43 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
42   -import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
43 44 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
44 45 import gov.nist.javax.sip.message.SIPResponse;
45 46 import org.slf4j.Logger;
... ... @@ -51,6 +52,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
51 52 import org.springframework.stereotype.Service;
52 53 import org.springframework.util.ObjectUtils;
53 54  
  55 +import javax.sdp.*;
54 56 import javax.sip.InvalidArgumentException;
55 57 import javax.sip.ResponseEvent;
56 58 import javax.sip.SipException;
... ... @@ -88,6 +90,9 @@ public class PlayServiceImpl implements IPlayService {
88 90 private ZLMRTPServerFactory zlmrtpServerFactory;
89 91  
90 92 @Autowired
  93 + private IInviteStreamService inviteStreamService;
  94 +
  95 + @Autowired
91 96 private DeferredResultHolder resultHolder;
92 97  
93 98 @Autowired
... ... @@ -132,125 +137,81 @@ public class PlayServiceImpl implements IPlayService {
132 137 private ZlmHttpHookSubscribe hookSubscribe;
133 138  
134 139 @Autowired
  140 + private SSRCFactory ssrcFactory;
  141 +
  142 + @Autowired
135 143 private RedisTemplate<Object, Object> redisTemplate;
136 144  
137 145  
138 146 @Override
139   - public void play(MediaServerItem mediaServerItem, String deviceId, String channelId,
140   - ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
141   - Runnable timeoutCallback) {
  147 + public SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, InviteErrorCallback<Object> callback) {
142 148 if (mediaServerItem == null) {
143 149 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
144 150 }
145   - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
146   -
147   - RequestMessage msg = new RequestMessage();
148   - msg.setKey(key);
149 151  
150 152 Device device = redisCatchStorage.getDevice(deviceId);
151   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
152   -
153   - if (streamInfo != null) {
154   - String streamId = streamInfo.getStream();
155   - if (streamId == null) {
156   - WVPResult wvpResult = new WVPResult();
157   - wvpResult.setCode(ErrorCode.ERROR100.getCode());
158   - wvpResult.setMsg("点播失败, redis缓存streamId等于null");
159   - msg.setData(wvpResult);
160   - resultHolder.invokeAllResult(msg);
161   - return;
162   - }
163   - String mediaServerId = streamInfo.getMediaServerId();
164   - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
165   -
166   - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
167   - if (rtpInfo.getInteger("code") == 0) {
168   - if (rtpInfo.getBoolean("exist")) {
169   - int localPort = rtpInfo.getInteger("local_port");
170   - if (localPort == 0) {
171   - logger.warn("[点播],点播时发现rtpServerC存在,但是尚未开始推流");
172   - // 此时说明rtpServer已经创建但是流还没有推上来
173   - WVPResult wvpResult = new WVPResult();
174   - wvpResult.setCode(ErrorCode.ERROR100.getCode());
175   - wvpResult.setMsg("点播已经在进行中,请稍候重试");
176   - msg.setData(wvpResult);
177   -
178   - resultHolder.invokeAllResult(msg);
179   - return;
180   - } else {
181   - WVPResult wvpResult = new WVPResult();
182   - wvpResult.setCode(ErrorCode.SUCCESS.getCode());
183   - wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
184   - wvpResult.setData(streamInfo);
185   - msg.setData(wvpResult);
186   - resultHolder.invokeAllResult(msg);
187   - if (hookEvent != null) {
188   - hookEvent.response(mediaServerItem, JSON.parseObject(JSON.toJSONString(streamInfo)));
189   - }
190   - }
191   -
192   - } else {
193   - redisCatchStorage.stopPlay(streamInfo);
  153 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
  154 +
  155 + if (inviteInfo != null ) {
  156 + if (inviteInfo.getStreamInfo() == null) {
  157 + // 点播发起了但是尚未成功, 仅注册回调等待结果即可
  158 + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
  159 + return inviteInfo.getSsrcInfo();
  160 + }else {
  161 + StreamInfo streamInfo = inviteInfo.getStreamInfo();
  162 + String streamId = streamInfo.getStream();
  163 + if (streamId == null) {
  164 + callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null);
  165 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  166 + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(),
  167 + "点播失败, redis缓存streamId等于null",
  168 + null);
  169 + return inviteInfo.getSsrcInfo();
  170 + }
  171 + String mediaServerId = streamInfo.getMediaServerId();
  172 + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  173 +
  174 + Boolean ready = zlmrtpServerFactory.isStreamReady(mediaInfo, "rtp", streamId);
  175 + if (ready != null && ready) {
  176 + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
  177 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  178 + InviteErrorCode.SUCCESS.getCode(),
  179 + InviteErrorCode.SUCCESS.getMsg(),
  180 + streamInfo);
  181 + return inviteInfo.getSsrcInfo();
  182 + }else {
  183 + // 点播发起了但是尚未成功, 仅注册回调等待结果即可
  184 + inviteStreamService.once(InviteSessionType.PLAY, deviceId, channelId, null, callback);
194 185 storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
195   - streamInfo = null;
  186 + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
196 187 }
197   - } else {
198   - //zlm连接失败
199   - redisCatchStorage.stopPlay(streamInfo);
200   - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
201   - streamInfo = null;
202 188 }
203 189 }
204   - if (streamInfo == null) {
205   - String streamId = null;
206   - if (mediaServerItem.isRtpEnable()) {
207   - streamId = String.format("%s_%s", device.getDeviceId(), channelId);
208   - }
209   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false);
210   - if (ssrcInfo == null) {
211   - WVPResult wvpResult = new WVPResult();
212   - wvpResult.setCode(ErrorCode.ERROR100.getCode());
213   - wvpResult.setMsg("开启收流失败");
214   - msg.setData(wvpResult);
215   -
216   - resultHolder.invokeAllResult(msg);
217   - return;
218   - }
219   - play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response) -> {
220   - if (hookEvent != null) {
221   - hookEvent.response(mediaServerItem, response);
222   - }
223   - }, event -> {
224   - // sip error错误
225   - WVPResult wvpResult = new WVPResult();
226   - wvpResult.setCode(ErrorCode.ERROR100.getCode());
227   - wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg));
228   - msg.setData(wvpResult);
229   - resultHolder.invokeAllResult(msg);
230   - if (errorEvent != null) {
231   - errorEvent.response(event);
232   - }
233   - }, (code, msgStr) -> {
234   - // invite点播超时
235   - WVPResult wvpResult = new WVPResult();
236   - wvpResult.setCode(ErrorCode.ERROR100.getCode());
237   - if (code == 0) {
238   - wvpResult.setMsg("点播超时,请稍候重试");
239   - } else if (code == 1) {
240   - wvpResult.setMsg("收流超时,请稍候重试");
241   - }
242   - msg.setData(wvpResult);
243   - // 回复之前所有的点播请求
244   - resultHolder.invokeAllResult(msg);
245   - });
  190 +
  191 + String streamId = null;
  192 + if (mediaServerItem.isRtpEnable()) {
  193 + streamId = String.format("%s_%s", device.getDeviceId(), channelId);
  194 + }
  195 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, false, device.getStreamModeForParam());
  196 + if (ssrcInfo == null) {
  197 + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(), null);
  198 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  199 + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(),
  200 + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getMsg(),
  201 + null);
  202 + return null;
246 203 }
  204 + // TODO 记录点播的状态
  205 + play(mediaServerItem, ssrcInfo, device, channelId, callback);
  206 + return ssrcInfo;
247 207 }
248 208  
249 209 private void talk(MediaServerItem mediaServerItem, Device device, String channelId, String stream,
250 210 ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
251 211 Runnable timeoutCallback, AudioBroadcastEvent audioEvent) {
252 212  
253   - String playSsrc = mediaServerItem.getSsrcConfig().getPlaySsrc();
  213 + String playSsrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId());
  214 +
254 215 if (playSsrc == null) {
255 216 audioEvent.call("ssrc已经用尽");
256 217 return;
... ... @@ -353,7 +314,7 @@ public class PlayServiceImpl implements IPlayService {
353 314  
354 315 streamSession.put(device.getDeviceId(), channelId, "talk",
355 316 sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(),
356   - response, VideoStreamSessionManager.SessionType.talk);
  317 + response, InviteSessionType.TALK);
357 318 } else {
358 319 logger.error("[语音对讲]收到的消息错误,response不是SIPResponse");
359 320 }
... ... @@ -378,7 +339,9 @@ public class PlayServiceImpl implements IPlayService {
378 339 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
379 340  
380 341 streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream());
381   - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
  342 + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult();
  343 + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent;
  344 + eventResult.statusCode = -1;
382 345 eventResult.msg = "命令发送失败";
383 346 errorEvent.response(eventResult);
384 347 }
... ... @@ -390,24 +353,62 @@ public class PlayServiceImpl implements IPlayService {
390 353  
391 354 @Override
392 355 public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
393   - ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
394   - InviteTimeOutCallback timeoutCallback) {
  356 + InviteErrorCallback<Object> callback) {
  357 +
  358 + if (mediaServerItem == null || ssrcInfo == null) {
  359 + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(),
  360 + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(),
  361 + null);
  362 + return;
  363 + }
  364 + logger.info("[点播开始] deviceId: {}, channelId: {},收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
  365 +
  366 + //端口获取失败的ssrcInfo 没有必要发送点播指令
  367 + if (ssrcInfo.getPort() <= 0) {
  368 + logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
  369 + // 释放ssrc
  370 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  371 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
395 372  
396   - logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
  373 + callback.run(InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null);
  374 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  375 + InviteErrorCode.ERROR_FOR_RESOURCE_EXHAUSTION.getCode(), "点播端口分配异常", null);
  376 + return;
  377 + }
  378 +
  379 + // 初始化redis中的invite消息状态
  380 + InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
  381 + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY,
  382 + InviteSessionStatus.ready);
  383 + inviteStreamService.updateInviteInfo(inviteInfo);
397 384 // 超时处理
398 385 String timeOutTaskKey = UUID.randomUUID().toString();
399 386 dynamicTask.startDelay(timeOutTaskKey, () -> {
400 387 // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况
401   - if (redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId) == null) {
  388 + InviteInfo inviteInfoForTimeOut = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
  389 + if (inviteInfoForTimeOut == null || inviteInfoForTimeOut.getStreamInfo() == null) {
402 390 logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc());
403 391 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  392 +// InviteInfo inviteInfoForTimeout = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.play, device.getDeviceId(), channelId);
  393 +// if (inviteInfoForTimeout == null) {
  394 +// return;
  395 +// }
  396 +// if (InviteSessionStatus.ok == inviteInfoForTimeout.getStatus() ) {
  397 +// // TODO 发送bye
  398 +// }else {
  399 +// // TODO 发送cancel
  400 +// }
  401 + callback.run(InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
  402 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  403 + InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_STREAM_TIMEOUT.getMsg(), null);
  404 +
  405 + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
404 406 try {
405 407 cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null);
406 408 } catch (InvalidArgumentException | ParseException | SipException |
407 409 SsrcTransactionNotFoundException e) {
408 410 logger.error("[点播超时], 发送BYE失败 {}", e.getMessage());
409 411 } finally {
410   - timeoutCallback.run(1, "收流超时");
411 412 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
412 413 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
413 414 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
... ... @@ -418,28 +419,26 @@ public class PlayServiceImpl implements IPlayService {
418 419 }
419 420 }
420 421 }, userSetting.getPlayTimeout());
421   - //端口获取失败的ssrcInfo 没有必要发送点播指令
422   - if (ssrcInfo.getPort() <= 0) {
423   - logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo);
424   - dynamicTask.stop(timeOutTaskKey);
425   - // 释放ssrc
426   - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
427   - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
428 422  
429   - RequestMessage msg = new RequestMessage();
430   - msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + device.getDeviceId() + channelId);
431   - msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "点播端口分配异常"));
432   - resultHolder.invokeAllResult(msg);
433   - return;
434   - }
435 423 try {
436 424 cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
437 425 logger.info("收到订阅消息: " + response.toJSONString());
438 426 dynamicTask.stop(timeOutTaskKey);
439   -
440 427 // hook响应
441   - onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
442   - hookEvent.response(mediaServerItemInuse, response);
  428 + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
  429 + if (streamInfo == null){
  430 + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
  431 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
  432 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  433 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
  434 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
  435 + return;
  436 + }
  437 + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
  438 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  439 + InviteErrorCode.SUCCESS.getCode(),
  440 + InviteErrorCode.SUCCESS.getMsg(),
  441 + streamInfo);
443 442 logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
444 443 String streamUrl;
445 444 if (mediaServerItemInuse.getRtspPort() != 0) {
... ... @@ -454,6 +453,8 @@ public class PlayServiceImpl implements IPlayService {
454 453 zlmresTfulUtils.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName);
455 454  
456 455 }, (event) -> {
  456 + inviteInfo.setStatus(InviteSessionStatus.ok);
  457 +
457 458 ResponseEvent responseEvent = (ResponseEvent) event.event;
458 459 String contentString = new String(responseEvent.getResponse().getRawContent());
459 460 // 获取ssrc
... ... @@ -464,44 +465,124 @@ public class PlayServiceImpl implements IPlayService {
464 465 String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim();
465 466 // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
466 467 if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
  468 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
  469 + String substring = contentString.substring(0, contentString.indexOf("y="));
  470 + try {
  471 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  472 + int port = -1;
  473 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  474 + for (Object description : mediaDescriptions) {
  475 + MediaDescription mediaDescription = (MediaDescription) description;
  476 + Media media = mediaDescription.getMedia();
  477 +
  478 + Vector mediaFormats = media.getMediaFormats(false);
  479 + if (mediaFormats.contains("96")) {
  480 + port = media.getMediaPort();
  481 + break;
  482 + }
  483 + }
  484 + logger.info("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
  485 + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());
  486 + logger.info("[点播-TCP主动连接对方] 结果: {}", jsonObject);
  487 + } catch (SdpException e) {
  488 + logger.error("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e);
  489 + dynamicTask.stop(timeOutTaskKey);
  490 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
  491 + // 释放ssrc
  492 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  493 +
  494 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  495 +
  496 + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  497 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
  498 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  499 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  500 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
  501 + }
  502 + }
467 503 return;
468 504 }
469 505 logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
470 506 if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
471 507 logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
472   -
473   - if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
  508 + if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) {
474 509 // ssrc 不可用
  510 + logger.info("[点播消息] SSRC修正时发现ssrc不可使用 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
475 511 // 释放ssrc
476   - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  512 + ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
477 513 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
478   - event.msg = "下级自定义了ssrc,但是此ssrc不可用";
479   - event.statusCode = 400;
480   - errorEvent.response(event);
  514 +
  515 + callback.run(InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(),
  516 + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null);
  517 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  518 + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(),
  519 + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null);
  520 +
481 521 return;
482 522 }
483   -
484   - // 单端口模式streamId也有变化,需要重新设置监听
  523 + // 单端口模式streamId也有变化,重新设置监听即可
485 524 if (!mediaServerItem.isRtpEnable()) {
486 525 // 添加订阅
487 526 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
488 527 subscribe.removeSubscribe(hookSubscribe);
489   - hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
  528 + String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();
  529 + hookSubscribe.getContent().put("stream", stream);
  530 + inviteInfo.setStream(stream);
490 531 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
491 532 logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
492 533 dynamicTask.stop(timeOutTaskKey);
493 534 // hook响应
494   - onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId);
495   - hookEvent.response(mediaServerItemInUse, response);
  535 + StreamInfo streamInfo = onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId);
  536 + if (streamInfo == null){
  537 + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
  538 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
  539 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  540 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
  541 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
  542 + return;
  543 + }
  544 + callback.run(InviteErrorCode.SUCCESS.getCode(),
  545 + InviteErrorCode.SUCCESS.getMsg(), null);
  546 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  547 + InviteErrorCode.SUCCESS.getCode(),
  548 + InviteErrorCode.SUCCESS.getMsg(),
  549 + streamInfo);
496 550 });
  551 + return;
497 552 }
498   - // 关闭rtp server
499   - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
500   - // 重新开启ssrc server
501   - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort(), false);
502 553  
  554 + // 更新ssrc
  555 + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse);
  556 + if (!result) {
  557 + try {
  558 + logger.warn("[点播] 更新ssrc失败,停止点播 {}/{}", device.getDeviceId(), channelId);
  559 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
  560 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  561 + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
  562 + }
  563 +
  564 + dynamicTask.stop(timeOutTaskKey);
  565 + // 释放ssrc
  566 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  567 +
  568 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  569 +
  570 + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
  571 + "下级自定义了ssrc,重新设置收流信息失败", null);
  572 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  573 + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
  574 + "下级自定义了ssrc,重新设置收流信息失败", null);
  575 +
  576 + }else {
  577 + ssrcInfo.setSsrc(ssrcInResponse);
  578 + inviteInfo.setSsrcInfo(ssrcInfo);
  579 + inviteInfo.setStream(ssrcInfo.getStream());
  580 + }
  581 + }else {
  582 + logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");
503 583 }
504 584 }
  585 + inviteStreamService.updateInviteInfo(inviteInfo);
505 586 }, (event) -> {
506 587 dynamicTask.stop(timeOutTaskKey);
507 588 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
... ... @@ -509,7 +590,14 @@ public class PlayServiceImpl implements IPlayService {
509 590 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
510 591  
511 592 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
512   - errorEvent.response(event);
  593 +
  594 + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(),
  595 + String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null);
  596 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  597 + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
  598 + String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg), null);
  599 +
  600 + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
513 601 });
514 602 } catch (InvalidArgumentException | SipException | ParseException e) {
515 603  
... ... @@ -520,63 +608,58 @@ public class PlayServiceImpl implements IPlayService {
520 608 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
521 609  
522 610 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
523   - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
524   - eventResult.msg = "命令发送失败";
525   - errorEvent.response(eventResult);
  611 +
  612 + callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(),
  613 + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null);
  614 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  615 + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(),
  616 + InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null);
  617 +
  618 + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, device.getDeviceId(), channelId);
526 619 }
527 620 }
528 621  
529 622 @Override
530   - public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) {
  623 + public StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) {
531 624 StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId);
532   - RequestMessage msg = new RequestMessage();
533   - msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId);
534 625 if (streamInfo != null) {
535 626 DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
536 627 if (deviceChannel != null) {
537 628 deviceChannel.setStreamId(streamInfo.getStream());
538 629 storager.startPlay(deviceId, channelId, streamInfo.getStream());
539 630 }
540   - redisCatchStorage.startPlay(streamInfo);
541   -
542   - WVPResult wvpResult = new WVPResult();
543   - wvpResult.setCode(ErrorCode.SUCCESS.getCode());
544   - wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
545   - wvpResult.setData(streamInfo);
546   -
547   - msg.setData(wvpResult);
548   - resultHolder.invokeAllResult(msg);
549   -
550   - } else {
551   - logger.warn("设备预览API调用失败!");
552   - msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "设备预览API调用失败!"));
553   - resultHolder.invokeAllResult(msg);
  631 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
  632 + if (inviteInfo != null) {
  633 + inviteInfo.setStatus(InviteSessionStatus.ok);
  634 + inviteInfo.setStreamInfo(streamInfo);
  635 + inviteStreamService.updateInviteInfo(inviteInfo);
  636 + }
554 637 }
  638 + return streamInfo;
  639 +
555 640 }
556 641  
557   - private void onPublishHandlerForPlayback(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, PlayBackCallback playBackCallback) {
  642 + private StreamInfo onPublishHandlerForPlayback(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, String startTime, String endTime) {
558 643  
559 644 StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId);
560   - PlayBackResult<StreamInfo> playBackResult = new PlayBackResult<>();
561 645 if (streamInfo != null) {
  646 + streamInfo.setStartTime(startTime);
  647 + streamInfo.setEndTime(endTime);
562 648 DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
563 649 if (deviceChannel != null) {
564 650 deviceChannel.setStreamId(streamInfo.getStream());
565 651 storager.startPlay(deviceId, channelId, streamInfo.getStream());
566 652 }
567   - redisCatchStorage.startPlay(streamInfo);
  653 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, deviceId, channelId);
  654 + if (inviteInfo != null) {
  655 + inviteInfo.setStatus(InviteSessionStatus.ok);
568 656  
  657 + inviteInfo.setStreamInfo(streamInfo);
  658 + inviteStreamService.updateInviteInfo(inviteInfo);
  659 + }
569 660  
570   - playBackResult.setCode(ErrorCode.SUCCESS.getCode());
571   - playBackResult.setMsg(ErrorCode.SUCCESS.getMsg());
572   - playBackResult.setData(streamInfo);
573   - playBackCallback.call(playBackResult);
574   - } else {
575   - logger.warn("录像回放调用失败!");
576   - playBackResult.setCode(ErrorCode.ERROR100.getCode());
577   - playBackResult.setMsg("录像回放调用失败!");
578   - playBackCallback.call(playBackResult);
579 661 }
  662 + return streamInfo;
580 663 }
581 664  
582 665 @Override
... ... @@ -615,24 +698,24 @@ public class PlayServiceImpl implements IPlayService {
615 698  
616 699 @Override
617 700 public void playBack(String deviceId, String channelId, String startTime,
618   - String endTime, InviteStreamCallback inviteStreamCallback,
619   - PlayBackCallback callback) {
  701 + String endTime, InviteErrorCallback<Object> callback) {
620 702 Device device = storager.queryVideoDevice(deviceId);
621 703 if (device == null) {
622 704 return;
623 705 }
624 706 MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
625   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, device.isSsrcCheck(), true);
626   -
627   - playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, inviteStreamCallback, callback);
  707 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,false, device.getStreamModeForParam());
  708 + playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, callback);
628 709 }
629 710  
630 711 @Override
631 712 public void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,
632   - String deviceId, String channelId, String startTime,
633   - String endTime, InviteStreamCallback infoCallBack,
634   - PlayBackCallback playBackCallback) {
  713 + String deviceId, String channelId, String startTime,
  714 + String endTime, InviteErrorCallback<Object> callback) {
635 715 if (mediaServerItem == null || ssrcInfo == null) {
  716 + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(),
  717 + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(),
  718 + null);
636 719 return;
637 720 }
638 721  
... ... @@ -640,115 +723,176 @@ public class PlayServiceImpl implements IPlayService {
640 723 if (device == null) {
641 724 throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备: " + deviceId + "不存在");
642 725 }
643   -
644   - PlayBackResult<StreamInfo> playBackResult = new PlayBackResult<>();
  726 + logger.info("[录像回放] deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}",
  727 + device.getDeviceId(), channelId, startTime, endTime, ssrcInfo.getPort(), device.getStreamMode(),
  728 + ssrcInfo.getSsrc(), device.isSsrcCheck());
  729 + // 初始化redis中的invite消息状态
  730 + InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
  731 + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAYBACK,
  732 + InviteSessionStatus.ready);
  733 + inviteStreamService.updateInviteInfo(inviteInfo);
645 734 String playBackTimeOutTaskKey = UUID.randomUUID().toString();
646 735 dynamicTask.startDelay(playBackTimeOutTaskKey, () -> {
647   - logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
648   - playBackResult.setCode(ErrorCode.ERROR100.getCode());
649   - playBackResult.setMsg("回放超时");
  736 + logger.warn("[录像回放] 超时,deviceId:{} ,channelId:{}", deviceId, channelId);
  737 + inviteStreamService.removeInviteInfo(inviteInfo);
  738 + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(), InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getMsg(), null);
650 739  
651 740 try {
652 741 cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null);
653 742 } catch (InvalidArgumentException | ParseException | SipException e) {
654   - logger.error("[录像流]回放超时 发送BYE失败 {}", e.getMessage());
  743 + logger.error("[录像回放] 超时 发送BYE失败 {}", e.getMessage());
655 744 } catch (SsrcTransactionNotFoundException e) {
656 745 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
657 746 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
658 747 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
659 748 streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
660 749 }
661   - // 回复之前所有的点播请求
662   - playBackCallback.call(playBackResult);
663 750 }, userSetting.getPlayTimeout());
664 751  
665 752 SipSubscribe.Event errorEvent = event -> {
  753 + logger.info("[录像回放] 失败,{} {}", event.statusCode, event.msg);
666 754 dynamicTask.stop(playBackTimeOutTaskKey);
667   - playBackResult.setCode(ErrorCode.ERROR100.getCode());
668   - playBackResult.setMsg(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg));
669   - playBackResult.setEvent(event);
670   - playBackCallback.call(playBackResult);
  755 + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_ERROR.getCode(),
  756 + String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg), null);
  757 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  758 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
671 759 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  760 + inviteStreamService.removeInviteInfo(inviteInfo);
672 761 };
673 762  
674   - InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
675   - logger.info("收到回放订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
  763 + ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInuse, jsonObject) -> {
  764 + logger.info("收到回放订阅消息: " + jsonObject);
676 765 dynamicTask.stop(playBackTimeOutTaskKey);
677   - StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
  766 + StreamInfo streamInfo = onPublishHandlerForPlayback(mediaServerItemInuse, jsonObject, deviceId, channelId, startTime, endTime);
678 767 if (streamInfo == null) {
679 768 logger.warn("设备回放API调用失败!");
680   - playBackResult.setCode(ErrorCode.ERROR100.getCode());
681   - playBackResult.setMsg("设备回放API调用失败!");
682   - playBackCallback.call(playBackResult);
  769 + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
  770 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
683 771 return;
684 772 }
685   - redisCatchStorage.startPlayback(streamInfo, inviteStreamInfo.getCallId());
686   - playBackResult.setCode(ErrorCode.SUCCESS.getCode());
687   - playBackResult.setMsg(ErrorCode.SUCCESS.getMsg());
688   - playBackResult.setData(streamInfo);
689   - playBackResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
690   - playBackResult.setResponse(inviteStreamInfo.getResponse());
691   - playBackCallback.call(playBackResult);
  773 + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
  774 + logger.info("[录像回放] 成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channelId, startTime, endTime);
692 775 };
693 776  
694 777 try {
695   - cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
  778 + cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime,
696 779 hookEvent, eventResult -> {
697   - if (eventResult.type == SipSubscribe.EventResultType.response) {
698   - ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
699   - String contentString = new String(responseEvent.getResponse().getRawContent());
700   - // 获取ssrc
701   - int ssrcIndex = contentString.indexOf("y=");
702   - // 检查是否有y字段
703   - if (ssrcIndex >= 0) {
704   - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
705   - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
706   - // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
707   - if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
708   - return;
709   - }
710   - logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
711   - if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
712   - logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
713   -
714   - if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
715   - // ssrc 不可用
  780 + inviteInfo.setStatus(InviteSessionStatus.ok);
  781 + ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
  782 + String contentString = new String(responseEvent.getResponse().getRawContent());
  783 + // 获取ssrc
  784 + int ssrcIndex = contentString.indexOf("y=");
  785 + // 检查是否有y字段
  786 + if (ssrcIndex >= 0) {
  787 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
  788 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  789 + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
  790 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
  791 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
  792 + String substring = contentString.substring(0, contentString.indexOf("y="));
  793 + try {
  794 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  795 + int port = -1;
  796 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  797 + for (Object description : mediaDescriptions) {
  798 + MediaDescription mediaDescription = (MediaDescription) description;
  799 + Media media = mediaDescription.getMedia();
  800 +
  801 + Vector mediaFormats = media.getMediaFormats(false);
  802 + if (mediaFormats.contains("96")) {
  803 + port = media.getMediaPort();
  804 + break;
  805 + }
  806 + }
  807 + logger.info("[录像回放-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
  808 + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());
  809 + logger.info("[录像回放-TCP主动连接对方] 结果: {}", jsonObject);
  810 + } catch (SdpException e) {
  811 + logger.error("[录像回放-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e);
  812 + dynamicTask.stop(playBackTimeOutTaskKey);
  813 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
716 814 // 释放ssrc
717 815 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  816 +
718 817 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
719   - eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
720   - eventResult.statusCode = 400;
721   - errorEvent.response(eventResult);
722   - return;
  818 +
  819 + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  820 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
  821 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  822 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  823 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
723 824 }
  825 + }
  826 + return;
  827 + }
  828 + logger.info("[录像回放] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
  829 + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
  830 + logger.info("[录像回放] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
  831 +
  832 + if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) {
  833 + // ssrc 不可用
  834 + logger.info("[录像回放] SSRC修正时发现ssrc不可使用 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
  835 + // 释放ssrc
  836 + dynamicTask.stop(playBackTimeOutTaskKey);
  837 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  838 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  839 + callback.run(InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(),
  840 + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null);
  841 + return;
  842 + }
  843 +
  844 + // 单端口模式streamId也有变化,需要重新设置监听
  845 + if (!mediaServerItem.isRtpEnable()) {
  846 + // 添加订阅
  847 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
  848 + subscribe.removeSubscribe(hookSubscribe);
  849 + String stream = String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase();
  850 + hookSubscribe.getContent().put("stream", stream);
  851 + inviteInfo.setStream(stream);
  852 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
  853 + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
  854 + dynamicTask.stop(playBackTimeOutTaskKey);
  855 + // hook响应
  856 + hookEvent.response(mediaServerItemInUse, response);
  857 + });
  858 + }
  859 + // 更新ssrc
  860 + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse);
  861 + if (!result) {
  862 + try {
  863 + logger.warn("[录像回放] 更新ssrc失败,停止录像回放 {}/{}", device.getDeviceId(), channelId);
  864 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
  865 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  866 + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
724 867  
725   - // 单端口模式streamId也有变化,需要重新设置监听
726   - if (!mediaServerItem.isRtpEnable()) {
727   - // 添加订阅
728   - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
729   - subscribe.removeSubscribe(hookSubscribe);
730   - hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
731   - subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
732   - logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
733   - dynamicTask.stop(playBackTimeOutTaskKey);
734   - // hook响应
735   - onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, playBackCallback);
736   - hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
737   - });
738 868 }
739   - // 关闭rtp server
740   - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
741   - // 重新开启ssrc server
742   - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort(), false);
  869 +
  870 + dynamicTask.stop(playBackTimeOutTaskKey);
  871 + // 释放ssrc
  872 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  873 +
  874 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  875 +
  876 + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
  877 + "下级自定义了ssrc,重新设置收流信息失败", null);
  878 +
  879 + }else {
  880 + ssrcInfo.setSsrc(ssrcInResponse);
  881 + inviteInfo.setSsrcInfo(ssrcInfo);
  882 + inviteInfo.setStream(ssrcInfo.getStream());
743 883 }
  884 + }else {
  885 + logger.info("[点播消息] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");
744 886 }
745 887 }
746   -
  888 + inviteStreamService.updateInviteInfo(inviteInfo);
747 889 }, errorEvent);
748 890 } catch (InvalidArgumentException | SipException | ParseException e) {
749 891 logger.error("[命令发送失败] 回放: {}", e.getMessage());
750 892  
751   - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
  893 + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult();
  894 + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent;
  895 + eventResult.statusCode = -1;
752 896 eventResult.msg = "命令发送失败";
753 897 errorEvent.response(eventResult);
754 898 }
... ... @@ -756,43 +900,50 @@ public class PlayServiceImpl implements IPlayService {
756 900  
757 901  
758 902 @Override
759   - public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback) {
  903 + public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteErrorCallback<Object> callback) {
760 904 Device device = storager.queryVideoDevice(deviceId);
761 905 if (device == null) {
762 906 return;
763 907 }
764 908 MediaServerItem newMediaServerItem = getNewMediaServerItemHasAssist(device);
765 909 if (newMediaServerItem == null) {
766   - PlayBackResult<StreamInfo> downloadResult = new PlayBackResult<>();
767   - downloadResult.setCode(ErrorCode.ERROR100.getCode());
768   - downloadResult.setMsg("未找到assist服务");
769   - playBackCallback.call(downloadResult);
  910 + callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(),
  911 + InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(),
  912 + null);
770 913 return;
771 914 }
772   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, device.isSsrcCheck(), true);
773   -
774   - download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, infoCallBack, playBackCallback);
  915 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false,false, device.getStreamModeForParam());
  916 + download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, callback);
775 917 }
776 918  
777 919  
778 920 @Override
779   - public void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack) {
  921 + public void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteErrorCallback<Object> callback) {
780 922 if (mediaServerItem == null || ssrcInfo == null) {
  923 + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(),
  924 + InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getMsg(),
  925 + null);
781 926 return;
782 927 }
783   -
784 928 Device device = storager.queryVideoDevice(deviceId);
785 929 if (device == null) {
786   - throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "不存在");
  930 + callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(),
  931 + "设备:" + deviceId + "不存在",
  932 + null);
  933 + return;
787 934 }
788   - PlayBackResult<StreamInfo> downloadResult = new PlayBackResult<>();
789   -
  935 + logger.info("[录像下载] deviceId: {}, channelId: {}, 下载速度:{}, 收流端口:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, downloadSpeed, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
  936 + // 初始化redis中的invite消息状态
  937 + InviteInfo inviteInfo = InviteInfo.getinviteInfo(device.getDeviceId(), channelId, ssrcInfo.getStream(), ssrcInfo,
  938 + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD,
  939 + InviteSessionStatus.ready);
  940 + inviteStreamService.updateInviteInfo(inviteInfo);
790 941 String downLoadTimeOutTaskKey = UUID.randomUUID().toString();
791 942 dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> {
792 943 logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId));
793   - downloadResult.setCode(ErrorCode.ERROR100.getCode());
794   - downloadResult.setMsg("录像下载请求超时");
795   - hookCallBack.call(downloadResult);
  944 + inviteStreamService.removeInviteInfo(inviteInfo);
  945 + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(),
  946 + InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getMsg(), null);
796 947  
797 948 // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
798 949 try {
... ... @@ -808,85 +959,137 @@ public class PlayServiceImpl implements IPlayService {
808 959  
809 960 SipSubscribe.Event errorEvent = event -> {
810 961 dynamicTask.stop(downLoadTimeOutTaskKey);
811   - downloadResult.setCode(ErrorCode.ERROR100.getCode());
812   - downloadResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg));
813   - downloadResult.setEvent(event);
814   - hookCallBack.call(downloadResult);
  962 + callback.run(InviteErrorCode.ERROR_FOR_SIGNALLING_TIMEOUT.getCode(),
  963 + String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg), null);
815 964 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  965 + inviteStreamService.removeInviteInfo(inviteInfo);
816 966 };
817   - InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
818   - logger.info("收到订阅消息: " + inviteStreamInfo.getCallId());
  967 + ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInuse, jsonObject) -> {
  968 + logger.info("[录像下载]收到订阅消息: " + jsonObject);
819 969 dynamicTask.stop(downLoadTimeOutTaskKey);
820   - StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
821   - streamInfo.setStartTime(startTime);
822   - streamInfo.setEndTime(endTime);
823   - redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
824   - downloadResult.setCode(ErrorCode.SUCCESS.getCode());
825   - downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
826   - downloadResult.setData(streamInfo);
827   - downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
828   - downloadResult.setResponse(inviteStreamInfo.getResponse());
829   - hookCallBack.call(downloadResult);
  970 + StreamInfo streamInfo = onPublishHandlerForDownload(mediaServerItemInuse, jsonObject, deviceId, channelId, startTime, endTime);
  971 + if (streamInfo == null) {
  972 + logger.warn("[录像下载] 获取流地址信息失败");
  973 + callback.run(InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getCode(),
  974 + InviteErrorCode.ERROR_FOR_STREAM_PARSING_EXCEPTIONS.getMsg(), null);
  975 + return;
  976 + }
  977 + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
  978 + logger.info("[录像下载] 调用成功 deviceId: {}, channelId: {}, 开始时间: {}, 结束时间: {}", device.getDeviceId(), channelId, startTime, endTime);
830 979 };
831 980 try {
832   - cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
833   - hookEvent, errorEvent, eventResult ->
834   - {
835   - if (eventResult.type == SipSubscribe.EventResultType.response) {
836   - ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
837   - String contentString = new String(responseEvent.getResponse().getRawContent());
838   - // 获取ssrc
839   - int ssrcIndex = contentString.indexOf("y=");
840   - // 检查是否有y字段
841   - if (ssrcIndex >= 0) {
842   - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
843   - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
844   - // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
845   - if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
846   - return;
847   - }
848   - logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
849   - if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
850   - logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
851   -
852   - if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
853   - // ssrc 不可用
  981 + cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed,
  982 + hookEvent, errorEvent, eventResult ->{
  983 + inviteInfo.setStatus(InviteSessionStatus.ok);
  984 + ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
  985 + String contentString = new String(responseEvent.getResponse().getRawContent());
  986 + // 获取ssrc
  987 + int ssrcIndex = contentString.indexOf("y=");
  988 + // 检查是否有y字段
  989 + if (ssrcIndex >= 0) {
  990 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
  991 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  992 + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
  993 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
  994 + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
  995 + String substring = contentString.substring(0, contentString.indexOf("y="));
  996 + try {
  997 + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
  998 + int port = -1;
  999 + Vector mediaDescriptions = sdp.getMediaDescriptions(true);
  1000 + for (Object description : mediaDescriptions) {
  1001 + MediaDescription mediaDescription = (MediaDescription) description;
  1002 + Media media = mediaDescription.getMedia();
  1003 +
  1004 + Vector mediaFormats = media.getMediaFormats(false);
  1005 + if (mediaFormats.contains("96")) {
  1006 + port = media.getMediaPort();
  1007 + break;
  1008 + }
  1009 + }
  1010 + logger.info("[录像下载-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck());
  1011 + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream());
  1012 + logger.info("[录像下载-TCP主动连接对方] 结果: {}", jsonObject);
  1013 + } catch (SdpException e) {
  1014 + logger.error("[录像下载-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e);
  1015 + dynamicTask.stop(downLoadTimeOutTaskKey);
  1016 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
854 1017 // 释放ssrc
855 1018 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  1019 +
856 1020 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
857   - eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
858   - eventResult.statusCode = 400;
859   - errorEvent.response(eventResult);
860   - return;
  1021 +
  1022 + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  1023 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
  1024 + inviteStreamService.call(InviteSessionType.PLAY, device.getDeviceId(), channelId, null,
  1025 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(),
  1026 + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null);
861 1027 }
  1028 + }
  1029 + return;
  1030 + }
  1031 + logger.info("[录像下载] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
  1032 + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
  1033 + logger.info("[录像下载] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
  1034 +
  1035 + if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) {
  1036 + // ssrc 不可用
  1037 + // 释放ssrc
  1038 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  1039 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  1040 + callback.run(InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getCode(),
  1041 + InviteErrorCode.ERROR_FOR_SSRC_UNAVAILABLE.getMsg(), null);
  1042 + return;
  1043 + }
  1044 +
  1045 + // 单端口模式streamId也有变化,需要重新设置监听
  1046 + if (!mediaServerItem.isRtpEnable()) {
  1047 + // 添加订阅
  1048 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
  1049 + subscribe.removeSubscribe(hookSubscribe);
  1050 + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
  1051 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
  1052 + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
  1053 + dynamicTask.stop(downLoadTimeOutTaskKey);
  1054 + hookEvent.response(mediaServerItemInUse, response);
  1055 + });
  1056 + }
862 1057  
863   - // 单端口模式streamId也有变化,需要重新设置监听
864   - if (!mediaServerItem.isRtpEnable()) {
865   - // 添加订阅
866   - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
867   - subscribe.removeSubscribe(hookSubscribe);
868   - hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
869   - subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
870   - logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
871   - dynamicTask.stop(downLoadTimeOutTaskKey);
872   - // hook响应
873   - onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, hookCallBack);
874   - hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
875   - });
  1058 + // 更新ssrc
  1059 + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse);
  1060 + if (!result) {
  1061 + try {
  1062 + logger.warn("[录像下载] 更新ssrc失败,停止录像回放 {}/{}", device.getDeviceId(), channelId);
  1063 + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null);
  1064 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  1065 + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
876 1066 }
877   - // 关闭rtp server
878   - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
879   - // 重新开启ssrc server
880   - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort(), false);
  1067 +
  1068 + dynamicTask.stop(downLoadTimeOutTaskKey);
  1069 + // 释放ssrc
  1070 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  1071 +
  1072 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  1073 +
  1074 + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(),
  1075 + "下级自定义了ssrc,重新设置收流信息失败", null);
  1076 +
  1077 + }else {
  1078 + ssrcInfo.setSsrc(ssrcInResponse);
  1079 + inviteInfo.setSsrcInfo(ssrcInfo);
  1080 + inviteInfo.setStream(ssrcInfo.getStream());
881 1081 }
  1082 + }else {
  1083 + logger.info("[录像下载] 收到invite 200, 下级自定义了ssrc, 但是当前模式无需修正");
882 1084 }
883 1085 }
884   -
885 1086 });
886 1087 } catch (InvalidArgumentException | SipException | ParseException e) {
887 1088 logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
888 1089  
889   - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null));
  1090 + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult();
  1091 + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent;
  1092 + eventResult.statusCode = -1;
890 1093 eventResult.msg = "命令发送失败";
891 1094 errorEvent.response(eventResult);
892 1095 }
... ... @@ -894,21 +1097,22 @@ public class PlayServiceImpl implements IPlayService {
894 1097  
895 1098 @Override
896 1099 public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) {
897   - StreamInfo streamInfo = redisCatchStorage.queryDownload(deviceId, channelId, stream, null);
898   - if (streamInfo != null) {
899   - if (streamInfo.getProgress() == 1) {
900   - return streamInfo;
  1100 + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream);
  1101 +
  1102 + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
  1103 + if (inviteInfo.getStreamInfo().getProgress() == 1) {
  1104 + return inviteInfo.getStreamInfo();
901 1105 }
902 1106  
903 1107 // 获取当前已下载时长
904   - String mediaServerId = streamInfo.getMediaServerId();
  1108 + String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
905 1109 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
906 1110 if (mediaServerItem == null) {
907 1111 logger.warn("查询录像信息时发现节点已离线");
908 1112 return null;
909 1113 }
910 1114 if (mediaServerItem.getRecordAssistPort() > 0) {
911   - JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, streamInfo.getApp(), streamInfo.getStream(), null);
  1115 + JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream(), null);
912 1116 if (jsonObject == null) {
913 1117 throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接Assist服务失败");
914 1118 }
... ... @@ -916,10 +1120,10 @@ public class PlayServiceImpl implements IPlayService {
916 1120 long duration = jsonObject.getLong("data");
917 1121  
918 1122 if (duration == 0) {
919   - streamInfo.setProgress(0);
  1123 + inviteInfo.getStreamInfo().setProgress(0);
920 1124 } else {
921   - String startTime = streamInfo.getStartTime();
922   - String endTime = streamInfo.getEndTime();
  1125 + String startTime = inviteInfo.getStreamInfo().getStartTime();
  1126 + String endTime = inviteInfo.getStreamInfo().getEndTime();
923 1127 long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
924 1128 long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
925 1129  
... ... @@ -927,29 +1131,31 @@ public class PlayServiceImpl implements IPlayService {
927 1131 BigDecimal totalCount = new BigDecimal(end - start);
928 1132 BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
929 1133 double process = divide.doubleValue();
930   - streamInfo.setProgress(process);
  1134 + inviteInfo.getStreamInfo().setProgress(process);
931 1135 }
  1136 + inviteStreamService.updateInviteInfo(inviteInfo);
932 1137 }
933 1138 }
  1139 + return inviteInfo.getStreamInfo();
934 1140 }
935   - return streamInfo;
  1141 + return null;
936 1142 }
937 1143  
938   - @Override
939   - public void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String uuid) {
940   - RequestMessage msg = new RequestMessage();
941   - msg.setKey(DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId);
942   - msg.setId(uuid);
943   - StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
  1144 + private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, JSONObject response, String deviceId, String channelId, String startTime, String endTime) {
  1145 + StreamInfo streamInfo = onPublishHandler(mediaServerItemInuse, response, deviceId, channelId);
944 1146 if (streamInfo != null) {
945   - redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
946   - msg.setData(JSON.toJSONString(streamInfo));
947   - resultHolder.invokeResult(msg);
948   - } else {
949   - logger.warn("设备预览API调用失败!");
950   - msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "设备预览API调用失败!"));
951   - resultHolder.invokeResult(msg);
  1147 + streamInfo.setProgress(0);
  1148 + streamInfo.setStartTime(startTime);
  1149 + streamInfo.setEndTime(endTime);
  1150 + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, streamInfo.getStream());
  1151 + if (inviteInfo != null) {
  1152 + logger.info("[录像下载] 更新invite消息中的stream信息");
  1153 + inviteInfo.setStatus(InviteSessionStatus.ok);
  1154 + inviteInfo.setStreamInfo(streamInfo);
  1155 + inviteStreamService.updateInviteInfo(inviteInfo);
  1156 + }
952 1157 }
  1158 + return streamInfo;
953 1159 }
954 1160  
955 1161  
... ... @@ -1189,15 +1395,14 @@ public class PlayServiceImpl implements IPlayService {
1189 1395  
1190 1396 @Override
1191 1397 public void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException {
1192   - String key = redisCatchStorage.queryPlaybackForKey(null, null, streamId, null);
1193   - StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
1194   - if (null == streamInfo) {
  1398 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId);
  1399 + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) {
1195 1400 logger.warn("streamId不存在!");
1196 1401 throw new ServiceException("streamId不存在");
1197 1402 }
1198   - streamInfo.setPause(true);
1199   - redisTemplate.opsForValue().set(key, streamInfo);
1200   - MediaServerItem mediaServerItem = mediaServerService.getOne(streamInfo.getMediaServerId());
  1403 + inviteInfo.getStreamInfo().setPause(true);
  1404 + inviteStreamService.updateInviteInfo(inviteInfo);
  1405 + MediaServerItem mediaServerItem = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId());
1201 1406 if (null == mediaServerItem) {
1202 1407 logger.warn("mediaServer 不存在!");
1203 1408 throw new ServiceException("mediaServer不存在");
... ... @@ -1207,21 +1412,20 @@ public class PlayServiceImpl implements IPlayService {
1207 1412 if (jsonObject == null || jsonObject.getInteger("code") != 0) {
1208 1413 throw new ServiceException("暂停RTP接收失败");
1209 1414 }
1210   - Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
1211   - cmder.playPauseCmd(device, streamInfo);
  1415 + Device device = storager.queryVideoDevice(inviteInfo.getDeviceId());
  1416 + cmder.playPauseCmd(device, inviteInfo.getStreamInfo());
1212 1417 }
1213 1418  
1214 1419 @Override
1215 1420 public void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException {
1216   - String key = redisCatchStorage.queryPlaybackForKey(null, null, streamId, null);
1217   - StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
1218   - if (null == streamInfo) {
  1421 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId);
  1422 + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) {
1219 1423 logger.warn("streamId不存在!");
1220 1424 throw new ServiceException("streamId不存在");
1221 1425 }
1222   - streamInfo.setPause(false);
1223   - redisTemplate.opsForValue().set(key, streamInfo);
1224   - MediaServerItem mediaServerItem = mediaServerService.getOne(streamInfo.getMediaServerId());
  1426 + inviteInfo.getStreamInfo().setPause(false);
  1427 + inviteStreamService.updateInviteInfo(inviteInfo);
  1428 + MediaServerItem mediaServerItem = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId());
1225 1429 if (null == mediaServerItem) {
1226 1430 logger.warn("mediaServer 不存在!");
1227 1431 throw new ServiceException("mediaServer不存在");
... ... @@ -1231,8 +1435,8 @@ public class PlayServiceImpl implements IPlayService {
1231 1435 if (jsonObject == null || jsonObject.getInteger("code") != 0) {
1232 1436 throw new ServiceException("继续RTP接收失败");
1233 1437 }
1234   - Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
1235   - cmder.playResumeCmd(device, streamInfo);
  1438 + Device device = storager.queryVideoDevice(inviteInfo.getDeviceId());
  1439 + cmder.playResumeCmd(device, inviteInfo.getStreamInfo());
1236 1440 }
1237 1441  
1238 1442 @Override
... ... @@ -1308,7 +1512,7 @@ public class PlayServiceImpl implements IPlayService {
1308 1512 logger.info("调用ZLM推流接口, 结果: {}", jsonObject);
1309 1513 logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, ", param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
1310 1514 } else {
1311   - logger.error("RTP推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param));
  1515 + logger.error("RTP推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSONObject.toJSONString(param));
1312 1516 if (sendRtpItem.isOnlyAudio()) {
1313 1517 Device device = deviceService.getDevice(sendRtpItem.getDeviceId());
1314 1518 AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
... ... @@ -1419,7 +1623,7 @@ public class PlayServiceImpl implements IPlayService {
1419 1623 zlmrtpServerFactory.stopSendRtpStream(mediaServer, param);
1420 1624 }
1421 1625  
1422   - mediaServer.getSsrcConfig().releaseSsrc(sendRtpItem.getSsrc());
  1626 + ssrcFactory.releaseSsrc(mediaServerId, sendRtpItem.getSsrc());
1423 1627  
1424 1628 SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, null, sendRtpItem.getStream());
1425 1629 if (ssrcTransaction != null) {
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
... ... @@ -264,8 +264,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
264 264 return;
265 265 }
266 266 // 确定流是否在线
267   - boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
268   - if (streamReady) {
  267 + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream());
  268 + if (streamReady != null && streamReady) {
269 269 logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream());
270 270 responseSendItem(mediaServerItem, content, toId, serial);
271 271 }else {
... ... @@ -301,9 +301,6 @@ public class RedisGbPlayMsgListener implements MessageListener {
301 301 String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
302 302 logger.info("[redis发送通知] 推流被请求 {}: {}/{}", key, messageForPushChannel.getApp(), messageForPushChannel.getStream());
303 303 redisTemplate.convertAndSend(key, JSON.toJSON(messageForPushChannel));
304   -
305   -// redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel);
306   -
307 304 }
308 305 }
309 306  
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java
... ... @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.service.redisMsg;
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.genersoft.iot.vmp.common.VideoManagerConstants;
5 5 import com.genersoft.iot.vmp.conf.DynamicTask;
  6 +import com.genersoft.iot.vmp.conf.UserSetting;
6 7 import com.genersoft.iot.vmp.service.IStreamPushService;
7 8 import com.genersoft.iot.vmp.service.bean.PushStreamStatusChangeFromRedisDto;
8 9 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
... ... @@ -38,6 +39,9 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
38 39 @Autowired
39 40 private DynamicTask dynamicTask;
40 41  
  42 + @Autowired
  43 + private UserSetting userSetting;
  44 +
41 45  
42 46  
43 47 private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
... ... @@ -89,13 +93,15 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
89 93  
90 94 @Override
91 95 public void run(ApplicationArguments args) throws Exception {
92   - // 启动时设置所有推流通道离线,发起查询请求
93   - redisCatchStorage.sendStreamPushRequestedMsgForStatus();
94   - dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{
95   - logger.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线");
96   - // 五秒收不到请求就设置通道离线,然后通知上级离线
97   - streamPushService.allStreamOffline();
98   - }, 5000);
  96 + if (!userSetting.isUsePushingAsStatus()) {
  97 + // 启动时设置所有推流通道离线,发起查询请求
  98 + redisCatchStorage.sendStreamPushRequestedMsgForStatus();
  99 + dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{
  100 + logger.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线");
  101 + // 五秒收不到请求就设置通道离线,然后通知上级离线
  102 + streamPushService.allStreamOffline();
  103 + }, 5000);
  104 + }
99 105 }
100 106  
101 107 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
1 1 package com.genersoft.iot.vmp.storager;
2 2  
3 3 import com.alibaba.fastjson2.JSONObject;
4   -import com.genersoft.iot.vmp.common.StreamInfo;
5 4 import com.genersoft.iot.vmp.common.SystemAllInfo;
6   -import com.genersoft.iot.vmp.gb28181.bean.*;
7   -import com.genersoft.iot.vmp.media.zlm.dto.*;
  5 +import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
  6 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  7 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
  8 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  9 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  10 +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
8 11 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
9 12 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
10 13 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
11   -import com.genersoft.iot.vmp.service.bean.ThirdPartyGB;
12 14 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
13 15  
14 16 import java.util.List;
... ... @@ -23,42 +25,6 @@ public interface IRedisCatchStorage {
23 25 */
24 26 Long getCSEQ();
25 27  
26   - /**
27   - * 开始播放时将流存入
28   - *
29   - * @param stream 流信息
30   - * @return
31   - */
32   - boolean startPlay(StreamInfo stream);
33   -
34   -
35   - /**
36   - * 停止播放时删除
37   - *
38   - * @return
39   - */
40   - boolean stopPlay(StreamInfo streamInfo);
41   -
42   - /**
43   - * 查询播放列表
44   - * @return
45   - */
46   - StreamInfo queryPlay(StreamInfo streamInfo);
47   -
48   - StreamInfo queryPlayByStreamId(String steamId);
49   -
50   - StreamInfo queryPlayByDevice(String deviceId, String channelId);
51   -
52   - Map<String, StreamInfo> queryPlayByDeviceId(String deviceId);
53   -
54   - boolean startPlayback(StreamInfo stream, String callId);
55   -
56   - boolean stopPlayback(String deviceId, String channelId, String stream, String callId);
57   -
58   - StreamInfo queryPlayback(String deviceId, String channelID, String stream, String callId);
59   -
60   - String queryPlaybackForKey(String deviceId, String channelId, String stream, String callId);
61   -
62 28 void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch);
63 29  
64 30 ParentPlatformCatch queryPlatformCatchInfo(String platformGbId);
... ... @@ -75,8 +41,6 @@ public interface IRedisCatchStorage {
75 41  
76 42 void delPlatformRegisterInfo(String callId);
77 43  
78   - void cleanPlatformRegisterInfos();
79   -
80 44 void updateSendRTPSever(SendRtpItem sendRtpItem);
81 45  
82 46 /**
... ... @@ -103,12 +67,6 @@ public interface IRedisCatchStorage {
103 67 boolean isChannelSendingRTP(String channelId);
104 68  
105 69 /**
106   - * 清空某个设备的所有缓存
107   - * @param deviceId 设备ID
108   - */
109   - void clearCatchByDeviceId(String deviceId);
110   -
111   - /**
112 70 * 在redis添加wvp的信息
113 71 */
114 72 void updateWVPInfo(JSONObject jsonObject, int time);
... ... @@ -148,23 +106,6 @@ public interface IRedisCatchStorage {
148 106 */
149 107 void removeStream(String mediaServerId, String type);
150 108  
151   - /**
152   - * 开始下载录像时存入
153   - * @param streamInfo
154   - */
155   - boolean startDownload(StreamInfo streamInfo, String callId);
156   -
157   - StreamInfo queryDownload(String deviceId, String channelId, String stream, String callId);
158   -
159   - boolean stopDownload(String deviceId, String channelId, String stream, String callId);
160   -
161   - /**
162   - * 查找第三方系统留下的国标预设值
163   - * @param queryKey
164   - * @return
165   - */
166   - ThirdPartyGB queryMemberNoGBId(String queryKey);
167   -
168 109 List<OnStreamChangedHookParam> getStreams(String mediaServerId, String pull);
169 110  
170 111 /**
... ... @@ -261,4 +202,6 @@ public interface IRedisCatchStorage {
261 202 List<Device> getAllDevices();
262 203  
263 204 void removeAllDevice();
  205 +
  206 + void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online);
264 207 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
... ... @@ -197,6 +197,81 @@ public interface DeviceChannelMapper {
197 197 @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId}"})
198 198 void offlineByDeviceId(String deviceId);
199 199  
  200 +// @Insert("<script> " +
  201 +// "insert into device_channel " +
  202 +// "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
  203 +// " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
  204 +// " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " +
  205 +// " longitudeWgs84, latitudeWgs84, hasAudio, createTime, updateTime, businessGroupId, gpsTime) " +
  206 +// "values " +
  207 +// "<foreach collection='addChannels' index='index' item='item' separator=','> " +
  208 +// "(#{item.channelId}, #{item.deviceId}, #{item.name}, #{item.manufacture}, #{item.model}, " +
  209 +// "#{item.owner}, #{item.civilCode}, #{item.block},#{item.subCount}," +
  210 +// "#{item.address}, #{item.parental}, #{item.parentId}, #{item.safetyWay}, #{item.registerWay}, " +
  211 +// "#{item.certNum}, #{item.certifiable}, #{item.errCode}, #{item.secrecy}, " +
  212 +// "#{item.ipAddress}, #{item.port}, #{item.password}, #{item.PTZType}, #{item.status}, " +
  213 +// "#{item.streamId}, #{item.longitude}, #{item.latitude},#{item.longitudeGcj02}, " +
  214 +// "#{item.latitudeGcj02},#{item.longitudeWgs84}, #{item.latitudeWgs84}, #{item.hasAudio}, now(), now(), " +
  215 +// "#{item.businessGroupId}, #{item.gpsTime}) " +
  216 +// "</foreach> " +
  217 +// "ON DUPLICATE KEY UPDATE " +
  218 +// "updateTime=VALUES(updateTime), " +
  219 +// "name=VALUES(name), " +
  220 +// "manufacture=VALUES(manufacture), " +
  221 +// "model=VALUES(model), " +
  222 +// "owner=VALUES(owner), " +
  223 +// "civilCode=VALUES(civilCode), " +
  224 +// "block=VALUES(block), " +
  225 +// "subCount=VALUES(subCount), " +
  226 +// "address=VALUES(address), " +
  227 +// "parental=VALUES(parental), " +
  228 +// "parentId=VALUES(parentId), " +
  229 +// "safetyWay=VALUES(safetyWay), " +
  230 +// "registerWay=VALUES(registerWay), " +
  231 +// "certNum=VALUES(certNum), " +
  232 +// "certifiable=VALUES(certifiable), " +
  233 +// "errCode=VALUES(errCode), " +
  234 +// "secrecy=VALUES(secrecy), " +
  235 +// "ipAddress=VALUES(ipAddress), " +
  236 +// "port=VALUES(port), " +
  237 +// "password=VALUES(password), " +
  238 +// "PTZType=VALUES(PTZType), " +
  239 +// "status=VALUES(status), " +
  240 +// "streamId=VALUES(streamId), " +
  241 +// "longitude=VALUES(longitude), " +
  242 +// "latitude=VALUES(latitude), " +
  243 +// "longitudeGcj02=VALUES(longitudeGcj02), " +
  244 +// "latitudeGcj02=VALUES(latitudeGcj02), " +
  245 +// "longitudeWgs84=VALUES(longitudeWgs84), " +
  246 +// "latitudeWgs84=VALUES(latitudeWgs84), " +
  247 +// "hasAudio=VALUES(hasAudio), " +
  248 +// "businessGroupId=VALUES(businessGroupId), " +
  249 +// "gpsTime=VALUES(gpsTime)" +
  250 +// "</script>")
  251 +// int batchAdd(List<DeviceChannel> addChannels);
  252 +
  253 +
  254 + @Insert("<script> " +
  255 + "insert into device_channel " +
  256 + "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
  257 + " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
  258 + " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " +
  259 + " longitudeWgs84, latitudeWgs84, hasAudio, createTime, updateTime, businessGroupId, gpsTime) " +
  260 + "values " +
  261 + "<foreach collection='addChannels' index='index' item='item' separator=','> " +
  262 + "(#{item.channelId}, #{item.deviceId}, #{item.name}, #{item.manufacture}, #{item.model}, " +
  263 + "#{item.owner}, #{item.civilCode}, #{item.block},#{item.subCount}," +
  264 + "#{item.address}, #{item.parental}, #{item.parentId}, #{item.safetyWay}, #{item.registerWay}, " +
  265 + "#{item.certNum}, #{item.certifiable}, #{item.errCode}, #{item.secrecy}, " +
  266 + "#{item.ipAddress}, #{item.port}, #{item.password}, #{item.PTZType}, #{item.status}, " +
  267 + "#{item.streamId}, #{item.longitude}, #{item.latitude},#{item.longitudeGcj02}, " +
  268 + "#{item.latitudeGcj02},#{item.longitudeWgs84}, #{item.latitudeWgs84}, #{item.hasAudio}, now(), now(), " +
  269 + "#{item.businessGroupId}, #{item.gpsTime}) " +
  270 + "</foreach> " +
  271 + "</script>")
  272 + int batchAdd(List<DeviceChannel> addChannels);
  273 +
  274 +
200 275 @Insert("<script> " +
201 276 "insert into device_channel " +
202 277 "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
... ... @@ -248,7 +323,7 @@ public interface DeviceChannelMapper {
248 323 "businessGroupId=VALUES(businessGroupId), " +
249 324 "gpsTime=VALUES(gpsTime)" +
250 325 "</script>")
251   - int batchAdd(List<DeviceChannel> addChannels);
  326 + int batchAddOrUpdate(List<DeviceChannel> addChannels);
252 327  
253 328 @Update(value = {"UPDATE device_channel SET status=1 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"})
254 329 void online(String deviceId, String channelId);
... ... @@ -264,7 +339,7 @@ public interface DeviceChannelMapper {
264 339 "<if test='item.owner != null'>, owner=#{item.owner}</if>" +
265 340 "<if test='item.civilCode != null'>, civilCode=#{item.civilCode}</if>" +
266 341 "<if test='item.block != null'>, block=#{item.block}</if>" +
267   - "<if test='item.subCount != null'>, block=#{item.subCount}</if>" +
  342 + "<if test='item.subCount != null'>, subCount=#{item.subCount}</if>" +
268 343 "<if test='item.address != null'>, address=#{item.address}</if>" +
269 344 "<if test='item.parental != null'>, parental=#{item.parental}</if>" +
270 345 "<if test='item.parentId != null'>, parentId=#{item.parentId}</if>" +
... ... @@ -289,7 +364,8 @@ public interface DeviceChannelMapper {
289 364 "<if test='item.latitudeWgs84 != null'>, latitudeWgs84=#{item.latitudeWgs84}</if>" +
290 365 "<if test='item.businessGroupId != null'>, businessGroupId=#{item.businessGroupId}</if>" +
291 366 "<if test='item.gpsTime != null'>, gpsTime=#{item.gpsTime}</if>" +
292   - "WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}"+
  367 + "<if test='item.id > 0'>WHERE id=#{item.id}</if>" +
  368 + "<if test='item.id == 0'>WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}</if>" +
293 369 "</foreach>" +
294 370 "</script>"})
295 371 int batchUpdate(List<DeviceChannel> updateChannels);
... ... @@ -403,4 +479,26 @@ public interface DeviceChannelMapper {
403 479  
404 480 @Select("select de.* from device de left join device_channel dc on de.deviceId = dc.deviceId where dc.channelId=#{channelId}")
405 481 List<Device> getDeviceByChannelId(String channelId);
  482 +
  483 +
  484 + @Delete({"<script>" +
  485 + "<foreach collection='deleteChannelList' item='item' separator=';'>" +
  486 + "DELETE FROM device_channel WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}" +
  487 + "</foreach>" +
  488 + "</script>"})
  489 + int batchDel(List<DeviceChannel> deleteChannelList);
  490 +
  491 + @Update({"<script>" +
  492 + "<foreach collection='channels' item='item' separator=';'>" +
  493 + "UPDATE device_channel SET status=1 WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}" +
  494 + "</foreach>" +
  495 + "</script>"})
  496 + int batchOnline(List<DeviceChannel> channels);
  497 +
  498 + @Update({"<script>" +
  499 + "<foreach collection='channels' item='item' separator=';'>" +
  500 + "UPDATE device_channel SET status=0 WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}" +
  501 + "</foreach>" +
  502 + "</script>"})
  503 + int batchOffline(List<DeviceChannel> channels);
406 504 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -2,17 +2,18 @@ package com.genersoft.iot.vmp.storager.impl;
2 2  
3 3 import com.alibaba.fastjson2.JSON;
4 4 import com.alibaba.fastjson2.JSONObject;
5   -import com.genersoft.iot.vmp.common.StreamInfo;
6 5 import com.genersoft.iot.vmp.common.SystemAllInfo;
7 6 import com.genersoft.iot.vmp.common.VideoManagerConstants;
8 7 import com.genersoft.iot.vmp.conf.UserSetting;
9   -import com.genersoft.iot.vmp.gb28181.bean.*;
10   -import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
  8 +import com.genersoft.iot.vmp.gb28181.bean.AlarmChannelMessage;
  9 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  10 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
  11 +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
11 12 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
12 13 import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
  14 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
13 15 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
14 16 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
15   -import com.genersoft.iot.vmp.service.bean.ThirdPartyGB;
16 17 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
17 18 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
18 19 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
... ... @@ -24,8 +25,10 @@ import org.slf4j.Logger;
24 25 import org.slf4j.LoggerFactory;
25 26 import org.springframework.beans.factory.annotation.Autowired;
26 27 import org.springframework.data.redis.core.RedisTemplate;
  28 +import org.springframework.data.redis.core.StringRedisTemplate;
27 29 import org.springframework.stereotype.Component;
28 30  
  31 +import java.time.Duration;
29 32 import java.util.*;
30 33  
31 34 @SuppressWarnings("rawtypes")
... ... @@ -43,6 +46,9 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
43 46 @Autowired
44 47 private RedisTemplate<Object, Object> redisTemplate;
45 48  
  49 + @Autowired
  50 + private StringRedisTemplate stringRedisTemplate;
  51 +
46 52 @Override
47 53 public Long getCSEQ() {
48 54 String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId();
... ... @@ -87,240 +93,6 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
87 93 }
88 94 }
89 95  
90   - /**
91   - * 开始播放时将流存入redis
92   - */
93   - @Override
94   - public boolean startPlay(StreamInfo stream) {
95   -
96   - redisTemplate.opsForValue().set(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(),
97   - stream.getMediaServerId(), stream.getStream(), stream.getDeviceID(), stream.getChannelId()),
98   - stream);
99   - return true;
100   - }
101   -
102   - /**
103   - * 停止播放时从redis删除
104   - */
105   - @Override
106   - public boolean stopPlay(StreamInfo streamInfo) {
107   - if (streamInfo == null) {
108   - return false;
109   - }
110   - Boolean result = redisTemplate.delete(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
111   - userSetting.getServerId(),
112   - streamInfo.getMediaServerId(),
113   - streamInfo.getStream(),
114   - streamInfo.getDeviceID(),
115   - streamInfo.getChannelId()));
116   - return result != null && result;
117   - }
118   -
119   - /**
120   - * 查询播放列表
121   - */
122   - @Override
123   - public StreamInfo queryPlay(StreamInfo streamInfo) {
124   - return (StreamInfo)redisTemplate.opsForValue().get(String.format("%S_%s_%s_%s_%s_%s",
125   - VideoManagerConstants.PLAYER_PREFIX,
126   - userSetting.getServerId(),
127   - streamInfo.getMediaServerId(),
128   - streamInfo.getStream(),
129   - streamInfo.getDeviceID(),
130   - streamInfo.getChannelId()));
131   - }
132   - @Override
133   - public StreamInfo queryPlayByStreamId(String streamId) {
134   - List<Object> playLeys = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(), streamId));
135   - if (playLeys.size() == 0) {
136   - return null;
137   - }
138   - return (StreamInfo)redisTemplate.opsForValue().get(playLeys.get(0).toString());
139   - }
140   -
141   - @Override
142   - public StreamInfo queryPlayByDevice(String deviceId, String channelId) {
143   - List<Object> playLeys = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX,
144   - userSetting.getServerId(),
145   - deviceId,
146   - channelId));
147   - if (playLeys.size() == 0) {
148   - return null;
149   - }
150   - return (StreamInfo)redisTemplate.opsForValue().get(playLeys.get(0).toString());
151   - }
152   -
153   - @Override
154   - public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) {
155   - Map<String, StreamInfo> streamInfos = new HashMap<>();
156   - List<Object> players = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(),deviceId));
157   - if (players.size() == 0) {
158   - return streamInfos;
159   - }
160   - for (Object player : players) {
161   - String key = (String) player;
162   - StreamInfo streamInfo = JsonUtil.redisJsonToObject(redisTemplate, key, StreamInfo.class);
163   - if (Objects.isNull(streamInfo)) {
164   - continue;
165   - }
166   - streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo);
167   - }
168   - return streamInfos;
169   - }
170   -
171   -
172   - @Override
173   - public boolean startPlayback(StreamInfo stream, String callId) {
174   - redisTemplate.opsForValue().set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
175   - userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream);
176   - return true;
177   - }
178   -
179   - @Override
180   - public boolean startDownload(StreamInfo stream, String callId) {
181   - String key=String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
182   - userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId);
183   - if (stream.getProgress() == 1) {
184   - logger.debug("添加下载缓存==已完成下载=》{}",key);
185   - redisTemplate.opsForValue().set(key, stream);
186   - }else {
187   - logger.debug("添加下载缓存==未完成下载=》{}",key);
188   - redisTemplate.opsForValue().set(key, stream, 60*60);
189   - }
190   - return true;
191   - }
192   - @Override
193   - public boolean stopDownload(String deviceId, String channelId, String stream, String callId) {
194   - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId);
195   - if (deviceChannel != null) {
196   - deviceChannel.setStreamId(null);
197   - deviceChannel.setDeviceId(deviceId);
198   - deviceChannelMapper.update(deviceChannel);
199   - }
200   - if (deviceId == null) {
201   - deviceId = "*";
202   - }
203   - if (channelId == null) {
204   - channelId = "*";
205   - }
206   - if (stream == null) {
207   - stream = "*";
208   - }
209   - if (callId == null) {
210   - callId = "*";
211   - }
212   - String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
213   - userSetting.getServerId(),
214   - deviceId,
215   - channelId,
216   - stream,
217   - callId
218   - );
219   - List<Object> scan = RedisUtil.scan(redisTemplate, key);
220   - if (scan.size() > 0) {
221   - for (Object keyObj : scan) {
222   - redisTemplate.delete(keyObj);
223   - }
224   - }
225   - return true;
226   - }
227   -
228   - @Override
229   - public boolean stopPlayback(String deviceId, String channelId, String stream, String callId) {
230   - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId);
231   - if (deviceChannel != null) {
232   - deviceChannel.setStreamId(null);
233   - deviceChannel.setDeviceId(deviceId);
234   - deviceChannelMapper.update(deviceChannel);
235   - }
236   - if (deviceId == null) {
237   - deviceId = "*";
238   - }
239   - if (channelId == null) {
240   - channelId = "*";
241   - }
242   - if (stream == null) {
243   - stream = "*";
244   - }
245   - if (callId == null) {
246   - callId = "*";
247   - }
248   - String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
249   - userSetting.getServerId(),
250   - deviceId,
251   - channelId,
252   - stream,
253   - callId
254   - );
255   - List<Object> scan = RedisUtil.scan(redisTemplate, key);
256   - if (scan.size() > 0) {
257   - for (Object keyObj : scan) {
258   - redisTemplate.delete(keyObj);
259   - }
260   - }
261   - return true;
262   - }
263   -
264   - @Override
265   - public StreamInfo queryPlayback(String deviceId, String channelId, String stream, String callId) {
266   - if (stream == null && callId == null) {
267   - return null;
268   - }
269   - if (deviceId == null) {
270   - deviceId = "*";
271   - }
272   - if (channelId == null) {
273   - channelId = "*";
274   - }
275   - if (stream == null) {
276   - stream = "*";
277   - }
278   - if (callId == null) {
279   - callId = "*";
280   - }
281   - String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
282   - userSetting.getServerId(),
283   - deviceId,
284   - channelId,
285   - stream,
286   - callId
287   - );
288   - List<Object> streamInfoScan = RedisUtil.scan(redisTemplate, key);
289   - if (streamInfoScan.size() > 0) {
290   - return (StreamInfo) redisTemplate.opsForValue().get(streamInfoScan.get(0));
291   - }else {
292   - return null;
293   - }
294   - }
295   -
296   - @Override
297   - public String queryPlaybackForKey(String deviceId, String channelId, String stream, String callId) {
298   - if (stream == null && callId == null) {
299   - return null;
300   - }
301   - if (deviceId == null) {
302   - deviceId = "*";
303   - }
304   - if (channelId == null) {
305   - channelId = "*";
306   - }
307   - if (stream == null) {
308   - stream = "*";
309   - }
310   - if (callId == null) {
311   - callId = "*";
312   - }
313   - String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
314   - userSetting.getServerId(),
315   - deviceId,
316   - channelId,
317   - stream,
318   - callId
319   - );
320   - List<Object> streamInfoScan = RedisUtil.scan(redisTemplate, key);
321   - return (String) streamInfoScan.get(0);
322   - }
323   -
324 96 @Override
325 97 public void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch) {
326 98 String key = VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + parentPlatformCatch.getId();
... ... @@ -351,7 +123,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
351 123 @Override
352 124 public void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo) {
353 125 String key = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId;
354   - redisTemplate.opsForValue().set(key, platformRegisterInfo, 30);
  126 + Duration duration = Duration.ofSeconds(30L);
  127 + redisTemplate.opsForValue().set(key, platformRegisterInfo, duration);
355 128 }
356 129  
357 130  
... ... @@ -366,14 +139,6 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
366 139 }
367 140  
368 141 @Override
369   - public void cleanPlatformRegisterInfos() {
370   - List regInfos = RedisUtil.scan(redisTemplate, VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + "*");
371   - for (Object key : regInfos) {
372   - redisTemplate.delete(key.toString());
373   - }
374   - }
375   -
376   - @Override
377 142 public void updateSendRTPSever(SendRtpItem sendRtpItem) {
378 143  
379 144 String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX +
... ... @@ -530,39 +295,10 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
530 295 }
531 296  
532 297 @Override
533   - public void clearCatchByDeviceId(String deviceId) {
534   - List<Object> playLeys = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX,
535   - userSetting.getServerId(),
536   - deviceId));
537   - if (playLeys.size() > 0) {
538   - for (Object key : playLeys) {
539   - redisTemplate.delete(key.toString());
540   - }
541   - }
542   -
543   - List<Object> playBackers = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_%s_*_*_*", VideoManagerConstants.PLAY_BLACK_PREFIX,
544   - userSetting.getServerId(),
545   - deviceId));
546   - if (playBackers.size() > 0) {
547   - for (Object key : playBackers) {
548   - redisTemplate.delete(key.toString());
549   - }
550   - }
551   -
552   - List<Object> deviceCache = RedisUtil.scan(redisTemplate, String.format("%S%s_%s", VideoManagerConstants.DEVICE_PREFIX,
553   - userSetting.getServerId(),
554   - deviceId));
555   - if (deviceCache.size() > 0) {
556   - for (Object key : deviceCache) {
557   - redisTemplate.delete(key.toString());
558   - }
559   - }
560   - }
561   -
562   - @Override
563 298 public void updateWVPInfo(JSONObject jsonObject, int time) {
564 299 String key = VideoManagerConstants.WVP_SERVER_PREFIX + userSetting.getServerId();
565   - redisTemplate.opsForValue().set(key, jsonObject, time);
  300 + Duration duration = Duration.ofSeconds(time);
  301 + redisTemplate.opsForValue().set(key, jsonObject, duration);
566 302 }
567 303  
568 304 @Override
... ... @@ -590,44 +326,6 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
590 326 }
591 327  
592 328 @Override
593   - public StreamInfo queryDownload(String deviceId, String channelId, String stream, String callId) {
594   - if (stream == null && callId == null) {
595   - return null;
596   - }
597   - if (deviceId == null) {
598   - deviceId = "*";
599   - }
600   - if (channelId == null) {
601   - channelId = "*";
602   - }
603   - if (stream == null) {
604   - stream = "*";
605   - }
606   - if (callId == null) {
607   - callId = "*";
608   - }
609   - String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
610   - userSetting.getServerId(),
611   - deviceId,
612   - channelId,
613   - stream,
614   - callId
615   - );
616   - List<Object> streamInfoScan = RedisUtil.scan(redisTemplate, key);
617   - if (streamInfoScan.size() > 0) {
618   - return (StreamInfo) redisTemplate.opsForValue().get(streamInfoScan.get(0));
619   - }else {
620   - return null;
621   - }
622   - }
623   -
624   - @Override
625   - public ThirdPartyGB queryMemberNoGBId(String queryKey) {
626   - String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey;
627   - return JsonUtil.redisJsonToObject(redisTemplate, key, ThirdPartyGB.class);
628   - }
629   -
630   - @Override
631 329 public void removeStream(String mediaServerId, String type) {
632 330 String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId;
633 331 List<Object> streams = RedisUtil.scan(redisTemplate, key);
... ... @@ -694,7 +392,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
694 392 @Override
695 393 public void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo) {
696 394 String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gpsMsgInfo.getId();
697   - redisTemplate.opsForValue().set(key, gpsMsgInfo, 60); // 默认GPS消息保存1分钟
  395 + Duration duration = Duration.ofSeconds(60L);
  396 + redisTemplate.opsForValue().set(key, gpsMsgInfo, duration); // 默认GPS消息保存1分钟
698 397 }
699 398  
700 399 @Override
... ... @@ -902,4 +601,23 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
902 601 + userSetting.getServerId() + "_*_" + id + "_*";
903 602 return RedisUtil.scan(redisTemplate, key).size();
904 603 }
  604 +
  605 + @Override
  606 + public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) {
  607 + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
  608 + if (channelId == null) {
  609 + logger.info("[redis通知] 推送设备状态, {}-{}", deviceId, online);
  610 + }else {
  611 + logger.info("[redis通知] 推送通道状态, {}/{}-{}", deviceId, channelId, online);
  612 + }
  613 +
  614 + StringBuilder msg = new StringBuilder();
  615 + msg.append(deviceId);
  616 + if (channelId != null) {
  617 + msg.append(":").append(channelId);
  618 + }
  619 + msg.append(" ").append(online? "ON":"OFF");
  620 + // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
  621 + stringRedisTemplate.convertAndSend(key, msg.toString());
  622 + }
905 623 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
... ... @@ -177,10 +177,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
177 177 if (i + limitCount > channels.size()) {
178 178 toIndex = channels.size();
179 179 }
180   - result = result || deviceChannelMapper.batchAdd(channels.subList(i, toIndex)) < 0;
  180 + result = result || deviceChannelMapper.batchAddOrUpdate(channels.subList(i, toIndex)) < 0;
181 181 }
182 182 }else {
183   - result = result || deviceChannelMapper.batchAdd(channels) < 0;
  183 + result = result || deviceChannelMapper.batchAddOrUpdate(channels) < 0;
184 184 }
185 185 }
186 186 if (result) {
... ... @@ -278,10 +278,10 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
278 278 if (i + limitCount > addChannels.size()) {
279 279 toIndex = addChannels.size();
280 280 }
281   - result = result || deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)) < 0;
  281 + result = result || deviceChannelMapper.batchAddOrUpdate(addChannels.subList(i, toIndex)) < 0;
282 282 }
283 283 }else {
284   - result = result || deviceChannelMapper.batchAdd(addChannels) < 0;
  284 + result = result || deviceChannelMapper.batchAddOrUpdate(addChannels) < 0;
285 285 }
286 286 }
287 287 if (updateChannels.size() > 0) {
... ... @@ -434,9 +434,6 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
434 434 */
435 435 @Override
436 436 public synchronized boolean insertMobilePosition(MobilePosition mobilePosition) {
437   - if (mobilePosition.getDeviceId().equals(mobilePosition.getChannelId())) {
438   - mobilePosition.setChannelId(null);
439   - }
440 437 return deviceMobilePositionMapper.insertNewPosition(mobilePosition) > 0;
441 438 }
442 439  
... ...
src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.utils;
2   -
3   -public class ConfigConst {
4   - /**
5   - * 播流最大并发个数
6   - */
7   - public static final Integer MAX_STRTEAM_COUNT = 10000;
8   -}
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
... ... @@ -14,6 +14,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
14 14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
15 15 import com.genersoft.iot.vmp.service.IDeviceChannelService;
16 16 import com.genersoft.iot.vmp.service.IDeviceService;
  17 +import com.genersoft.iot.vmp.service.IInviteStreamService;
17 18 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
18 19 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
19 20 import com.genersoft.iot.vmp.vmanager.bean.BaseTree;
... ... @@ -62,6 +63,9 @@ public class DeviceQuery {
62 63  
63 64 @Autowired
64 65 private IRedisCatchStorage redisCatchStorage;
  66 +
  67 + @Autowired
  68 + private IInviteStreamService inviteStreamService;
65 69  
66 70 @Autowired
67 71 private SIPCommander cmder;
... ... @@ -184,7 +188,7 @@ public class DeviceQuery {
184 188 // 清除redis记录
185 189 boolean isSuccess = deviceService.delete(deviceId);
186 190 if (isSuccess) {
187   - redisCatchStorage.clearCatchByDeviceId(deviceId);
  191 + inviteStreamService.clearInviteInfo(deviceId);
188 192 // 停止此设备的订阅更新
189 193 Set<String> allKeys = dynamicTask.getAllKeys();
190 194 for (String key : allKeys) {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
... ... @@ -2,6 +2,9 @@ package com.genersoft.iot.vmp.vmanager.gb28181.play;
2 2  
3 3 import com.alibaba.fastjson2.JSONArray;
4 4 import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.common.InviteInfo;
  6 +import com.genersoft.iot.vmp.common.InviteSessionStatus;
  7 +import com.genersoft.iot.vmp.common.InviteSessionType;
5 8 import com.genersoft.iot.vmp.common.StreamInfo;
6 9 import com.genersoft.iot.vmp.conf.UserSetting;
7 10 import com.genersoft.iot.vmp.conf.exception.ControllerException;
... ... @@ -14,12 +17,17 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
14 17 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
15 18 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
16 19 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  20 +import com.genersoft.iot.vmp.service.IInviteStreamService;
17 21 import com.genersoft.iot.vmp.service.IMediaServerService;
18 22 import com.genersoft.iot.vmp.service.IMediaService;
19 23 import com.genersoft.iot.vmp.service.IPlayService;
  24 +import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
20 25 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
21 26 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
22 27 import com.genersoft.iot.vmp.vmanager.bean.*;
  28 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
  29 +import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
  30 +import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
23 31 import io.swagger.v3.oas.annotations.Operation;
24 32 import io.swagger.v3.oas.annotations.Parameter;
25 33 import io.swagger.v3.oas.annotations.tags.Tag;
... ... @@ -61,6 +69,9 @@ public class PlayController {
61 69 private IRedisCatchStorage redisCatchStorage;
62 70  
63 71 @Autowired
  72 + private IInviteStreamService inviteStreamService;
  73 +
  74 + @Autowired
64 75 private ZLMRESTfulUtils zlmresTfulUtils;
65 76  
66 77 @Autowired
... ... @@ -89,14 +100,12 @@ public class PlayController {
89 100 Device device = storager.queryVideoDevice(deviceId);
90 101 MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
91 102  
92   - RequestMessage msg = new RequestMessage();
  103 + RequestMessage requestMessage = new RequestMessage();
93 104 String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
94   - boolean exist = resultHolder.exist(key, null);
95   - msg.setKey(key);
  105 + requestMessage.setKey(key);
96 106 String uuid = UUID.randomUUID().toString();
97   - msg.setId(uuid);
  107 + requestMessage.setId(uuid);
98 108 DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
99   - DeferredResultEx<WVPResult<StreamContent>> deferredResultEx = new DeferredResultEx<>(result);
100 109  
101 110 result.onTimeout(()->{
102 111 logger.info("点播接口等待超时");
... ... @@ -104,32 +113,33 @@ public class PlayController {
104 113 WVPResult<StreamInfo> wvpResult = new WVPResult<>();
105 114 wvpResult.setCode(ErrorCode.ERROR100.getCode());
106 115 wvpResult.setMsg("点播超时");
107   - msg.setData(wvpResult);
108   - resultHolder.invokeResult(msg);
109   - });
110   - // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误
111   - deferredResultEx.setFilter(result1 -> {
112   - WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>)result1;
113   - WVPResult<StreamContent> resultStream = new WVPResult<>();
114   - resultStream.setCode(wvpResult1.getCode());
115   - resultStream.setMsg(wvpResult1.getMsg());
116   - if (wvpResult1.getCode() == ErrorCode.SUCCESS.getCode()) {
117   - StreamInfo data = wvpResult1.getData().clone();
118   - if (userSetting.getUseSourceIpAsStreamIp()) {
119   - data.channgeStreamIp(request.getLocalName());
120   - }
121   - resultStream.setData(new StreamContent(wvpResult1.getData()));
122   - }
123   - return resultStream;
  116 + requestMessage.setData(wvpResult);
  117 + resultHolder.invokeResult(requestMessage);
124 118 });
125 119  
126   -
127 120 // 录像查询以channelId作为deviceId查询
128   - resultHolder.put(key, uuid, deferredResultEx);
  121 + resultHolder.put(key, uuid, result);
129 122  
130   - if (!exist) {
131   - playService.play(newMediaServerItem, deviceId, channelId, null, null, null);
132   - }
  123 + playService.play(newMediaServerItem, deviceId, channelId, (code, msg, data) -> {
  124 + WVPResult<StreamContent> wvpResult = new WVPResult<>();
  125 + if (code == InviteErrorCode.SUCCESS.getCode()) {
  126 + wvpResult.setCode(ErrorCode.SUCCESS.getCode());
  127 + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
  128 +
  129 + if (data != null) {
  130 + StreamInfo streamInfo = (StreamInfo)data;
  131 + if (userSetting.getUseSourceIpAsStreamIp()) {
  132 + streamInfo.channgeStreamIp(request.getLocalName());
  133 + }
  134 + wvpResult.setData(new StreamContent(streamInfo));
  135 + }
  136 + }else {
  137 + wvpResult.setCode(code);
  138 + wvpResult.setMsg(msg);
  139 + }
  140 + requestMessage.setData(wvpResult);
  141 + resultHolder.invokeResult(requestMessage);
  142 + });
133 143 return result;
134 144 }
135 145  
... ... @@ -150,21 +160,22 @@ public class PlayController {
150 160 throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在");
151 161 }
152 162  
153   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
154   - if (streamInfo == null) {
  163 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
  164 + if (inviteInfo == null) {
155 165 throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到");
156 166 }
157   -
158   - try {
159   - logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId);
160   - cmder.streamByeCmd(device, channelId, streamInfo.getStream(), null, null);
161   - } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
162   - logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
163   - throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  167 + if (InviteSessionStatus.ok == inviteInfo.getStatus()) {
  168 + try {
  169 + logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId);
  170 + cmder.streamByeCmd(device, channelId, inviteInfo.getStream(), null, null);
  171 + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) {
  172 + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage());
  173 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  174 + }
164 175 }
165   - redisCatchStorage.stopPlay(streamInfo);
  176 + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
166 177  
167   - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
  178 + storager.stopPlay(deviceId, channelId);
168 179 JSONObject json = new JSONObject();
169 180 json.put("deviceId", deviceId);
170 181 json.put("channelId", channelId);
... ... @@ -179,15 +190,14 @@ public class PlayController {
179 190 @Parameter(name = "streamId", description = "视频流ID", required = true)
180 191 @PostMapping("/convert/{streamId}")
181 192 public JSONObject playConvert(@PathVariable String streamId) {
182   - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
183   - if (streamInfo == null) {
184   - streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
185   - }
186   - if (streamInfo == null) {
  193 +// StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
  194 +
  195 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, streamId);
  196 + if (inviteInfo == null || inviteInfo.getStreamInfo() == null) {
187 197 logger.warn("视频转码API调用失败!, 视频流已经停止!");
188 198 throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到视频流信息, 视频流可能已经停止");
189 199 }
190   - MediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
  200 + MediaServerItem mediaInfo = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId());
191 201 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
192 202 if (!rtpInfo.getBoolean("exist")) {
193 203 logger.warn("视频转码API调用失败!, 视频流已停止推流!");
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.playback;
2 2  
  3 +import com.genersoft.iot.vmp.common.InviteInfo;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
3 5 import com.genersoft.iot.vmp.common.StreamInfo;
4 6 import com.genersoft.iot.vmp.conf.UserSetting;
5 7 import com.genersoft.iot.vmp.conf.exception.ControllerException;
6 8 import com.genersoft.iot.vmp.conf.exception.ServiceException;
7 9 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  10 +import com.genersoft.iot.vmp.gb28181.bean.Device;
8 11 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
9 12 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  13 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
10 14 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
11   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  15 +import com.genersoft.iot.vmp.service.IInviteStreamService;
12 16 import com.genersoft.iot.vmp.service.IPlayService;
  17 +import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
  18 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  19 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
13 20 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
14 21 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
15 22 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
... ... @@ -20,17 +27,13 @@ import org.slf4j.Logger;
20 27 import org.slf4j.LoggerFactory;
21 28 import org.springframework.beans.factory.annotation.Autowired;
22 29 import org.springframework.util.ObjectUtils;
23   -import org.springframework.web.bind.annotation.CrossOrigin;
24 30 import org.springframework.web.bind.annotation.GetMapping;
25 31 import org.springframework.web.bind.annotation.PathVariable;
26 32 import org.springframework.web.bind.annotation.RequestMapping;
27 33 import org.springframework.web.bind.annotation.RestController;
28   -
29   -import com.genersoft.iot.vmp.gb28181.bean.Device;
30   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
31   -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
32 34 import org.springframework.web.context.request.async.DeferredResult;
33 35  
  36 +import javax.servlet.http.HttpServletRequest;
34 37 import javax.sip.InvalidArgumentException;
35 38 import javax.sip.SipException;
36 39 import java.text.ParseException;
... ... @@ -60,6 +63,9 @@ public class PlaybackController {
60 63 private IRedisCatchStorage redisCatchStorage;
61 64  
62 65 @Autowired
  66 + private IInviteStreamService inviteStreamService;
  67 +
  68 + @Autowired
63 69 private IPlayService playService;
64 70  
65 71 @Autowired
... ... @@ -74,8 +80,8 @@ public class PlaybackController {
74 80 @Parameter(name = "startTime", description = "开始时间", required = true)
75 81 @Parameter(name = "endTime", description = "结束时间", required = true)
76 82 @GetMapping("/start/{deviceId}/{channelId}")
77   - public DeferredResult<WVPResult<StreamContent>> start(@PathVariable String deviceId, @PathVariable String channelId,
78   - String startTime, String endTime) {
  83 + public DeferredResult<WVPResult<StreamContent>> start(HttpServletRequest request, @PathVariable String deviceId, @PathVariable String channelId,
  84 + String startTime, String endTime) {
79 85  
80 86 if (logger.isDebugEnabled()) {
81 87 logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
... ... @@ -86,22 +92,31 @@ public class PlaybackController {
86 92 DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
87 93 resultHolder.put(key, uuid, result);
88 94  
89   - WVPResult<StreamContent> wvpResult = new WVPResult<>();
90   -
91   - RequestMessage msg = new RequestMessage();
92   - msg.setKey(key);
93   - msg.setId(uuid);
94   -
95   - playService.playBack(deviceId, channelId, startTime, endTime, null,
96   - playBackResult->{
97   - wvpResult.setCode(playBackResult.getCode());
98   - wvpResult.setMsg(playBackResult.getMsg());
99   - if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) {
100   - StreamInfo streamInfo = (StreamInfo)playBackResult.getData();
101   - wvpResult.setData(new StreamContent(streamInfo));
  95 + RequestMessage requestMessage = new RequestMessage();
  96 + requestMessage.setKey(key);
  97 + requestMessage.setId(uuid);
  98 +
  99 + playService.playBack(deviceId, channelId, startTime, endTime,
  100 + (code, msg, data)->{
  101 +
  102 + WVPResult<StreamContent> wvpResult = new WVPResult<>();
  103 + if (code == InviteErrorCode.SUCCESS.getCode()) {
  104 + wvpResult.setCode(ErrorCode.SUCCESS.getCode());
  105 + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
  106 +
  107 + if (data != null) {
  108 + StreamInfo streamInfo = (StreamInfo)data;
  109 + if (userSetting.getUseSourceIpAsStreamIp()) {
  110 + streamInfo.channgeStreamIp(request.getLocalName());
  111 + }
  112 + wvpResult.setData(new StreamContent(streamInfo));
  113 + }
  114 + }else {
  115 + wvpResult.setCode(code);
  116 + wvpResult.setMsg(msg);
102 117 }
103   - msg.setData(wvpResult);
104   - resultHolder.invokeResult(msg);
  118 + requestMessage.setData(wvpResult);
  119 + resultHolder.invokeResult(requestMessage);
105 120 });
106 121  
107 122 return result;
... ... @@ -169,14 +184,15 @@ public class PlaybackController {
169 184 @GetMapping("/seek/{streamId}/{seekTime}")
170 185 public void playSeek(@PathVariable String streamId, @PathVariable long seekTime) {
171 186 logger.info("playSeek: "+streamId+", "+seekTime);
172   - StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
173   - if (null == streamInfo) {
  187 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId);
  188 +
  189 + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) {
174 190 logger.warn("streamId不存在!");
175 191 throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
176 192 }
177   - Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
  193 + Device device = storager.queryVideoDevice(inviteInfo.getDeviceId());
178 194 try {
179   - cmder.playSeekCmd(device, streamInfo, seekTime);
  195 + cmder.playSeekCmd(device, inviteInfo.getStreamInfo(), seekTime);
180 196 } catch (InvalidArgumentException | ParseException | SipException e) {
181 197 throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
182 198 }
... ... @@ -188,8 +204,9 @@ public class PlaybackController {
188 204 @GetMapping("/speed/{streamId}/{speed}")
189 205 public void playSpeed(@PathVariable String streamId, @PathVariable Double speed) {
190 206 logger.info("playSpeed: "+streamId+", "+speed);
191   - StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
192   - if (null == streamInfo) {
  207 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(InviteSessionType.PLAYBACK, streamId);
  208 +
  209 + if (null == inviteInfo || inviteInfo.getStreamInfo() == null) {
193 210 logger.warn("streamId不存在!");
194 211 throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在");
195 212 }
... ... @@ -197,9 +214,9 @@ public class PlaybackController {
197 214 logger.warn("不支持的speed: " + speed);
198 215 throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)");
199 216 }
200   - Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
  217 + Device device = storager.queryVideoDevice(inviteInfo.getDeviceId());
201 218 try {
202   - cmder.playSpeedCmd(device, streamInfo, speed);
  219 + cmder.playSpeedCmd(device, inviteInfo.getStreamInfo(), speed);
203 220 } catch (InvalidArgumentException | ParseException | SipException e) {
204 221 throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
205 222 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.record;
2 2  
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.conf.UserSetting;
4 5 import com.genersoft.iot.vmp.conf.exception.ControllerException;
5 6 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
6 7 import com.genersoft.iot.vmp.gb28181.bean.Device;
... ... @@ -10,6 +11,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
10 11 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
11 12 import com.genersoft.iot.vmp.service.IDeviceService;
12 13 import com.genersoft.iot.vmp.service.IPlayService;
  14 +import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
13 15 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
14 16 import com.genersoft.iot.vmp.utils.DateUtil;
15 17 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
... ... @@ -27,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
27 29 import org.springframework.web.bind.annotation.RestController;
28 30 import org.springframework.web.context.request.async.DeferredResult;
29 31  
  32 +import javax.servlet.http.HttpServletRequest;
30 33 import javax.sip.InvalidArgumentException;
31 34 import javax.sip.SipException;
32 35 import java.text.ParseException;
... ... @@ -55,8 +58,8 @@ public class GBRecordController {
55 58 @Autowired
56 59 private IDeviceService deviceService;
57 60  
58   -
59   -
  61 + @Autowired
  62 + private UserSetting userSetting;
60 63  
61 64 @Operation(summary = "录像查询")
62 65 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
... ... @@ -119,8 +122,8 @@ public class GBRecordController {
119 122 @Parameter(name = "endTime", description = "结束时间", required = true)
120 123 @Parameter(name = "downloadSpeed", description = "下载倍速", required = true)
121 124 @GetMapping("/download/start/{deviceId}/{channelId}")
122   - public DeferredResult<WVPResult<StreamContent>> download(@PathVariable String deviceId, @PathVariable String channelId,
123   - String startTime, String endTime, String downloadSpeed) {
  125 + public DeferredResult<WVPResult<StreamContent>> download(HttpServletRequest request, @PathVariable String deviceId, @PathVariable String channelId,
  126 + String startTime, String endTime, String downloadSpeed) {
124 127  
125 128 if (logger.isDebugEnabled()) {
126 129 logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed));
... ... @@ -130,22 +133,32 @@ public class GBRecordController {
130 133 String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId;
131 134 DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(30000L);
132 135 resultHolder.put(key, uuid, result);
133   - RequestMessage msg = new RequestMessage();
134   - msg.setId(uuid);
135   - msg.setKey(key);
136   -
137   - WVPResult<StreamContent> wvpResult = new WVPResult<>();
138   -
139   - playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed), null, playBackResult->{
140   -
141   - wvpResult.setCode(playBackResult.getCode());
142   - wvpResult.setMsg(playBackResult.getMsg());
143   - if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) {
144   - StreamInfo streamInfo = (StreamInfo)playBackResult.getData();
145   - wvpResult.setData(new StreamContent(streamInfo));
  136 + RequestMessage requestMessage = new RequestMessage();
  137 + requestMessage.setId(uuid);
  138 + requestMessage.setKey(key);
  139 +
  140 +
  141 + playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed),
  142 + (code, msg, data)->{
  143 +
  144 + WVPResult<StreamContent> wvpResult = new WVPResult<>();
  145 + if (code == InviteErrorCode.SUCCESS.getCode()) {
  146 + wvpResult.setCode(ErrorCode.SUCCESS.getCode());
  147 + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
  148 +
  149 + if (data != null) {
  150 + StreamInfo streamInfo = (StreamInfo)data;
  151 + if (userSetting.getUseSourceIpAsStreamIp()) {
  152 + streamInfo.channgeStreamIp(request.getLocalName());
  153 + }
  154 + wvpResult.setData(new StreamContent(streamInfo));
  155 + }
  156 + }else {
  157 + wvpResult.setCode(code);
  158 + wvpResult.setMsg(msg);
146 159 }
147   - msg.setData(wvpResult);
148   - resultHolder.invokeResult(msg);
  160 + requestMessage.setData(wvpResult);
  161 + resultHolder.invokeResult(requestMessage);
149 162 });
150 163  
151 164 return result;
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
... ... @@ -57,7 +57,7 @@ public class UserController {
57 57 if (user == null) {
58 58 throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误");
59 59 }else {
60   - String jwt = JwtUtils.createToken(username, password);
  60 + String jwt = JwtUtils.createToken(username, password, user.getRole().getId());
61 61 response.setHeader(JwtUtils.getHeader(), jwt);
62 62 user.setAccessToken(jwt);
63 63 }
... ...
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
1 1 package com.genersoft.iot.vmp.web.gb28181;
2 2  
3 3 import com.alibaba.fastjson2.JSONObject;
4   -import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.common.InviteInfo;
  5 +import com.genersoft.iot.vmp.common.InviteSessionType;
5 6 import com.genersoft.iot.vmp.conf.UserSetting;
6 7 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
7 8 import com.genersoft.iot.vmp.gb28181.bean.Device;
... ... @@ -9,13 +10,18 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
9 10 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
10 11 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
11 12 import com.genersoft.iot.vmp.service.IDeviceService;
  13 +import com.genersoft.iot.vmp.service.IInviteStreamService;
12 14 import com.genersoft.iot.vmp.service.IPlayService;
  15 +import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
13 16 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
14 17 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
15 18 import org.slf4j.Logger;
16 19 import org.slf4j.LoggerFactory;
17 20 import org.springframework.beans.factory.annotation.Autowired;
18   -import org.springframework.web.bind.annotation.*;
  21 +import org.springframework.web.bind.annotation.RequestMapping;
  22 +import org.springframework.web.bind.annotation.RequestParam;
  23 +import org.springframework.web.bind.annotation.ResponseBody;
  24 +import org.springframework.web.bind.annotation.RestController;
19 25 import org.springframework.web.context.request.async.DeferredResult;
20 26  
21 27 import javax.sip.InvalidArgumentException;
... ... @@ -46,6 +52,9 @@ public class ApiStreamController {
46 52 private IRedisCatchStorage redisCatchStorage;
47 53  
48 54 @Autowired
  55 + private IInviteStreamService inviteStreamService;
  56 +
  57 + @Autowired
49 58 private IDeviceService deviceService;
50 59  
51 60 @Autowired
... ... @@ -111,46 +120,53 @@ public class ApiStreamController {
111 120 return resultDeferredResult;
112 121 }
113 122 MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
114   - playService.play(newMediaServerItem, serial, code, (mediaServerItem, response)->{
115   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code);
116   - JSONObject result = new JSONObject();
117   - result.put("StreamID", streamInfo.getStream());
118   - result.put("DeviceID", device.getDeviceId());
119   - result.put("ChannelID", code);
120   - result.put("ChannelName", deviceChannel.getName());
121   - result.put("ChannelCustomName", "");
122   - result.put("FLV", streamInfo.getFlv().getUrl());
123   - result.put("WS_FLV", streamInfo.getWs_flv().getUrl());
124   - result.put("RTMP", streamInfo.getRtmp().getUrl());
125   - result.put("HLS", streamInfo.getHls().getUrl());
126   - result.put("RTSP", streamInfo.getRtsp().getUrl());
127   - result.put("WEBRTC", streamInfo.getRtc().getUrl());
128   - result.put("CDN", "");
129   - result.put("SnapURL", "");
130   - result.put("Transport", device.getTransport());
131   - result.put("StartAt", "");
132   - result.put("Duration", "");
133   - result.put("SourceVideoCodecName", "");
134   - result.put("SourceVideoWidth", "");
135   - result.put("SourceVideoHeight", "");
136   - result.put("SourceVideoFrameRate", "");
137   - result.put("SourceAudioCodecName", "");
138   - result.put("SourceAudioSampleRate", "");
139   - result.put("AudioEnable", "");
140   - result.put("Ondemand", "");
141   - result.put("InBytes", "");
142   - result.put("InBitRate", "");
143   - result.put("OutBytes", "");
144   - result.put("NumOutputs", "");
145   - result.put("CascadeSize", "");
146   - result.put("RelaySize", "");
147   - result.put("ChannelPTZType", "0");
148   - resultDeferredResult.setResult(result);
149   - }, (eventResult) -> {
150   - JSONObject result = new JSONObject();
151   - result.put("error", "channel[ " + code + " ] " + eventResult.msg);
152   - resultDeferredResult.setResult(result);
153   - }, null);
  123 +
  124 +
  125 + playService.play(newMediaServerItem, serial, code, (errorCode, msg, data) -> {
  126 + if (errorCode == InviteErrorCode.SUCCESS.getCode()) {
  127 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code);
  128 + if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
  129 + JSONObject result = new JSONObject();
  130 + result.put("StreamID", inviteInfo.getStreamInfo().getStream());
  131 + result.put("DeviceID", device.getDeviceId());
  132 + result.put("ChannelID", code);
  133 + result.put("ChannelName", deviceChannel.getName());
  134 + result.put("ChannelCustomName", "");
  135 + result.put("FLV", inviteInfo.getStreamInfo().getFlv().getUrl());
  136 + result.put("WS_FLV", inviteInfo.getStreamInfo().getWs_flv().getUrl());
  137 + result.put("RTMP", inviteInfo.getStreamInfo().getRtmp().getUrl());
  138 + result.put("HLS", inviteInfo.getStreamInfo().getHls().getUrl());
  139 + result.put("RTSP", inviteInfo.getStreamInfo().getRtsp().getUrl());
  140 + result.put("WEBRTC", inviteInfo.getStreamInfo().getRtc().getUrl());
  141 + result.put("CDN", "");
  142 + result.put("SnapURL", "");
  143 + result.put("Transport", device.getTransport());
  144 + result.put("StartAt", "");
  145 + result.put("Duration", "");
  146 + result.put("SourceVideoCodecName", "");
  147 + result.put("SourceVideoWidth", "");
  148 + result.put("SourceVideoHeight", "");
  149 + result.put("SourceVideoFrameRate", "");
  150 + result.put("SourceAudioCodecName", "");
  151 + result.put("SourceAudioSampleRate", "");
  152 + result.put("AudioEnable", "");
  153 + result.put("Ondemand", "");
  154 + result.put("InBytes", "");
  155 + result.put("InBitRate", "");
  156 + result.put("OutBytes", "");
  157 + result.put("NumOutputs", "");
  158 + result.put("CascadeSize", "");
  159 + result.put("RelaySize", "");
  160 + result.put("ChannelPTZType", "0");
  161 + resultDeferredResult.setResult(result);
  162 + }
  163 + }else {
  164 + JSONObject result = new JSONObject();
  165 + result.put("error", "channel[ " + code + " ] " + msg);
  166 + resultDeferredResult.setResult(result);
  167 + }
  168 + });
  169 +
154 170 return resultDeferredResult;
155 171 }
156 172  
... ... @@ -171,8 +187,8 @@ public class ApiStreamController {
171 187  
172 188 ){
173 189  
174   - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code);
175   - if (streamInfo == null) {
  190 + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, serial, code);
  191 + if (inviteInfo == null) {
176 192 JSONObject result = new JSONObject();
177 193 result.put("error","未找到流信息");
178 194 return result;
... ... @@ -184,14 +200,14 @@ public class ApiStreamController {
184 200 return result;
185 201 }
186 202 try {
187   - cmder.streamByeCmd(device, code, streamInfo.getStream(), null);
188   - } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
  203 + cmder.streamByeCmd(device, code, inviteInfo.getStream(), null);
  204 + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) {
189 205 JSONObject result = new JSONObject();
190 206 result.put("error","发送BYE失败:" + e.getMessage());
191 207 return result;
192 208 }
193   - redisCatchStorage.stopPlay(streamInfo);
194   - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
  209 + inviteStreamService.removeInviteInfo(inviteInfo);
  210 + storager.stopPlay(inviteInfo.getDeviceId(), inviteInfo.getChannelId());
195 211 return null;
196 212 }
197 213  
... ...
src/main/resources/all-application.yml
... ... @@ -65,8 +65,11 @@ server:
65 65  
66 66 # 作为28181服务器的配置
67 67 sip:
68   - # [必须修改] 本机的IP
69   - ip: 192.168.0.100
  68 + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡,
  69 + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4
  70 + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的
  71 + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。
  72 + ip: 0.0.0.0
70 73 # [可选] 没有任何业务需求,仅仅是在前端展示的时候用
71 74 show-ip: 192.168.0.100
72 75 # [可选] 28181服务监听的端口
... ... @@ -89,6 +92,15 @@ sip:
89 92 # 是否存储alarm信息
90 93 alarm: false
91 94  
  95 +# 做为JT1078服务器的配置
  96 +jt1078:
  97 + #[必须修改] 是否开启1078的服务
  98 + enable: true
  99 + #[必修修改] 1708设备接入的端口
  100 + port: 21078
  101 + #[可选] 设备鉴权的密码
  102 + password: admin123
  103 +
92 104 #zlm 默认服务器配置
93 105 media:
94 106 # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId
... ... @@ -182,6 +194,12 @@ user-settings:
182 194 send-to-platforms-when-id-lost: true
183 195 # 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息
184 196 refuse-channel-status-channel-form-notify: false
  197 + # 设置notify缓存队列最大长度,超过此长度的数据将返回486 BUSY_HERE,消息丢弃, 默认10000
  198 + max-notify-count-queue: 10000
  199 + # 设备/通道状态变化时发送消息
  200 + device-status-notify: false
  201 + # 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
  202 + use-custom-ssrc-for-parent-invite: true
185 203 # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
186 204 allowed-origins:
187 205 - http://localhost:8008
... ...
src/main/resources/application-dev.yml
... ... @@ -18,29 +18,19 @@ spring:
18 18 timeout: 10000
19 19 # mysql数据源
20 20 datasource:
21   - type: com.alibaba.druid.pool.DruidDataSource
  21 + type: com.zaxxer.hikari.HikariDataSource
22 22 driver-class-name: com.mysql.cj.jdbc.Driver
23 23 url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
24 24 username: root
25 25 password: 123456
26   - druid:
  26 + hikari:
  27 + connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数
27 28 initialSize: 10 # 连接池初始化连接数
28   - maxActive: 200 # 连接池最大连接数
29   - minIdle: 5 # 连接池最小空闲连接数
30   - maxWait: 60000 # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
31   - keepAlive: true # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
32   - validationQuery: select 1 # 检测连接是否有效sql,要求是查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
33   - testWhileIdle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
34   - testOnBorrow: false # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
35   - testOnReturn: false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
36   - poolPreparedStatements: false # 是否開啟PSCache,並且指定每個連線上PSCache的大小
37   - timeBetweenEvictionRunsMillis: 60000 # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
38   - minEvictableIdleTimeMillis: 300000 # 配置一個連線在池中最小生存的時間,單位是毫秒
39   - filters: stat,slf4j # 配置监控统计拦截的filters,监控统计用的filter:sta, 日志用的filter:log4j
40   - useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据
41   - # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
42   - connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000
43   - #stat-view-servlet.url-pattern: /admin/druid/*
  29 + maximum-pool-size: 200 # 连接池最大连接数
  30 + minimum-idle: 5 # 连接池最小空闲连接数
  31 + idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位)
  32 + max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位)
  33 +
44 34  
45 35 #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
46 36 server:
... ... @@ -48,7 +38,10 @@ server:
48 38  
49 39 # 作为28181服务器的配置
50 40 sip:
51   - # [必须修改] 本机的IP
  41 + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡,
  42 + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4
  43 + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的
  44 + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。
52 45 ip: 192.168.41.16
53 46 # [可选] 28181服务监听的端口
54 47 port: 5060
... ...
src/main/resources/application-docker.yml
... ... @@ -23,7 +23,7 @@ spring:
23 23 url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true
24 24 username: root
25 25 password: root
26   - type: com.alibaba.druid.pool.DruidDataSource
  26 + type: com.zaxxer.hikari.HikariDataSource
27 27 driver-class-name: com.mysql.cj.jdbc.Driver
28 28  
29 29 # [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
... ... @@ -32,7 +32,10 @@ server:
32 32  
33 33 # 作为28181服务器的配置
34 34 sip:
35   - # [必须修改] 本机的IP
  35 + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡,
  36 + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4
  37 + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的
  38 + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。
36 39 ip: ${WVP_HOST:127.0.0.1}
37 40 # [可选] 28181服务监听的端口
38 41 port: ${WVP_PORT:5060}
... ...
src/main/resources/application.yml
... ... @@ -2,5 +2,4 @@ spring:
2 2 application:
3 3 name: wvp
4 4 profiles:
5   - active: local
6   - include: device-compatible
7 5 \ No newline at end of file
  6 + active: local
8 7 \ No newline at end of file
... ...
src/test/java/com/genersoft/iot/vmp/jt1078/JT1078ServerTest.java 0 → 100644
  1 +package com.genersoft.iot.vmp.jt1078;
  2 +
  3 +import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template;
  4 +import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer;
  5 +import com.genersoft.iot.vmp.jt1078.proc.response.J9102;
  6 +import com.genersoft.iot.vmp.jt1078.proc.response.J9201;
  7 +import com.genersoft.iot.vmp.jt1078.proc.response.J9202;
  8 +import com.genersoft.iot.vmp.jt1078.proc.response.J9205;
  9 +
  10 +import java.util.Scanner;
  11 +
  12 +/**
  13 + * @author QingtaiJiang
  14 + * @date 2023/4/28 14:22
  15 + * @email qingtaij@163.com
  16 + */
  17 +public class JT1078ServerTest {
  18 +
  19 + private static final JT1078Template jt1078Template = new JT1078Template();
  20 +
  21 + public static void main(String[] args) {
  22 + System.out.println("Starting jt1078 server...");
  23 + TcpServer tcpServer = new TcpServer(21078);
  24 + tcpServer.start();
  25 + System.out.println("Start jt1078 server success!");
  26 +
  27 +
  28 + Scanner s = new Scanner(System.in);
  29 + while (true) {
  30 + String code = s.nextLine();
  31 + switch (code) {
  32 + case "1":
  33 + test9102();
  34 + break;
  35 + case "2":
  36 + test9201();
  37 + break;
  38 + case "3":
  39 + test9202();
  40 + break;
  41 + case "4":
  42 + test9205();
  43 + break;
  44 + default:
  45 + break;
  46 + }
  47 + }
  48 + }
  49 +
  50 + private static void test9102() {
  51 + J9102 j9102 = new J9102();
  52 + j9102.setChannel(1);
  53 + j9102.setCommand(0);
  54 + j9102.setCloseType(0);
  55 + j9102.setStreamType(0);
  56 +
  57 + String s = jt1078Template.stopLive("18864197066", j9102, 6);
  58 + System.out.println(s);
  59 + }
  60 +
  61 + private static void test9201() {
  62 + J9201 j9201 = new J9201();
  63 + j9201.setIp("192.168.1.1");
  64 + j9201.setChannel(1);
  65 + j9201.setTcpPort(7618);
  66 + j9201.setUdpPort(7618);
  67 + j9201.setType(0);
  68 + j9201.setRate(0);
  69 + j9201.setStorageType(0);
  70 + j9201.setPlaybackType(0);
  71 + j9201.setPlaybackSpeed(0);
  72 + j9201.setStartTime("230428134100");
  73 + j9201.setEndTime("230428134200");
  74 +
  75 + String s = jt1078Template.startBackLive("18864197066", j9201, 6);
  76 + System.out.println(s);
  77 + }
  78 +
  79 + private static void test9202() {
  80 + J9202 j9202 = new J9202();
  81 +
  82 + j9202.setChannel(1);
  83 + j9202.setPlaybackType(2);
  84 + j9202.setPlaybackSpeed(0);
  85 + j9202.setPlaybackTime("230428134100");
  86 +
  87 + String s = jt1078Template.controlBackLive("18864197066", j9202, 6);
  88 + System.out.println(s);
  89 + }
  90 +
  91 + private static void test9205() {
  92 + J9205 j9205 = new J9205();
  93 + j9205.setChannelId(1);
  94 + j9205.setStartTime("230428134100");
  95 + j9205.setEndTime("230428134100");
  96 + j9205.setMediaType(0);
  97 + j9205.setStreamType(0);
  98 + j9205.setStorageType(0);
  99 +
  100 + String s = jt1078Template.queryBackTime("18864197066", j9205, 6);
  101 + System.out.println(s);
  102 + }
  103 +}
... ...
web_src/src/components/DeviceList.vue
... ... @@ -25,11 +25,13 @@
25 25 </el-table-column>
26 26 <el-table-column prop="manufacturer" label="厂家" min-width="120" >
27 27 </el-table-column>
  28 + <el-table-column prop="transport" label="信令传输模式" min-width="120" >
  29 + </el-table-column>
28 30 <el-table-column label="流传输模式" min-width="160" >
29 31 <template slot-scope="scope">
30 32 <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择" style="width: 120px">
31 33 <el-option key="UDP" label="UDP" value="UDP"></el-option>
32   - <el-option key="TCP-ACTIVE" label="TCP主动模式" :disabled="true" value="TCP-ACTIVE"></el-option>
  34 + <el-option key="TCP-ACTIVE" label="TCP主动模式" value="TCP-ACTIVE"></el-option>
33 35 <el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option>
34 36 </el-select>
35 37 </template>
... ...
web_src/src/components/GBRecordDetail.vue
... ... @@ -182,9 +182,11 @@
182 182 this.playerStyle["height"] = this.winHeight + "px";
183 183 this.chooseDate = moment().format('YYYY-MM-DD')
184 184 this.dateChange();
  185 + window.addEventListener('beforeunload', this.stopPlayRecord)
185 186 },
186 187 destroyed() {
187 188 this.$destroy('recordVideoPlayer');
  189 + window.removeEventListener('beforeunload', this.stopPlayRecord)
188 190 },
189 191 methods: {
190 192 dateChange(){
... ... @@ -338,14 +340,18 @@
338 340 });
339 341 },
340 342 stopPlayRecord: function (callback) {
341   - this.$refs["recordVideoPlayer"].pause();
342   - this.videoUrl = '';
343   - this.$axios({
344   - method: 'get',
345   - url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
346   - }).then(function (res) {
347   - if (callback) callback()
348   - });
  343 + console.log("停止录像回放")
  344 + if (this.streamId !== "") {
  345 + this.$refs["recordVideoPlayer"].pause();
  346 + this.videoUrl = '';
  347 + this.$axios({
  348 + method: 'get',
  349 + url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
  350 + }).then(function (res) {
  351 + if (callback) callback()
  352 + });
  353 + }
  354 +
349 355 },
350 356 getDataWidth(item){
351 357 let timeForFile = this.getTimeForFile(item);
... ... @@ -423,8 +429,14 @@
423 429 return hStr + ":" + mStr + ":" + sStr
424 430 },
425 431 goBack(){
  432 + // 如果正在进行录像回放则,发送停止
  433 + if (this.streamId !== "") {
  434 + this.stopPlayRecord(()=> {
  435 + this.streamId = "";
  436 + })
  437 + }
426 438 window.history.go(-1);
427   - }
  439 + },
428 440 }
429 441 };
430 442 </script>
... ...
web_src/src/components/channelList.vue
... ... @@ -123,7 +123,6 @@
123 123 <script>
124 124 import devicePlayer from './dialog/devicePlayer.vue'
125 125 import uiHeader from '../layout/UiHeader.vue'
126   -import moment from "moment";
127 126 import DeviceService from "./service/DeviceService";
128 127 import DeviceTree from "./common/DeviceTree";
129 128  
... ... @@ -290,8 +289,8 @@ export default {
290 289 });
291 290 },
292 291 getSnap: function (row) {
293   - let url = (process.env.NODE_ENV === 'development'? "debug": "") + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId
294   - return url
  292 + let baseUrl = window.baseUrl ? window.baseUrl : "";
  293 + return ((process.env.NODE_ENV === 'development') ? process.env.BASE_API : baseUrl) + '/api/device/query/snap/' + row.deviceId + '/' + row.channelId;
295 294 },
296 295 getBigSnap: function (row) {
297 296 return [this.getSnap(row)]
... ...
web_src/src/components/dialog/recordDownload.vue
... ... @@ -7,6 +7,7 @@
7 7 </el-col>
8 8 <el-col :span="6" >
9 9 <el-button icon="el-icon-download" v-if="percentage < 100" size="mini" title="点击下载可将以缓存部分下载到本地" @click="download()">停止缓存并下载</el-button>
  10 + <el-button icon="el-icon-download" v-if="downloadFile" size="mini" title="点击下载" @click="downloadFileClientEvent()">点击下载</el-button>
10 11 </el-col>
11 12 </el-row>
12 13 </el-dialog>
... ... @@ -21,7 +22,7 @@ import moment from &quot;moment&quot;;
21 22 export default {
22 23 name: 'recordDownload',
23 24 created() {
24   -
  25 + window.addEventListener('beforeunload', this.stopDownloadRecord)
25 26  
26 27 },
27 28 data() {
... ... @@ -39,7 +40,8 @@ export default {
39 40 taskId: null,
40 41 getProgressRun: false,
41 42 getProgressForFileRun: false,
42   - timer: null
  43 + timer: null,
  44 + downloadFile: null,
43 45  
44 46 };
45 47 },
... ... @@ -96,7 +98,10 @@ export default {
96 98 });
97 99 },
98 100 close: function (){
99   - this.stopDownloadRecord();
  101 + if (this.streamInfo.progress < 1) {
  102 + this.stopDownloadRecord();
  103 + }
  104 +
100 105 if (this.timer !== null) {
101 106 window.clearTimeout(this.timer);
102 107 this.timer = null;
... ... @@ -158,7 +163,7 @@ export default {
158 163 }
159 164 setTimeout( ()=>{
160 165 if (!this.showDialog) return;
161   - this.getProgressForFile(this.getProgressForFileTimer())
  166 + this.getProgressForFile(this.getProgressForFileTimer)
162 167 }, 1000)
163 168 },
164 169 getProgressForFile: function (callback){
... ... @@ -176,13 +181,17 @@ export default {
176 181 if (res.data.code === 0) {
177 182 if (res.data.data.length === 0){
178 183 this.percentage = 0
  184 + // 往往在多次请求后(实验五分钟的视频是三次请求),才会返回数据,第一次请求通常是返回空数组
  185 + if (callback)callback()
179 186 return
180 187 }
181   - this.percentage = parseFloat(res.data.data.percentage)*100
  188 + // res.data.data应是数组类型
  189 + this.percentage = parseFloat(res.data.data[0].percentage)*100
182 190 if (res.data.data[0].percentage === '1') {
183 191 this.getProgressForFileRun = false;
184   - window.open(res.data.data[0].downloadFile)
185   - this.close();
  192 + this.downloadFile = res.data.data[0].downloadFile
  193 + this.title = "文件处理完成,点击按扭下载"
  194 + // window.open(res.data.data[0].downloadFile)
186 195 }else {
187 196 if (callback)callback()
188 197 }
... ... @@ -190,7 +199,13 @@ export default {
190 199 }).catch(function (error) {
191 200 console.log(error);
192 201 });
193   - }
  202 + },
  203 + downloadFileClientEvent: function (){
  204 + window.open(this.downloadFile )
  205 + }
  206 + },
  207 + destroyed() {
  208 + window.removeEventListener('beforeunload', this.stopDownloadRecord)
194 209 }
195 210 };
196 211 </script>
... ...
web_src/src/layout/UiHeader.vue
... ... @@ -40,6 +40,7 @@
40 40  
41 41 import changePasswordDialog from '../components/dialog/changePassword.vue'
42 42 import userService from '../components/service/UserService'
  43 +import {Notification} from 'element-ui';
43 44  
44 45 export default {
45 46 name: "UiHeader",
... ...