Commit b86f0aaae2f0212bc01ddfd3e5d646c22eb1d8c0

Authored by panll
Committed by Gitee
2 parents d8b24fc7 1fcdf5a8

!28 语音对讲合并

语音对讲合并
Showing 31 changed files with 2591 additions and 1814 deletions

Too many changes to show.

To preserve performance only 31 of 81 files are displayed.

README.md
... ... @@ -134,3 +134,11 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
134 134 [mk1990](https://github.com/mk1990) [SaltFish001](https://github.com/SaltFish001)
135 135  
136 136  
  137 +ffmpeg -re -i 123.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp rtsp://192.168.1.3:30554/broadcast/34020000001320000101_34020000001310000001
  138 +
  139 +ffmpeg -re -i 123.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp rtsp://192.168.1.3:30554/talk/34020000001320000011_34020000001370000001
  140 +
  141 +
  142 +
  143 +ffmpeg -re -i 123.mp3 -acodec pcm_alaw -ar 8000 -ac 1 -f rtsp rtsp://192.168.1.3:30554/talk/34020000001320000101_34020000001310000001
  144 +
... ...
doc/README.md
... ... @@ -56,7 +56,7 @@
56 56 - [X] 报警订阅
57 57 - [X] 目录订阅
58 58 - [ ] 语音广播
59   -- [ ] 语音对讲
  59 +- [ ] 语音喊话
60 60  
61 61 **作为下级平台**
62 62 - [X] 注册
... ... @@ -95,7 +95,7 @@
95 95 - [ ] 报警订阅
96 96 - [X] 目录订阅
97 97 - [ ] 语音广播
98   -- [ ] 语音对讲
  98 +- [ ] 语音喊话
99 99  
100 100  
101 101  
... ...
doc/_content/broadcast.md 0 → 100644
  1 +# 原理图
  2 +
  3 +## 使用ffmpeg测试语音对讲原理
  4 +```plantuml
  5 +@startuml
  6 +"FFMPEG" -> "ZLMediaKit": 推流到zlm
  7 +"WVP-PRO" <- "ZLMediaKit": 通知收到语音对讲推流,携带设备和通道信息
  8 +"WVP-PRO" -> "设备": 开始语音对讲
  9 +"WVP-PRO" <-- "设备": 语音对讲建立成功,携带收流端口
  10 +"WVP-PRO" -> "ZLMediaKit": 通知zlm将流推送到设备收流端口
  11 +"ZLMediaKit" -> "设备": 向设备推流
  12 +@enduml
  13 +```
  14 +
  15 +## 使用网页测试语音对讲原理
  16 +```plantuml
  17 +@startuml
  18 +"前端页面" -> "WVP-PRO": 请求推流地址
  19 +"前端页面" <-- "WVP-PRO": 返回推流地址
  20 +"前端页面" -> "ZLMediaKit": 使用webrtc推流到zlm,以下过程相同
  21 +"WVP-PRO" <- "ZLMediaKit": 通知收到语音对讲推流,携带设备和通道信息
  22 +"WVP-PRO" -> "设备": 开始语音对讲
  23 +"WVP-PRO" <-- "设备": 语音对讲建立成功,携带收流端口
  24 +"WVP-PRO" -> "ZLMediaKit": 通知zlm将流推送到设备收流端口
  25 +"ZLMediaKit" -> "设备": 向设备推流
  26 +@enduml
  27 +```
0 28 \ No newline at end of file
... ...
doc/_content/introduction/deployment.md
... ... @@ -21,9 +21,9 @@
21 21 4. WVP-PRO与ZLM支持分开部署,但是wvp-pro-assist必须与zlm部署在同一台主机;
22 22 5. 生产环境按需开放端口,但是建议修改默认端口,尤其是5060端口,易受到攻击;
23 23 6. zlm使用docker部署的情况,要求端口映射一致,比如映射5060,应将外部端口也映射为5060端口;
24   -7. 启动服务,以linux为例
25   -### 启动WVP-PRO
26   -**jar包:**
  24 +7. zlm与wvp会保持高频率的通信,所以不要去将wvp与zlm分属在两个网络,比如wvp在内网,zlm却在公网的情况。
  25 +8. 启动服务,以linux为例
  26 +**启动WVP-PRO**
27 27 ```shell
28 28 nohup java -jar wvp-pro-*.jar &
29 29 ```
... ...
doc/_content/theory/broadcast_cascade.md 0 → 100644
  1 +<!-- 点播流程 -->
  2 +
  3 +# 点播流程
  4 +> 以下为WVP-PRO级联语音喊话流程。
  5 +
  6 +```plantuml
  7 +@startuml
  8 +"上级平台" -> "下级平台": 1. 发起语音喊话请求
  9 +"上级平台" <-- "下级平台": 2. 200OK
  10 +"上级平台" <- "下级平台": 3. 回复Result OK
  11 +"上级平台" --> "下级平台": 4. 200OK
  12 +
  13 +"下级平台" -> "设备": 5. 发起语音喊话请求
  14 +"下级平台" <-- "设备": 6. 200OK
  15 +"下级平台" <- "设备": 7. 回复Result OK
  16 +"下级平台" --> "设备": 8. 200OK
  17 +
  18 +"下级平台" <- "设备": 9. invite(broadcast)
  19 +"下级平台" --> "设备": 10. 100 trying
  20 +"下级平台" --> "设备": 11. 200OK SDP
  21 +"下级平台" <-- "设备": 12. ack
  22 +
  23 +"上级平台" <- "下级平台": 13. invite(broadcast)
  24 +"上级平台" --> "下级平台": 14. 100 trying
  25 +"上级平台" --> "下级平台": 15. 200OK SDP
  26 +"上级平台" <-- "下级平台": 16. ack
  27 +
  28 +"上级平台" -> "下级平台": 17. 推送RTP
  29 +"下级平台" -> "设备": 18. 推送RTP
  30 +
  31 +@enduml
  32 +```
  33 +
  34 +
  35 +## 注册流程描述如下:
  36 +1. 用户从网页或调用接口发起点播请求;
  37 +2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、
  38 + 接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播,y字段描述SSRC值,f字段描述媒体参数。
  39 +3. 摄像机向WVP-PRO回复200OK,消息体中描述了媒体流发送者发送媒体流的IP、端口、媒体格式、SSRC字段等内容。
  40 +4. WVP-PRO向设备回复Ack, 会话建立成功。
  41 +5. 设备向ZLMediaKit发送实时流。
  42 +6. ZLMediaKit向WVP-PRO发送流改变事件。
  43 +7. WVP-PRO向WEB用户回复播放地址。
  44 +8. ZLMediaKit向WVP发送流无人观看事件。
  45 +9. WVP-PRO向设备回复Bye, 结束会话。
  46 +10. 设备回复200OK,会话结束成功。
... ...
doc/_sidebar.md
... ... @@ -20,6 +20,7 @@
20 20 * [树形结构](_content/theory/channel_tree.md)
21 21 * [注册流程](_content/theory/register.md)
22 22 * [点播流程](_content/theory/play.md)
  23 + * [级联语音喊话流程](_content/theory/broadcast_cascade.md)
23 24 * **必备技巧**
24 25 * [抓包](_content/skill/tcpdump.md)
25 26  
... ...
src/main/java/com/genersoft/iot/vmp/common/InviteSessionType.java
... ... @@ -3,5 +3,7 @@ package com.genersoft.iot.vmp.common;
3 3 public enum InviteSessionType {
4 4 PLAY,
5 5 PLAYBACK,
6   - DOWNLOAD
  6 + DOWNLOAD,
  7 + BROADCAST,
  8 + TALK
7 9 }
... ...
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
... ... @@ -240,11 +240,11 @@ public class StreamInfo implements Serializable, Cloneable{
240 240 }
241 241 }
242 242  
243   - public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) {
  243 + public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam, boolean isPlay) {
244 244 if (callIdParam != null) {
245 245 callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&");
246 246 }
247   - String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam);
  247 + String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, isPlay?"play":"push", callIdParam);
248 248 if (port > 0) {
249 249 this.rtc = new StreamURL("http", host, port, file);
250 250 }
... ...
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
... ... @@ -68,6 +68,7 @@ public class VideoManagerConstants {
68 68 public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_";
69 69  
70 70 public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_";
  71 + public static final String BROADCAST_WAITE_INVITE = "task_broadcast_waite_invite_";
71 72  
72 73 public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_";
73 74 public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_";
... ...
src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java
... ... @@ -32,7 +32,7 @@ public class GlobalResponseAdvice implements ResponseBodyAdvice&lt;Object&gt; {
32 32 @Override
33 33 public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType, @NotNull MediaType selectedContentType, @NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType, @NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) {
34 34 // 排除api文档的接口,这个接口不需要统一
35   - String[] excludePath = {"/v3/api-docs","/api/v1","/index/hook"};
  35 + String[] excludePath = {"/v3/api-docs","/api/v1","/index/hook","/api/video-"};
36 36 for (String path : excludePath) {
37 37 if (request.getURI().getPath().startsWith(path)) {
38 38 return body;
... ... @@ -59,8 +59,8 @@ public class GlobalResponseAdvice implements ResponseBodyAdvice&lt;Object&gt; {
59 59 * 防止返回string时出错
60 60 * @return
61 61 */
62   - @Bean
63   - public HttpMessageConverters fast() {
  62 + /*@Bean
  63 + public HttpMessageConverters custHttpMessageConverter() {
64 64 return new HttpMessageConverters(new FastJsonHttpMessageConverter());
65   - }
  65 + }*/
66 66 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
... ... @@ -97,7 +97,7 @@ public class MediaConfig{
97 97  
98 98 public String getHookIp() {
99 99 if (ObjectUtils.isEmpty(hookIp)){
100   - return sipIp.split(",")[0];
  100 + return sipIp;
101 101 }else {
102 102 return hookIp;
103 103 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
... ... @@ -33,7 +33,7 @@ public class UserSetting {
33 33  
34 34 private Boolean logInDatabase = Boolean.TRUE;
35 35  
36   - private Boolean usePushingAsStatus = Boolean.TRUE;
  36 + private Boolean usePushingAsStatus = Boolean.FALSE;
37 37  
38 38 private Boolean useSourceIpAsStreamIp = Boolean.FALSE;
39 39  
... ... @@ -58,6 +58,8 @@ public class UserSetting {
58 58  
59 59 private String thirdPartyGBIdReg = "[\\s\\S]*";
60 60  
  61 + private String broadcastForPlatform = "UDP";
  62 +
61 63 private String civilCodeFile = "classpath:civilCode.csv";
62 64  
63 65 private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
... ... @@ -210,6 +212,14 @@ public class UserSetting {
210 212 this.syncChannelOnDeviceOnline = syncChannelOnDeviceOnline;
211 213 }
212 214  
  215 + public String getBroadcastForPlatform() {
  216 + return broadcastForPlatform;
  217 + }
  218 +
  219 + public void setBroadcastForPlatform(String broadcastForPlatform) {
  220 + this.broadcastForPlatform = broadcastForPlatform;
  221 + }
  222 +
213 223 public Boolean getSipUseSourceIpAsRemoteAddress() {
214 224 return sipUseSourceIpAsRemoteAddress;
215 225 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java 0 → 100644
src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java
... ... @@ -100,6 +100,9 @@ public class LoginUser implements UserDetails, CredentialsContainer {
100 100 return user.getRole();
101 101 }
102 102  
  103 + public String getPushKey() {
  104 + return user.getPushKey();
  105 + }
103 106  
104 107 public String getAccessToken() {
105 108 return accessToken;
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +
  4 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  5 +import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent;
  6 +import gov.nist.javax.sip.message.SIPResponse;
  7 +
  8 +/**
  9 + * 缓存语音广播的状态
  10 + * @author lin
  11 + */
  12 +public class AudioBroadcastCatch {
  13 +
  14 +
  15 + public AudioBroadcastCatch(
  16 + String deviceId,
  17 + String channelId,
  18 + MediaServerItem mediaServerItem,
  19 + String app,
  20 + String stream,
  21 + AudioBroadcastEvent event,
  22 + AudioBroadcastCatchStatus status,
  23 + boolean isFromPlatform
  24 + ) {
  25 + this.deviceId = deviceId;
  26 + this.channelId = channelId;
  27 + this.status = status;
  28 + this.event = event;
  29 + this.isFromPlatform = isFromPlatform;
  30 + this.app = app;
  31 + this.stream = stream;
  32 + this.mediaServerItem = mediaServerItem;
  33 + }
  34 +
  35 + public AudioBroadcastCatch() {
  36 + }
  37 +
  38 + /**
  39 + * 设备编号
  40 + */
  41 + private String deviceId;
  42 +
  43 + /**
  44 + * 通道编号
  45 + */
  46 + private String channelId;
  47 +
  48 + /**
  49 + * 流媒体信息
  50 + */
  51 + private MediaServerItem mediaServerItem;
  52 +
  53 + /**
  54 + * 关联的流APP
  55 + */
  56 + private String app;
  57 +
  58 + /**
  59 + * 关联的流STREAM
  60 + */
  61 + private String stream;
  62 +
  63 + /**
  64 + * 是否是级联语音喊话
  65 + */
  66 + private boolean isFromPlatform;
  67 +
  68 + /**
  69 + * 语音广播状态
  70 + */
  71 + private AudioBroadcastCatchStatus status;
  72 +
  73 + /**
  74 + * 请求信息
  75 + */
  76 + private SipTransactionInfo sipTransactionInfo;
  77 +
  78 + /**
  79 + * 请求结果回调
  80 + */
  81 + private AudioBroadcastEvent event;
  82 +
  83 +
  84 + public String getDeviceId() {
  85 + return deviceId;
  86 + }
  87 +
  88 + public void setDeviceId(String deviceId) {
  89 + this.deviceId = deviceId;
  90 + }
  91 +
  92 + public String getChannelId() {
  93 + return channelId;
  94 + }
  95 +
  96 + public void setChannelId(String channelId) {
  97 + this.channelId = channelId;
  98 + }
  99 +
  100 + public AudioBroadcastCatchStatus getStatus() {
  101 + return status;
  102 + }
  103 +
  104 + public void setStatus(AudioBroadcastCatchStatus status) {
  105 + this.status = status;
  106 + }
  107 +
  108 + public SipTransactionInfo getSipTransactionInfo() {
  109 + return sipTransactionInfo;
  110 + }
  111 +
  112 + public MediaServerItem getMediaServerItem() {
  113 + return mediaServerItem;
  114 + }
  115 +
  116 + public void setMediaServerItem(MediaServerItem mediaServerItem) {
  117 + this.mediaServerItem = mediaServerItem;
  118 + }
  119 +
  120 + public String getApp() {
  121 + return app;
  122 + }
  123 +
  124 + public void setApp(String app) {
  125 + this.app = app;
  126 + }
  127 +
  128 + public String getStream() {
  129 + return stream;
  130 + }
  131 +
  132 + public void setStream(String stream) {
  133 + this.stream = stream;
  134 + }
  135 +
  136 + public boolean isFromPlatform() {
  137 + return isFromPlatform;
  138 + }
  139 +
  140 + public void setFromPlatform(boolean fromPlatform) {
  141 + isFromPlatform = fromPlatform;
  142 + }
  143 +
  144 + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
  145 + this.sipTransactionInfo = sipTransactionInfo;
  146 + }
  147 +
  148 + public AudioBroadcastEvent getEvent() {
  149 + return event;
  150 + }
  151 +
  152 + public void setEvent(AudioBroadcastEvent event) {
  153 + this.event = event;
  154 + }
  155 +
  156 + public void setSipTransactionInfoByRequset(SIPResponse sipResponse) {
  157 + this.sipTransactionInfo = new SipTransactionInfo(sipResponse);
  158 + }
  159 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatchStatus.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.bean;
  2 +
  3 +/**
  4 + * 语音广播状态
  5 + * @author lin
  6 + */
  7 +public enum AudioBroadcastCatchStatus {
  8 +
  9 + // 发送语音广播消息等待对方回复语音广播
  10 + Ready,
  11 + // 收到回复等待invite消息
  12 + WaiteInvite,
  13 + // 收到invite消息
  14 + Ok,
  15 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
... ... @@ -188,8 +188,8 @@ public class Device {
188 188 @Schema(description = "设备注册的事务信息")
189 189 private SipTransactionInfo sipTransactionInfo;
190 190  
191   -
192   -
  191 + @Schema(description = "控制语音对讲流程,释放收到ACK后发流")
  192 + private boolean broadcastPushAfterAck;
193 193  
194 194 public String getDeviceId() {
195 195 return deviceId;
... ... @@ -452,5 +452,11 @@ public class Device {
452 452 public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
453 453 this.sipTransactionInfo = sipTransactionInfo;
454 454 }
  455 + public boolean isBroadcastPushAfterAck() {
  456 + return broadcastPushAfterAck;
  457 + }
455 458  
  459 + public void setBroadcastPushAfterAck(boolean broadcastPushAfterAck) {
  460 + this.broadcastPushAfterAck = broadcastPushAfterAck;
  461 + }
456 462 }
... ...
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,DOWNLOAD,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY
  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/ParentPlatform.java
... ... @@ -66,7 +66,7 @@ public class ParentPlatform {
66 66 * 设备端口
67 67 */
68 68 @Schema(description = "设备端口")
69   - private String devicePort;
  69 + private int devicePort;
70 70  
71 71 /**
72 72 * SIP认证用户名(默认使用设备国标编号)
... ... @@ -261,11 +261,11 @@ public class ParentPlatform {
261 261 this.deviceIp = deviceIp;
262 262 }
263 263  
264   - public String getDevicePort() {
  264 + public int getDevicePort() {
265 265 return devicePort;
266 266 }
267 267  
268   - public void setDevicePort(String devicePort) {
  268 + public void setDevicePort(int devicePort) {
269 269 this.devicePort = devicePort;
270 270 }
271 271  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java
... ... @@ -49,7 +49,7 @@ public class SendRtpItem {
49 49 /**
50 50 * 设备推流的streamId
51 51 */
52   - private String streamId;
  52 + private String stream;
53 53  
54 54 /**
55 55 * 是否为tcp
... ... @@ -117,6 +117,11 @@ public class SendRtpItem {
117 117 */
118 118 private InviteStreamType playType;
119 119  
  120 + /**
  121 + * 发流的同时收流
  122 + */
  123 + private String receiveStream;
  124 +
120 125 public String getIp() {
121 126 return ip;
122 127 }
... ... @@ -181,12 +186,12 @@ public class SendRtpItem {
181 186 this.app = app;
182 187 }
183 188  
184   - public String getStreamId() {
185   - return streamId;
  189 + public String getStream() {
  190 + return stream;
186 191 }
187 192  
188   - public void setStreamId(String streamId) {
189   - this.streamId = streamId;
  193 + public void setStream(String stream) {
  194 + this.stream = stream;
190 195 }
191 196  
192 197 public boolean isTcp() {
... ... @@ -292,4 +297,12 @@ public class SendRtpItem {
292 297 public void setRtcp(boolean rtcp) {
293 298 this.rtcp = rtcp;
294 299 }
  300 +
  301 + public String getReceiveStream() {
  302 + return receiveStream;
  303 + }
  304 +
  305 + public void setReceiveStream(String receiveStream) {
  306 + this.receiveStream = receiveStream;
  307 + }
295 308 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipTransactionInfo.java
1 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3   -import gov.nist.javax.sip.message.SIPRequest;
4 3 import gov.nist.javax.sip.message.SIPResponse;
5 4  
6 5 public class SipTransactionInfo {
... ... @@ -10,11 +9,23 @@ public class SipTransactionInfo {
10 9 private String toTag;
11 10 private String viaBranch;
12 11  
  12 + // 自己是否媒体流发送者
  13 + private boolean asSender;
  14 +
  15 + public SipTransactionInfo(SIPResponse response, boolean asSender) {
  16 + this.callId = response.getCallIdHeader().getCallId();
  17 + this.fromTag = response.getFromTag();
  18 + this.toTag = response.getToTag();
  19 + this.viaBranch = response.getTopmostViaHeader().getBranch();
  20 + this.asSender = asSender;
  21 + }
  22 +
13 23 public SipTransactionInfo(SIPResponse response) {
14 24 this.callId = response.getCallIdHeader().getCallId();
15 25 this.fromTag = response.getFromTag();
16 26 this.toTag = response.getToTag();
17 27 this.viaBranch = response.getTopmostViaHeader().getBranch();
  28 + this.asSender = false;
18 29 }
19 30  
20 31 public SipTransactionInfo() {
... ... @@ -51,4 +62,12 @@ public class SipTransactionInfo {
51 62 public void setViaBranch(String viaBranch) {
52 63 this.viaBranch = viaBranch;
53 64 }
  65 +
  66 + public boolean isAsSender() {
  67 + return asSender;
  68 + }
  69 +
  70 + public void setAsSender(boolean asSender) {
  71 + this.asSender = asSender;
  72 + }
54 73 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
... ... @@ -61,7 +61,7 @@ public class SipSubscribe {
61 61 logger.debug("errorSubscribes.size:{}",errorSubscribes.size());
62 62 }
63 63  
64   - public interface Event { void response(EventResult eventResult) ;
  64 + public interface Event { void response(EventResult eventResult);
65 65 }
66 66  
67 67 /**
... ... @@ -78,8 +78,10 @@ public class SipSubscribe {
78 78 dialogTerminated,
79 79 // 设备未找到
80 80 deviceNotFoundEvent,
81   - // 设备未找到
82   - cmdSendFailEvent
  81 + // 消息发送失败
  82 + cmdSendFailEvent,
  83 + // 消息发送失败
  84 + failedToGetPort
83 85 }
84 86  
85 87 public static class EventResult<EventObject>{
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/AudioBroadcastManager.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.session;
  2 +
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import com.genersoft.iot.vmp.gb28181.bean.AudioBroadcastCatch;
  5 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  6 +import org.springframework.beans.factory.annotation.Autowired;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import java.util.*;
  10 +import java.util.concurrent.ConcurrentHashMap;
  11 +import java.util.stream.Collectors;
  12 +import java.util.stream.Stream;
  13 +
  14 +/**
  15 + * 语音广播消息管理类
  16 + * @author lin
  17 + */
  18 +@Component
  19 +public class AudioBroadcastManager {
  20 +
  21 + @Autowired
  22 + private SipConfig config;
  23 +
  24 + public static Map<String, AudioBroadcastCatch> data = new ConcurrentHashMap<>();
  25 +
  26 + public void update(AudioBroadcastCatch audioBroadcastCatch) {
  27 + if (SipUtils.isFrontEnd(audioBroadcastCatch.getDeviceId())) {
  28 + data.put(audioBroadcastCatch.getDeviceId(), audioBroadcastCatch);
  29 + }else {
  30 + data.put(audioBroadcastCatch.getDeviceId() + audioBroadcastCatch.getChannelId(), audioBroadcastCatch);
  31 + }
  32 + }
  33 +
  34 + public void del(String deviceId, String channelId) {
  35 + if (SipUtils.isFrontEnd(deviceId)) {
  36 + data.remove(deviceId);
  37 + }else {
  38 + data.remove(deviceId + channelId);
  39 + }
  40 +
  41 + }
  42 +
  43 + public void delByDeviceId(String deviceId) {
  44 + for (String key : data.keySet()) {
  45 + if (key.startsWith(deviceId)) {
  46 + data.remove(key);
  47 + }
  48 + }
  49 + }
  50 +
  51 + public List<AudioBroadcastCatch> getAll(){
  52 + Collection<AudioBroadcastCatch> values = data.values();
  53 + return new ArrayList<>(values);
  54 + }
  55 +
  56 +
  57 + public boolean exit(String deviceId, String channelId) {
  58 + for (String key : data.keySet()) {
  59 + if (SipUtils.isFrontEnd(deviceId)) {
  60 + return key.equals(deviceId);
  61 + }else {
  62 + return key.equals(deviceId + channelId);
  63 + }
  64 + }
  65 + return false;
  66 + }
  67 +
  68 + public AudioBroadcastCatch get(String deviceId, String channelId) {
  69 + AudioBroadcastCatch audioBroadcastCatch;
  70 + if (SipUtils.isFrontEnd(deviceId)) {
  71 + audioBroadcastCatch = data.get(deviceId);
  72 + }else {
  73 + audioBroadcastCatch = data.get(deviceId + channelId);
  74 + }
  75 + if (audioBroadcastCatch == null) {
  76 + Stream<AudioBroadcastCatch> allAudioBroadcastCatchStreamForDevice = data.values().stream().filter(
  77 + audioBroadcastCatchItem -> Objects.equals(audioBroadcastCatchItem.getDeviceId(), deviceId));
  78 + List<AudioBroadcastCatch> audioBroadcastCatchList = allAudioBroadcastCatchStreamForDevice.collect(Collectors.toList());
  79 + if (audioBroadcastCatchList.size() == 1 && Objects.equals(config.getId(), channelId)) {
  80 + audioBroadcastCatch = audioBroadcastCatchList.get(0);
  81 + }
  82 + }
  83 +
  84 + return audioBroadcastCatch;
  85 + }
  86 +
  87 + public List<AudioBroadcastCatch> get(String deviceId) {
  88 + List<AudioBroadcastCatch> audioBroadcastCatchList= new ArrayList<>();
  89 + if (SipUtils.isFrontEnd(deviceId)) {
  90 + if (data.get(deviceId) != null) {
  91 + audioBroadcastCatchList.add(data.get(deviceId));
  92 + }
  93 + }else {
  94 + for (String key : data.keySet()) {
  95 + if (key.startsWith(deviceId)) {
  96 + audioBroadcastCatchList.add(data.get(key));
  97 + }
  98 + }
  99 + }
  100 +
  101 + return audioBroadcastCatchList;
  102 + }
  103 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
... ... @@ -106,13 +106,13 @@ public class SipRunner implements CommandLineRunner {
106 106 if (sendRtpItems.size() > 0) {
107 107 for (SendRtpItem sendRtpItem : sendRtpItems) {
108 108 MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
109   - redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStreamId());
  109 + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStream());
110 110 if (mediaServerItem != null) {
111 111 ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
112 112 Map<String, Object> param = new HashMap<>();
113 113 param.put("vhost","__defaultVhost__");
114 114 param.put("app",sendRtpItem.getApp());
115   - param.put("stream",sendRtpItem.getStreamId());
  115 + param.put("stream",sendRtpItem.getStream());
116 116 param.put("ssrc",sendRtpItem.getSsrc());
117 117 JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
118 118 if (jsonObject != null && jsonObject.getInteger("code") == 0) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
2 2  
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
4 4 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  5 +import com.genersoft.iot.vmp.gb28181.bean.*;
5 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
6 7 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
7 8 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
... ... @@ -124,13 +125,19 @@ public interface ISIPCommander {
124 125 String startTime, String endTime, int downloadSpeed, ZlmHttpHookSubscribe.Event hookEvent,
125 126 SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
126 127  
  128 +
127 129 /**
128 130 * 视频流停止
129 131 */
130 132 void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
131 133  
  134 + void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
  135 +
  136 +
132 137 void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException;
133 138  
  139 + void streamByeCmd(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
  140 +
134 141 /**
135 142 * 回放暂停
136 143 */
... ... @@ -160,22 +167,16 @@ public interface ISIPCommander {
160 167 void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException;
161 168  
162 169  
  170 + void streamByeCmdForDeviceInvite(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
  171 +
163 172 /**
  173 + * /**
164 174 * 语音广播
165   - *
166   - * @param device 视频设备
167   - * @param channelId 预览通道
168   - */
169   - void audioBroadcastCmd(Device device,String channelId);
170   -
171   - /**
172   - * 语音广播
173   - *
174   - * @param device 视频设备
  175 + *
  176 + * @param device 视频设备
175 177 */
176   - void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
177   - void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException;
178   -
  178 + void audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
  179 +
179 180 /**
180 181 * 音视频录像控制
181 182 *
... ... @@ -362,4 +363,5 @@ public interface ISIPCommander {
362 363 */
363 364 void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException;
364 365  
  366 +
365 367 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
2 2  
  3 +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
3 4 import com.genersoft.iot.vmp.gb28181.bean.*;
4 5 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  6 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  7 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
5 8 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
  9 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
6 10  
7 11 import javax.sip.InvalidArgumentException;
8 12 import javax.sip.SipException;
... ... @@ -14,6 +18,7 @@ public interface ISIPCommanderForPlatform {
14 18  
15 19 /**
16 20 * 向上级平台注册
  21 + *
17 22 * @param parentPlatform
18 23 * @return
19 24 */
... ... @@ -26,6 +31,7 @@ public interface ISIPCommanderForPlatform {
26 31  
27 32 /**
28 33 * 向上级平台注销
  34 + *
29 35 * @param parentPlatform
30 36 * @return
31 37 */
... ... @@ -34,26 +40,33 @@ public interface ISIPCommanderForPlatform {
34 40  
35 41 /**
36 42 * 向上级平发送心跳信息
  43 + *
37 44 * @param parentPlatform
38 45 * @return callId(作为接受回复的判定)
39 46 */
40   - String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException;
  47 + String keepalive(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
  48 + throws SipException, InvalidArgumentException, ParseException;
41 49  
42 50  
43 51 /**
44 52 * 向上级回复通道信息
45   - * @param channel 通道信息
  53 + *
  54 + * @param channel 通道信息
46 55 * @param parentPlatform 平台信息
47 56 * @param sn
48 57 * @param fromTag
49 58 * @param size
50 59 * @return
51 60 */
52   - void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException;
53   - void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException;
  61 + void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size)
  62 + throws SipException, InvalidArgumentException, ParseException;
  63 +
  64 + void catalogQuery(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag)
  65 + throws InvalidArgumentException, ParseException, SipException;
54 66  
55 67 /**
56 68 * 向上级回复DeviceInfo查询信息
  69 + *
57 70 * @param parentPlatform 平台信息
58 71 * @param sn SN
59 72 * @param fromTag FROM头的tag信息
... ... @@ -63,6 +76,7 @@ public interface ISIPCommanderForPlatform {
63 76  
64 77 /**
65 78 * 向上级回复DeviceStatus查询信息
  79 + *
66 80 * @param parentPlatform 平台信息
67 81 * @param sn
68 82 * @param fromTag
... ... @@ -72,23 +86,27 @@ public interface ISIPCommanderForPlatform {
72 86  
73 87 /**
74 88 * 向上级回复移动位置订阅消息
  89 + *
75 90 * @param parentPlatform 平台信息
76   - * @param gpsMsgInfo GPS信息
77   - * @param subscribeInfo 订阅相关的信息
  91 + * @param gpsMsgInfo GPS信息
  92 + * @param subscribeInfo 订阅相关的信息
78 93 * @return
79 94 */
80   - void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
  95 + void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo)
  96 + throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
81 97  
82 98 /**
83 99 * 向上级回复报警消息
  100 + *
84 101 * @param parentPlatform 平台信息
85   - * @param deviceAlarm 报警信息信息
  102 + * @param deviceAlarm 报警信息信息
86 103 * @return
87 104 */
88 105 void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException;
89 106  
90 107 /**
91 108 * 回复catalog事件-增加/更新
  109 + *
92 110 * @param parentPlatform
93 111 * @param deviceChannels
94 112 */
... ... @@ -96,22 +114,28 @@ public interface ISIPCommanderForPlatform {
96 114  
97 115 /**
98 116 * 回复catalog事件-删除
  117 + *
99 118 * @param parentPlatform
100 119 * @param deviceChannels
101 120 */
102   - void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException;
  121 + void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels,
  122 + SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException,
  123 + ParseException, NoSuchFieldException, SipException, IllegalAccessException;
103 124  
104 125 /**
105 126 * 回复recordInfo
106   - * @param deviceChannel 通道信息
  127 + *
  128 + * @param deviceChannel 通道信息
107 129 * @param parentPlatform 平台信息
108   - * @param fromTag fromTag
109   - * @param recordInfo 录像信息
  130 + * @param fromTag fromTag
  131 + * @param recordInfo 录像信息
110 132 */
111   - void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException;
  133 + void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo)
  134 + throws SipException, InvalidArgumentException, ParseException;
112 135  
113 136 /**
114 137 * 录像播放推送完成时发送MediaStatus消息
  138 + *
115 139 * @param platform
116 140 * @param sendRtpItem
117 141 * @return
... ... @@ -120,9 +144,19 @@ public interface ISIPCommanderForPlatform {
120 144  
121 145 /**
122 146 * 向发起点播的上级回复bye
  147 + *
123 148 * @param platform 平台信息
124   - * @param callId callId
  149 + * @param callId callId
125 150 */
126 151 void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException;
  152 +
127 153 void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException;
  154 +
  155 + void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException;
  156 +
  157 + void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
  158 + SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
  159 + SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException;
  160 +
  161 + void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
128 162 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
... ... @@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.conf.SipConfig;
4 4 import com.genersoft.iot.vmp.gb28181.SipLayer;
5 5 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
6 6 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
  7 +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
7 8 import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
8 9 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
9 10 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
... ... @@ -55,7 +56,7 @@ public class SIPRequestHeaderPlarformProvider {
55 56 //via
56 57 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
57 58 ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(),
58   - Integer.parseInt(parentPlatform.getDevicePort()), parentPlatform.getTransport(), SipUtils.getNewViaTag());
  59 + parentPlatform.getDevicePort(), parentPlatform.getTransport(), SipUtils.getNewViaTag());
59 60 viaHeader.setRPort();
60 61 viaHeaders.add(viaHeader);
61 62 //from
... ... @@ -182,7 +183,7 @@ public class SIPRequestHeaderPlarformProvider {
182 183 SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
183 184 // via
184 185 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
185   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
  186 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
186 187 parentPlatform.getTransport(), viaTag);
187 188 viaHeader.setRPort();
188 189 viaHeaders.add(viaHeader);
... ... @@ -219,7 +220,7 @@ public class SIPRequestHeaderPlarformProvider {
219 220 SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
220 221 // via
221 222 ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
222   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
  223 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), parentPlatform.getDevicePort(),
223 224 parentPlatform.getTransport(), SipUtils.getNewViaTag());
224 225 viaHeader.setRPort();
225 226 viaHeaders.add(viaHeader);
... ... @@ -279,7 +280,7 @@ public class SIPRequestHeaderPlarformProvider {
279 280 SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort());
280 281 // via
281 282 ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
282   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()),
  283 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(platform.getDeviceIp(), platform.getDevicePort(),
283 284 platform.getTransport(), SipUtils.getNewViaTag());
284 285 viaHeader.setRPort();
285 286 viaHeaders.add(viaHeader);
... ... @@ -313,4 +314,80 @@ public class SIPRequestHeaderPlarformProvider {
313 314  
314 315 return request;
315 316 }
  317 +
  318 + public Request createInviteRequest(ParentPlatform platform, String channelId, String content, String viaTag, String fromTag, String ssrc, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException {
  319 + Request request = null;
  320 + //请求行
  321 + String platformHostAddress = platform.getServerIP() + ":" + platform.getServerPort();
  322 + String localHostAddress = sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort();
  323 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, platformHostAddress);
  324 + //via
  325 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  326 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), viaTag);
  327 + viaHeader.setRPort();
  328 + viaHeaders.add(viaHeader);
  329 +
  330 + //from
  331 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getDeviceGBId(), sipConfig.getDomain());
  332 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  333 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
  334 + //to
  335 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, platformHostAddress);
  336 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  337 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null);
  338 +
  339 + //Forwards
  340 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  341 +
  342 + //ceq
  343 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
  344 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  345 +
  346 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  347 +
  348 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),localHostAddress));
  349 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
  350 + // Subject
  351 + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
  352 + request.addHeader(subjectHeader);
  353 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  354 + request.setContent(content, contentTypeHeader);
  355 + return request;
  356 + }
  357 +
  358 + public Request createByteRequest(ParentPlatform platform, String channelId, SipTransactionInfo transactionInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException {
  359 + String deviceHostAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
  360 + Request request = null;
  361 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, deviceHostAddress);
  362 +
  363 + // via
  364 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  365 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getDevicePort(), platform.getTransport(), SipUtils.getNewViaTag());
  366 + viaHeaders.add(viaHeader);
  367 + //from
  368 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
  369 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  370 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.isAsSender()?transactionInfo.getFromTag():transactionInfo.getToTag());
  371 + //to
  372 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, deviceHostAddress);
  373 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  374 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,transactionInfo.isAsSender()?transactionInfo.getToTag():transactionInfo.getFromTag());
  375 +
  376 + //Forwards
  377 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  378 +
  379 + //ceq
  380 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
  381 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
  382 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  383 +
  384 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  385 +
  386 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(platform.getDeviceIp())+":"+ platform.getDevicePort()));
  387 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
  388 +
  389 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  390 +
  391 + return request;
  392 + }
316 393 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
1   -package com.genersoft.iot.vmp.gb28181.transmit.cmd;
2   -
3   -import com.genersoft.iot.vmp.conf.SipConfig;
4   -import com.genersoft.iot.vmp.gb28181.SipLayer;
5   -import com.genersoft.iot.vmp.gb28181.bean.Device;
6   -import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
7   -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
8   -import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
9   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
10   -import com.genersoft.iot.vmp.utils.GitUtil;
11   -import gov.nist.javax.sip.message.SIPRequest;
12   -import gov.nist.javax.sip.message.SIPResponse;
13   -import org.springframework.beans.factory.annotation.Autowired;
14   -import org.springframework.stereotype.Component;
15   -
16   -import javax.sip.InvalidArgumentException;
17   -import javax.sip.PeerUnavailableException;
18   -import javax.sip.SipException;
19   -import javax.sip.SipFactory;
20   -import javax.sip.address.Address;
21   -import javax.sip.address.SipURI;
22   -import javax.sip.header.*;
23   -import javax.sip.message.Request;
24   -import java.text.ParseException;
25   -import java.util.ArrayList;
26   -
27   -/**
28   - * @description:摄像头命令request创造器 TODO 冗余代码太多待优化
29   - * @author: swwheihei
30   - * @date: 2020年5月6日 上午9:29:02
31   - */
32   -@Component
33   -public class SIPRequestHeaderProvider {
34   -
35   - @Autowired
36   - private SipConfig sipConfig;
37   -
38   - @Autowired
39   - private SipLayer sipLayer;
40   -
41   - @Autowired
42   - private GitUtil gitUtil;
43   -
44   - @Autowired
45   - private IRedisCatchStorage redisCatchStorage;
46   -
47   - @Autowired
48   - private VideoStreamSessionManager streamSession;
49   -
50   - public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
51   - Request request = null;
52   - // sipuri
53   - SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
54   - // via
55   - ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
56   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
57   - viaHeader.setRPort();
58   - viaHeaders.add(viaHeader);
59   - // from
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);
63   - // to
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);
67   -
68   - // Forwards
69   - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
70   - // ceq
71   - CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
72   -
73   - request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
74   - toHeader, viaHeaders, maxForwards);
75   -
76   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
77   -
78   - ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
79   - request.setContent(content, contentTypeHeader);
80   - return request;
81   - }
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 {
84   - Request request = null;
85   - //请求行
86   - SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
87   - //via
88   - ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
89   - HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory();
90   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
91   - viaHeader.setRPort();
92   - viaHeaders.add(viaHeader);
93   -
94   - //from
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
98   - //to
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);
102   -
103   - //Forwards
104   - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
105   -
106   - //ceq
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);
109   -
110   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
111   -
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));
115   - // Subject
116   - SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
117   - request.addHeader(subjectHeader);
118   - ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
119   - request.setContent(content, contentTypeHeader);
120   - return request;
121   - }
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 {
124   - Request request = null;
125   - //请求行
126   - SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
127   - // via
128   - ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
129   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
130   - viaHeader.setRPort();
131   - viaHeaders.add(viaHeader);
132   - //from
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
136   - //to
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);
140   -
141   - //Forwards
142   - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
143   -
144   - //ceq
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);
147   -
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));
151   -
152   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
153   -
154   - // Subject
155   - SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
156   - request.addHeader(subjectHeader);
157   -
158   - ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
159   - request.setContent(content, contentTypeHeader);
160   - return request;
161   - }
162   -
163   - public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException {
164   - Request request = null;
165   - //请求行
166   - SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
167   -// SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
168   - // via
169   - ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
170   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
171   - viaHeaders.add(viaHeader);
172   - //from
173   - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
174   - Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
175   - FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
176   - //to
177   - SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
178   -// SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(),device.getHostAddress());
179   - Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
180   - ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
181   -
182   - //Forwards
183   - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
184   -
185   - //ceq
186   - CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
187   - CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
188   - request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
189   -
190   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
191   -
192   - Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
193   - request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
194   -
195   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
196   -
197   - return request;
198   - }
199   -
200   - public Request createSubscribeRequest(Device device, String content, SIPRequest requestOld, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
201   - Request request = null;
202   - // sipuri
203   - SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
204   - // via
205   - ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
206   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(),
207   - device.getTransport(), SipUtils.getNewViaTag());
208   - viaHeader.setRPort();
209   - viaHeaders.add(viaHeader);
210   - // from
211   - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
212   - Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
213   - FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag());
214   - // to
215   - SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
216   - Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
217   - ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag());
218   -
219   - // Forwards
220   - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
221   -
222   - // ceq
223   - CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE);
224   -
225   - request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
226   - toHeader, viaHeaders, maxForwards);
227   -
228   -
229   - Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
230   - request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
231   -
232   - // Expires
233   - ExpiresHeader expireHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires);
234   - request.addHeader(expireHeader);
235   -
236   - // Event
237   - EventHeader eventHeader = SipFactory.getInstance().createHeaderFactory().createEventHeader(event);
238   -
239   - int random = (int) Math.floor(Math.random() * 10000);
240   - eventHeader.setEventId(random + "");
241   - request.addHeader(eventHeader);
242   -
243   - ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
244   - request.setContent(content, contentTypeHeader);
245   -
246   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
247   -
248   - return request;
249   - }
250   -
251   - public SIPRequest createInfoRequest(Device device, String channelId, String content, SipTransactionInfo transactionInfo)
252   - throws SipException, ParseException, InvalidArgumentException {
253   - if (device == null || transactionInfo == null) {
254   - return null;
255   - }
256   - SIPRequest request = null;
257   - //请求行
258   - SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
259   - // via
260   - ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
261   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
262   - viaHeaders.add(viaHeader);
263   - //from
264   - SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
265   - Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
266   - FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
267   - //to
268   - SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
269   - Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
270   - ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
271   -
272   - //Forwards
273   - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
274   -
275   - //ceq
276   - CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO);
277   - CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
278   - request = (SIPRequest)SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
279   -
280   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
281   -
282   - Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
283   - request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
284   -
285   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
286   -
287   - if (content != null) {
288   - ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application",
289   - "MANSRTSP");
290   - request.setContent(content, contentTypeHeader);
291   - }
292   - return request;
293   - }
294   -
295   - public Request createAckRequest(String localIp, SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException {
296   -
297   -
298   - // via
299   - ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
300   - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag());
301   - viaHeaders.add(viaHeader);
302   -
303   - //Forwards
304   - MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
305   -
306   - //ceq
307   - CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK);
308   -
309   - Request request = SipFactory.getInstance().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards);
310   -
311   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
312   -
313   - Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort()));
314   - request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
315   -
316   - request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
317   -
318   - return request;
319   - }
320   -}
  1 +package com.genersoft.iot.vmp.gb28181.transmit.cmd;
  2 +
  3 +import com.genersoft.iot.vmp.conf.SipConfig;
  4 +import com.genersoft.iot.vmp.gb28181.SipLayer;
  5 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  6 +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
  7 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  8 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  9 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  10 +import com.genersoft.iot.vmp.utils.GitUtil;
  11 +import gov.nist.javax.sip.message.SIPRequest;
  12 +import gov.nist.javax.sip.message.SIPResponse;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.stereotype.Component;
  15 +
  16 +import javax.sip.InvalidArgumentException;
  17 +import javax.sip.PeerUnavailableException;
  18 +import javax.sip.SipException;
  19 +import javax.sip.SipFactory;
  20 +import javax.sip.address.Address;
  21 +import javax.sip.address.SipURI;
  22 +import javax.sip.header.*;
  23 +import javax.sip.message.Request;
  24 +import java.text.ParseException;
  25 +import java.util.ArrayList;
  26 +
  27 +/**
  28 + * @description:摄像头命令request创造器 TODO 冗余代码太多待优化
  29 + * @author: swwheihei
  30 + * @date: 2020年5月6日 上午9:29:02
  31 + */
  32 +@Component
  33 +public class SIPRequestHeaderProvider {
  34 +
  35 + @Autowired
  36 + private SipConfig sipConfig;
  37 +
  38 + @Autowired
  39 + private SipLayer sipLayer;
  40 +
  41 + @Autowired
  42 + private GitUtil gitUtil;
  43 +
  44 + @Autowired
  45 + private IRedisCatchStorage redisCatchStorage;
  46 +
  47 + @Autowired
  48 + private VideoStreamSessionManager streamSession;
  49 +
  50 + public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  51 + Request request = null;
  52 + // sipuri
  53 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  54 + // via
  55 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  56 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
  57 + viaHeader.setRPort();
  58 + viaHeaders.add(viaHeader);
  59 + // from
  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);
  63 + // to
  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);
  67 +
  68 + // Forwards
  69 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  70 + // ceq
  71 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
  72 +
  73 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
  74 + toHeader, viaHeaders, maxForwards);
  75 +
  76 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  77 +
  78 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  79 + request.setContent(content, contentTypeHeader);
  80 + return request;
  81 + }
  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 {
  84 + Request request = null;
  85 + //请求行
  86 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  87 + //via
  88 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  89 + HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory();
  90 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
  91 + viaHeader.setRPort();
  92 + viaHeaders.add(viaHeader);
  93 +
  94 + //from
  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
  98 + //to
  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);
  102 +
  103 + //Forwards
  104 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  105 +
  106 + //ceq
  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);
  109 +
  110 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  111 +
  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));
  115 + // Subject
  116 + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
  117 + request.addHeader(subjectHeader);
  118 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  119 + request.setContent(content, contentTypeHeader);
  120 + return request;
  121 + }
  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 {
  124 + Request request = null;
  125 + //请求行
  126 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  127 + // via
  128 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  129 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
  130 + viaHeader.setRPort();
  131 + viaHeaders.add(viaHeader);
  132 + //from
  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
  136 + //to
  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);
  140 +
  141 + //Forwards
  142 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  143 +
  144 + //ceq
  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);
  147 +
  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));
  151 +
  152 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  153 +
  154 + // Subject
  155 + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
  156 + request.addHeader(subjectHeader);
  157 +
  158 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
  159 + request.setContent(content, contentTypeHeader);
  160 + return request;
  161 + }
  162 +
  163 + public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  164 + Request request = null;
  165 + //请求行
  166 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  167 +// SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  168 + // via
  169 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  170 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
  171 + viaHeaders.add(viaHeader);
  172 + //from
  173 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
  174 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  175 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
  176 + //to
  177 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
  178 +// SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(),device.getHostAddress());
  179 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  180 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
  181 +
  182 + //Forwards
  183 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  184 +
  185 + //ceq
  186 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
  187 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
  188 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  189 +
  190 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  191 +
  192 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  193 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
  194 +
  195 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  196 +
  197 + return request;
  198 + }
  199 +
  200 + public Request createByteRequestForDeviceInvite(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  201 + Request request = null;
  202 + //请求行
  203 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  204 + // via
  205 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  206 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
  207 + viaHeaders.add(viaHeader);
  208 + //from
  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, transactionInfo.getToTag());
  212 + //to
  213 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
  214 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  215 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getFromTag());
  216 +
  217 + //Forwards
  218 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  219 +
  220 + //ceq
  221 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
  222 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
  223 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  224 +
  225 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  226 +
  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));
  229 +
  230 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  231 +
  232 + return request;
  233 + }
  234 +
  235 + public Request createSubscribeRequest(Device device, String content, SIPRequest requestOld, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  236 + Request request = null;
  237 + // sipuri
  238 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  239 + // via
  240 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  241 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(),
  242 + device.getTransport(), SipUtils.getNewViaTag());
  243 + viaHeader.setRPort();
  244 + viaHeaders.add(viaHeader);
  245 + // from
  246 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
  247 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  248 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag());
  249 + // to
  250 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  251 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  252 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag());
  253 +
  254 + // Forwards
  255 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  256 +
  257 + // ceq
  258 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE);
  259 +
  260 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
  261 + toHeader, viaHeaders, maxForwards);
  262 +
  263 +
  264 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  265 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
  266 +
  267 + // Expires
  268 + ExpiresHeader expireHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires);
  269 + request.addHeader(expireHeader);
  270 +
  271 + // Event
  272 + EventHeader eventHeader = SipFactory.getInstance().createHeaderFactory().createEventHeader(event);
  273 +
  274 + int random = (int) Math.floor(Math.random() * 10000);
  275 + eventHeader.setEventId(random + "");
  276 + request.addHeader(eventHeader);
  277 +
  278 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  279 + request.setContent(content, contentTypeHeader);
  280 +
  281 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  282 +
  283 + return request;
  284 + }
  285 +
  286 + public SIPRequest createInfoRequest(Device device, String channelId, String content, SipTransactionInfo transactionInfo)
  287 + throws SipException, ParseException, InvalidArgumentException {
  288 + if (device == null || transactionInfo == null) {
  289 + return null;
  290 + }
  291 + SIPRequest request = null;
  292 + //请求行
  293 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  294 + // via
  295 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  296 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
  297 + viaHeaders.add(viaHeader);
  298 + //from
  299 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
  300 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  301 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
  302 + //to
  303 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
  304 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  305 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
  306 +
  307 + //Forwards
  308 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  309 +
  310 + //ceq
  311 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO);
  312 + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
  313 + request = (SIPRequest)SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
  314 +
  315 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  316 +
  317 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
  318 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
  319 +
  320 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  321 +
  322 + if (content != null) {
  323 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application",
  324 + "MANSRTSP");
  325 + request.setContent(content, contentTypeHeader);
  326 + }
  327 + return request;
  328 + }
  329 +
  330 + public Request createAckRequest(String localIp, SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  331 +
  332 +
  333 + // via
  334 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  335 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag());
  336 + viaHeaders.add(viaHeader);
  337 +
  338 + //Forwards
  339 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  340 +
  341 + //ceq
  342 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK);
  343 +
  344 + Request request = SipFactory.getInstance().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards);
  345 +
  346 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  347 +
  348 + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort()));
  349 + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
  350 +
  351 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  352 +
  353 + return request;
  354 + }
  355 + public Request createBroadcastMessageRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  356 + Request request = null;
  357 + // sipuri
  358 + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  359 + // via
  360 + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
  361 + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
  362 + viaHeader.setRPort();
  363 + viaHeaders.add(viaHeader);
  364 + // from
  365 + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
  366 + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
  367 + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag);
  368 + // to
  369 + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  370 + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
  371 + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag);
  372 +
  373 + // Forwards
  374 + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
  375 + // ceq
  376 + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
  377 +
  378 + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  379 +
  380 + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
  381 + toHeader, viaHeaders, maxForwards, contentTypeHeader, content);
  382 +
  383 + request.addHeader(SipUtils.createUserAgentHeader(gitUtil));
  384 +
  385 + return request;
  386 + }
  387 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
1   -package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2   -
3   -import com.genersoft.iot.vmp.common.InviteSessionType;
4   -import com.genersoft.iot.vmp.common.StreamInfo;
5   -import com.genersoft.iot.vmp.conf.SipConfig;
6   -import com.genersoft.iot.vmp.conf.UserSetting;
7   -import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
8   -import com.genersoft.iot.vmp.gb28181.SipLayer;
9   -import com.genersoft.iot.vmp.gb28181.bean.Device;
10   -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
11   -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
12   -import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
13   -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
14   -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
15   -import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
16   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
17   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
18   -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
19   -import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
20   -import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
21   -import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
22   -import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
23   -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
24   -import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
25   -import com.genersoft.iot.vmp.service.IMediaServerService;
26   -import com.genersoft.iot.vmp.service.bean.SSRCInfo;
27   -import com.genersoft.iot.vmp.utils.DateUtil;
28   -import gov.nist.javax.sip.message.SIPRequest;
29   -import gov.nist.javax.sip.message.SIPResponse;
30   -import org.slf4j.Logger;
31   -import org.slf4j.LoggerFactory;
32   -import org.springframework.beans.factory.annotation.Autowired;
33   -import org.springframework.context.annotation.DependsOn;
34   -import org.springframework.stereotype.Component;
35   -import org.springframework.util.ObjectUtils;
36   -
37   -import javax.sip.InvalidArgumentException;
38   -import javax.sip.ResponseEvent;
39   -import javax.sip.SipException;
40   -import javax.sip.SipFactory;
41   -import javax.sip.header.CallIdHeader;
42   -import javax.sip.message.Request;
43   -import java.text.ParseException;
44   -import java.util.List;
45   -
46   -/**
47   - * @description:设备能力接口,用于定义设备的控制、查询能力
48   - * @author: swwheihei
49   - * @date: 2020年5月3日 下午9:22:48
50   - */
51   -@Component
52   -@DependsOn("sipLayer")
53   -public class SIPCommander implements ISIPCommander {
54   -
55   - private final Logger logger = LoggerFactory.getLogger(SIPCommander.class);
56   -
57   - @Autowired
58   - private SipConfig sipConfig;
59   -
60   - @Autowired
61   - private SipLayer sipLayer;
62   -
63   - @Autowired
64   - private SIPSender sipSender;
65   -
66   - @Autowired
67   - private SIPRequestHeaderProvider headerProvider;
68   -
69   - @Autowired
70   - private VideoStreamSessionManager streamSession;
71   -
72   - @Autowired
73   - private UserSetting userSetting;
74   -
75   - @Autowired
76   - private ZlmHttpHookSubscribe subscribe;
77   -
78   -
79   -
80   - @Autowired
81   - private IMediaServerService mediaServerService;
82   -
83   -
84   - /**
85   - * 云台方向放控制,使用配置文件中的默认镜头移动速度
86   - *
87   - * @param device 控制设备
88   - * @param channelId 预览通道
89   - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
90   - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
91   - */
92   - @Override
93   - public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException {
94   - ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0);
95   - }
96   -
97   - /**
98   - * 云台方向放控制
99   - *
100   - * @param device 控制设备
101   - * @param channelId 预览通道
102   - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
103   - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
104   - * @param moveSpeed 镜头移动速度
105   - */
106   - @Override
107   - public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException {
108   - ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0);
109   - }
110   -
111   - /**
112   - * 云台缩放控制,使用配置文件中的默认镜头缩放速度
113   - *
114   - * @param device 控制设备
115   - * @param channelId 预览通道
116   - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
117   - */
118   - @Override
119   - public void ptzZoomCmd(Device device, String channelId, int inOut) throws InvalidArgumentException, ParseException, SipException {
120   - ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed());
121   - }
122   -
123   - /**
124   - * 云台缩放控制
125   - *
126   - * @param device 控制设备
127   - * @param channelId 预览通道
128   - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
129   - * @param zoomSpeed 镜头缩放速度
130   - */
131   - @Override
132   - public void ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) throws InvalidArgumentException, ParseException, SipException {
133   - ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed);
134   - }
135   -
136   - /**
137   - * 云台指令码计算
138   - *
139   - * @param cmdCode 指令码
140   - * @param parameter1 数据1
141   - * @param parameter2 数据2
142   - * @param combineCode2 组合码2
143   - */
144   - public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) {
145   - StringBuilder builder = new StringBuilder("A50F01");
146   - String strTmp;
147   - strTmp = String.format("%02X", cmdCode);
148   - builder.append(strTmp, 0, 2);
149   - strTmp = String.format("%02X", parameter1);
150   - builder.append(strTmp, 0, 2);
151   - strTmp = String.format("%02X", parameter2);
152   - builder.append(strTmp, 0, 2);
153   - //优化zoom变倍速率
154   - if ((combineCode2 > 0) && (combineCode2 <16))
155   - {
156   - combineCode2 = 16;
157   - }
158   - strTmp = String.format("%X", combineCode2);
159   - builder.append(strTmp, 0, 1).append("0");
160   - //计算校验码
161   - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100;
162   - strTmp = String.format("%02X", checkCode);
163   - builder.append(strTmp, 0, 2);
164   - return builder.toString();
165   - }
166   -
167   - /**
168   - * 云台控制,支持方向与缩放控制
169   - *
170   - * @param device 控制设备
171   - * @param channelId 预览通道
172   - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
173   - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
174   - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
175   - * @param moveSpeed 镜头移动速度
176   - * @param zoomSpeed 镜头缩放速度
177   - */
178   - @Override
179   - public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
180   - int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
181   - String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
182   - StringBuilder ptzXml = new StringBuilder(200);
183   - String charset = device.getCharset();
184   - ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
185   - ptzXml.append("<Control>\r\n");
186   - ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
187   - ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
188   - ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
189   - ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
190   - ptzXml.append("<Info>\r\n");
191   - ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
192   - ptzXml.append("</Info>\r\n");
193   - ptzXml.append("</Control>\r\n");
194   -
195   - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
196   -
197   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
198   - }
199   -
200   - /**
201   - * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
202   - *
203   - * @param device 控制设备
204   - * @param channelId 预览通道
205   - * @param cmdCode 指令码
206   - * @param parameter1 数据1
207   - * @param parameter2 数据2
208   - * @param combineCode2 组合码2
209   - */
210   - @Override
211   - public void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException {
212   -
213   - String cmdStr = frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2);
214   - StringBuffer ptzXml = new StringBuffer(200);
215   - String charset = device.getCharset();
216   - ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
217   - ptzXml.append("<Control>\r\n");
218   - ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
219   - ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
220   - ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
221   - ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
222   - ptzXml.append("<Info>\r\n");
223   - ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
224   - ptzXml.append("</Info>\r\n");
225   - ptzXml.append("</Control>\r\n");
226   -
227   -
228   -
229   -
230   - SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
231   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
232   -
233   - }
234   -
235   - /**
236   - * 前端控制指令(用于转发上级指令)
237   - *
238   - * @param device 控制设备
239   - * @param channelId 预览通道
240   - * @param cmdString 前端控制指令串
241   - */
242   - @Override
243   - public void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
244   -
245   - StringBuffer ptzXml = new StringBuffer(200);
246   - String charset = device.getCharset();
247   - ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
248   - ptzXml.append("<Control>\r\n");
249   - ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
250   - ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
251   - ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
252   - ptzXml.append("<PTZCmd>" + cmdString + "</PTZCmd>\r\n");
253   - ptzXml.append("<Info>\r\n");
254   - ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
255   - ptzXml.append("</Info>\r\n");
256   - ptzXml.append("</Control>\r\n");
257   -
258   -
259   - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
260   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request, errorEvent, okEvent);
261   -
262   - }
263   -
264   - /**
265   - * 请求预览视频流
266   - *
267   - * @param device 视频设备
268   - * @param channel 预览通道
269   - * @param event hook订阅
270   - * @param errorEvent sip错误订阅
271   - */
272   - @Override
273   - public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel,
274   - ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
275   - String stream = ssrcInfo.getStream();
276   -
277   - if (device == null) {
278   - return;
279   - }
280   -
281   - logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
282   - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
283   - subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
284   - if (event != null) {
285   - event.response(mediaServerItemInUse, hookParam);
286   - subscribe.removeSubscribe(hookSubscribe);
287   - }
288   - });
289   - String sdpIp;
290   - if (!ObjectUtils.isEmpty(device.getSdpIp())) {
291   - sdpIp = device.getSdpIp();
292   - }else {
293   - sdpIp = mediaServerItem.getSdpIp();
294   - }
295   - StringBuffer content = new StringBuffer(200);
296   - content.append("v=0\r\n");
297   - content.append("o=" + channel.getChannelId() + " 0 0 IN IP4 " + sdpIp + "\r\n");
298   - content.append("s=Play\r\n");
299   - content.append("c=IN IP4 " + sdpIp + "\r\n");
300   - content.append("t=0 0\r\n");
301   -
302   - if (userSetting.isSeniorSdp()) {
303   - if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
304   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
305   - } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
306   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
307   - } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
308   - content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
309   - }
310   - content.append("a=recvonly\r\n");
311   - content.append("a=rtpmap:96 PS/90000\r\n");
312   - content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
313   - content.append("a=rtpmap:126 H264/90000\r\n");
314   - content.append("a=rtpmap:125 H264S/90000\r\n");
315   - content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
316   - content.append("a=rtpmap:99 H265/90000\r\n");
317   - content.append("a=rtpmap:98 H264/90000\r\n");
318   - content.append("a=rtpmap:97 MPEG4/90000\r\n");
319   - if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
320   - content.append("a=setup:passive\r\n");
321   - content.append("a=connection:new\r\n");
322   - } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
323   - content.append("a=setup:active\r\n");
324   - content.append("a=connection:new\r\n");
325   - }
326   - } else {
327   - if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
328   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
329   - } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
330   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
331   - } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
332   - content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
333   - }
334   - content.append("a=recvonly\r\n");
335   - content.append("a=rtpmap:96 PS/90000\r\n");
336   - content.append("a=rtpmap:98 H264/90000\r\n");
337   - content.append("a=rtpmap:97 MPEG4/90000\r\n");
338   - content.append("a=rtpmap:99 H265/90000\r\n");
339   - if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
340   - content.append("a=setup:passive\r\n");
341   - content.append("a=connection:new\r\n");
342   - } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
343   - content.append("a=setup:active\r\n");
344   - content.append("a=connection:new\r\n");
345   - }
346   - }
347   -
348   - if (!ObjectUtils.isEmpty(channel.getStreamIdentification())) {
349   - content.append("a=" + channel.getStreamIdentification() + "\r\n");
350   - }
351   -
352   - content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
353   - // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
354   -// content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备
355   -
356   -
357   -
358   - Request request = headerProvider.createInviteRequest(device, channel.getChannelId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
359   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> {
360   - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream());
361   - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
362   - errorEvent.response(e);
363   - }), e -> {
364   - ResponseEvent responseEvent = (ResponseEvent) e.event;
365   - SIPResponse response = (SIPResponse) responseEvent.getResponse();
366   - String callId = response.getCallIdHeader().getCallId();
367   - streamSession.put(device.getDeviceId(), channel.getChannelId(), callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response,
368   - InviteSessionType.PLAY);
369   - okEvent.response(e);
370   - });
371   - }
372   -
373   - /**
374   - * 请求回放视频流
375   - *
376   - * @param device 视频设备
377   - * @param channelId 预览通道
378   - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
379   - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
380   - */
381   - @Override
382   - public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
383   - String startTime, String endTime, ZlmHttpHookSubscribe.Event hookEvent,
384   - SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
385   -
386   -
387   - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
388   - String sdpIp;
389   - if (!ObjectUtils.isEmpty(device.getSdpIp())) {
390   - sdpIp = device.getSdpIp();
391   - }else {
392   - sdpIp = mediaServerItem.getSdpIp();
393   - }
394   - StringBuffer content = new StringBuffer(200);
395   - content.append("v=0\r\n");
396   - content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
397   - content.append("s=Playback\r\n");
398   - content.append("u=" + channelId + ":0\r\n");
399   - content.append("c=IN IP4 " + sdpIp + "\r\n");
400   - content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
401   - + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
402   -
403   - String streamMode = device.getStreamMode();
404   -
405   - if (userSetting.isSeniorSdp()) {
406   - if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
407   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
408   - } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
409   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
410   - } else if ("UDP".equalsIgnoreCase(streamMode)) {
411   - content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
412   - }
413   - content.append("a=recvonly\r\n");
414   - content.append("a=rtpmap:96 PS/90000\r\n");
415   - content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
416   - content.append("a=rtpmap:126 H264/90000\r\n");
417   - content.append("a=rtpmap:125 H264S/90000\r\n");
418   - content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
419   - content.append("a=rtpmap:99 H265/90000\r\n");
420   - content.append("a=rtpmap:98 H264/90000\r\n");
421   - content.append("a=rtpmap:97 MPEG4/90000\r\n");
422   - if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { // tcp被动模式
423   - content.append("a=setup:passive\r\n");
424   - content.append("a=connection:new\r\n");
425   - } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式
426   - content.append("a=setup:active\r\n");
427   - content.append("a=connection:new\r\n");
428   - }
429   - } else {
430   - if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
431   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
432   - } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
433   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
434   - } else if ("UDP".equalsIgnoreCase(streamMode)) {
435   - content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
436   - }
437   - content.append("a=recvonly\r\n");
438   - content.append("a=rtpmap:96 PS/90000\r\n");
439   - content.append("a=rtpmap:97 MPEG4/90000\r\n");
440   - content.append("a=rtpmap:98 H264/90000\r\n");
441   - content.append("a=rtpmap:99 H265/90000\r\n");
442   - if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
443   - // tcp被动模式
444   - content.append("a=setup:passive\r\n");
445   - content.append("a=connection:new\r\n");
446   - } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
447   - // tcp主动模式
448   - content.append("a=setup:active\r\n");
449   - content.append("a=connection:new\r\n");
450   - }
451   - }
452   -
453   - //ssrc
454   - content.append("y=" + ssrcInfo.getSsrc() + "\r\n");
455   -
456   - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
457   - // 添加订阅
458   - subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
459   - if (hookEvent != null) {
460   - hookEvent.response(mediaServerItemInUse, hookParam);
461   - }
462   - subscribe.removeSubscribe(hookSubscribe);
463   - });
464   - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
465   -
466   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
467   - ResponseEvent responseEvent = (ResponseEvent) event.event;
468   - SIPResponse response = (SIPResponse) responseEvent.getResponse();
469   - streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAYBACK);
470   - okEvent.response(event);
471   - });
472   - }
473   -
474   - /**
475   - * 请求历史媒体下载
476   - *
477   - * @param device 视频设备
478   - * @param channelId 预览通道
479   - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
480   - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
481   - * @param downloadSpeed 下载倍速参数
482   - */
483   - @Override
484   - public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
485   - String startTime, String endTime, int downloadSpeed,
486   - ZlmHttpHookSubscribe.Event hookEvent,
487   - SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
488   -
489   - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
490   - String sdpIp;
491   - if (!ObjectUtils.isEmpty(device.getSdpIp())) {
492   - sdpIp = device.getSdpIp();
493   - }else {
494   - sdpIp = mediaServerItem.getSdpIp();
495   - }
496   - StringBuffer content = new StringBuffer(200);
497   - content.append("v=0\r\n");
498   - content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
499   - content.append("s=Download\r\n");
500   - content.append("u=" + channelId + ":0\r\n");
501   - content.append("c=IN IP4 " + sdpIp + "\r\n");
502   - content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
503   - + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
504   -
505   - String streamMode = device.getStreamMode().toUpperCase();
506   -
507   - if (userSetting.isSeniorSdp()) {
508   - if ("TCP-PASSIVE".equals(streamMode)) {
509   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
510   - } else if ("TCP-ACTIVE".equals(streamMode)) {
511   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
512   - } else if ("UDP".equals(streamMode)) {
513   - content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
514   - }
515   - content.append("a=recvonly\r\n");
516   - content.append("a=rtpmap:96 PS/90000\r\n");
517   - content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
518   - content.append("a=rtpmap:126 H264/90000\r\n");
519   - content.append("a=rtpmap:125 H264S/90000\r\n");
520   - content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
521   - content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
522   - content.append("a=fmtp:99 profile-level-id=3\r\n");
523   - content.append("a=rtpmap:98 H264/90000\r\n");
524   - content.append("a=rtpmap:97 MPEG4/90000\r\n");
525   - if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
526   - content.append("a=setup:passive\r\n");
527   - content.append("a=connection:new\r\n");
528   - } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
529   - content.append("a=setup:active\r\n");
530   - content.append("a=connection:new\r\n");
531   - }
532   - } else {
533   - if ("TCP-PASSIVE".equals(streamMode)) {
534   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
535   - } else if ("TCP-ACTIVE".equals(streamMode)) {
536   - content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
537   - } else if ("UDP".equals(streamMode)) {
538   - content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
539   - }
540   - content.append("a=recvonly\r\n");
541   - content.append("a=rtpmap:96 PS/90000\r\n");
542   - content.append("a=rtpmap:97 MPEG4/90000\r\n");
543   - content.append("a=rtpmap:98 H264/90000\r\n");
544   - content.append("a=rtpmap:99 H265/90000\r\n");
545   - if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
546   - content.append("a=setup:passive\r\n");
547   - content.append("a=connection:new\r\n");
548   - } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
549   - content.append("a=setup:active\r\n");
550   - content.append("a=connection:new\r\n");
551   - }
552   - }
553   - content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
554   -
555   - content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
556   - logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
557   - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
558   - // 添加订阅
559   - CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
560   - String callId= newCallIdHeader.getCallId();
561   - subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {
562   - logger.debug("sipc 添加订阅===callId {}",callId);
563   - hookEvent.response(mediaServerItemInUse, hookParam);
564   - subscribe.removeSubscribe(hookSubscribe);
565   - hookSubscribe.getContent().put("regist", false);
566   - hookSubscribe.getContent().put("schema", "rtsp");
567   - // 添加流注销的订阅,注销了后向设备发送bye
568   - subscribe.addSubscribe(hookSubscribe,
569   - (mediaServerItemForEnd, hookParam1) -> {
570   - logger.info("[录像]下载结束, 发送BYE");
571   - try {
572   - streamByeCmd(device, channelId, ssrcInfo.getStream(), callId);
573   - } catch (InvalidArgumentException | ParseException | SipException |
574   - SsrcTransactionNotFoundException e) {
575   - logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
576   - }
577   - });
578   - });
579   -
580   - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
581   -
582   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
583   - ResponseEvent responseEvent = (ResponseEvent) event.event;
584   - SIPResponse response = (SIPResponse) responseEvent.getResponse();
585   - String contentString =new String(response.getRawContent());
586   - String ssrc = SipUtils.getSsrcFromSdp(contentString);
587   - streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD);
588   - okEvent.response(event);
589   - });
590   - }
591   -
592   - /**
593   - * 视频流停止, 不使用回调
594   - */
595   - @Override
596   - public void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException {
597   - streamByeCmd(device, channelId, stream, callId, null);
598   - }
599   -
600   - /**
601   - * 视频流停止
602   - */
603   - @Override
604   - public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
605   - if (device == null) {
606   - logger.warn("[发送BYE] device为null");
607   - return;
608   - }
609   - List<SsrcTransaction> ssrcTransactionList = streamSession.getSsrcTransactionForAll(device.getDeviceId(), channelId, callId, stream);
610   - if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
611   - logger.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId);
612   - throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream);
613   - }
614   -
615   - for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
616   - logger.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId());
617   - mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
618   -
619   - mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
620   - streamSession.removeByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getCallId());
621   - Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
622   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
623   -
624   - }
625   - }
626   -
627   - /**
628   - * 语音广播
629   - *
630   - * @param device 视频设备
631   - * @param channelId 预览通道
632   - */
633   - @Override
634   - public void audioBroadcastCmd(Device device, String channelId) {
635   - }
636   -
637   - /**
638   - * 语音广播
639   - *
640   - * @param device 视频设备
641   - */
642   - @Override
643   - public void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException {
644   -
645   - StringBuffer broadcastXml = new StringBuffer(200);
646   - String charset = device.getCharset();
647   - broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
648   - broadcastXml.append("<Notify>\r\n");
649   - broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
650   - broadcastXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
651   - broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
652   - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
653   - broadcastXml.append("</Notify>\r\n");
654   -
655   -
656   -
657   - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
658   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
659   -
660   - }
661   -
662   - @Override
663   - public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
664   -
665   - StringBuffer broadcastXml = new StringBuffer(200);
666   - String charset = device.getCharset();
667   - broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
668   - broadcastXml.append("<Notify>\r\n");
669   - broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
670   - broadcastXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
671   - broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
672   - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
673   - broadcastXml.append("</Notify>\r\n");
674   -
675   -
676   -
677   - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
678   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
679   -
680   - }
681   -
682   -
683   - /**
684   - * 音视频录像控制
685   - *
686   - * @param device 视频设备
687   - * @param channelId 预览通道
688   - * @param recordCmdStr 录像命令:Record / StopRecord
689   - */
690   - @Override
691   - public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
692   - StringBuffer cmdXml = new StringBuffer(200);
693   - String charset = device.getCharset();
694   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
695   - cmdXml.append("<Control>\r\n");
696   - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
697   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
698   - if (ObjectUtils.isEmpty(channelId)) {
699   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
700   - } else {
701   - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
702   - }
703   - cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
704   - cmdXml.append("</Control>\r\n");
705   -
706   -
707   -
708   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
709   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
710   - }
711   -
712   - /**
713   - * 远程启动控制命令
714   - *
715   - * @param device 视频设备
716   - */
717   - @Override
718   - public void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException {
719   -
720   - StringBuffer cmdXml = new StringBuffer(200);
721   - String charset = device.getCharset();
722   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
723   - cmdXml.append("<Control>\r\n");
724   - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
725   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
726   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
727   - cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
728   - cmdXml.append("</Control>\r\n");
729   -
730   -
731   -
732   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
733   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
734   - }
735   -
736   - /**
737   - * 报警布防/撤防命令
738   - *
739   - * @param device 视频设备
740   - * @param guardCmdStr "SetGuard"/"ResetGuard"
741   - */
742   - @Override
743   - public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
744   -
745   - StringBuffer cmdXml = new StringBuffer(200);
746   - String charset = device.getCharset();
747   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
748   - cmdXml.append("<Control>\r\n");
749   - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
750   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
751   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
752   - cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
753   - cmdXml.append("</Control>\r\n");
754   -
755   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
756   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
757   - }
758   -
759   - /**
760   - * 报警复位命令
761   - *
762   - * @param device 视频设备
763   - */
764   - @Override
765   - public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
766   -
767   - StringBuffer cmdXml = new StringBuffer(200);
768   - String charset = device.getCharset();
769   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
770   - cmdXml.append("<Control>\r\n");
771   - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
772   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
773   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
774   - cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
775   - if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
776   - cmdXml.append("<Info>\r\n");
777   - }
778   - if (!ObjectUtils.isEmpty(alarmMethod)) {
779   - cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
780   - }
781   - if (!ObjectUtils.isEmpty(alarmType)) {
782   - cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
783   - }
784   - if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
785   - cmdXml.append("</Info>\r\n");
786   - }
787   - cmdXml.append("</Control>\r\n");
788   -
789   -
790   -
791   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
792   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
793   - }
794   -
795   - /**
796   - * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
797   - *
798   - * @param device 视频设备
799   - * @param channelId 预览通道
800   - */
801   - @Override
802   - public void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException {
803   -
804   - StringBuffer cmdXml = new StringBuffer(200);
805   - String charset = device.getCharset();
806   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
807   - cmdXml.append("<Control>\r\n");
808   - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
809   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
810   - if (ObjectUtils.isEmpty(channelId)) {
811   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
812   - } else {
813   - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
814   - }
815   - cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
816   - cmdXml.append("</Control>\r\n");
817   -
818   -
819   -
820   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
821   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
822   - }
823   -
824   - /**
825   - * 看守位控制命令
826   - *
827   - * @param device 视频设备
828   - * @param channelId 通道id,非通道则是设备本身
829   - * @param enabled 看守位使能:1 = 开启,0 = 关闭
830   - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
831   - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
832   - */
833   - @Override
834   - public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
835   -
836   - StringBuffer cmdXml = new StringBuffer(200);
837   - String charset = device.getCharset();
838   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
839   - cmdXml.append("<Control>\r\n");
840   - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
841   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
842   - if (ObjectUtils.isEmpty(channelId)) {
843   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
844   - } else {
845   - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
846   - }
847   - cmdXml.append("<HomePosition>\r\n");
848   - if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
849   - cmdXml.append("<Enabled>1</Enabled>\r\n");
850   - if (NumericUtil.isInteger(resetTime)) {
851   - cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
852   - } else {
853   - cmdXml.append("<ResetTime>0</ResetTime>\r\n");
854   - }
855   - if (NumericUtil.isInteger(presetIndex)) {
856   - cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
857   - } else {
858   - cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
859   - }
860   - } else {
861   - cmdXml.append("<Enabled>0</Enabled>\r\n");
862   - }
863   - cmdXml.append("</HomePosition>\r\n");
864   - cmdXml.append("</Control>\r\n");
865   -
866   -
867   -
868   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
869   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
870   - }
871   -
872   - /**
873   - * 设备配置命令
874   - *
875   - * @param device 视频设备
876   - */
877   - @Override
878   - public void deviceConfigCmd(Device device) {
879   - // TODO Auto-generated method stub
880   - }
881   -
882   - /**
883   - * 设备配置命令:basicParam
884   - *
885   - * @param device 视频设备
886   - * @param channelId 通道编码(可选)
887   - * @param name 设备/通道名称(可选)
888   - * @param expiration 注册过期时间(可选)
889   - * @param heartBeatInterval 心跳间隔时间(可选)
890   - * @param heartBeatCount 心跳超时次数(可选)
891   - */
892   - @Override
893   - public void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration,
894   - String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
895   -
896   - StringBuffer cmdXml = new StringBuffer(200);
897   - String charset = device.getCharset();
898   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
899   - cmdXml.append("<Control>\r\n");
900   - cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
901   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
902   - if (ObjectUtils.isEmpty(channelId)) {
903   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
904   - } else {
905   - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
906   - }
907   - cmdXml.append("<BasicParam>\r\n");
908   - if (!ObjectUtils.isEmpty(name)) {
909   - cmdXml.append("<Name>" + name + "</Name>\r\n");
910   - }
911   - if (NumericUtil.isInteger(expiration)) {
912   - if (Integer.valueOf(expiration) > 0) {
913   - cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");
914   - }
915   - }
916   - if (NumericUtil.isInteger(heartBeatInterval)) {
917   - if (Integer.valueOf(heartBeatInterval) > 0) {
918   - cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");
919   - }
920   - }
921   - if (NumericUtil.isInteger(heartBeatCount)) {
922   - if (Integer.valueOf(heartBeatCount) > 0) {
923   - cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");
924   - }
925   - }
926   - cmdXml.append("</BasicParam>\r\n");
927   - cmdXml.append("</Control>\r\n");
928   -
929   -
930   -
931   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
932   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
933   - }
934   -
935   - /**
936   - * 查询设备状态
937   - *
938   - * @param device 视频设备
939   - */
940   - @Override
941   - public void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
942   -
943   - String charset = device.getCharset();
944   - StringBuffer catalogXml = new StringBuffer(200);
945   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
946   - catalogXml.append("<Query>\r\n");
947   - catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
948   - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
949   - catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
950   - catalogXml.append("</Query>\r\n");
951   -
952   - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
953   -
954   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
955   - }
956   -
957   - /**
958   - * 查询设备信息
959   - *
960   - * @param device 视频设备
961   - */
962   - @Override
963   - public void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException {
964   -
965   - StringBuffer catalogXml = new StringBuffer(200);
966   - String charset = device.getCharset();
967   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
968   - catalogXml.append("<Query>\r\n");
969   - catalogXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
970   - catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
971   - catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
972   - catalogXml.append("</Query>\r\n");
973   -
974   -
975   -
976   - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
977   -
978   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
979   -
980   - }
981   -
982   - /**
983   - * 查询目录列表
984   - *
985   - * @param device 视频设备
986   - */
987   - @Override
988   - public void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException {
989   -
990   - StringBuffer catalogXml = new StringBuffer(200);
991   - String charset = device.getCharset();
992   - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
993   - catalogXml.append("<Query>\r\n");
994   - catalogXml.append(" <CmdType>Catalog</CmdType>\r\n");
995   - catalogXml.append(" <SN>" + sn + "</SN>\r\n");
996   - catalogXml.append(" <DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
997   - catalogXml.append("</Query>\r\n");
998   -
999   -
1000   -
1001   - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1002   -
1003   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
1004   - }
1005   -
1006   - /**
1007   - * 查询录像信息
1008   - *
1009   - * @param device 视频设备
1010   - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
1011   - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
1012   - */
1013   - @Override
1014   - public void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
1015   - if (secrecy == null) {
1016   - secrecy = 0;
1017   - }
1018   - if (type == null) {
1019   - type = "all";
1020   - }
1021   -
1022   - StringBuffer recordInfoXml = new StringBuffer(200);
1023   - String charset = device.getCharset();
1024   - recordInfoXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1025   - recordInfoXml.append("<Query>\r\n");
1026   - recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
1027   - recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
1028   - recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
1029   - if (startTime != null) {
1030   - recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
1031   - }
1032   - if (endTime != null) {
1033   - recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
1034   - }
1035   - if (secrecy != null) {
1036   - recordInfoXml.append("<Secrecy> " + secrecy + " </Secrecy>\r\n");
1037   - }
1038   - if (type != null) {
1039   - // 大华NVR要求必须增加一个值为all的文本元素节点Type
1040   - recordInfoXml.append("<Type>" + type + "</Type>\r\n");
1041   - }
1042   - recordInfoXml.append("</Query>\r\n");
1043   -
1044   -
1045   -
1046   - Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
1047   - SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1048   -
1049   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
1050   - }
1051   -
1052   - /**
1053   - * 查询报警信息
1054   - *
1055   - * @param device 视频设备
1056   - * @param startPriority 报警起始级别(可选)
1057   - * @param endPriority 报警终止级别(可选)
1058   - * @param alarmMethod 报警方式条件(可选)
1059   - * @param alarmType 报警类型
1060   - * @param startTime 报警发生起始时间(可选)
1061   - * @param endTime 报警发生终止时间(可选)
1062   - * @return true = 命令发送成功
1063   - */
1064   - @Override
1065   - public void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,
1066   - String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
1067   -
1068   - StringBuffer cmdXml = new StringBuffer(200);
1069   - String charset = device.getCharset();
1070   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1071   - cmdXml.append("<Query>\r\n");
1072   - cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
1073   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1074   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1075   - if (!ObjectUtils.isEmpty(startPriority)) {
1076   - cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
1077   - }
1078   - if (!ObjectUtils.isEmpty(endPriority)) {
1079   - cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
1080   - }
1081   - if (!ObjectUtils.isEmpty(alarmMethod)) {
1082   - cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
1083   - }
1084   - if (!ObjectUtils.isEmpty(alarmType)) {
1085   - cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
1086   - }
1087   - if (!ObjectUtils.isEmpty(startTime)) {
1088   - cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
1089   - }
1090   - if (!ObjectUtils.isEmpty(endTime)) {
1091   - cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
1092   - }
1093   - cmdXml.append("</Query>\r\n");
1094   -
1095   -
1096   -
1097   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1098   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
1099   - }
1100   -
1101   - /**
1102   - * 查询设备配置
1103   - *
1104   - * @param device 视频设备
1105   - * @param channelId 通道编码(可选)
1106   - * @param configType 配置类型:
1107   - */
1108   - @Override
1109   - public void deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
1110   -
1111   - StringBuffer cmdXml = new StringBuffer(200);
1112   - String charset = device.getCharset();
1113   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1114   - cmdXml.append("<Query>\r\n");
1115   - cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
1116   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1117   - if (ObjectUtils.isEmpty(channelId)) {
1118   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1119   - } else {
1120   - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
1121   - }
1122   - cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
1123   - cmdXml.append("</Query>\r\n");
1124   -
1125   -
1126   -
1127   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1128   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
1129   - }
1130   -
1131   - /**
1132   - * 查询设备预置位置
1133   - *
1134   - * @param device 视频设备
1135   - */
1136   - @Override
1137   - public void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
1138   -
1139   - StringBuffer cmdXml = new StringBuffer(200);
1140   - String charset = device.getCharset();
1141   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1142   - cmdXml.append("<Query>\r\n");
1143   - cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
1144   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1145   - if (ObjectUtils.isEmpty(channelId)) {
1146   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1147   - } else {
1148   - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
1149   - }
1150   - cmdXml.append("</Query>\r\n");
1151   -
1152   -
1153   - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1154   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
1155   - }
1156   -
1157   - /**
1158   - * 查询移动设备位置数据
1159   - *
1160   - * @param device 视频设备
1161   - */
1162   - @Override
1163   - public void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
1164   -
1165   - StringBuffer mobilePostitionXml = new StringBuffer(200);
1166   - String charset = device.getCharset();
1167   - mobilePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1168   - mobilePostitionXml.append("<Query>\r\n");
1169   - mobilePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
1170   - mobilePostitionXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1171   - mobilePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1172   - mobilePostitionXml.append("<Interval>60</Interval>\r\n");
1173   - mobilePostitionXml.append("</Query>\r\n");
1174   -
1175   -
1176   -
1177   - Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1178   -
1179   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
1180   -
1181   - }
1182   -
1183   - /**
1184   - * 订阅、取消订阅移动位置
1185   - *
1186   - * @param device 视频设备
1187   - * @return true = 命令发送成功
1188   - */
1189   - @Override
1190   - public SIPRequest mobilePositionSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
1191   -
1192   - StringBuffer subscribePostitionXml = new StringBuffer(200);
1193   - String charset = device.getCharset();
1194   - subscribePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1195   - subscribePostitionXml.append("<Query>\r\n");
1196   - subscribePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
1197   - subscribePostitionXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1198   - subscribePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1199   - if (device.getSubscribeCycleForMobilePosition() > 0) {
1200   - subscribePostitionXml.append("<Interval>" + device.getMobilePositionSubmissionInterval() + "</Interval>\r\n");
1201   - }
1202   - subscribePostitionXml.append("</Query>\r\n");
1203   -
1204   - CallIdHeader callIdHeader;
1205   -
1206   - if (requestOld != null) {
1207   - callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
1208   - } else {
1209   - callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
1210   - }
1211   - SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence",callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
1212   -
1213   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
1214   - return request;
1215   - }
1216   -
1217   - /**
1218   - * 订阅、取消订阅报警信息
1219   - *
1220   - * @param device 视频设备
1221   - * @param expires 订阅过期时间(0 = 取消订阅)
1222   - * @param startPriority 报警起始级别(可选)
1223   - * @param endPriority 报警终止级别(可选)
1224   - * @param alarmMethod 报警方式条件(可选)
1225   - * @param alarmType 报警类型
1226   - * @param startTime 报警发生起始时间(可选)
1227   - * @param endTime 报警发生终止时间(可选)
1228   - * @return true = 命令发送成功
1229   - */
1230   - @Override
1231   - public void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException {
1232   -
1233   - StringBuffer cmdXml = new StringBuffer(200);
1234   - String charset = device.getCharset();
1235   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1236   - cmdXml.append("<Query>\r\n");
1237   - cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
1238   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1239   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1240   - if (!ObjectUtils.isEmpty(startPriority)) {
1241   - cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
1242   - }
1243   - if (!ObjectUtils.isEmpty(endPriority)) {
1244   - cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
1245   - }
1246   - if (!ObjectUtils.isEmpty(alarmMethod)) {
1247   - cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
1248   - }
1249   - if (!ObjectUtils.isEmpty(startTime)) {
1250   - cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
1251   - }
1252   - if (!ObjectUtils.isEmpty(endTime)) {
1253   - cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
1254   - }
1255   - cmdXml.append("</Query>\r\n");
1256   -
1257   -
1258   -
1259   - Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence",sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1260   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
1261   -
1262   - }
1263   -
1264   - @Override
1265   - public SIPRequest catalogSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
1266   -
1267   - StringBuffer cmdXml = new StringBuffer(200);
1268   - String charset = device.getCharset();
1269   - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1270   - cmdXml.append("<Query>\r\n");
1271   - cmdXml.append("<CmdType>Catalog</CmdType>\r\n");
1272   - cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1273   - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1274   - cmdXml.append("</Query>\r\n");
1275   -
1276   - CallIdHeader callIdHeader;
1277   -
1278   - if (requestOld != null) {
1279   - callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
1280   - } else {
1281   - callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
1282   - }
1283   -
1284   - // 有效时间默认为60秒以上
1285   - SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld, device.getSubscribeCycleForCatalog(), "Catalog",
1286   - callIdHeader);
1287   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
1288   - return request;
1289   - }
1290   -
1291   - @Override
1292   - public void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException {
1293   -
1294   - StringBuffer dragXml = new StringBuffer(200);
1295   - String charset = device.getCharset();
1296   - dragXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
1297   - dragXml.append("<Control>\r\n");
1298   - dragXml.append("<CmdType>DeviceControl</CmdType>\r\n");
1299   - dragXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1300   - if (ObjectUtils.isEmpty(channelId)) {
1301   - dragXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
1302   - } else {
1303   - dragXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
1304   - }
1305   - dragXml.append(cmdString);
1306   - dragXml.append("</Control>\r\n");
1307   -
1308   - Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1309   - logger.debug("拉框信令: " + request.toString());
1310   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
1311   - }
1312   -
1313   -
1314   -
1315   -
1316   -
1317   - /**
1318   - * 回放暂停
1319   - */
1320   - @Override
1321   - public void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException {
1322   - StringBuffer content = new StringBuffer(200);
1323   - content.append("PAUSE RTSP/1.0\r\n");
1324   - content.append("CSeq: " + getInfoCseq() + "\r\n");
1325   - content.append("PauseTime: now\r\n");
1326   -
1327   - playbackControlCmd(device, streamInfo, content.toString(), null, null);
1328   - }
1329   -
1330   -
1331   - /**
1332   - * 回放恢复
1333   - */
1334   - @Override
1335   - public void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException {
1336   - StringBuffer content = new StringBuffer(200);
1337   - content.append("PLAY RTSP/1.0\r\n");
1338   - content.append("CSeq: " + getInfoCseq() + "\r\n");
1339   - content.append("Range: npt=now-\r\n");
1340   -
1341   - playbackControlCmd(device, streamInfo, content.toString(), null, null);
1342   - }
1343   -
1344   - /**
1345   - * 回放拖动播放
1346   - */
1347   - @Override
1348   - public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException {
1349   - StringBuffer content = new StringBuffer(200);
1350   - content.append("PLAY RTSP/1.0\r\n");
1351   - content.append("CSeq: " + getInfoCseq() + "\r\n");
1352   - content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
1353   -
1354   - playbackControlCmd(device, streamInfo, content.toString(), null, null);
1355   - }
1356   -
1357   - /**
1358   - * 回放倍速播放
1359   - */
1360   - @Override
1361   - public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException {
1362   - StringBuffer content = new StringBuffer(200);
1363   - content.append("PLAY RTSP/1.0\r\n");
1364   - content.append("CSeq: " + getInfoCseq() + "\r\n");
1365   - content.append("Scale: " + String.format("%.6f", speed) + "\r\n");
1366   -
1367   - playbackControlCmd(device, streamInfo, content.toString(), null, null);
1368   - }
1369   -
1370   - private int getInfoCseq() {
1371   - return (int) ((Math.random() * 9 + 1) * Math.pow(10, 8));
1372   - }
1373   -
1374   - @Override
1375   - public void playbackControlCmd(Device device, StreamInfo streamInfo, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
1376   -
1377   - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), streamInfo.getChannelId(), null, streamInfo.getStream());
1378   - if (ssrcTransaction == null) {
1379   - logger.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream());
1380   - return;
1381   - }
1382   -
1383   - SIPRequest request = headerProvider.createInfoRequest(device, streamInfo.getChannelId(), content.toString(), ssrcTransaction.getSipTransactionInfo());
1384   - if (request == null) {
1385   - logger.info("[回放控制]构建Request信息失败,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream());
1386   - return;
1387   - }
1388   -
1389   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
1390   - }
1391   -
1392   - @Override
1393   - public void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException {
1394   - if (device == null) {
1395   - return;
1396   - }
1397   - logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
1398   - deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
1399   -
1400   - String characterSet = device.getCharset();
1401   - StringBuffer deviceStatusXml = new StringBuffer(600);
1402   - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
1403   - deviceStatusXml.append("<Notify>\r\n");
1404   - deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
1405   - deviceStatusXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
1406   - deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
1407   - deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
1408   - deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
1409   - deviceStatusXml.append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n");
1410   - deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
1411   - deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
1412   - deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
1413   - deviceStatusXml.append("<info>\r\n");
1414   - deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
1415   - deviceStatusXml.append("</info>\r\n");
1416   - deviceStatusXml.append("</Notify>\r\n");
1417   -
1418   -
1419   - Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1420   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
1421   -
1422   -
1423   - }
1424   -}
  1 +package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
  2 +
  3 +import com.genersoft.iot.vmp.common.InviteSessionType;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.conf.SipConfig;
  6 +import com.genersoft.iot.vmp.conf.UserSetting;
  7 +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  8 +import com.genersoft.iot.vmp.gb28181.SipLayer;
  9 +import com.genersoft.iot.vmp.gb28181.bean.*;
  10 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  11 +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
  12 +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  13 +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
  14 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  15 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  16 +import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
  17 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  18 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
  19 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  20 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  21 +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
  22 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  23 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
  24 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
  25 +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamPush;
  26 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  27 +import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
  28 +import com.genersoft.iot.vmp.service.IMediaServerService;
  29 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  30 +import com.genersoft.iot.vmp.utils.DateUtil;
  31 +import gov.nist.javax.sip.message.SIPRequest;
  32 +import gov.nist.javax.sip.message.SIPResponse;
  33 +import org.slf4j.Logger;
  34 +import org.slf4j.LoggerFactory;
  35 +import org.springframework.beans.factory.annotation.Autowired;
  36 +import org.springframework.context.annotation.DependsOn;
  37 +import org.springframework.stereotype.Component;
  38 +import org.springframework.util.ObjectUtils;
  39 +
  40 +import javax.sip.InvalidArgumentException;
  41 +import javax.sip.ResponseEvent;
  42 +import javax.sip.SipException;
  43 +import javax.sip.SipFactory;
  44 +import javax.sip.header.CallIdHeader;
  45 +import javax.sip.message.Request;
  46 +import java.text.ParseException;
  47 +import java.util.ArrayList;
  48 +import java.util.List;
  49 +
  50 +/**
  51 + * @description:设备能力接口,用于定义设备的控制、查询能力
  52 + * @author: swwheihei
  53 + * @date: 2020年5月3日 下午9:22:48
  54 + */
  55 +@Component
  56 +@DependsOn("sipLayer")
  57 +public class SIPCommander implements ISIPCommander {
  58 +
  59 + private final Logger logger = LoggerFactory.getLogger(SIPCommander.class);
  60 +
  61 + @Autowired
  62 + private SipConfig sipConfig;
  63 +
  64 + @Autowired
  65 + private SipLayer sipLayer;
  66 +
  67 + @Autowired
  68 + private SIPSender sipSender;
  69 +
  70 + @Autowired
  71 + private SIPRequestHeaderProvider headerProvider;
  72 +
  73 + @Autowired
  74 + private VideoStreamSessionManager streamSession;
  75 +
  76 + @Autowired
  77 + private UserSetting userSetting;
  78 +
  79 + @Autowired
  80 + private ZlmHttpHookSubscribe subscribe;
  81 +
  82 + @Autowired
  83 + private IMediaServerService mediaServerService;
  84 +
  85 + @Autowired
  86 + private ZLMServerFactory zlmServerFactory;
  87 +
  88 +
  89 + /**
  90 + * 云台方向放控制,使用配置文件中的默认镜头移动速度
  91 + *
  92 + * @param device 控制设备
  93 + * @param channelId 预览通道
  94 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  95 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  96 + */
  97 + @Override
  98 + public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException {
  99 + ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0);
  100 + }
  101 +
  102 + /**
  103 + * 云台方向放控制
  104 + *
  105 + * @param device 控制设备
  106 + * @param channelId 预览通道
  107 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  108 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  109 + * @param moveSpeed 镜头移动速度
  110 + */
  111 + @Override
  112 + public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException {
  113 + ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0);
  114 + }
  115 +
  116 + /**
  117 + * 云台缩放控制,使用配置文件中的默认镜头缩放速度
  118 + *
  119 + * @param device 控制设备
  120 + * @param channelId 预览通道
  121 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  122 + */
  123 + @Override
  124 + public void ptzZoomCmd(Device device, String channelId, int inOut) throws InvalidArgumentException, ParseException, SipException {
  125 + ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed());
  126 + }
  127 +
  128 + /**
  129 + * 云台缩放控制
  130 + *
  131 + * @param device 控制设备
  132 + * @param channelId 预览通道
  133 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  134 + * @param zoomSpeed 镜头缩放速度
  135 + */
  136 + @Override
  137 + public void ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) throws InvalidArgumentException, ParseException, SipException {
  138 + ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed);
  139 + }
  140 +
  141 + /**
  142 + * 云台指令码计算
  143 + *
  144 + * @param cmdCode 指令码
  145 + * @param parameter1 数据1
  146 + * @param parameter2 数据2
  147 + * @param combineCode2 组合码2
  148 + */
  149 + public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) {
  150 + StringBuilder builder = new StringBuilder("A50F01");
  151 + String strTmp;
  152 + strTmp = String.format("%02X", cmdCode);
  153 + builder.append(strTmp, 0, 2);
  154 + strTmp = String.format("%02X", parameter1);
  155 + builder.append(strTmp, 0, 2);
  156 + strTmp = String.format("%02X", parameter2);
  157 + builder.append(strTmp, 0, 2);
  158 + //优化zoom变倍速率
  159 + if ((combineCode2 > 0) && (combineCode2 <16))
  160 + {
  161 + combineCode2 = 16;
  162 + }
  163 + strTmp = String.format("%X", combineCode2);
  164 + builder.append(strTmp, 0, 1).append("0");
  165 + //计算校验码
  166 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100;
  167 + strTmp = String.format("%02X", checkCode);
  168 + builder.append(strTmp, 0, 2);
  169 + return builder.toString();
  170 + }
  171 +
  172 + /**
  173 + * 云台控制,支持方向与缩放控制
  174 + *
  175 + * @param device 控制设备
  176 + * @param channelId 预览通道
  177 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  178 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  179 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  180 + * @param moveSpeed 镜头移动速度
  181 + * @param zoomSpeed 镜头缩放速度
  182 + */
  183 + @Override
  184 + public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
  185 + int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
  186 + String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
  187 + StringBuilder ptzXml = new StringBuilder(200);
  188 + String charset = device.getCharset();
  189 + ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  190 + ptzXml.append("<Control>\r\n");
  191 + ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  192 + ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  193 + ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  194 + ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
  195 + ptzXml.append("<Info>\r\n");
  196 + ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
  197 + ptzXml.append("</Info>\r\n");
  198 + ptzXml.append("</Control>\r\n");
  199 +
  200 + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  201 +
  202 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
  203 + }
  204 +
  205 + /**
  206 + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
  207 + *
  208 + * @param device 控制设备
  209 + * @param channelId 预览通道
  210 + * @param cmdCode 指令码
  211 + * @param parameter1 数据1
  212 + * @param parameter2 数据2
  213 + * @param combineCode2 组合码2
  214 + */
  215 + @Override
  216 + public void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException {
  217 +
  218 + String cmdStr = frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2);
  219 + StringBuffer ptzXml = new StringBuffer(200);
  220 + String charset = device.getCharset();
  221 + ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  222 + ptzXml.append("<Control>\r\n");
  223 + ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  224 + ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  225 + ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  226 + ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
  227 + ptzXml.append("<Info>\r\n");
  228 + ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
  229 + ptzXml.append("</Info>\r\n");
  230 + ptzXml.append("</Control>\r\n");
  231 +
  232 +
  233 +
  234 +
  235 + SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  236 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
  237 +
  238 + }
  239 +
  240 + /**
  241 + * 前端控制指令(用于转发上级指令)
  242 + *
  243 + * @param device 控制设备
  244 + * @param channelId 预览通道
  245 + * @param cmdString 前端控制指令串
  246 + */
  247 + @Override
  248 + public void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
  249 +
  250 + StringBuffer ptzXml = new StringBuffer(200);
  251 + String charset = device.getCharset();
  252 + ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  253 + ptzXml.append("<Control>\r\n");
  254 + ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  255 + ptzXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  256 + ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  257 + ptzXml.append("<PTZCmd>" + cmdString + "</PTZCmd>\r\n");
  258 + ptzXml.append("<Info>\r\n");
  259 + ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
  260 + ptzXml.append("</Info>\r\n");
  261 + ptzXml.append("</Control>\r\n");
  262 +
  263 +
  264 + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  265 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request, errorEvent, okEvent);
  266 +
  267 + }
  268 +
  269 + /**
  270 + * 请求预览视频流
  271 + *
  272 + * @param device 视频设备
  273 + * @param channel 预览通道
  274 + * @param event hook订阅
  275 + * @param errorEvent sip错误订阅
  276 + */
  277 + @Override
  278 + public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, DeviceChannel channel,
  279 + ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  280 + String stream = ssrcInfo.getStream();
  281 +
  282 + if (device == null) {
  283 + return;
  284 + }
  285 +
  286 + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
  287 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
  288 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
  289 + if (event != null) {
  290 + event.response(mediaServerItemInUse, hookParam);
  291 + subscribe.removeSubscribe(hookSubscribe);
  292 + }
  293 + });
  294 + String sdpIp;
  295 + if (!ObjectUtils.isEmpty(device.getSdpIp())) {
  296 + sdpIp = device.getSdpIp();
  297 + }else {
  298 + sdpIp = mediaServerItem.getSdpIp();
  299 + }
  300 + StringBuffer content = new StringBuffer(200);
  301 + content.append("v=0\r\n");
  302 + content.append("o=" + channel.getChannelId() + " 0 0 IN IP4 " + sdpIp + "\r\n");
  303 + content.append("s=Play\r\n");
  304 + content.append("c=IN IP4 " + sdpIp + "\r\n");
  305 + content.append("t=0 0\r\n");
  306 +
  307 + if (userSetting.isSeniorSdp()) {
  308 + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
  309 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  310 + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
  311 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  312 + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
  313 + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
  314 + }
  315 + content.append("a=recvonly\r\n");
  316 + content.append("a=rtpmap:96 PS/90000\r\n");
  317 + content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
  318 + content.append("a=rtpmap:126 H264/90000\r\n");
  319 + content.append("a=rtpmap:125 H264S/90000\r\n");
  320 + content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
  321 + content.append("a=rtpmap:99 H265/90000\r\n");
  322 + content.append("a=rtpmap:98 H264/90000\r\n");
  323 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  324 + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
  325 + content.append("a=setup:passive\r\n");
  326 + content.append("a=connection:new\r\n");
  327 + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
  328 + content.append("a=setup:active\r\n");
  329 + content.append("a=connection:new\r\n");
  330 + }
  331 + } else {
  332 + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
  333 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
  334 + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
  335 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
  336 + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
  337 + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
  338 + }
  339 + content.append("a=recvonly\r\n");
  340 + content.append("a=rtpmap:96 PS/90000\r\n");
  341 + content.append("a=rtpmap:98 H264/90000\r\n");
  342 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  343 + content.append("a=rtpmap:99 H265/90000\r\n");
  344 + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
  345 + content.append("a=setup:passive\r\n");
  346 + content.append("a=connection:new\r\n");
  347 + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
  348 + content.append("a=setup:active\r\n");
  349 + content.append("a=connection:new\r\n");
  350 + }
  351 + }
  352 +
  353 + if (!ObjectUtils.isEmpty(channel.getStreamIdentification())) {
  354 + content.append("a=" + channel.getStreamIdentification() + "\r\n");
  355 + }
  356 +
  357 + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
  358 + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
  359 +// content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备
  360 +
  361 +
  362 +
  363 + Request request = headerProvider.createInviteRequest(device, channel.getChannelId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  364 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> {
  365 + streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcInfo.getStream());
  366 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  367 + errorEvent.response(e);
  368 + }), e -> {
  369 + ResponseEvent responseEvent = (ResponseEvent) e.event;
  370 + SIPResponse response = (SIPResponse) responseEvent.getResponse();
  371 + String callId = response.getCallIdHeader().getCallId();
  372 + streamSession.put(device.getDeviceId(), channel.getChannelId(), callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response,
  373 + InviteSessionType.PLAY);
  374 + okEvent.response(e);
  375 + });
  376 + }
  377 +
  378 + /**
  379 + * 请求回放视频流
  380 + *
  381 + * @param device 视频设备
  382 + * @param channelId 预览通道
  383 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  384 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  385 + */
  386 + @Override
  387 + public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  388 + String startTime, String endTime, ZlmHttpHookSubscribe.Event hookEvent,
  389 + SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  390 +
  391 +
  392 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
  393 + String sdpIp;
  394 + if (!ObjectUtils.isEmpty(device.getSdpIp())) {
  395 + sdpIp = device.getSdpIp();
  396 + }else {
  397 + sdpIp = mediaServerItem.getSdpIp();
  398 + }
  399 + StringBuffer content = new StringBuffer(200);
  400 + content.append("v=0\r\n");
  401 + content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
  402 + content.append("s=Playback\r\n");
  403 + content.append("u=" + channelId + ":0\r\n");
  404 + content.append("c=IN IP4 " + sdpIp + "\r\n");
  405 + content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
  406 + + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
  407 +
  408 + String streamMode = device.getStreamMode();
  409 +
  410 + if (userSetting.isSeniorSdp()) {
  411 + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
  412 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  413 + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
  414 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  415 + } else if ("UDP".equalsIgnoreCase(streamMode)) {
  416 + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
  417 + }
  418 + content.append("a=recvonly\r\n");
  419 + content.append("a=rtpmap:96 PS/90000\r\n");
  420 + content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
  421 + content.append("a=rtpmap:126 H264/90000\r\n");
  422 + content.append("a=rtpmap:125 H264S/90000\r\n");
  423 + content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
  424 + content.append("a=rtpmap:99 H265/90000\r\n");
  425 + content.append("a=rtpmap:98 H264/90000\r\n");
  426 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  427 + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { // tcp被动模式
  428 + content.append("a=setup:passive\r\n");
  429 + content.append("a=connection:new\r\n");
  430 + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式
  431 + content.append("a=setup:active\r\n");
  432 + content.append("a=connection:new\r\n");
  433 + }
  434 + } else {
  435 + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
  436 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
  437 + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
  438 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
  439 + } else if ("UDP".equalsIgnoreCase(streamMode)) {
  440 + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
  441 + }
  442 + content.append("a=recvonly\r\n");
  443 + content.append("a=rtpmap:96 PS/90000\r\n");
  444 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  445 + content.append("a=rtpmap:98 H264/90000\r\n");
  446 + content.append("a=rtpmap:99 H265/90000\r\n");
  447 + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
  448 + // tcp被动模式
  449 + content.append("a=setup:passive\r\n");
  450 + content.append("a=connection:new\r\n");
  451 + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
  452 + // tcp主动模式
  453 + content.append("a=setup:active\r\n");
  454 + content.append("a=connection:new\r\n");
  455 + }
  456 + }
  457 +
  458 + //ssrc
  459 + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");
  460 +
  461 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
  462 + // 添加订阅
  463 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
  464 + if (hookEvent != null) {
  465 + hookEvent.response(mediaServerItemInUse, hookParam);
  466 + }
  467 + subscribe.removeSubscribe(hookSubscribe);
  468 + });
  469 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
  470 +
  471 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
  472 + ResponseEvent responseEvent = (ResponseEvent) event.event;
  473 + SIPResponse response = (SIPResponse) responseEvent.getResponse();
  474 + streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAYBACK);
  475 + okEvent.response(event);
  476 + });
  477 + }
  478 +
  479 + /**
  480 + * 请求历史媒体下载
  481 + *
  482 + * @param device 视频设备
  483 + * @param channelId 预览通道
  484 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  485 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  486 + * @param downloadSpeed 下载倍速参数
  487 + */
  488 + @Override
  489 + public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  490 + String startTime, String endTime, int downloadSpeed,
  491 + ZlmHttpHookSubscribe.Event hookEvent,
  492 + SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
  493 +
  494 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
  495 + String sdpIp;
  496 + if (!ObjectUtils.isEmpty(device.getSdpIp())) {
  497 + sdpIp = device.getSdpIp();
  498 + }else {
  499 + sdpIp = mediaServerItem.getSdpIp();
  500 + }
  501 + StringBuffer content = new StringBuffer(200);
  502 + content.append("v=0\r\n");
  503 + content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
  504 + content.append("s=Download\r\n");
  505 + content.append("u=" + channelId + ":0\r\n");
  506 + content.append("c=IN IP4 " + sdpIp + "\r\n");
  507 + content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
  508 + + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
  509 +
  510 + String streamMode = device.getStreamMode().toUpperCase();
  511 +
  512 + if (userSetting.isSeniorSdp()) {
  513 + if ("TCP-PASSIVE".equals(streamMode)) {
  514 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  515 + } else if ("TCP-ACTIVE".equals(streamMode)) {
  516 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  517 + } else if ("UDP".equals(streamMode)) {
  518 + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
  519 + }
  520 + content.append("a=recvonly\r\n");
  521 + content.append("a=rtpmap:96 PS/90000\r\n");
  522 + content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
  523 + content.append("a=rtpmap:126 H264/90000\r\n");
  524 + content.append("a=rtpmap:125 H264S/90000\r\n");
  525 + content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
  526 + content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
  527 + content.append("a=fmtp:99 profile-level-id=3\r\n");
  528 + content.append("a=rtpmap:98 H264/90000\r\n");
  529 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  530 + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
  531 + content.append("a=setup:passive\r\n");
  532 + content.append("a=connection:new\r\n");
  533 + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  534 + content.append("a=setup:active\r\n");
  535 + content.append("a=connection:new\r\n");
  536 + }
  537 + } else {
  538 + if ("TCP-PASSIVE".equals(streamMode)) {
  539 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
  540 + } else if ("TCP-ACTIVE".equals(streamMode)) {
  541 + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
  542 + } else if ("UDP".equals(streamMode)) {
  543 + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
  544 + }
  545 + content.append("a=recvonly\r\n");
  546 + content.append("a=rtpmap:96 PS/90000\r\n");
  547 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  548 + content.append("a=rtpmap:98 H264/90000\r\n");
  549 + content.append("a=rtpmap:99 H265/90000\r\n");
  550 + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
  551 + content.append("a=setup:passive\r\n");
  552 + content.append("a=connection:new\r\n");
  553 + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  554 + content.append("a=setup:active\r\n");
  555 + content.append("a=connection:new\r\n");
  556 + }
  557 + }
  558 + content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
  559 +
  560 + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
  561 + logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
  562 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
  563 + // 添加订阅
  564 + CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
  565 + String callId= newCallIdHeader.getCallId();
  566 + subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> {
  567 + logger.debug("sipc 添加订阅===callId {}",callId);
  568 + hookEvent.response(mediaServerItemInUse, hookParam);
  569 + subscribe.removeSubscribe(hookSubscribe);
  570 + hookSubscribe.getContent().put("regist", false);
  571 + hookSubscribe.getContent().put("schema", "rtsp");
  572 + // 添加流注销的订阅,注销了后向设备发送bye
  573 + subscribe.addSubscribe(hookSubscribe,
  574 + (mediaServerItemForEnd, hookParam1) -> {
  575 + logger.info("[录像]下载结束, 发送BYE");
  576 + try {
  577 + streamByeCmd(device, channelId, ssrcInfo.getStream(), callId);
  578 + } catch (InvalidArgumentException | ParseException | SipException |
  579 + SsrcTransactionNotFoundException e) {
  580 + logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
  581 + }
  582 + });
  583 + });
  584 +
  585 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
  586 +
  587 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
  588 + ResponseEvent responseEvent = (ResponseEvent) event.event;
  589 + SIPResponse response = (SIPResponse) responseEvent.getResponse();
  590 + String contentString =new String(response.getRawContent());
  591 + String ssrc = SipUtils.getSsrcFromSdp(contentString);
  592 + streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD);
  593 + okEvent.response(event);
  594 + });
  595 + }
  596 +
  597 + @Override
  598 + public void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  599 +
  600 + String stream = sendRtpItem.getStream();
  601 +
  602 + if (device == null) {
  603 + return;
  604 + }
  605 + if (!mediaServerItem.isRtpEnable()) {
  606 + // 单端口暂不支持语音喊话
  607 + logger.info("[语音喊话] 单端口暂不支持此操作");
  608 + return;
  609 + }
  610 +
  611 + logger.info("[语音喊话] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), sendRtpItem.getPort());
  612 + HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
  613 + subscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItemInUse, hookParam) -> {
  614 + if (event != null) {
  615 + event.response(mediaServerItemInUse, hookParam);
  616 + subscribe.removeSubscribe(hookSubscribeForStreamChange);
  617 + }
  618 + });
  619 +
  620 + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
  621 + callIdHeader.setCallId(callId);
  622 + HookSubscribeForStreamPush hookSubscribeForStreamPush = HookSubscribeFactory.on_publish("rtp", stream, null, mediaServerItem.getId());
  623 + subscribe.addSubscribe(hookSubscribeForStreamPush, (mediaServerItemInUse, hookParam) -> {
  624 + if (eventForPush != null) {
  625 + eventForPush.response(mediaServerItemInUse, hookParam);
  626 + }
  627 + });
  628 + //
  629 + StringBuffer content = new StringBuffer(200);
  630 + content.append("v=0\r\n");
  631 + content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
  632 + content.append("s=Talk\r\n");
  633 + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
  634 + content.append("t=0 0\r\n");
  635 +
  636 + content.append("m=audio " + sendRtpItem.getPort() + " TCP/RTP/AVP 8\r\n");
  637 + content.append("a=setup:passive\r\n");
  638 + content.append("a=connection:new\r\n");
  639 + content.append("a=sendrecv\r\n");
  640 + content.append("a=rtpmap:8 PCMA/8000\r\n");
  641 +
  642 + content.append("y=" + sendRtpItem.getSsrc() + "\r\n");//ssrc
  643 + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
  644 + content.append("f=v/////a/1/8/1" + "\r\n");
  645 +
  646 + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(),
  647 + SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sendRtpItem.getSsrc(), callIdHeader);
  648 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> {
  649 + streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStream());
  650 + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
  651 + errorEvent.response(e);
  652 + }), e -> {
  653 + // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
  654 + ResponseEvent responseEvent = (ResponseEvent) e.event;
  655 + SIPResponse response = (SIPResponse) responseEvent.getResponse();
  656 + streamSession.put(device.getDeviceId(), channelId, "talk", stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.TALK);
  657 + okEvent.response(e);
  658 + });
  659 + }
  660 +
  661 + /**
  662 + * 视频流停止, 不使用回调
  663 + */
  664 + @Override
  665 + public void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException {
  666 + streamByeCmd(device, channelId, stream, callId, null);
  667 + }
  668 +
  669 + /**
  670 + * 视频流停止
  671 + */
  672 + @Override
  673 + public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
  674 + if (device == null) {
  675 + logger.warn("[发送BYE] device为null");
  676 + return;
  677 + }
  678 + List<SsrcTransaction> ssrcTransactionList = streamSession.getSsrcTransactionForAll(device.getDeviceId(), channelId, callId, stream);
  679 + if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
  680 + logger.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId);
  681 + throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream);
  682 + }
  683 +
  684 + for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
  685 + logger.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId());
  686 + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
  687 +
  688 + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
  689 + streamSession.removeByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getCallId());
  690 + Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
  691 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
  692 + }
  693 + }
  694 +
  695 + @Override
  696 + public void streamByeCmd(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
  697 + Request byteRequest = headerProvider.createByteRequest(device, channelId, sipTransactionInfo);
  698 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
  699 + }
  700 +
  701 + @Override
  702 + public void streamByeCmdForDeviceInvite(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
  703 + Request byteRequest = headerProvider.createByteRequestForDeviceInvite(device, channelId, sipTransactionInfo);
  704 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
  705 + }
  706 +
  707 + /**
  708 + * 语音广播
  709 + *
  710 + * @param device 视频设备
  711 + */
  712 + @Override
  713 + public void audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  714 + StringBuffer broadcastXml = new StringBuffer(200);
  715 + String charset = device.getCharset();
  716 + broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  717 + broadcastXml.append("<Notify>\r\n");
  718 + broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
  719 + broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  720 + broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
  721 + broadcastXml.append("<TargetID>" + channelId + "</TargetID>\r\n");
  722 + broadcastXml.append("</Notify>\r\n");
  723 +
  724 + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  725 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
  726 +
  727 + }
  728 +
  729 +
  730 + /**
  731 + * 音视频录像控制
  732 + *
  733 + * @param device 视频设备
  734 + * @param channelId 预览通道
  735 + * @param recordCmdStr 录像命令:Record / StopRecord
  736 + */
  737 + @Override
  738 + public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
  739 + StringBuffer cmdXml = new StringBuffer(200);
  740 + String charset = device.getCharset();
  741 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  742 + cmdXml.append("<Control>\r\n");
  743 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  744 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  745 + if (ObjectUtils.isEmpty(channelId)) {
  746 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  747 + } else {
  748 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  749 + }
  750 + cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
  751 + cmdXml.append("</Control>\r\n");
  752 +
  753 +
  754 +
  755 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  756 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
  757 + }
  758 +
  759 + /**
  760 + * 远程启动控制命令
  761 + *
  762 + * @param device 视频设备
  763 + */
  764 + @Override
  765 + public void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException {
  766 +
  767 + StringBuffer cmdXml = new StringBuffer(200);
  768 + String charset = device.getCharset();
  769 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  770 + cmdXml.append("<Control>\r\n");
  771 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  772 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  773 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  774 + cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
  775 + cmdXml.append("</Control>\r\n");
  776 +
  777 +
  778 +
  779 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  780 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
  781 + }
  782 +
  783 + /**
  784 + * 报警布防/撤防命令
  785 + *
  786 + * @param device 视频设备
  787 + * @param guardCmdStr "SetGuard"/"ResetGuard"
  788 + */
  789 + @Override
  790 + public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
  791 +
  792 + StringBuffer cmdXml = new StringBuffer(200);
  793 + String charset = device.getCharset();
  794 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  795 + cmdXml.append("<Control>\r\n");
  796 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  797 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  798 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  799 + cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
  800 + cmdXml.append("</Control>\r\n");
  801 +
  802 +
  803 +
  804 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  805 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
  806 + }
  807 +
  808 + /**
  809 + * 报警复位命令
  810 + *
  811 + * @param device 视频设备
  812 + */
  813 + @Override
  814 + public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
  815 +
  816 + StringBuffer cmdXml = new StringBuffer(200);
  817 + String charset = device.getCharset();
  818 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  819 + cmdXml.append("<Control>\r\n");
  820 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  821 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  822 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  823 + cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
  824 + if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
  825 + cmdXml.append("<Info>\r\n");
  826 + }
  827 + if (!ObjectUtils.isEmpty(alarmMethod)) {
  828 + cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
  829 + }
  830 + if (!ObjectUtils.isEmpty(alarmType)) {
  831 + cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
  832 + }
  833 + if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) {
  834 + cmdXml.append("</Info>\r\n");
  835 + }
  836 + cmdXml.append("</Control>\r\n");
  837 +
  838 +
  839 +
  840 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  841 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
  842 + }
  843 +
  844 + /**
  845 + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
  846 + *
  847 + * @param device 视频设备
  848 + * @param channelId 预览通道
  849 + */
  850 + @Override
  851 + public void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException {
  852 +
  853 + StringBuffer cmdXml = new StringBuffer(200);
  854 + String charset = device.getCharset();
  855 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  856 + cmdXml.append("<Control>\r\n");
  857 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  858 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  859 + if (ObjectUtils.isEmpty(channelId)) {
  860 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  861 + } else {
  862 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  863 + }
  864 + cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
  865 + cmdXml.append("</Control>\r\n");
  866 +
  867 +
  868 +
  869 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  870 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
  871 + }
  872 +
  873 + /**
  874 + * 看守位控制命令
  875 + *
  876 + * @param device 视频设备
  877 + * @param channelId 通道id,非通道则是设备本身
  878 + * @param enabled 看守位使能:1 = 开启,0 = 关闭
  879 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
  880 + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
  881 + */
  882 + @Override
  883 + public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
  884 +
  885 + StringBuffer cmdXml = new StringBuffer(200);
  886 + String charset = device.getCharset();
  887 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  888 + cmdXml.append("<Control>\r\n");
  889 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  890 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  891 + if (ObjectUtils.isEmpty(channelId)) {
  892 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  893 + } else {
  894 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  895 + }
  896 + cmdXml.append("<HomePosition>\r\n");
  897 + if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
  898 + cmdXml.append("<Enabled>1</Enabled>\r\n");
  899 + if (NumericUtil.isInteger(resetTime)) {
  900 + cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
  901 + } else {
  902 + cmdXml.append("<ResetTime>0</ResetTime>\r\n");
  903 + }
  904 + if (NumericUtil.isInteger(presetIndex)) {
  905 + cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
  906 + } else {
  907 + cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
  908 + }
  909 + } else {
  910 + cmdXml.append("<Enabled>0</Enabled>\r\n");
  911 + }
  912 + cmdXml.append("</HomePosition>\r\n");
  913 + cmdXml.append("</Control>\r\n");
  914 +
  915 +
  916 +
  917 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  918 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
  919 + }
  920 +
  921 + /**
  922 + * 设备配置命令
  923 + *
  924 + * @param device 视频设备
  925 + */
  926 + @Override
  927 + public void deviceConfigCmd(Device device) {
  928 + // TODO Auto-generated method stub
  929 + }
  930 +
  931 + /**
  932 + * 设备配置命令:basicParam
  933 + *
  934 + * @param device 视频设备
  935 + * @param channelId 通道编码(可选)
  936 + * @param name 设备/通道名称(可选)
  937 + * @param expiration 注册过期时间(可选)
  938 + * @param heartBeatInterval 心跳间隔时间(可选)
  939 + * @param heartBeatCount 心跳超时次数(可选)
  940 + */
  941 + @Override
  942 + public void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration,
  943 + String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  944 +
  945 + StringBuffer cmdXml = new StringBuffer(200);
  946 + String charset = device.getCharset();
  947 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  948 + cmdXml.append("<Control>\r\n");
  949 + cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
  950 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  951 + if (ObjectUtils.isEmpty(channelId)) {
  952 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  953 + } else {
  954 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  955 + }
  956 + cmdXml.append("<BasicParam>\r\n");
  957 + if (!ObjectUtils.isEmpty(name)) {
  958 + cmdXml.append("<Name>" + name + "</Name>\r\n");
  959 + }
  960 + if (NumericUtil.isInteger(expiration)) {
  961 + if (Integer.valueOf(expiration) > 0) {
  962 + cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");
  963 + }
  964 + }
  965 + if (NumericUtil.isInteger(heartBeatInterval)) {
  966 + if (Integer.valueOf(heartBeatInterval) > 0) {
  967 + cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");
  968 + }
  969 + }
  970 + if (NumericUtil.isInteger(heartBeatCount)) {
  971 + if (Integer.valueOf(heartBeatCount) > 0) {
  972 + cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");
  973 + }
  974 + }
  975 + cmdXml.append("</BasicParam>\r\n");
  976 + cmdXml.append("</Control>\r\n");
  977 +
  978 +
  979 +
  980 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  981 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  982 + }
  983 +
  984 + /**
  985 + * 查询设备状态
  986 + *
  987 + * @param device 视频设备
  988 + */
  989 + @Override
  990 + public void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  991 +
  992 + String charset = device.getCharset();
  993 + StringBuffer catalogXml = new StringBuffer(200);
  994 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  995 + catalogXml.append("<Query>\r\n");
  996 + catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
  997 + catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  998 + catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  999 + catalogXml.append("</Query>\r\n");
  1000 +
  1001 +
  1002 +
  1003 + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1004 +
  1005 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  1006 + }
  1007 +
  1008 + /**
  1009 + * 查询设备信息
  1010 + *
  1011 + * @param device 视频设备
  1012 + */
  1013 + @Override
  1014 + public void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException {
  1015 +
  1016 + StringBuffer catalogXml = new StringBuffer(200);
  1017 + String charset = device.getCharset();
  1018 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1019 + catalogXml.append("<Query>\r\n");
  1020 + catalogXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
  1021 + catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1022 + catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1023 + catalogXml.append("</Query>\r\n");
  1024 +
  1025 +
  1026 +
  1027 + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1028 +
  1029 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
  1030 +
  1031 + }
  1032 +
  1033 + /**
  1034 + * 查询目录列表
  1035 + *
  1036 + * @param device 视频设备
  1037 + */
  1038 + @Override
  1039 + public void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException {
  1040 +
  1041 + StringBuffer catalogXml = new StringBuffer(200);
  1042 + String charset = device.getCharset();
  1043 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1044 + catalogXml.append("<Query>\r\n");
  1045 + catalogXml.append(" <CmdType>Catalog</CmdType>\r\n");
  1046 + catalogXml.append(" <SN>" + sn + "</SN>\r\n");
  1047 + catalogXml.append(" <DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1048 + catalogXml.append("</Query>\r\n");
  1049 +
  1050 +
  1051 +
  1052 + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1053 +
  1054 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  1055 + }
  1056 +
  1057 + /**
  1058 + * 查询录像信息
  1059 + *
  1060 + * @param device 视频设备
  1061 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  1062 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  1063 + */
  1064 + @Override
  1065 + public void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  1066 + if (secrecy == null) {
  1067 + secrecy = 0;
  1068 + }
  1069 + if (type == null) {
  1070 + type = "all";
  1071 + }
  1072 +
  1073 + StringBuffer recordInfoXml = new StringBuffer(200);
  1074 + String charset = device.getCharset();
  1075 + recordInfoXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1076 + recordInfoXml.append("<Query>\r\n");
  1077 + recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
  1078 + recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
  1079 + recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1080 + if (startTime != null) {
  1081 + recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
  1082 + }
  1083 + if (endTime != null) {
  1084 + recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
  1085 + }
  1086 + if (secrecy != null) {
  1087 + recordInfoXml.append("<Secrecy> " + secrecy + " </Secrecy>\r\n");
  1088 + }
  1089 + if (type != null) {
  1090 + // 大华NVR要求必须增加一个值为all的文本元素节点Type
  1091 + recordInfoXml.append("<Type>" + type + "</Type>\r\n");
  1092 + }
  1093 + recordInfoXml.append("</Query>\r\n");
  1094 +
  1095 +
  1096 +
  1097 + Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
  1098 + SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1099 +
  1100 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
  1101 + }
  1102 +
  1103 + /**
  1104 + * 查询报警信息
  1105 + *
  1106 + * @param device 视频设备
  1107 + * @param startPriority 报警起始级别(可选)
  1108 + * @param endPriority 报警终止级别(可选)
  1109 + * @param alarmMethod 报警方式条件(可选)
  1110 + * @param alarmType 报警类型
  1111 + * @param startTime 报警发生起始时间(可选)
  1112 + * @param endTime 报警发生终止时间(可选)
  1113 + * @return true = 命令发送成功
  1114 + */
  1115 + @Override
  1116 + public void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,
  1117 + String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  1118 +
  1119 + StringBuffer cmdXml = new StringBuffer(200);
  1120 + String charset = device.getCharset();
  1121 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1122 + cmdXml.append("<Query>\r\n");
  1123 + cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
  1124 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1125 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1126 + if (!ObjectUtils.isEmpty(startPriority)) {
  1127 + cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
  1128 + }
  1129 + if (!ObjectUtils.isEmpty(endPriority)) {
  1130 + cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
  1131 + }
  1132 + if (!ObjectUtils.isEmpty(alarmMethod)) {
  1133 + cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
  1134 + }
  1135 + if (!ObjectUtils.isEmpty(alarmType)) {
  1136 + cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
  1137 + }
  1138 + if (!ObjectUtils.isEmpty(startTime)) {
  1139 + cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
  1140 + }
  1141 + if (!ObjectUtils.isEmpty(endTime)) {
  1142 + cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
  1143 + }
  1144 + cmdXml.append("</Query>\r\n");
  1145 +
  1146 +
  1147 +
  1148 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1149 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  1150 + }
  1151 +
  1152 + /**
  1153 + * 查询设备配置
  1154 + *
  1155 + * @param device 视频设备
  1156 + * @param channelId 通道编码(可选)
  1157 + * @param configType 配置类型:
  1158 + */
  1159 + @Override
  1160 + public void deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  1161 +
  1162 + StringBuffer cmdXml = new StringBuffer(200);
  1163 + String charset = device.getCharset();
  1164 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1165 + cmdXml.append("<Query>\r\n");
  1166 + cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
  1167 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1168 + if (ObjectUtils.isEmpty(channelId)) {
  1169 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1170 + } else {
  1171 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1172 + }
  1173 + cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
  1174 + cmdXml.append("</Query>\r\n");
  1175 +
  1176 +
  1177 +
  1178 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1179 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  1180 + }
  1181 +
  1182 + /**
  1183 + * 查询设备预置位置
  1184 + *
  1185 + * @param device 视频设备
  1186 + */
  1187 + @Override
  1188 + public void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  1189 +
  1190 + StringBuffer cmdXml = new StringBuffer(200);
  1191 + String charset = device.getCharset();
  1192 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1193 + cmdXml.append("<Query>\r\n");
  1194 + cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
  1195 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1196 + if (ObjectUtils.isEmpty(channelId)) {
  1197 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1198 + } else {
  1199 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1200 + }
  1201 + cmdXml.append("</Query>\r\n");
  1202 +
  1203 +
  1204 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1205 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  1206 + }
  1207 +
  1208 + /**
  1209 + * 查询移动设备位置数据
  1210 + *
  1211 + * @param device 视频设备
  1212 + */
  1213 + @Override
  1214 + public void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  1215 +
  1216 + StringBuffer mobilePostitionXml = new StringBuffer(200);
  1217 + String charset = device.getCharset();
  1218 + mobilePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1219 + mobilePostitionXml.append("<Query>\r\n");
  1220 + mobilePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
  1221 + mobilePostitionXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1222 + mobilePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1223 + mobilePostitionXml.append("<Interval>60</Interval>\r\n");
  1224 + mobilePostitionXml.append("</Query>\r\n");
  1225 +
  1226 +
  1227 +
  1228 + Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1229 +
  1230 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
  1231 +
  1232 + }
  1233 +
  1234 + /**
  1235 + * 订阅、取消订阅移动位置
  1236 + *
  1237 + * @param device 视频设备
  1238 + * @return true = 命令发送成功
  1239 + */
  1240 + @Override
  1241 + public SIPRequest mobilePositionSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  1242 +
  1243 + StringBuffer subscribePostitionXml = new StringBuffer(200);
  1244 + String charset = device.getCharset();
  1245 + subscribePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1246 + subscribePostitionXml.append("<Query>\r\n");
  1247 + subscribePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
  1248 + subscribePostitionXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1249 + subscribePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1250 + if (device.getSubscribeCycleForMobilePosition() > 0) {
  1251 + subscribePostitionXml.append("<Interval>" + device.getMobilePositionSubmissionInterval() + "</Interval>\r\n");
  1252 + }
  1253 + subscribePostitionXml.append("</Query>\r\n");
  1254 +
  1255 + CallIdHeader callIdHeader;
  1256 +
  1257 + if (requestOld != null) {
  1258 + callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
  1259 + } else {
  1260 + callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
  1261 + }
  1262 + SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence",callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
  1263 +
  1264 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
  1265 + return request;
  1266 + }
  1267 +
  1268 + /**
  1269 + * 订阅、取消订阅报警信息
  1270 + *
  1271 + * @param device 视频设备
  1272 + * @param expires 订阅过期时间(0 = 取消订阅)
  1273 + * @param startPriority 报警起始级别(可选)
  1274 + * @param endPriority 报警终止级别(可选)
  1275 + * @param alarmMethod 报警方式条件(可选)
  1276 + * @param alarmType 报警类型
  1277 + * @param startTime 报警发生起始时间(可选)
  1278 + * @param endTime 报警发生终止时间(可选)
  1279 + * @return true = 命令发送成功
  1280 + */
  1281 + @Override
  1282 + public void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException {
  1283 +
  1284 + StringBuffer cmdXml = new StringBuffer(200);
  1285 + String charset = device.getCharset();
  1286 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1287 + cmdXml.append("<Query>\r\n");
  1288 + cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
  1289 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1290 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1291 + if (!ObjectUtils.isEmpty(startPriority)) {
  1292 + cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
  1293 + }
  1294 + if (!ObjectUtils.isEmpty(endPriority)) {
  1295 + cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
  1296 + }
  1297 + if (!ObjectUtils.isEmpty(alarmMethod)) {
  1298 + cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
  1299 + }
  1300 + if (!ObjectUtils.isEmpty(startTime)) {
  1301 + cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
  1302 + }
  1303 + if (!ObjectUtils.isEmpty(endTime)) {
  1304 + cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
  1305 + }
  1306 + cmdXml.append("</Query>\r\n");
  1307 +
  1308 +
  1309 +
  1310 + Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence",sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1311 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
  1312 +
  1313 + }
  1314 +
  1315 + @Override
  1316 + public SIPRequest catalogSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  1317 +
  1318 + StringBuffer cmdXml = new StringBuffer(200);
  1319 + String charset = device.getCharset();
  1320 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1321 + cmdXml.append("<Query>\r\n");
  1322 + cmdXml.append("<CmdType>Catalog</CmdType>\r\n");
  1323 + cmdXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1324 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1325 + cmdXml.append("</Query>\r\n");
  1326 +
  1327 + CallIdHeader callIdHeader;
  1328 +
  1329 + if (requestOld != null) {
  1330 + callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
  1331 + } else {
  1332 + callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
  1333 + }
  1334 +
  1335 + // 有效时间默认为60秒以上
  1336 + SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld, device.getSubscribeCycleForCatalog(), "Catalog",
  1337 + callIdHeader);
  1338 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
  1339 + return request;
  1340 + }
  1341 +
  1342 + @Override
  1343 + public void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException {
  1344 +
  1345 + StringBuffer dragXml = new StringBuffer(200);
  1346 + String charset = device.getCharset();
  1347 + dragXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1348 + dragXml.append("<Control>\r\n");
  1349 + dragXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  1350 + dragXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1351 + if (ObjectUtils.isEmpty(channelId)) {
  1352 + dragXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1353 + } else {
  1354 + dragXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1355 + }
  1356 + dragXml.append(cmdString);
  1357 + dragXml.append("</Control>\r\n");
  1358 +
  1359 + Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1360 + logger.debug("拉框信令: " + request.toString());
  1361 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
  1362 + }
  1363 +
  1364 +
  1365 +
  1366 + /**
  1367 + * 回放暂停
  1368 + */
  1369 + @Override
  1370 + public void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException {
  1371 + StringBuffer content = new StringBuffer(200);
  1372 + content.append("PAUSE RTSP/1.0\r\n");
  1373 + content.append("CSeq: " + getInfoCseq() + "\r\n");
  1374 + content.append("PauseTime: now\r\n");
  1375 +
  1376 + playbackControlCmd(device, streamInfo, content.toString(), null, null);
  1377 + }
  1378 +
  1379 +
  1380 + /**
  1381 + * 回放恢复
  1382 + */
  1383 + @Override
  1384 + public void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException {
  1385 + StringBuffer content = new StringBuffer(200);
  1386 + content.append("PLAY RTSP/1.0\r\n");
  1387 + content.append("CSeq: " + getInfoCseq() + "\r\n");
  1388 + content.append("Range: npt=now-\r\n");
  1389 +
  1390 + playbackControlCmd(device, streamInfo, content.toString(), null, null);
  1391 + }
  1392 +
  1393 + /**
  1394 + * 回放拖动播放
  1395 + */
  1396 + @Override
  1397 + public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException {
  1398 + StringBuffer content = new StringBuffer(200);
  1399 + content.append("PLAY RTSP/1.0\r\n");
  1400 + content.append("CSeq: " + getInfoCseq() + "\r\n");
  1401 + content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
  1402 +
  1403 + playbackControlCmd(device, streamInfo, content.toString(), null, null);
  1404 + }
  1405 +
  1406 + /**
  1407 + * 回放倍速播放
  1408 + */
  1409 + @Override
  1410 + public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException {
  1411 + StringBuffer content = new StringBuffer(200);
  1412 + content.append("PLAY RTSP/1.0\r\n");
  1413 + content.append("CSeq: " + getInfoCseq() + "\r\n");
  1414 + content.append("Scale: " + String.format("%.6f", speed) + "\r\n");
  1415 +
  1416 + playbackControlCmd(device, streamInfo, content.toString(), null, null);
  1417 + }
  1418 +
  1419 + private int getInfoCseq() {
  1420 + return (int) ((Math.random() * 9 + 1) * Math.pow(10, 8));
  1421 + }
  1422 +
  1423 + @Override
  1424 + public void playbackControlCmd(Device device, StreamInfo streamInfo, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
  1425 +
  1426 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), streamInfo.getChannelId(), null, streamInfo.getStream());
  1427 + if (ssrcTransaction == null) {
  1428 + logger.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream());
  1429 + return;
  1430 + }
  1431 +
  1432 + SIPRequest request = headerProvider.createInfoRequest(device, streamInfo.getChannelId(), content.toString(), ssrcTransaction.getSipTransactionInfo());
  1433 + if (request == null) {
  1434 + logger.info("[回放控制]构建Request信息失败,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream());
  1435 + return;
  1436 + }
  1437 +
  1438 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
  1439 + }
  1440 +
  1441 + @Override
  1442 + public void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException {
  1443 + if (device == null) {
  1444 + return;
  1445 + }
  1446 + logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
  1447 + deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
  1448 +
  1449 + String characterSet = device.getCharset();
  1450 + StringBuffer deviceStatusXml = new StringBuffer(600);
  1451 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
  1452 + deviceStatusXml.append("<Notify>\r\n");
  1453 + deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
  1454 + deviceStatusXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1455 + deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
  1456 + deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
  1457 + deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
  1458 + deviceStatusXml.append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n");
  1459 + deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
  1460 + deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
  1461 + deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
  1462 + deviceStatusXml.append("<info>\r\n");
  1463 + deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
  1464 + deviceStatusXml.append("</info>\r\n");
  1465 + deviceStatusXml.append("</Notify>\r\n");
  1466 +
  1467 +
  1468 + Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
  1469 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
  1470 +
  1471 +
  1472 + }
  1473 +}
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
2 2  
3 3 import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
4 5 import com.genersoft.iot.vmp.conf.DynamicTask;
  6 +import com.genersoft.iot.vmp.conf.UserSetting;
  7 +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
5 8 import com.genersoft.iot.vmp.gb28181.SipLayer;
6 9 import com.genersoft.iot.vmp.gb28181.bean.*;
7 10 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  11 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
8 12 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
9 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
10 14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider;
11 15 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  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;
12 19 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
13 20 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  21 +import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
14 22 import com.genersoft.iot.vmp.service.IMediaServerService;
15 23 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
  24 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
16 25 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
17 26 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
18 27 import com.genersoft.iot.vmp.utils.DateUtil;
19 28 import com.genersoft.iot.vmp.utils.GitUtil;
20 29 import gov.nist.javax.sip.message.MessageFactoryImpl;
21 30 import gov.nist.javax.sip.message.SIPRequest;
  31 +import gov.nist.javax.sip.message.SIPResponse;
22 32 import org.slf4j.Logger;
23 33 import org.slf4j.LoggerFactory;
24 34 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -28,6 +38,7 @@ import org.springframework.stereotype.Component;
28 38 import org.springframework.util.ObjectUtils;
29 39  
30 40 import javax.sip.InvalidArgumentException;
  41 +import javax.sip.ResponseEvent;
31 42 import javax.sip.SipException;
32 43 import javax.sip.SipFactory;
33 44 import javax.sip.header.CallIdHeader;
... ... @@ -65,6 +76,16 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
65 76 private SIPSender sipSender;
66 77  
67 78 @Autowired
  79 + private ZlmHttpHookSubscribe subscribe;
  80 +
  81 + @Autowired
  82 + private UserSetting userSetting;
  83 +
  84 +
  85 + @Autowired
  86 + private VideoStreamSessionManager streamSession;
  87 +
  88 + @Autowired
68 89 private DynamicTask dynamicTask;
69 90  
70 91 @Autowired
... ... @@ -462,6 +483,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
462 483 sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
463 484 }
464 485  
  486 +
465 487 /**
466 488 * 向上级回复DeviceStatus查询信息
467 489 * @param parentPlatform 平台信息
... ... @@ -811,26 +833,129 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
811 833 }
812 834  
813 835 @Override
814   - public void streamByeCmd(ParentPlatform parentPlatform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException {
  836 + public synchronized void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException {
815 837 if (sendRtpItem == null ) {
816 838 logger.info("[向上级发送BYE], sendRtpItem 为NULL");
817 839 return;
818 840 }
819   - if (parentPlatform == null) {
  841 + if (platform == null) {
820 842 logger.info("[向上级发送BYE], platform 为NULL");
821 843 return;
822 844 }
823   - logger.info("[向上级发送BYE], {}/{}", parentPlatform.getServerGBId(), sendRtpItem.getChannelId());
  845 + logger.info("[向上级发送BYE], {}/{}", platform.getServerGBId(), sendRtpItem.getChannelId());
824 846 String mediaServerId = sendRtpItem.getMediaServerId();
825 847 MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
826 848 if (mediaServerItem != null) {
827 849 mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
828   - zlmServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId());
  850 + zlmServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStream());
829 851 }
830   - SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem);
  852 + SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
831 853 if (byeRequest == null) {
832 854 logger.warn("[向上级发送bye]:无法创建 byeRequest");
833 855 }
834   - sipSender.transmitRequest(parentPlatform.getDeviceIp(),byeRequest);
  856 + sipSender.transmitRequest(platform.getDeviceIp(),byeRequest);
  857 + }
  858 +
  859 + @Override
  860 + public void streamByeCmd(ParentPlatform platform, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
  861 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(platform.getServerGBId(), channelId, callId, stream);
  862 + if (ssrcTransaction == null) {
  863 + throw new SsrcTransactionNotFoundException(platform.getServerGBId(), channelId, callId, stream);
  864 + }
  865 +
  866 + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
  867 + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
  868 + streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  869 +
  870 + Request byteRequest = headerProviderPlatformProvider.createByteRequest(platform, channelId, ssrcTransaction.getSipTransactionInfo());
  871 + sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), byteRequest, null, okEvent);
  872 + }
  873 +
  874 + @Override
  875 + public void broadcastResultCmd(ParentPlatform platform, DeviceChannel deviceChannel, String sn, boolean result, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
  876 + if (platform == null || deviceChannel == null) {
  877 + return;
  878 + }
  879 + String characterSet = platform.getCharacterSet();
  880 + StringBuffer mediaStatusXml = new StringBuffer(200);
  881 + mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
  882 + mediaStatusXml.append("<Response>\r\n");
  883 + mediaStatusXml.append("<CmdType>Broadcast</CmdType>\r\n");
  884 + mediaStatusXml.append("<SN>" + sn + "</SN>\r\n");
  885 + mediaStatusXml.append("<DeviceID>" + deviceChannel.getChannelId() + "</DeviceID>\r\n");
  886 + mediaStatusXml.append("<Result>" + (result?"OK":"ERROR") + "</Result>\r\n");
  887 + mediaStatusXml.append("</Response>\r\n");
  888 +
  889 + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(platform.getDeviceIp(), platform.getTransport());
  890 +
  891 + SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(platform, mediaStatusXml.toString(),
  892 + SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader);
  893 +
  894 + sipSender.transmitRequest(platform.getDeviceIp(),messageRequest, errorEvent, okEvent);
  895 + }
  896 +
  897 + @Override
  898 + public void broadcastInviteCmd(ParentPlatform platform, String channelId, MediaServerItem mediaServerItem,
  899 + SSRCInfo ssrcInfo, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent,
  900 + SipSubscribe.Event errorEvent) throws ParseException, SipException, InvalidArgumentException {
  901 + String stream = ssrcInfo.getStream();
  902 +
  903 + if (platform == null) {
  904 + return;
  905 + }
  906 +
  907 + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  908 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
  909 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, HookParam hookParam) -> {
  910 + if (event != null) {
  911 + event.response(mediaServerItemInUse, hookParam);
  912 + subscribe.removeSubscribe(hookSubscribe);
  913 + }
  914 + });
  915 + String sdpIp = mediaServerItem.getSdpIp();
  916 +
  917 + StringBuffer content = new StringBuffer(200);
  918 + content.append("v=0\r\n");
  919 + content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
  920 + content.append("s=Play\r\n");
  921 + content.append("c=IN IP4 " + sdpIp + "\r\n");
  922 + content.append("t=0 0\r\n");
  923 +
  924 + if ("TCP-PASSIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
  925 + content.append("m=audio " + ssrcInfo.getPort() + " TCP/RTP/AVP 8 96\r\n");
  926 + } else if ("TCP-ACTIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
  927 + content.append("m=audio " + ssrcInfo.getPort() + " TCP/RTP/AVP 8 96\r\n");
  928 + } else if ("UDP".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
  929 + content.append("m=audio " + ssrcInfo.getPort() + " RTP/AVP 8 96\r\n");
  930 + }
  931 +
  932 + content.append("a=recvonly\r\n");
  933 + content.append("a=rtpmap:8 PCMA/8000\r\n");
  934 + content.append("a=rtpmap:96 PS/90000\r\n");
  935 + if ("TCP-PASSIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
  936 + content.append("a=setup:passive\r\n");
  937 + content.append("a=connection:new\r\n");
  938 + }else if ("TCP-ACTIVE".equalsIgnoreCase(userSetting.getBroadcastForPlatform())) {
  939 + content.append("a=setup:active\r\n");
  940 + content.append("a=connection:new\r\n");
  941 + }
  942 +
  943 + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
  944 + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getTransport());
  945 +
  946 + Request request = headerProviderPlatformProvider.createInviteRequest(platform, channelId,
  947 + content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), ssrcInfo.getSsrc(),
  948 + callIdHeader);
  949 + sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), request, (e -> {
  950 + streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream());
  951 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  952 + subscribe.removeSubscribe(hookSubscribe);
  953 + errorEvent.response(e);
  954 + }), e -> {
  955 + ResponseEvent responseEvent = (ResponseEvent) e.event;
  956 + SIPResponse response = (SIPResponse) responseEvent.getResponse();
  957 + streamSession.put(platform.getServerGBId(), channelId, callIdHeader.getCallId(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.BROADCAST);
  958 + okEvent.response(e);
  959 + });
835 960 }
836 961 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
... ... @@ -82,6 +82,7 @@ public abstract class SIPRequestProcessorParent {
82 82 return responseAck(sipRequest, statusCode, msg, null);
83 83 }
84 84  
  85 +
85 86 public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException {
86 87 if (sipRequest.getToHeader().getTag() == null) {
87 88 sipRequest.getToHeader().setTag(SipUtils.getNewTag());
... ... @@ -124,6 +125,8 @@ public abstract class SIPRequestProcessorParent {
124 125 return response;
125 126 }
126 127  
  128 +
  129 +
127 130 /**
128 131 * 回复带sdp的200
129 132 */
... ... @@ -141,7 +144,10 @@ public abstract class SIPRequestProcessorParent {
141 144 responseAckExtraParam.content = sdp;
142 145 responseAckExtraParam.sipURI = sipURI;
143 146  
144   - return responseAck(request, Response.OK, null, responseAckExtraParam);
  147 + SIPResponse sipResponse = responseAck(request, Response.OK, null, responseAckExtraParam);
  148 +
  149 +
  150 + return sipResponse;
145 151 }
146 152  
147 153 /**
... ... @@ -174,7 +180,8 @@ public abstract class SIPRequestProcessorParent {
174 180 reader.setEncoding(charset);
175 181 // 对海康出现的未转义字符做处理。
176 182 String[] destStrArray = new String[]{"&lt;","&gt;","&amp;","&apos;","&quot;"};
177   - char despChar = '&'; // 或许可扩展兼容其他字符
  183 + // 或许可扩展兼容其他字符
  184 + char despChar = '&';
178 185 byte destBye = (byte) despChar;
179 186 List<Byte> result = new ArrayList<>();
180 187 byte[] rawContent = request.getRawContent();
... ... @@ -220,4 +227,5 @@ public abstract class SIPRequestProcessorParent {
220 227 return xml.getRootElement();
221 228 }
222 229  
  230 +
223 231 }
... ...