Commit 8eff2f1d11c0fdd85fbd1654279e4928ccc587dd

Authored by mk1990
1 parent 3d6db747

添加国标级联录像控制功能;

src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
1 -package com.genersoft.iot.vmp.gb28181.transmit.cmd;  
2 -  
3 -import com.genersoft.iot.vmp.common.StreamInfo;  
4 -import com.genersoft.iot.vmp.gb28181.bean.*;  
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;  
8 -import com.genersoft.iot.vmp.service.bean.SSRCInfo;  
9 -  
10 -import javax.sip.Dialog;  
11 -  
12 -/**  
13 - * @description:设备能力接口,用于定义设备的控制、查询能力  
14 - * @author: swwheihei  
15 - * @date: 2020年5月3日 下午9:16:34  
16 - */  
17 -public interface ISIPCommander {  
18 -  
19 - /**  
20 - * 云台方向放控制,使用配置文件中的默认镜头移动速度  
21 - *  
22 - * @param device 控制设备  
23 - * @param channelId 预览通道  
24 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移  
25 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移  
26 - */  
27 - boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);  
28 -  
29 - /**  
30 - * 云台方向放控制  
31 - *  
32 - * @param device 控制设备  
33 - * @param channelId 预览通道  
34 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移  
35 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移  
36 - * @param moveSpeed 镜头移动速度  
37 - */  
38 - boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);  
39 -  
40 - /**  
41 - * 云台缩放控制,使用配置文件中的默认镜头缩放速度  
42 - *  
43 - * @param device 控制设备  
44 - * @param channelId 预览通道  
45 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大  
46 - */  
47 - boolean ptzZoomCmd(Device device,String channelId,int inOut);  
48 -  
49 - /**  
50 - * 云台缩放控制  
51 - *  
52 - * @param device 控制设备  
53 - * @param channelId 预览通道  
54 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大  
55 - */  
56 - boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);  
57 -  
58 - /**  
59 - * 云台控制,支持方向与缩放控制  
60 - *  
61 - * @param device 控制设备  
62 - * @param channelId 预览通道  
63 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移  
64 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移  
65 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大  
66 - * @param moveSpeed 镜头移动速度  
67 - * @param zoomSpeed 镜头缩放速度  
68 - */  
69 - boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);  
70 -  
71 - /**  
72 - * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令  
73 - *  
74 - * @param device 控制设备  
75 - * @param channelId 预览通道  
76 - * @param cmdCode 指令码  
77 - * @param parameter1 数据1  
78 - * @param parameter2 数据2  
79 - * @param combineCode2 组合码2  
80 - */  
81 - boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);  
82 -  
83 - /**  
84 - * 前端控制指令(用于转发上级指令)  
85 - * @param device 控制设备  
86 - * @param channelId 预览通道  
87 - * @param cmdString 前端控制指令串  
88 - */  
89 - boolean fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);  
90 -  
91 - /**  
92 - * 请求预览视频流  
93 - * @param device 视频设备  
94 - * @param channelId 预览通道  
95 - */  
96 - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);  
97 -  
98 - /**  
99 - * 请求回放视频流  
100 - *  
101 - * @param device 视频设备  
102 - * @param channelId 预览通道  
103 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss  
104 - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss  
105 - */  
106 - void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,InviteStreamCallback inviteStreamCallback, InviteStreamCallback event, SipSubscribe.Event errorEvent);  
107 -  
108 - /**  
109 - * 请求历史媒体下载  
110 - *  
111 - * @param device 视频设备  
112 - * @param channelId 预览通道  
113 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss  
114 - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss  
115 - * @param downloadSpeed 下载倍速参数  
116 - */  
117 - void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,  
118 - String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,  
119 - SipSubscribe.Event errorEvent);  
120 -  
121 - /**  
122 - * 视频流停止  
123 - */  
124 - void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent);  
125 - void streamByeCmd(String deviceId, String channelId, String stream, String callId);  
126 -  
127 - /**  
128 - * 回放暂停  
129 - */  
130 - void playPauseCmd(Device device, StreamInfo streamInfo);  
131 -  
132 - /**  
133 - * 回放恢复  
134 - */  
135 - void playResumeCmd(Device device, StreamInfo streamInfo);  
136 -  
137 - /**  
138 - * 回放拖动播放  
139 - */  
140 - void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime);  
141 -  
142 - /**  
143 - * 回放倍速播放  
144 - */  
145 - void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);  
146 -  
147 - /**  
148 - * 语音广播  
149 - *  
150 - * @param device 视频设备  
151 - * @param channelId 预览通道  
152 - */  
153 - boolean audioBroadcastCmd(Device device,String channelId);  
154 -  
155 - /**  
156 - * 语音广播  
157 - *  
158 - * @param device 视频设备  
159 - */  
160 - void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent);  
161 - boolean audioBroadcastCmd(Device device);  
162 -  
163 - /**  
164 - * 音视频录像控制  
165 - *  
166 - * @param device 视频设备  
167 - * @param channelId 预览通道  
168 - * @param recordCmdStr 录像命令:Record / StopRecord  
169 - */  
170 - boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);  
171 -  
172 - /**  
173 - * 远程启动控制命令  
174 - *  
175 - * @param device 视频设备  
176 - */  
177 - boolean teleBootCmd(Device device);  
178 -  
179 - /**  
180 - * 报警布防/撤防命令  
181 - *  
182 - * @param device 视频设备  
183 - */  
184 - boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);  
185 -  
186 - /**  
187 - * 报警复位命令  
188 - *  
189 - * @param device 视频设备  
190 - * @param alarmMethod 报警方式(可选)  
191 - * @param alarmType 报警类型(可选)  
192 - */  
193 - boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);  
194 -  
195 - /**  
196 - * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧  
197 - *  
198 - * @param device 视频设备  
199 - * @param channelId 预览通道  
200 - */  
201 - boolean iFrameCmd(Device device, String channelId);  
202 -  
203 - /**  
204 - * 看守位控制命令  
205 - *  
206 - * @param device 视频设备  
207 - * @param enabled 看守位使能:1 = 开启,0 = 关闭  
208 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)  
209 - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255  
210 - */  
211 - boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);  
212 -  
213 - /**  
214 - * 设备配置命令  
215 - *  
216 - * @param device 视频设备  
217 - */  
218 - boolean deviceConfigCmd(Device device);  
219 -  
220 - /**  
221 - * 设备配置命令:basicParam  
222 - *  
223 - * @param device 视频设备  
224 - * @param channelId 通道编码(可选)  
225 - * @param name 设备/通道名称(可选)  
226 - * @param expiration 注册过期时间(可选)  
227 - * @param heartBeatInterval 心跳间隔时间(可选)  
228 - * @param heartBeatCount 心跳超时次数(可选)  
229 - */  
230 - boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);  
231 -  
232 - /**  
233 - * 查询设备状态  
234 - *  
235 - * @param device 视频设备  
236 - */  
237 - boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);  
238 -  
239 - /**  
240 - * 查询设备信息  
241 - *  
242 - * @param device 视频设备  
243 - * @return  
244 - */  
245 - boolean deviceInfoQuery(Device device);  
246 -  
247 - /**  
248 - * 查询目录列表  
249 - *  
250 - * @param device 视频设备  
251 - */  
252 - boolean catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent);  
253 -  
254 - /**  
255 - * 查询录像信息  
256 - *  
257 - * @param device 视频设备  
258 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss  
259 - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss  
260 - * @param sn  
261 - */  
262 - boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer Secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);  
263 -  
264 - /**  
265 - * 查询报警信息  
266 - *  
267 - * @param device 视频设备  
268 - * @param startPriority 报警起始级别(可选)  
269 - * @param endPriority 报警终止级别(可选)  
270 - * @param alarmMethod 报警方式条件(可选)  
271 - * @param alarmType 报警类型  
272 - * @param startTime 报警发生起始时间(可选)  
273 - * @param endTime 报警发生终止时间(可选)  
274 - * @return true = 命令发送成功  
275 - */  
276 - boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,  
277 - String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);  
278 -  
279 - /**  
280 - * 查询设备配置  
281 - *  
282 - * @param device 视频设备  
283 - * @param channelId 通道编码(可选)  
284 - * @param configType 配置类型:  
285 - */  
286 - boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent);  
287 -  
288 - /**  
289 - * 查询设备预置位置  
290 - *  
291 - * @param device 视频设备  
292 - */  
293 - boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);  
294 -  
295 - /**  
296 - * 查询移动设备位置数据  
297 - *  
298 - * @param device 视频设备  
299 - */  
300 - boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);  
301 -  
302 - /**  
303 - * 订阅、取消订阅移动位置  
304 - *  
305 - * @param device 视频设备  
306 - * @return true = 命令发送成功  
307 - */  
308 - boolean mobilePositionSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent);  
309 -  
310 - /**  
311 - * 订阅、取消订阅报警信息  
312 - * @param device 视频设备  
313 - * @param expires 订阅过期时间(0 = 取消订阅)  
314 - * @param startPriority 报警起始级别(可选)  
315 - * @param endPriority 报警终止级别(可选)  
316 - * @param alarmType 报警类型  
317 - * @param startTime 报警发生起始时间(可选)  
318 - * @param endTime 报警发生终止时间(可选)  
319 - * @return true = 命令发送成功  
320 - */  
321 - boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);  
322 -  
323 - /**  
324 - * 订阅、取消订阅目录信息  
325 - * @param device 视频设备  
326 - * @return true = 命令发送成功  
327 - */  
328 - boolean catalogSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent);  
329 -  
330 - /**  
331 - * 拉框控制命令  
332 - *  
333 - * @param device 控制设备  
334 - * @param channelId 通道id  
335 - * @param cmdString 前端控制指令串  
336 - */  
337 - boolean dragZoomCmd(Device device, String channelId, String cmdString);  
338 -  
339 -  
340 - /**  
341 - * 向设备发送报警NOTIFY消息, 用于互联结构下,此时将设备当成一个平级平台看待  
342 - * @param device 设备  
343 - * @param deviceAlarm 报警信息信息  
344 - * @return  
345 - */  
346 - boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm);  
347 -} 1 +package com.genersoft.iot.vmp.gb28181.transmit.cmd;
  2 +
  3 +import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.gb28181.bean.*;
  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;
  8 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  9 +
  10 +import javax.sip.Dialog;
  11 +
  12 +/**
  13 + * @description:设备能力接口,用于定义设备的控制、查询能力
  14 + * @author: swwheihei
  15 + * @date: 2020年5月3日 下午9:16:34
  16 + */
  17 +public interface ISIPCommander {
  18 +
  19 + /**
  20 + * 云台方向放控制,使用配置文件中的默认镜头移动速度
  21 + *
  22 + * @param device 控制设备
  23 + * @param channelId 预览通道
  24 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  25 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  26 + */
  27 + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown);
  28 +
  29 + /**
  30 + * 云台方向放控制
  31 + *
  32 + * @param device 控制设备
  33 + * @param channelId 预览通道
  34 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  35 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  36 + * @param moveSpeed 镜头移动速度
  37 + */
  38 + boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed);
  39 +
  40 + /**
  41 + * 云台缩放控制,使用配置文件中的默认镜头缩放速度
  42 + *
  43 + * @param device 控制设备
  44 + * @param channelId 预览通道
  45 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  46 + */
  47 + boolean ptzZoomCmd(Device device,String channelId,int inOut);
  48 +
  49 + /**
  50 + * 云台缩放控制
  51 + *
  52 + * @param device 控制设备
  53 + * @param channelId 预览通道
  54 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  55 + */
  56 + boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed);
  57 +
  58 + /**
  59 + * 云台控制,支持方向与缩放控制
  60 + *
  61 + * @param device 控制设备
  62 + * @param channelId 预览通道
  63 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  64 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  65 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  66 + * @param moveSpeed 镜头移动速度
  67 + * @param zoomSpeed 镜头缩放速度
  68 + */
  69 + boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
  70 +
  71 + /**
  72 + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
  73 + *
  74 + * @param device 控制设备
  75 + * @param channelId 预览通道
  76 + * @param cmdCode 指令码
  77 + * @param parameter1 数据1
  78 + * @param parameter2 数据2
  79 + * @param combineCode2 组合码2
  80 + */
  81 + boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
  82 +
  83 + /**
  84 + * 前端控制指令(用于转发上级指令)
  85 + * @param device 控制设备
  86 + * @param channelId 预览通道
  87 + * @param cmdString 前端控制指令串
  88 + */
  89 + boolean fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
  90 +
  91 + /**
  92 + * 请求预览视频流
  93 + * @param device 视频设备
  94 + * @param channelId 预览通道
  95 + */
  96 + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
  97 +
  98 + /**
  99 + * 请求回放视频流
  100 + *
  101 + * @param device 视频设备
  102 + * @param channelId 预览通道
  103 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  104 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  105 + */
  106 + void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,InviteStreamCallback inviteStreamCallback, InviteStreamCallback event, SipSubscribe.Event errorEvent);
  107 +
  108 + /**
  109 + * 请求历史媒体下载
  110 + *
  111 + * @param device 视频设备
  112 + * @param channelId 预览通道
  113 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  114 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  115 + * @param downloadSpeed 下载倍速参数
  116 + */
  117 + void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  118 + String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  119 + SipSubscribe.Event errorEvent);
  120 +
  121 + /**
  122 + * 视频流停止
  123 + */
  124 + void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent);
  125 + void streamByeCmd(String deviceId, String channelId, String stream, String callId);
  126 +
  127 + /**
  128 + * 回放暂停
  129 + */
  130 + void playPauseCmd(Device device, StreamInfo streamInfo);
  131 +
  132 + /**
  133 + * 回放恢复
  134 + */
  135 + void playResumeCmd(Device device, StreamInfo streamInfo);
  136 +
  137 + /**
  138 + * 回放拖动播放
  139 + */
  140 + void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime);
  141 +
  142 + /**
  143 + * 回放倍速播放
  144 + */
  145 + void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
  146 +
  147 + /**
  148 + * 回放控制
  149 + * @param device
  150 + * @param streamInfo
  151 + * @param content
  152 + */
  153 + void playbackControlCmd(Device device, StreamInfo streamInfo, String content);
  154 +
  155 + /**
  156 + * 语音广播
  157 + *
  158 + * @param device 视频设备
  159 + * @param channelId 预览通道
  160 + */
  161 + boolean audioBroadcastCmd(Device device,String channelId);
  162 +
  163 + /**
  164 + * 语音广播
  165 + *
  166 + * @param device 视频设备
  167 + */
  168 + void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent);
  169 + boolean audioBroadcastCmd(Device device);
  170 +
  171 + /**
  172 + * 音视频录像控制
  173 + *
  174 + * @param device 视频设备
  175 + * @param channelId 预览通道
  176 + * @param recordCmdStr 录像命令:Record / StopRecord
  177 + */
  178 + boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
  179 +
  180 + /**
  181 + * 远程启动控制命令
  182 + *
  183 + * @param device 视频设备
  184 + */
  185 + boolean teleBootCmd(Device device);
  186 +
  187 + /**
  188 + * 报警布防/撤防命令
  189 + *
  190 + * @param device 视频设备
  191 + */
  192 + boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
  193 +
  194 + /**
  195 + * 报警复位命令
  196 + *
  197 + * @param device 视频设备
  198 + * @param alarmMethod 报警方式(可选)
  199 + * @param alarmType 报警类型(可选)
  200 + */
  201 + boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
  202 +
  203 + /**
  204 + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
  205 + *
  206 + * @param device 视频设备
  207 + * @param channelId 预览通道
  208 + */
  209 + boolean iFrameCmd(Device device, String channelId);
  210 +
  211 + /**
  212 + * 看守位控制命令
  213 + *
  214 + * @param device 视频设备
  215 + * @param enabled 看守位使能:1 = 开启,0 = 关闭
  216 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
  217 + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
  218 + */
  219 + boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
  220 +
  221 + /**
  222 + * 设备配置命令
  223 + *
  224 + * @param device 视频设备
  225 + */
  226 + boolean deviceConfigCmd(Device device);
  227 +
  228 + /**
  229 + * 设备配置命令:basicParam
  230 + *
  231 + * @param device 视频设备
  232 + * @param channelId 通道编码(可选)
  233 + * @param name 设备/通道名称(可选)
  234 + * @param expiration 注册过期时间(可选)
  235 + * @param heartBeatInterval 心跳间隔时间(可选)
  236 + * @param heartBeatCount 心跳超时次数(可选)
  237 + */
  238 + boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
  239 +
  240 + /**
  241 + * 查询设备状态
  242 + *
  243 + * @param device 视频设备
  244 + */
  245 + boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
  246 +
  247 + /**
  248 + * 查询设备信息
  249 + *
  250 + * @param device 视频设备
  251 + * @return
  252 + */
  253 + boolean deviceInfoQuery(Device device);
  254 +
  255 + /**
  256 + * 查询目录列表
  257 + *
  258 + * @param device 视频设备
  259 + */
  260 + boolean catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent);
  261 +
  262 + /**
  263 + * 查询录像信息
  264 + *
  265 + * @param device 视频设备
  266 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  267 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  268 + * @param sn
  269 + */
  270 + boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer Secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent);
  271 +
  272 + /**
  273 + * 查询报警信息
  274 + *
  275 + * @param device 视频设备
  276 + * @param startPriority 报警起始级别(可选)
  277 + * @param endPriority 报警终止级别(可选)
  278 + * @param alarmMethod 报警方式条件(可选)
  279 + * @param alarmType 报警类型
  280 + * @param startTime 报警发生起始时间(可选)
  281 + * @param endTime 报警发生终止时间(可选)
  282 + * @return true = 命令发送成功
  283 + */
  284 + boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
  285 + String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);
  286 +
  287 + /**
  288 + * 查询设备配置
  289 + *
  290 + * @param device 视频设备
  291 + * @param channelId 通道编码(可选)
  292 + * @param configType 配置类型:
  293 + */
  294 + boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent);
  295 +
  296 + /**
  297 + * 查询设备预置位置
  298 + *
  299 + * @param device 视频设备
  300 + */
  301 + boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
  302 +
  303 + /**
  304 + * 查询移动设备位置数据
  305 + *
  306 + * @param device 视频设备
  307 + */
  308 + boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);
  309 +
  310 + /**
  311 + * 订阅、取消订阅移动位置
  312 + *
  313 + * @param device 视频设备
  314 + * @return true = 命令发送成功
  315 + */
  316 + boolean mobilePositionSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent);
  317 +
  318 + /**
  319 + * 订阅、取消订阅报警信息
  320 + * @param device 视频设备
  321 + * @param expires 订阅过期时间(0 = 取消订阅)
  322 + * @param startPriority 报警起始级别(可选)
  323 + * @param endPriority 报警终止级别(可选)
  324 + * @param alarmType 报警类型
  325 + * @param startTime 报警发生起始时间(可选)
  326 + * @param endTime 报警发生终止时间(可选)
  327 + * @return true = 命令发送成功
  328 + */
  329 + boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
  330 +
  331 + /**
  332 + * 订阅、取消订阅目录信息
  333 + * @param device 视频设备
  334 + * @return true = 命令发送成功
  335 + */
  336 + boolean catalogSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent);
  337 +
  338 + /**
  339 + * 拉框控制命令
  340 + *
  341 + * @param device 控制设备
  342 + * @param channelId 通道id
  343 + * @param cmdString 前端控制指令串
  344 + */
  345 + boolean dragZoomCmd(Device device, String channelId, String cmdString);
  346 +
  347 +
  348 + /**
  349 + * 向设备发送报警NOTIFY消息, 用于互联结构下,此时将设备当成一个平级平台看待
  350 + * @param device 设备
  351 + * @param deviceAlarm 报警信息信息
  352 + * @return
  353 + */
  354 + boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm);
  355 +}
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.alibaba.fastjson.JSONObject;  
4 -import com.genersoft.iot.vmp.common.StreamInfo;  
5 -import com.genersoft.iot.vmp.conf.DynamicTask;  
6 -import com.genersoft.iot.vmp.conf.SipConfig;  
7 -import com.genersoft.iot.vmp.conf.UserSetting;  
8 -import com.genersoft.iot.vmp.gb28181.bean.*;  
9 -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;  
10 -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;  
11 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;  
12 -import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;  
13 -import com.genersoft.iot.vmp.utils.DateUtil;  
14 -import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;  
15 -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;  
16 -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;  
17 -import com.genersoft.iot.vmp.service.IMediaServerService;  
18 -import com.genersoft.iot.vmp.service.bean.SSRCInfo;  
19 -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;  
20 -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;  
21 -import gov.nist.javax.sip.SipProviderImpl;  
22 -import gov.nist.javax.sip.SipStackImpl;  
23 -import gov.nist.javax.sip.message.MessageFactoryImpl;  
24 -import gov.nist.javax.sip.message.SIPRequest;  
25 -import gov.nist.javax.sip.stack.SIPDialog;  
26 -import org.slf4j.Logger;  
27 -import org.slf4j.LoggerFactory;  
28 -import org.springframework.beans.factory.annotation.Autowired;  
29 -import org.springframework.beans.factory.annotation.Qualifier;  
30 -import org.springframework.context.annotation.DependsOn;  
31 -import org.springframework.stereotype.Component;  
32 -import org.springframework.util.StringUtils;  
33 -  
34 -import javax.sip.*;  
35 -import javax.sip.address.Address;  
36 -import javax.sip.address.SipURI;  
37 -import javax.sip.address.URI;  
38 -import javax.sip.header.*;  
39 -import javax.sip.message.Request;  
40 -import java.lang.reflect.Field;  
41 -import java.text.ParseException;  
42 -import java.util.ArrayList;  
43 -import java.util.HashSet;  
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 SipFactory sipFactory;  
62 -  
63 - @Autowired  
64 - @Qualifier(value="tcpSipProvider")  
65 - private SipProviderImpl tcpSipProvider;  
66 -  
67 - @Autowired  
68 - @Qualifier(value="udpSipProvider")  
69 - private SipProviderImpl udpSipProvider;  
70 -  
71 - @Autowired  
72 - private SIPRequestHeaderProvider headerProvider;  
73 -  
74 - @Autowired  
75 - private VideoStreamSessionManager streamSession;  
76 -  
77 - @Autowired  
78 - private IVideoManagerStorage storager;  
79 -  
80 - @Autowired  
81 - private IRedisCatchStorage redisCatchStorage;  
82 -  
83 - @Autowired  
84 - private UserSetting userSetting;  
85 -  
86 - @Autowired  
87 - private ZLMHttpHookSubscribe subscribe;  
88 -  
89 - @Autowired  
90 - private SipSubscribe sipSubscribe;  
91 -  
92 - @Autowired  
93 - private IMediaServerService mediaServerService;  
94 -  
95 - @Autowired  
96 - private DynamicTask dynamicTask;  
97 -  
98 -  
99 - /**  
100 - * 云台方向放控制,使用配置文件中的默认镜头移动速度  
101 - *  
102 - * @param device 控制设备  
103 - * @param channelId 预览通道  
104 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移  
105 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移  
106 - */  
107 - @Override  
108 - public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) {  
109 - return ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0);  
110 - }  
111 -  
112 - /**  
113 - * 云台方向放控制  
114 - *  
115 - * @param device 控制设备  
116 - * @param channelId 预览通道  
117 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移  
118 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移  
119 - * @param moveSpeed 镜头移动速度  
120 - */  
121 - @Override  
122 - public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) {  
123 - return ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0);  
124 - }  
125 -  
126 - /**  
127 - * 云台缩放控制,使用配置文件中的默认镜头缩放速度  
128 - *  
129 - * @param device 控制设备  
130 - * @param channelId 预览通道  
131 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大  
132 - */  
133 - @Override  
134 - public boolean ptzZoomCmd(Device device, String channelId, int inOut) {  
135 - return ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed());  
136 - }  
137 -  
138 - /**  
139 - * 云台缩放控制  
140 - *  
141 - * @param device 控制设备  
142 - * @param channelId 预览通道  
143 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大  
144 - * @param zoomSpeed 镜头缩放速度  
145 - */  
146 - @Override  
147 - public boolean ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) {  
148 - return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed);  
149 - }  
150 -  
151 - /**  
152 - * 云台指令码计算  
153 - *  
154 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移  
155 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移  
156 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大  
157 - * @param moveSpeed 镜头移动速度 默认 0XFF (0-255)  
158 - * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255)  
159 - */  
160 - public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) {  
161 - int cmdCode = 0;  
162 - if (leftRight == 2) {  
163 - cmdCode|=0x01; // 右移  
164 - } else if(leftRight == 1) {  
165 - cmdCode|=0x02; // 左移  
166 - }  
167 - if (upDown == 2) {  
168 - cmdCode|=0x04; // 下移  
169 - } else if(upDown == 1) {  
170 - cmdCode|=0x08; // 上移  
171 - }  
172 - if (inOut == 2) {  
173 - cmdCode |= 0x10; // 放大  
174 - } else if(inOut == 1) {  
175 - cmdCode |= 0x20; // 缩小  
176 - }  
177 - StringBuilder builder = new StringBuilder("A50F01");  
178 - String strTmp;  
179 - strTmp = String.format("%02X", cmdCode);  
180 - builder.append(strTmp, 0, 2);  
181 - strTmp = String.format("%02X", moveSpeed);  
182 - builder.append(strTmp, 0, 2);  
183 - builder.append(strTmp, 0, 2);  
184 - strTmp = String.format("%X", zoomSpeed);  
185 - builder.append(strTmp, 0, 1).append("0");  
186 - //计算校验码  
187 - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100;  
188 - strTmp = String.format("%02X", checkCode);  
189 - builder.append(strTmp, 0, 2);  
190 - return builder.toString();  
191 -}  
192 -  
193 - /**  
194 - * 云台指令码计算  
195 - *  
196 - * @param cmdCode 指令码  
197 - * @param parameter1 数据1  
198 - * @param parameter2 数据2  
199 - * @param combineCode2 组合码2  
200 - */  
201 - public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) {  
202 - StringBuilder builder = new StringBuilder("A50F01");  
203 - String strTmp;  
204 - strTmp = String.format("%02X", cmdCode);  
205 - builder.append(strTmp, 0, 2);  
206 - strTmp = String.format("%02X", parameter1);  
207 - builder.append(strTmp, 0, 2);  
208 - strTmp = String.format("%02X", parameter2);  
209 - builder.append(strTmp, 0, 2);  
210 - strTmp = String.format("%X", combineCode2);  
211 - builder.append(strTmp, 0, 1).append("0");  
212 - //计算校验码  
213 - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100;  
214 - strTmp = String.format("%02X", checkCode);  
215 - builder.append(strTmp, 0, 2);  
216 - return builder.toString();  
217 - }  
218 -  
219 - /**  
220 - * 云台控制,支持方向与缩放控制  
221 - *  
222 - * @param device 控制设备  
223 - * @param channelId 预览通道  
224 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移  
225 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移  
226 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大  
227 - * @param moveSpeed 镜头移动速度  
228 - * @param zoomSpeed 镜头缩放速度  
229 - */  
230 - @Override  
231 - public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,  
232 - int zoomSpeed) {  
233 - try {  
234 - String cmdStr= cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);  
235 - StringBuffer ptzXml = new StringBuffer(200);  
236 - String charset = device.getCharset();  
237 - ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
238 - ptzXml.append("<Control>\r\n");  
239 - ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
240 - ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
241 - ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
242 - ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");  
243 - ptzXml.append("<Info>\r\n");  
244 - ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");  
245 - ptzXml.append("</Info>\r\n");  
246 - ptzXml.append("</Control>\r\n");  
247 -  
248 - String tm = Long.toString(System.currentTimeMillis());  
249 -  
250 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
251 - : udpSipProvider.getNewCallId();  
252 -  
253 - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);  
254 -  
255 - transmitRequest(device, request);  
256 - return true;  
257 - } catch (SipException | ParseException | InvalidArgumentException e) {  
258 - e.printStackTrace();  
259 - }  
260 - return false;  
261 - }  
262 -  
263 - /**  
264 - * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令  
265 - *  
266 - * @param device 控制设备  
267 - * @param channelId 预览通道  
268 - * @param cmdCode 指令码  
269 - * @param parameter1 数据1  
270 - * @param parameter2 数据2  
271 - * @param combineCode2 组合码2  
272 - */  
273 - @Override  
274 - public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) {  
275 - try {  
276 - String cmdStr= frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2);  
277 - logger.debug("控制字符串:" + cmdStr);  
278 - StringBuffer ptzXml = new StringBuffer(200);  
279 - String charset = device.getCharset();  
280 - ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
281 - ptzXml.append("<Control>\r\n");  
282 - ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
283 - ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
284 - ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
285 - ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");  
286 - ptzXml.append("<Info>\r\n");  
287 - ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");  
288 - ptzXml.append("</Info>\r\n");  
289 - ptzXml.append("</Control>\r\n");  
290 -  
291 - String tm = Long.toString(System.currentTimeMillis());  
292 -  
293 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
294 - : udpSipProvider.getNewCallId();  
295 -  
296 - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);  
297 - transmitRequest(device, request);  
298 - return true;  
299 - } catch (SipException | ParseException | InvalidArgumentException e) {  
300 - e.printStackTrace();  
301 - }  
302 - return false;  
303 - }  
304 -  
305 - /**  
306 - * 前端控制指令(用于转发上级指令)  
307 - * @param device 控制设备  
308 - * @param channelId 预览通道  
309 - * @param cmdString 前端控制指令串  
310 - */  
311 - @Override  
312 - public boolean fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {  
313 - try {  
314 - StringBuffer ptzXml = new StringBuffer(200);  
315 - String charset = device.getCharset();  
316 - ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
317 - ptzXml.append("<Control>\r\n");  
318 - ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
319 - ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
320 - ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
321 - ptzXml.append("<PTZCmd>" + cmdString + "</PTZCmd>\r\n");  
322 - ptzXml.append("<Info>\r\n");  
323 - ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");  
324 - ptzXml.append("</Info>\r\n");  
325 - ptzXml.append("</Control>\r\n");  
326 -  
327 - String tm = Long.toString(System.currentTimeMillis());  
328 -  
329 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
330 - : udpSipProvider.getNewCallId();  
331 -  
332 - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);  
333 - transmitRequest(device, request, errorEvent, okEvent);  
334 - return true;  
335 - } catch (SipException | ParseException | InvalidArgumentException e) {  
336 - e.printStackTrace();  
337 - }  
338 - return false;  
339 - }  
340 -  
341 - /**  
342 - * 请求预览视频流  
343 - * @param device 视频设备  
344 - * @param channelId 预览通道  
345 - * @param event hook订阅  
346 - * @param errorEvent sip错误订阅  
347 - */  
348 - @Override  
349 - public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,  
350 - ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {  
351 - String streamId = ssrcInfo.getStream();  
352 - try {  
353 - if (device == null) {  
354 - return;  
355 - }  
356 - String streamMode = device.getStreamMode().toUpperCase();  
357 -  
358 - logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());  
359 - // 添加订阅  
360 - JSONObject subscribeKey = new JSONObject();  
361 - subscribeKey.put("app", "rtp");  
362 - subscribeKey.put("stream", streamId);  
363 - subscribeKey.put("regist", true);  
364 - subscribeKey.put("schema", "rtmp");  
365 - subscribeKey.put("mediaServerId", mediaServerItem.getId());  
366 - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,  
367 - (MediaServerItem mediaServerItemInUse, JSONObject json)->{  
368 - if (event != null) {  
369 - event.response(mediaServerItemInUse, json);  
370 - }  
371 - });  
372 - //  
373 - StringBuffer content = new StringBuffer(200);  
374 - content.append("v=0\r\n");  
375 - content.append("o="+ channelId+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");  
376 - content.append("s=Play\r\n");  
377 - content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");  
378 - content.append("t=0 0\r\n");  
379 -  
380 - if (userSetting.isSeniorSdp()) {  
381 - if("TCP-PASSIVE".equals(streamMode)) {  
382 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");  
383 - }else if ("TCP-ACTIVE".equals(streamMode)) {  
384 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");  
385 - }else if("UDP".equals(streamMode)) {  
386 - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");  
387 - }  
388 - content.append("a=recvonly\r\n");  
389 - content.append("a=rtpmap:96 PS/90000\r\n");  
390 - content.append("a=fmtp:126 profile-level-id=42e01e\r\n");  
391 - content.append("a=rtpmap:126 H264/90000\r\n");  
392 - content.append("a=rtpmap:125 H264S/90000\r\n");  
393 - content.append("a=fmtp:125 profile-level-id=42e01e\r\n");  
394 - content.append("a=rtpmap:99 H265/90000\r\n");  
395 - content.append("a=rtpmap:98 H264/90000\r\n");  
396 - content.append("a=rtpmap:97 MPEG4/90000\r\n");  
397 - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式  
398 - content.append("a=setup:passive\r\n");  
399 - content.append("a=connection:new\r\n");  
400 - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式  
401 - content.append("a=setup:active\r\n");  
402 - content.append("a=connection:new\r\n");  
403 - }  
404 - }else {  
405 - if("TCP-PASSIVE".equals(streamMode)) {  
406 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");  
407 - }else if ("TCP-ACTIVE".equals(streamMode)) {  
408 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");  
409 - }else if("UDP".equals(streamMode)) {  
410 - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");  
411 - }  
412 - content.append("a=recvonly\r\n");  
413 - content.append("a=rtpmap:96 PS/90000\r\n");  
414 - content.append("a=rtpmap:98 H264/90000\r\n");  
415 - content.append("a=rtpmap:97 MPEG4/90000\r\n");  
416 - content.append("a=rtpmap:99 H265/90000\r\n");  
417 - if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式  
418 - content.append("a=setup:passive\r\n");  
419 - content.append("a=connection:new\r\n");  
420 - } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式  
421 - content.append("a=setup:active\r\n");  
422 - content.append("a=connection:new\r\n");  
423 - }  
424 - }  
425 -  
426 - content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc  
427 - // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率  
428 -// content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备  
429 -  
430 - String tm = Long.toString(System.currentTimeMillis());  
431 -  
432 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
433 - : udpSipProvider.getNewCallId();  
434 -  
435 - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrcInfo.getSsrc(), callIdHeader);  
436 -  
437 - transmitRequest(device, request, (e -> {  
438 - streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());  
439 - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());  
440 - errorEvent.response(e);  
441 - }), e ->{  
442 - // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值  
443 - streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);  
444 - streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);  
445 - okEvent.response(e);  
446 - });  
447 -  
448 -  
449 - } catch ( SipException | ParseException | InvalidArgumentException e) {  
450 - e.printStackTrace();  
451 - }  
452 - }  
453 -  
454 - /**  
455 - * 请求回放视频流  
456 - *  
457 - * @param device 视频设备  
458 - * @param channelId 预览通道  
459 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss  
460 - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss  
461 - */  
462 - @Override  
463 - public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,  
464 - String startTime, String endTime, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,  
465 - SipSubscribe.Event errorEvent) {  
466 - try {  
467 -  
468 - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());  
469 -  
470 - StringBuffer content = new StringBuffer(200);  
471 - content.append("v=0\r\n");  
472 - content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");  
473 - content.append("s=Playback\r\n");  
474 - content.append("u="+channelId+":0\r\n");  
475 - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");  
476 - content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "  
477 - +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");  
478 -  
479 - String streamMode = device.getStreamMode().toUpperCase();  
480 -  
481 - if (userSetting.isSeniorSdp()) {  
482 - if("TCP-PASSIVE".equals(streamMode)) {  
483 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");  
484 - }else if ("TCP-ACTIVE".equals(streamMode)) {  
485 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");  
486 - }else if("UDP".equals(streamMode)) {  
487 - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");  
488 - }  
489 - content.append("a=recvonly\r\n");  
490 - content.append("a=rtpmap:96 PS/90000\r\n");  
491 - content.append("a=fmtp:126 profile-level-id=42e01e\r\n");  
492 - content.append("a=rtpmap:126 H264/90000\r\n");  
493 - content.append("a=rtpmap:125 H264S/90000\r\n");  
494 - content.append("a=fmtp:125 profile-level-id=42e01e\r\n");  
495 - content.append("a=rtpmap:99 H265/90000\r\n");  
496 - content.append("a=rtpmap:98 H264/90000\r\n");  
497 - content.append("a=rtpmap:97 MPEG4/90000\r\n");  
498 - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式  
499 - content.append("a=setup:passive\r\n");  
500 - content.append("a=connection:new\r\n");  
501 - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式  
502 - content.append("a=setup:active\r\n");  
503 - content.append("a=connection:new\r\n");  
504 - }  
505 - }else {  
506 - if("TCP-PASSIVE".equals(streamMode)) {  
507 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");  
508 - }else if ("TCP-ACTIVE".equals(streamMode)) {  
509 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");  
510 - }else if("UDP".equals(streamMode)) {  
511 - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");  
512 - }  
513 - content.append("a=recvonly\r\n");  
514 - content.append("a=rtpmap:96 PS/90000\r\n");  
515 - content.append("a=rtpmap:97 MPEG4/90000\r\n");  
516 - content.append("a=rtpmap:98 H264/90000\r\n");  
517 - content.append("a=rtpmap:99 H265/90000\r\n");  
518 - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式  
519 - content.append("a=setup:passive\r\n");  
520 - content.append("a=connection:new\r\n");  
521 - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式  
522 - content.append("a=setup:active\r\n");  
523 - content.append("a=connection:new\r\n");  
524 - }  
525 - }  
526 -  
527 - content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc  
528 -  
529 - String tm = Long.toString(System.currentTimeMillis());  
530 -  
531 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
532 - : udpSipProvider.getNewCallId();  
533 -  
534 - // 添加订阅  
535 - JSONObject subscribeKey = new JSONObject();  
536 - subscribeKey.put("app", "rtp");  
537 - subscribeKey.put("stream", ssrcInfo.getStream());  
538 - subscribeKey.put("regist", true);  
539 - subscribeKey.put("schema", "rtmp");  
540 - subscribeKey.put("mediaServerId", mediaServerItem.getId());  
541 - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey);  
542 - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,  
543 - (MediaServerItem mediaServerItemInUse, JSONObject json)->{  
544 - if (hookEvent != null) {  
545 - InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream());  
546 - hookEvent.call(inviteStreamInfo);  
547 - }  
548 - });  
549 - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());  
550 -  
551 - transmitRequest(device, request, errorEvent, okEvent -> {  
552 - ResponseEvent responseEvent = (ResponseEvent) okEvent.event;  
553 - streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.playback);  
554 - streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);  
555 - });  
556 - if (inviteStreamCallback != null) {  
557 - inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));  
558 - }  
559 - } catch ( SipException | ParseException | InvalidArgumentException e) {  
560 - e.printStackTrace();  
561 - }  
562 - }  
563 -  
564 - /**  
565 - * 请求历史媒体下载  
566 - *  
567 - * @param device 视频设备  
568 - * @param channelId 预览通道  
569 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss  
570 - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss  
571 - * @param downloadSpeed 下载倍速参数  
572 - */  
573 - @Override  
574 - public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,  
575 - String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,  
576 - SipSubscribe.Event errorEvent) {  
577 - try {  
578 - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());  
579 -  
580 - StringBuffer content = new StringBuffer(200);  
581 - content.append("v=0\r\n");  
582 - content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");  
583 - content.append("s=Download\r\n");  
584 - content.append("u="+channelId+":0\r\n");  
585 - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");  
586 - content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "  
587 - +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");  
588 -  
589 - String streamMode = device.getStreamMode().toUpperCase();  
590 -  
591 - if (userSetting.isSeniorSdp()) {  
592 - if("TCP-PASSIVE".equals(streamMode)) {  
593 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");  
594 - }else if ("TCP-ACTIVE".equals(streamMode)) {  
595 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");  
596 - }else if("UDP".equals(streamMode)) {  
597 - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");  
598 - }  
599 - content.append("a=recvonly\r\n");  
600 - content.append("a=rtpmap:96 PS/90000\r\n");  
601 - content.append("a=fmtp:126 profile-level-id=42e01e\r\n");  
602 - content.append("a=rtpmap:126 H264/90000\r\n");  
603 - content.append("a=rtpmap:125 H264S/90000\r\n");  
604 - content.append("a=fmtp:125 profile-level-id=42e01e\r\n");  
605 - content.append("a=rtpmap:99 MP4V-ES/90000\r\n");  
606 - content.append("a=fmtp:99 profile-level-id=3\r\n");  
607 - content.append("a=rtpmap:98 H264/90000\r\n");  
608 - content.append("a=rtpmap:97 MPEG4/90000\r\n");  
609 - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式  
610 - content.append("a=setup:passive\r\n");  
611 - content.append("a=connection:new\r\n");  
612 - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式  
613 - content.append("a=setup:active\r\n");  
614 - content.append("a=connection:new\r\n");  
615 - }  
616 - }else {  
617 - if("TCP-PASSIVE".equals(streamMode)) {  
618 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");  
619 - }else if ("TCP-ACTIVE".equals(streamMode)) {  
620 - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");  
621 - }else if("UDP".equals(streamMode)) {  
622 - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");  
623 - }  
624 - content.append("a=recvonly\r\n");  
625 - content.append("a=rtpmap:96 PS/90000\r\n");  
626 - content.append("a=rtpmap:97 MPEG4/90000\r\n");  
627 - content.append("a=rtpmap:98 H264/90000\r\n");  
628 - content.append("a=rtpmap:99 H265/90000\r\n");  
629 - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式  
630 - content.append("a=setup:passive\r\n");  
631 - content.append("a=connection:new\r\n");  
632 - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式  
633 - content.append("a=setup:active\r\n");  
634 - content.append("a=connection:new\r\n");  
635 - }  
636 - }  
637 - content.append("a=downloadspeed:" + downloadSpeed + "\r\n");  
638 -  
639 - content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc  
640 -  
641 - String tm = Long.toString(System.currentTimeMillis());  
642 -  
643 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
644 - : udpSipProvider.getNewCallId();  
645 -  
646 - // 添加订阅  
647 - JSONObject subscribeKey = new JSONObject();  
648 - subscribeKey.put("app", "rtp");  
649 - subscribeKey.put("stream", ssrcInfo.getStream());  
650 - subscribeKey.put("regist", true);  
651 - subscribeKey.put("mediaServerId", mediaServerItem.getId());  
652 - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());  
653 - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,  
654 - (MediaServerItem mediaServerItemInUse, JSONObject json)->{  
655 - hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));  
656 - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);  
657 - subscribeKey.put("regist", false);  
658 - subscribeKey.put("schema", "rtmp");  
659 - // 添加流注销的订阅,注销了后向设备发送bye  
660 - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,  
661 - (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd)->{  
662 - ClientTransaction transaction = streamSession.getTransaction(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());  
663 - if (transaction != null) {  
664 - logger.info("[录像]下载结束, 发送BYE");  
665 - streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());  
666 - }  
667 - });  
668 - });  
669 -  
670 - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());  
671 - if (inviteStreamCallback != null) {  
672 - inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));  
673 - }  
674 - transmitRequest(device, request, errorEvent, okEvent->{  
675 - ResponseEvent responseEvent = (ResponseEvent) okEvent.event;  
676 - streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.download);  
677 - streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);  
678 - });  
679 -  
680 -  
681 - } catch ( SipException | ParseException | InvalidArgumentException e) {  
682 - e.printStackTrace();  
683 - }  
684 - }  
685 -  
686 - /**  
687 - * 视频流停止, 不使用回调  
688 - */  
689 - @Override  
690 - public void streamByeCmd(String deviceId, String channelId, String stream, String callId) {  
691 - streamByeCmd(deviceId, channelId, stream, callId, null);  
692 - }  
693 -  
694 - /**  
695 - * 视频流停止  
696 - */  
697 - @Override  
698 - public void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent) {  
699 - try {  
700 - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, callId, stream);  
701 - ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId, stream, callId);  
702 -  
703 - if (transaction == null ) {  
704 - logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);  
705 - SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();  
706 - if (okEvent != null) {  
707 - okEvent.response(eventResult);  
708 - }  
709 - return;  
710 - }  
711 - SIPDialog dialog;  
712 - if (callId != null) {  
713 - dialog = streamSession.getDialogByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), callId);  
714 - }else {  
715 - if (stream == null && ssrcTransaction == null && ssrcTransaction.getStream() == null) {  
716 - return;  
717 - }  
718 - dialog = streamSession.getDialogByStream(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());  
719 - }  
720 - mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());  
721 - mediaServerService.closeRTPServer(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());  
722 - streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());  
723 -  
724 - if (dialog == null) {  
725 - logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());  
726 - return;  
727 - }  
728 - SipStack sipStack = udpSipProvider.getSipStack();  
729 - SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);  
730 - if (dialog != sipDialog) {  
731 - dialog = sipDialog;  
732 - }else {  
733 - dialog.setSipProvider(udpSipProvider);  
734 - try {  
735 - Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");  
736 - sipStackField.setAccessible(true);  
737 - sipStackField.set(dialog, sipStack);  
738 - Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");  
739 - eventListenersField.setAccessible(true);  
740 - eventListenersField.set(dialog, new HashSet<>());  
741 - } catch (NoSuchFieldException | IllegalAccessException e) {  
742 - e.printStackTrace();  
743 - }  
744 - }  
745 -  
746 - Request byeRequest = dialog.createRequest(Request.BYE);  
747 - SipURI byeURI = (SipURI) byeRequest.getRequestURI();  
748 - SIPRequest request = (SIPRequest)transaction.getRequest();  
749 - byeURI.setHost(request.getRemoteAddress().getHostAddress());  
750 - byeURI.setPort(request.getRemotePort());  
751 - ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME);  
752 - String protocol = viaHeader.getTransport().toUpperCase();  
753 - ClientTransaction clientTransaction = null;  
754 - if("TCP".equals(protocol)) {  
755 - clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);  
756 - } else if("UDP".equals(protocol)) {  
757 - clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);  
758 - }  
759 -  
760 - CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME);  
761 - if (okEvent != null) {  
762 - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent);  
763 - }  
764 -  
765 - dialog.sendRequest(clientTransaction);  
766 -  
767 - } catch (SipException | ParseException e) {  
768 - e.printStackTrace();  
769 - }  
770 - }  
771 -  
772 - /**  
773 - * 语音广播  
774 - *  
775 - * @param device 视频设备  
776 - * @param channelId 预览通道  
777 - */  
778 - @Override  
779 - public boolean audioBroadcastCmd(Device device, String channelId) {  
780 - // 改为新的实现  
781 - return false;  
782 - }  
783 -  
784 - /**  
785 - * 语音广播  
786 - *  
787 - * @param device 视频设备  
788 - */  
789 - @Override  
790 - public boolean audioBroadcastCmd(Device device) {  
791 - try {  
792 - StringBuffer broadcastXml = new StringBuffer(200);  
793 - String charset = device.getCharset();  
794 - broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
795 - broadcastXml.append("<Notify>\r\n");  
796 - broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");  
797 - broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
798 - broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");  
799 - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");  
800 - broadcastXml.append("</Notify>\r\n");  
801 -  
802 - String tm = Long.toString(System.currentTimeMillis());  
803 -  
804 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
805 - : udpSipProvider.getNewCallId();  
806 -  
807 - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader);  
808 - transmitRequest(device, request);  
809 - return true;  
810 - } catch (SipException | ParseException | InvalidArgumentException e) {  
811 - e.printStackTrace();  
812 - }  
813 - return false;  
814 - }  
815 - @Override  
816 - public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) {  
817 - try {  
818 - StringBuffer broadcastXml = new StringBuffer(200);  
819 - String charset = device.getCharset();  
820 - broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
821 - broadcastXml.append("<Notify>\r\n");  
822 - broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");  
823 - broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
824 - broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");  
825 - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");  
826 - broadcastXml.append("</Notify>\r\n");  
827 -  
828 - String tm = Long.toString(System.currentTimeMillis());  
829 -  
830 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
831 - : udpSipProvider.getNewCallId();  
832 -  
833 - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader);  
834 - transmitRequest(device, request, errorEvent);  
835 - } catch (SipException | ParseException | InvalidArgumentException e) {  
836 - e.printStackTrace();  
837 - }  
838 - }  
839 -  
840 -  
841 - /**  
842 - * 音视频录像控制  
843 - *  
844 - * @param device 视频设备  
845 - * @param channelId 预览通道  
846 - * @param recordCmdStr 录像命令:Record / StopRecord  
847 - */  
848 - @Override  
849 - public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) {  
850 - try {  
851 - StringBuffer cmdXml = new StringBuffer(200);  
852 - String charset = device.getCharset();  
853 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
854 - cmdXml.append("<Control>\r\n");  
855 - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
856 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
857 - if (StringUtils.isEmpty(channelId)) {  
858 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
859 - } else {  
860 - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
861 - }  
862 - cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");  
863 - cmdXml.append("</Control>\r\n");  
864 -  
865 - String tm = Long.toString(System.currentTimeMillis());  
866 -  
867 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
868 - : udpSipProvider.getNewCallId();  
869 -  
870 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null, callIdHeader);  
871 - transmitRequest(device, request, errorEvent);  
872 - return true;  
873 - } catch (SipException | ParseException | InvalidArgumentException e) {  
874 - e.printStackTrace();  
875 - return false;  
876 - }  
877 - }  
878 -  
879 - /**  
880 - * 远程启动控制命令  
881 - *  
882 - * @param device 视频设备  
883 - */  
884 - @Override  
885 - public boolean teleBootCmd(Device device) {  
886 - try {  
887 - StringBuffer cmdXml = new StringBuffer(200);  
888 - String charset = device.getCharset();  
889 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
890 - cmdXml.append("<Control>\r\n");  
891 - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
892 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
893 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
894 - cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");  
895 - cmdXml.append("</Control>\r\n");  
896 -  
897 - String tm = Long.toString(System.currentTimeMillis());  
898 -  
899 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
900 - : udpSipProvider.getNewCallId();  
901 -  
902 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null, callIdHeader);  
903 - transmitRequest(device, request);  
904 - return true;  
905 - } catch (SipException | ParseException | InvalidArgumentException e) {  
906 - e.printStackTrace();  
907 - return false;  
908 - }  
909 - }  
910 -  
911 - /**  
912 - * 报警布防/撤防命令  
913 - *  
914 - * @param device 视频设备  
915 - * @param guardCmdStr "SetGuard"/"ResetGuard"  
916 - */  
917 - @Override  
918 - public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) {  
919 - try {  
920 - StringBuffer cmdXml = new StringBuffer(200);  
921 - String charset = device.getCharset();  
922 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
923 - cmdXml.append("<Control>\r\n");  
924 - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
925 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
926 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
927 - cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");  
928 - cmdXml.append("</Control>\r\n");  
929 -  
930 - String tm = Long.toString(System.currentTimeMillis());  
931 -  
932 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
933 - : udpSipProvider.getNewCallId();  
934 -  
935 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null, callIdHeader);  
936 - transmitRequest(device, request, errorEvent);  
937 - return true;  
938 - } catch (SipException | ParseException | InvalidArgumentException e) {  
939 - e.printStackTrace();  
940 - return false;  
941 - }  
942 - }  
943 -  
944 - /**  
945 - * 报警复位命令  
946 - *  
947 - * @param device 视频设备  
948 - */  
949 - @Override  
950 - public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) {  
951 - try {  
952 - StringBuffer cmdXml = new StringBuffer(200);  
953 - String charset = device.getCharset();  
954 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
955 - cmdXml.append("<Control>\r\n");  
956 - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
957 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
958 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
959 - cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");  
960 - if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) {  
961 - cmdXml.append("<Info>\r\n");  
962 - }  
963 - if (!StringUtils.isEmpty(alarmMethod)) {  
964 - cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");  
965 - }  
966 - if (!StringUtils.isEmpty(alarmType)) {  
967 - cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");  
968 - }  
969 - if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) {  
970 - cmdXml.append("</Info>\r\n");  
971 - }  
972 - cmdXml.append("</Control>\r\n");  
973 -  
974 - String tm = Long.toString(System.currentTimeMillis());  
975 -  
976 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
977 - : udpSipProvider.getNewCallId();  
978 -  
979 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null, callIdHeader);  
980 - transmitRequest(device, request, errorEvent);  
981 - return true;  
982 - } catch (SipException | ParseException | InvalidArgumentException e) {  
983 - e.printStackTrace();  
984 - return false;  
985 - }  
986 - }  
987 -  
988 - /**  
989 - * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧  
990 - *  
991 - * @param device 视频设备  
992 - * @param channelId 预览通道  
993 - */  
994 - @Override  
995 - public boolean iFrameCmd(Device device, String channelId) {  
996 - try {  
997 - StringBuffer cmdXml = new StringBuffer(200);  
998 - String charset = device.getCharset();  
999 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1000 - cmdXml.append("<Control>\r\n");  
1001 - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
1002 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1003 - if (StringUtils.isEmpty(channelId)) {  
1004 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1005 - } else {  
1006 - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
1007 - }  
1008 - cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");  
1009 - cmdXml.append("</Control>\r\n");  
1010 -  
1011 - String tm = Long.toString(System.currentTimeMillis());  
1012 -  
1013 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1014 - : udpSipProvider.getNewCallId();  
1015 -  
1016 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null, callIdHeader);  
1017 - transmitRequest(device, request);  
1018 - return true;  
1019 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1020 - e.printStackTrace();  
1021 - return false;  
1022 - }  
1023 - }  
1024 -  
1025 - /**  
1026 - * 看守位控制命令  
1027 - *  
1028 - * @param device 视频设备  
1029 - * @param enabled 看守位使能:1 = 开启,0 = 关闭  
1030 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)  
1031 - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255  
1032 - */  
1033 - @Override  
1034 - public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) {  
1035 - try {  
1036 - StringBuffer cmdXml = new StringBuffer(200);  
1037 - String charset = device.getCharset();  
1038 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1039 - cmdXml.append("<Control>\r\n");  
1040 - cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
1041 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1042 - if (StringUtils.isEmpty(channelId)) {  
1043 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1044 - } else {  
1045 - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
1046 - }  
1047 - cmdXml.append("<HomePosition>\r\n");  
1048 - if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {  
1049 - cmdXml.append("<Enabled>1</Enabled>\r\n");  
1050 - if (NumericUtil.isInteger(resetTime)) {  
1051 - cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");  
1052 - } else {  
1053 - cmdXml.append("<ResetTime>0</ResetTime>\r\n");  
1054 - }  
1055 - if (NumericUtil.isInteger(presetIndex)) {  
1056 - cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");  
1057 - } else {  
1058 - cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");  
1059 - }  
1060 - } else {  
1061 - cmdXml.append("<Enabled>0</Enabled>\r\n");  
1062 - }  
1063 - cmdXml.append("</HomePosition>\r\n");  
1064 - cmdXml.append("</Control>\r\n");  
1065 -  
1066 - String tm = Long.toString(System.currentTimeMillis());  
1067 -  
1068 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1069 - : udpSipProvider.getNewCallId();  
1070 -  
1071 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null, callIdHeader);  
1072 - transmitRequest(device, request, errorEvent);  
1073 - return true;  
1074 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1075 - e.printStackTrace();  
1076 - return false;  
1077 - }  
1078 - }  
1079 -  
1080 - /**  
1081 - * 设备配置命令  
1082 - *  
1083 - * @param device 视频设备  
1084 - */  
1085 - @Override  
1086 - public boolean deviceConfigCmd(Device device) {  
1087 - // TODO Auto-generated method stub  
1088 - return false;  
1089 - }  
1090 -  
1091 - /**  
1092 - * 设备配置命令:basicParam  
1093 - *  
1094 - * @param device 视频设备  
1095 - * @param channelId 通道编码(可选)  
1096 - * @param name 设备/通道名称(可选)  
1097 - * @param expiration 注册过期时间(可选)  
1098 - * @param heartBeatInterval 心跳间隔时间(可选)  
1099 - * @param heartBeatCount 心跳超时次数(可选)  
1100 - */  
1101 - @Override  
1102 - public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration,  
1103 - String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) {  
1104 - try {  
1105 - StringBuffer cmdXml = new StringBuffer(200);  
1106 - String charset = device.getCharset();  
1107 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1108 - cmdXml.append("<Control>\r\n");  
1109 - cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");  
1110 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1111 - if (StringUtils.isEmpty(channelId)) {  
1112 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1113 - } else {  
1114 - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
1115 - }  
1116 - cmdXml.append("<BasicParam>\r\n");  
1117 - if (!StringUtils.isEmpty(name)) {  
1118 - cmdXml.append("<Name>" + name + "</Name>\r\n");  
1119 - }  
1120 - if (NumericUtil.isInteger(expiration)) {  
1121 - if (Integer.valueOf(expiration) > 0) {  
1122 - cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");  
1123 - }  
1124 - }  
1125 - if (NumericUtil.isInteger(heartBeatInterval)) {  
1126 - if (Integer.valueOf(heartBeatInterval) > 0) {  
1127 - cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");  
1128 - }  
1129 - }  
1130 - if (NumericUtil.isInteger(heartBeatCount)) {  
1131 - if (Integer.valueOf(heartBeatCount) > 0) {  
1132 - cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");  
1133 - }  
1134 - }  
1135 - cmdXml.append("</BasicParam>\r\n");  
1136 - cmdXml.append("</Control>\r\n");  
1137 -  
1138 - String tm = Long.toString(System.currentTimeMillis());  
1139 -  
1140 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1141 - : udpSipProvider.getNewCallId();  
1142 -  
1143 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader);  
1144 - transmitRequest(device, request, errorEvent);  
1145 - return true;  
1146 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1147 - e.printStackTrace();  
1148 - return false;  
1149 - }  
1150 - }  
1151 -  
1152 - /**  
1153 - * 查询设备状态  
1154 - *  
1155 - * @param device 视频设备  
1156 - */  
1157 - @Override  
1158 - public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) {  
1159 - try {  
1160 - String charset = device.getCharset();  
1161 - StringBuffer catalogXml = new StringBuffer(200);  
1162 - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1163 - catalogXml.append("<Query>\r\n");  
1164 - catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");  
1165 - catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1166 - catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1167 - catalogXml.append("</Query>\r\n");  
1168 -  
1169 - String tm = Long.toString(System.currentTimeMillis());  
1170 -  
1171 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1172 - : udpSipProvider.getNewCallId();  
1173 -  
1174 - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null, callIdHeader);  
1175 -  
1176 - transmitRequest(device, request, errorEvent);  
1177 - return true;  
1178 -  
1179 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1180 - e.printStackTrace();  
1181 - return false;  
1182 - }  
1183 - }  
1184 -  
1185 - /**  
1186 - * 查询设备信息  
1187 - *  
1188 - * @param device 视频设备  
1189 - */  
1190 - @Override  
1191 - public boolean deviceInfoQuery(Device device) {  
1192 - try {  
1193 - StringBuffer catalogXml = new StringBuffer(200);  
1194 - String charset = device.getCharset();  
1195 - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1196 - catalogXml.append("<Query>\r\n");  
1197 - catalogXml.append("<CmdType>DeviceInfo</CmdType>\r\n");  
1198 - catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1199 - catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1200 - catalogXml.append("</Query>\r\n");  
1201 -  
1202 - String tm = Long.toString(System.currentTimeMillis());  
1203 -  
1204 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1205 - : udpSipProvider.getNewCallId();  
1206 -  
1207 - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaDeviceInfo-" + tm, "FromDev" + tm, null, callIdHeader);  
1208 -  
1209 - transmitRequest(device, request);  
1210 -  
1211 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1212 - e.printStackTrace();  
1213 - return false;  
1214 - }  
1215 - return true;  
1216 - }  
1217 -  
1218 - /**  
1219 - * 查询目录列表  
1220 - *  
1221 - * @param device 视频设备  
1222 - */  
1223 - @Override  
1224 - public boolean catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) {  
1225 - try {  
1226 - StringBuffer catalogXml = new StringBuffer(200);  
1227 - String charset = device.getCharset();  
1228 - catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1229 - catalogXml.append("<Query>\r\n");  
1230 - catalogXml.append("<CmdType>Catalog</CmdType>\r\n");  
1231 - catalogXml.append("<SN>" + sn + "</SN>\r\n");  
1232 - catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1233 - catalogXml.append("</Query>\r\n");  
1234 -  
1235 - String tm = Long.toString(System.currentTimeMillis());  
1236 -  
1237 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1238 - : udpSipProvider.getNewCallId();  
1239 -  
1240 - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaCatalog-" + tm, "FromCat" + tm, null, callIdHeader);  
1241 -  
1242 - transmitRequest(device, request, errorEvent);  
1243 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1244 - e.printStackTrace();  
1245 - return false;  
1246 - }  
1247 - return true;  
1248 - }  
1249 -  
1250 - /**  
1251 - * 查询录像信息  
1252 - *  
1253 - * @param device 视频设备  
1254 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss  
1255 - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss  
1256 - */  
1257 - @Override  
1258 - public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {  
1259 - if (secrecy == null) {  
1260 - secrecy = 0;  
1261 - }  
1262 - if (type == null) {  
1263 - type = "all";  
1264 - }  
1265 - try {  
1266 - StringBuffer recordInfoXml = new StringBuffer(200);  
1267 - String charset = device.getCharset();  
1268 - recordInfoXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1269 - recordInfoXml.append("<Query>\r\n");  
1270 - recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");  
1271 - recordInfoXml.append("<SN>" + sn + "</SN>\r\n");  
1272 - recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
1273 - if (startTime != null) {  
1274 - recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");  
1275 - }  
1276 - if (endTime != null) {  
1277 - recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");  
1278 - }  
1279 - if (secrecy != null) {  
1280 - recordInfoXml.append("<Secrecy> "+ secrecy + " </Secrecy>\r\n");  
1281 - }  
1282 - if (type != null) {  
1283 - // 大华NVR要求必须增加一个值为all的文本元素节点Type  
1284 - recordInfoXml.append("<Type>" + type+"</Type>\r\n");  
1285 - }  
1286 - recordInfoXml.append("</Query>\r\n");  
1287 -  
1288 - String tm = Long.toString(System.currentTimeMillis());  
1289 -  
1290 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1291 - : udpSipProvider.getNewCallId();  
1292 -  
1293 - Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),  
1294 - "z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader);  
1295 -  
1296 - transmitRequest(device, request, errorEvent, okEvent);  
1297 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1298 - e.printStackTrace();  
1299 - return false;  
1300 - }  
1301 - return true;  
1302 - }  
1303 -  
1304 - /**  
1305 - * 查询报警信息  
1306 - *  
1307 - * @param device 视频设备  
1308 - * @param startPriority 报警起始级别(可选)  
1309 - * @param endPriority 报警终止级别(可选)  
1310 - * @param alarmMethod 报警方式条件(可选)  
1311 - * @param alarmType 报警类型  
1312 - * @param startTime 报警发生起始时间(可选)  
1313 - * @param endTime 报警发生终止时间(可选)  
1314 - * @return true = 命令发送成功  
1315 - */  
1316 - @Override  
1317 - public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,  
1318 - String startTime, String endTime, SipSubscribe.Event errorEvent) {  
1319 - try {  
1320 - StringBuffer cmdXml = new StringBuffer(200);  
1321 - String charset = device.getCharset();  
1322 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1323 - cmdXml.append("<Query>\r\n");  
1324 - cmdXml.append("<CmdType>Alarm</CmdType>\r\n");  
1325 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1326 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1327 - if (!StringUtils.isEmpty(startPriority)) {  
1328 - cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");  
1329 - }  
1330 - if (!StringUtils.isEmpty(endPriority)) {  
1331 - cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");  
1332 - }  
1333 - if (!StringUtils.isEmpty(alarmMethod)) {  
1334 - cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");  
1335 - }  
1336 - if (!StringUtils.isEmpty(alarmType)) {  
1337 - cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");  
1338 - }  
1339 - if (!StringUtils.isEmpty(startTime)) {  
1340 - cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");  
1341 - }  
1342 - if (!StringUtils.isEmpty(endTime)) {  
1343 - cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");  
1344 - }  
1345 - cmdXml.append("</Query>\r\n");  
1346 -  
1347 - String tm = Long.toString(System.currentTimeMillis());  
1348 -  
1349 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1350 - : udpSipProvider.getNewCallId();  
1351 -  
1352 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null, callIdHeader);  
1353 - transmitRequest(device, request, errorEvent);  
1354 - return true;  
1355 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1356 - e.printStackTrace();  
1357 - return false;  
1358 - }  
1359 - }  
1360 -  
1361 - /**  
1362 - * 查询设备配置  
1363 - *  
1364 - * @param device 视频设备  
1365 - * @param channelId 通道编码(可选)  
1366 - * @param configType 配置类型:  
1367 - */  
1368 - @Override  
1369 - public boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) {  
1370 - try {  
1371 - StringBuffer cmdXml = new StringBuffer(200);  
1372 - String charset = device.getCharset();  
1373 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1374 - cmdXml.append("<Query>\r\n");  
1375 - cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");  
1376 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1377 - if (StringUtils.isEmpty(channelId)) {  
1378 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1379 - } else {  
1380 - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
1381 - }  
1382 - cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");  
1383 - cmdXml.append("</Query>\r\n");  
1384 -  
1385 - String tm = Long.toString(System.currentTimeMillis());  
1386 -  
1387 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1388 - : udpSipProvider.getNewCallId();  
1389 -  
1390 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader);  
1391 - transmitRequest(device, request, errorEvent);  
1392 - return true;  
1393 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1394 - e.printStackTrace();  
1395 - return false;  
1396 - }  
1397 - }  
1398 -  
1399 - /**  
1400 - * 查询设备预置位置  
1401 - *  
1402 - * @param device 视频设备  
1403 - */  
1404 - @Override  
1405 - public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) {  
1406 - try {  
1407 - StringBuffer cmdXml = new StringBuffer(200);  
1408 - String charset = device.getCharset();  
1409 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1410 - cmdXml.append("<Query>\r\n");  
1411 - cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");  
1412 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1413 - if (StringUtils.isEmpty(channelId)) {  
1414 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1415 - } else {  
1416 - cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
1417 - }  
1418 - cmdXml.append("</Query>\r\n");  
1419 -  
1420 - String tm = Long.toString(System.currentTimeMillis());  
1421 -  
1422 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1423 - : udpSipProvider.getNewCallId();  
1424 -  
1425 - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader);  
1426 - transmitRequest(device, request, errorEvent);  
1427 - return true;  
1428 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1429 - e.printStackTrace();  
1430 - return false;  
1431 - }  
1432 - }  
1433 -  
1434 - /**  
1435 - * 查询移动设备位置数据  
1436 - *  
1437 - * @param device 视频设备  
1438 - */  
1439 - @Override  
1440 - public boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) {  
1441 - try {  
1442 - StringBuffer mobilePostitionXml = new StringBuffer(200);  
1443 - String charset = device.getCharset();  
1444 - mobilePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1445 - mobilePostitionXml.append("<Query>\r\n");  
1446 - mobilePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");  
1447 - mobilePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1448 - mobilePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1449 - mobilePostitionXml.append("<Interval>60</Interval>\r\n");  
1450 - mobilePostitionXml.append("</Query>\r\n");  
1451 -  
1452 - String tm = Long.toString(System.currentTimeMillis());  
1453 -  
1454 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1455 - : udpSipProvider.getNewCallId();  
1456 -  
1457 - Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, callIdHeader);  
1458 -  
1459 - transmitRequest(device, request, errorEvent);  
1460 -  
1461 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1462 - e.printStackTrace();  
1463 - return false;  
1464 - }  
1465 - return true;  
1466 - }  
1467 -  
1468 - /**  
1469 - * 订阅、取消订阅移动位置  
1470 - *  
1471 - * @param device 视频设备  
1472 - * @return true = 命令发送成功  
1473 - */  
1474 - @Override  
1475 - public boolean mobilePositionSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) {  
1476 - try {  
1477 - StringBuffer subscribePostitionXml = new StringBuffer(200);  
1478 - String charset = device.getCharset();  
1479 - subscribePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1480 - subscribePostitionXml.append("<Query>\r\n");  
1481 - subscribePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");  
1482 - subscribePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1483 - subscribePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1484 - if (device.getSubscribeCycleForMobilePosition() > 0) {  
1485 - subscribePostitionXml.append("<Interval>" + String.valueOf(device.getMobilePositionSubmissionInterval()) + "</Interval>\r\n");  
1486 - }  
1487 - subscribePostitionXml.append("</Query>\r\n");  
1488 -  
1489 - Request request;  
1490 - if (dialog != null) {  
1491 - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());  
1492 - request = dialog.createRequest(Request.SUBSCRIBE);  
1493 - ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());  
1494 - request.setExpires(expiresHeader);  
1495 -  
1496 - request.setRequestURI(requestURI);  
1497 -  
1498 - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");  
1499 - request.setContent(subscribePostitionXml.toString(), contentTypeHeader);  
1500 -  
1501 - CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);  
1502 - cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));  
1503 - request.removeHeader(CSeqHeader.NAME);  
1504 - request.addHeader(cSeqHeader);  
1505 - }else {  
1506 - String tm = Long.toString(System.currentTimeMillis());  
1507 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1508 - : udpSipProvider.getNewCallId();  
1509 - request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, device.getSubscribeCycleForMobilePosition(), "presence" ,callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));  
1510 - }  
1511 - transmitRequest(device, request, errorEvent, okEvent);  
1512 -  
1513 - return true;  
1514 -  
1515 - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) {  
1516 - e.printStackTrace();  
1517 - return false;  
1518 - }  
1519 - }  
1520 -  
1521 - /**  
1522 - * 订阅、取消订阅报警信息  
1523 - *  
1524 - * @param device 视频设备  
1525 - * @param expires 订阅过期时间(0 = 取消订阅)  
1526 - * @param startPriority 报警起始级别(可选)  
1527 - * @param endPriority 报警终止级别(可选)  
1528 - * @param alarmMethod 报警方式条件(可选)  
1529 - * @param alarmType 报警类型  
1530 - * @param startTime 报警发生起始时间(可选)  
1531 - * @param endTime 报警发生终止时间(可选)  
1532 - * @return true = 命令发送成功  
1533 - */  
1534 - @Override  
1535 - public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) {  
1536 - try {  
1537 - StringBuffer cmdXml = new StringBuffer(200);  
1538 - String charset = device.getCharset();  
1539 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1540 - cmdXml.append("<Query>\r\n");  
1541 - cmdXml.append("<CmdType>Alarm</CmdType>\r\n");  
1542 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1543 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1544 - if (!StringUtils.isEmpty(startPriority)) {  
1545 - cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");  
1546 - }  
1547 - if (!StringUtils.isEmpty(endPriority)) {  
1548 - cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");  
1549 - }  
1550 - if (!StringUtils.isEmpty(alarmMethod)) {  
1551 - cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");  
1552 - }  
1553 - if (!StringUtils.isEmpty(alarmType)) {  
1554 - cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");  
1555 - }  
1556 - if (!StringUtils.isEmpty(startTime)) {  
1557 - cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");  
1558 - }  
1559 - if (!StringUtils.isEmpty(endTime)) {  
1560 - cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");  
1561 - }  
1562 - cmdXml.append("</Query>\r\n");  
1563 -  
1564 - String tm = Long.toString(System.currentTimeMillis());  
1565 -  
1566 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1567 - : udpSipProvider.getNewCallId();  
1568 -  
1569 - Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, expires, "presence" , callIdHeader);  
1570 - transmitRequest(device, request);  
1571 -  
1572 - return true;  
1573 -  
1574 - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) {  
1575 - e.printStackTrace();  
1576 - return false;  
1577 - }  
1578 - }  
1579 -  
1580 - @Override  
1581 - public boolean catalogSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {  
1582 - try {  
1583 - StringBuffer cmdXml = new StringBuffer(200);  
1584 - String charset = device.getCharset();  
1585 - cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1586 - cmdXml.append("<Query>\r\n");  
1587 - cmdXml.append("<CmdType>Catalog</CmdType>\r\n");  
1588 - cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1589 - cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1590 - cmdXml.append("</Query>\r\n");  
1591 -  
1592 -  
1593 - Request request;  
1594 - if (dialog != null) {  
1595 - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());  
1596 - request = dialog.createRequest(Request.SUBSCRIBE);  
1597 - ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());  
1598 - request.setExpires(expiresHeader);  
1599 -  
1600 - request.setRequestURI(requestURI);  
1601 -  
1602 - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");  
1603 - request.setContent(cmdXml.toString(), contentTypeHeader);  
1604 -  
1605 - CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);  
1606 - cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));  
1607 - request.removeHeader(CSeqHeader.NAME);  
1608 - request.addHeader(cSeqHeader);  
1609 -  
1610 - }else {  
1611 - String tm = Long.toString(System.currentTimeMillis());  
1612 -  
1613 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1614 - : udpSipProvider.getNewCallId();  
1615 -  
1616 - // 有效时间默认为60秒以上  
1617 - request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm,  
1618 - "fromTagPos" + tm, null, device.getSubscribeCycleForCatalog(), "Catalog" ,  
1619 - callIdHeader);  
1620 -  
1621 - }  
1622 - transmitRequest(device, request, errorEvent, okEvent);  
1623 - return true;  
1624 -  
1625 - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) {  
1626 - e.printStackTrace();  
1627 - return false;  
1628 - }  
1629 - }  
1630 -  
1631 - @Override  
1632 - public boolean dragZoomCmd(Device device, String channelId, String cmdString) {  
1633 - try {  
1634 - StringBuffer dragXml = new StringBuffer(200);  
1635 - String charset = device.getCharset();  
1636 - dragXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");  
1637 - dragXml.append("<Control>\r\n");  
1638 - dragXml.append("<CmdType>DeviceControl</CmdType>\r\n");  
1639 - dragXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");  
1640 - if (StringUtils.isEmpty(channelId)) {  
1641 - dragXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");  
1642 - } else {  
1643 - dragXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");  
1644 - }  
1645 - dragXml.append(cmdString);  
1646 - dragXml.append("</Control>\r\n");  
1647 - String tm = Long.toString(System.currentTimeMillis());  
1648 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1649 - : udpSipProvider.getNewCallId();  
1650 - Request request = headerProvider.createMessageRequest(device, dragXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);  
1651 - logger.debug("拉框信令: " + request.toString());  
1652 - transmitRequest(device, request);  
1653 - return true;  
1654 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1655 - e.printStackTrace();  
1656 - }  
1657 - return false;  
1658 - }  
1659 -  
1660 -  
1661 - private ClientTransaction transmitRequest(Device device, Request request) throws SipException {  
1662 - return transmitRequest(device, request, null, null);  
1663 - }  
1664 -  
1665 - private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent) throws SipException {  
1666 - return transmitRequest(device, request, errorEvent, null);  
1667 - }  
1668 -  
1669 - private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException {  
1670 - ClientTransaction clientTransaction = null;  
1671 - if("TCP".equals(device.getTransport())) {  
1672 - clientTransaction = tcpSipProvider.getNewClientTransaction(request);  
1673 - } else if("UDP".equals(device.getTransport())) {  
1674 - clientTransaction = udpSipProvider.getNewClientTransaction(request);  
1675 - }  
1676 - if (request.getHeader(UserAgentHeader.NAME) == null) {  
1677 - List<String> agentParam = new ArrayList<>();  
1678 - agentParam.add("wvp-pro");  
1679 - // TODO 添加版本信息以及日期  
1680 - UserAgentHeader userAgentHeader = null;  
1681 - try {  
1682 - userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);  
1683 - } catch (ParseException e) {  
1684 - throw new RuntimeException(e);  
1685 - }  
1686 - request.addHeader(userAgentHeader);  
1687 - }  
1688 - CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);  
1689 - // 添加错误订阅  
1690 - if (errorEvent != null) {  
1691 - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {  
1692 - errorEvent.response(eventResult);  
1693 - sipSubscribe.removeErrorSubscribe(eventResult.callId);  
1694 - sipSubscribe.removeOkSubscribe(eventResult.callId);  
1695 - }));  
1696 - }  
1697 - // 添加订阅  
1698 - if (okEvent != null) {  
1699 - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult ->{  
1700 - okEvent.response(eventResult);  
1701 - sipSubscribe.removeOkSubscribe(eventResult.callId);  
1702 - sipSubscribe.removeErrorSubscribe(eventResult.callId);  
1703 - });  
1704 - }  
1705 -  
1706 - clientTransaction.sendRequest();  
1707 - return clientTransaction;  
1708 - }  
1709 -  
1710 - /**  
1711 - * 回放暂停  
1712 - */  
1713 - @Override  
1714 - public void playPauseCmd(Device device, StreamInfo streamInfo) {  
1715 - try {  
1716 - Long cseq = redisCatchStorage.getCSEQ(Request.INFO);  
1717 - StringBuffer content = new StringBuffer(200);  
1718 - content.append("PAUSE RTSP/1.0\r\n");  
1719 - content.append("CSeq: " + cseq + "\r\n");  
1720 - content.append("PauseTime: now\r\n");  
1721 - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());  
1722 - if (request == null) {  
1723 - return;  
1724 - }  
1725 - logger.info(request.toString());  
1726 - ClientTransaction clientTransaction = null;  
1727 - if ("TCP".equals(device.getTransport())) {  
1728 - clientTransaction = tcpSipProvider.getNewClientTransaction(request);  
1729 - } else if ("UDP".equals(device.getTransport())) {  
1730 - clientTransaction = udpSipProvider.getNewClientTransaction(request);  
1731 - }  
1732 - if (clientTransaction != null) {  
1733 - clientTransaction.sendRequest();  
1734 - }  
1735 -  
1736 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1737 - e.printStackTrace();  
1738 - }  
1739 - }  
1740 -  
1741 - /**  
1742 - * 回放恢复  
1743 - */  
1744 - @Override  
1745 - public void playResumeCmd(Device device, StreamInfo streamInfo) {  
1746 - try {  
1747 - Long cseq = redisCatchStorage.getCSEQ(Request.INFO);  
1748 - StringBuffer content = new StringBuffer(200);  
1749 - content.append("PLAY RTSP/1.0\r\n");  
1750 - content.append("CSeq: " + cseq + "\r\n");  
1751 - content.append("Range: npt=now-\r\n");  
1752 - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());  
1753 - if (request == null) {  
1754 - return;  
1755 - }  
1756 - logger.info(request.toString());  
1757 - ClientTransaction clientTransaction = null;  
1758 - if ("TCP".equals(device.getTransport())) {  
1759 - clientTransaction = tcpSipProvider.getNewClientTransaction(request);  
1760 - } else if ("UDP".equals(device.getTransport())) {  
1761 - clientTransaction = udpSipProvider.getNewClientTransaction(request);  
1762 - }  
1763 -  
1764 - clientTransaction.sendRequest();  
1765 -  
1766 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1767 - e.printStackTrace();  
1768 - }  
1769 - }  
1770 -  
1771 - /**  
1772 - * 回放拖动播放  
1773 - */  
1774 - @Override  
1775 - public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) {  
1776 - try {  
1777 - Long cseq = redisCatchStorage.getCSEQ(Request.INFO);  
1778 - StringBuffer content = new StringBuffer(200);  
1779 - content.append("PLAY RTSP/1.0\r\n");  
1780 - content.append("CSeq: " + cseq + "\r\n");  
1781 - content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");  
1782 -  
1783 - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());  
1784 - if (request == null) {  
1785 - return;  
1786 - }  
1787 - logger.info(request.toString());  
1788 - ClientTransaction clientTransaction = null;  
1789 - if ("TCP".equals(device.getTransport())) {  
1790 - clientTransaction = tcpSipProvider.getNewClientTransaction(request);  
1791 - } else if ("UDP".equals(device.getTransport())) {  
1792 - clientTransaction = udpSipProvider.getNewClientTransaction(request);  
1793 - }  
1794 -  
1795 - clientTransaction.sendRequest();  
1796 -  
1797 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1798 - e.printStackTrace();  
1799 - }  
1800 - }  
1801 -  
1802 - /**  
1803 - * 回放倍速播放  
1804 - */  
1805 - @Override  
1806 - public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) {  
1807 - try {  
1808 - Long cseq = redisCatchStorage.getCSEQ(Request.INFO);  
1809 - StringBuffer content = new StringBuffer(200);  
1810 - content.append("PLAY RTSP/1.0\r\n");  
1811 - content.append("CSeq: " + cseq + "\r\n");  
1812 - content.append("Scale: " + String.format("%.1f",speed) + "\r\n");  
1813 - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());  
1814 - if (request == null) {  
1815 - return;  
1816 - }  
1817 - logger.info(request.toString());  
1818 - ClientTransaction clientTransaction = null;  
1819 - if ("TCP".equals(device.getTransport())) {  
1820 - clientTransaction = tcpSipProvider.getNewClientTransaction(request);  
1821 - } else if ("UDP".equals(device.getTransport())) {  
1822 - clientTransaction = udpSipProvider.getNewClientTransaction(request);  
1823 - }  
1824 -  
1825 - clientTransaction.sendRequest();  
1826 -  
1827 - } catch (SipException | ParseException | InvalidArgumentException e) {  
1828 - e.printStackTrace();  
1829 - }  
1830 - }  
1831 -  
1832 - @Override  
1833 - public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {  
1834 - if (device == null) {  
1835 - return false;  
1836 - }  
1837 - logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),  
1838 - deviceAlarm.getLongitude(), deviceAlarm.getLatitude());  
1839 - try {  
1840 - String characterSet = device.getCharset();  
1841 - StringBuffer deviceStatusXml = new StringBuffer(600);  
1842 - deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");  
1843 - deviceStatusXml.append("<Notify>\r\n");  
1844 - deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");  
1845 - deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");  
1846 - deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");  
1847 - deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");  
1848 - deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");  
1849 - deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");  
1850 - deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");  
1851 - deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");  
1852 - deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");  
1853 - deviceStatusXml.append("<info>\r\n");  
1854 - deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");  
1855 - deviceStatusXml.append("</info>\r\n");  
1856 - deviceStatusXml.append("</Notify>\r\n");  
1857 -  
1858 - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()  
1859 - : udpSipProvider.getNewCallId();  
1860 - String tm = Long.toString(System.currentTimeMillis());  
1861 - Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);  
1862 - transmitRequest(device, request);  
1863 -  
1864 -  
1865 - } catch (SipException | ParseException e) {  
1866 - e.printStackTrace();  
1867 - return false;  
1868 - } catch (InvalidArgumentException e) {  
1869 - throw new RuntimeException(e);  
1870 - }  
1871 - return true;  
1872 - }  
1873 -  
1874 - private void sendNotify(Device device, String catalogXmlContent,  
1875 - SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent )  
1876 - throws NoSuchFieldException, IllegalAccessException, SipException, ParseException {  
1877 - MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();  
1878 - String characterSet = device.getCharset();  
1879 - // 设置编码, 防止中文乱码  
1880 - messageFactory.setDefaultContentEncodingCharset(characterSet);  
1881 - Dialog dialog = subscribeInfo.getDialog();  
1882 - if (dialog == null || !dialog.getState().equals(DialogState.CONFIRMED)) {  
1883 - return;  
1884 - }  
1885 - SIPRequest notifyRequest = (SIPRequest)dialog.createRequest(Request.NOTIFY);  
1886 - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");  
1887 - notifyRequest.setContent(catalogXmlContent, contentTypeHeader);  
1888 -  
1889 - SubscriptionStateHeader subscriptionState = sipFactory.createHeaderFactory()  
1890 - .createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE);  
1891 - notifyRequest.addHeader(subscriptionState);  
1892 -  
1893 - EventHeader event = sipFactory.createHeaderFactory().createEventHeader(subscribeInfo.getEventType());  
1894 - if (subscribeInfo.getEventId() != null) {  
1895 - event.setEventId(subscribeInfo.getEventId());  
1896 - }  
1897 - notifyRequest.addHeader(event);  
1898 -  
1899 - SipURI sipURI = (SipURI) notifyRequest.getRequestURI();  
1900 - if (subscribeInfo.getTransaction() != null) {  
1901 - SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();  
1902 - sipURI.setHost(request.getRemoteAddress().getHostAddress());  
1903 - sipURI.setPort(request.getRemotePort());  
1904 - }else {  
1905 - sipURI.setHost(device.getIp());  
1906 - sipURI.setPort(device.getPort());  
1907 - }  
1908 -  
1909 - ClientTransaction transaction = null;  
1910 - if ("TCP".equals(device.getTransport())) {  
1911 - transaction = tcpSipProvider.getNewClientTransaction(notifyRequest);  
1912 - } else if ("UDP".equals(device.getTransport())) {  
1913 - transaction = udpSipProvider.getNewClientTransaction(notifyRequest);  
1914 - }  
1915 - // 添加错误订阅  
1916 - if (errorEvent != null) {  
1917 - sipSubscribe.addErrorSubscribe(subscribeInfo.getCallId(), errorEvent);  
1918 - }  
1919 - // 添加订阅  
1920 - if (okEvent != null) {  
1921 - sipSubscribe.addOkSubscribe(subscribeInfo.getCallId(), okEvent);  
1922 - }  
1923 - if (transaction == null) {  
1924 - logger.error("平台{}的Transport错误:{}",device.getDeviceId(), device.getTransport());  
1925 - return;  
1926 - }  
1927 - dialog.sendRequest(transaction);  
1928 -  
1929 - }  
1930 -} 1 +package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.conf.DynamicTask;
  6 +import com.genersoft.iot.vmp.conf.SipConfig;
  7 +import com.genersoft.iot.vmp.conf.UserSetting;
  8 +import com.genersoft.iot.vmp.gb28181.bean.*;
  9 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  10 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
  12 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
  13 +import com.genersoft.iot.vmp.utils.DateUtil;
  14 +import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
  15 +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
  16 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  17 +import com.genersoft.iot.vmp.service.IMediaServerService;
  18 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  19 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  20 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  21 +import gov.nist.javax.sip.SipProviderImpl;
  22 +import gov.nist.javax.sip.SipStackImpl;
  23 +import gov.nist.javax.sip.message.MessageFactoryImpl;
  24 +import gov.nist.javax.sip.message.SIPRequest;
  25 +import gov.nist.javax.sip.stack.SIPDialog;
  26 +import org.slf4j.Logger;
  27 +import org.slf4j.LoggerFactory;
  28 +import org.springframework.beans.factory.annotation.Autowired;
  29 +import org.springframework.beans.factory.annotation.Qualifier;
  30 +import org.springframework.context.annotation.DependsOn;
  31 +import org.springframework.stereotype.Component;
  32 +import org.springframework.util.StringUtils;
  33 +
  34 +import javax.sip.*;
  35 +import javax.sip.address.Address;
  36 +import javax.sip.address.SipURI;
  37 +import javax.sip.address.URI;
  38 +import javax.sip.header.*;
  39 +import javax.sip.message.Request;
  40 +import java.lang.reflect.Field;
  41 +import java.text.ParseException;
  42 +import java.util.ArrayList;
  43 +import java.util.HashSet;
  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 SipFactory sipFactory;
  62 +
  63 + @Autowired
  64 + @Qualifier(value="tcpSipProvider")
  65 + private SipProviderImpl tcpSipProvider;
  66 +
  67 + @Autowired
  68 + @Qualifier(value="udpSipProvider")
  69 + private SipProviderImpl udpSipProvider;
  70 +
  71 + @Autowired
  72 + private SIPRequestHeaderProvider headerProvider;
  73 +
  74 + @Autowired
  75 + private VideoStreamSessionManager streamSession;
  76 +
  77 + @Autowired
  78 + private IVideoManagerStorage storager;
  79 +
  80 + @Autowired
  81 + private IRedisCatchStorage redisCatchStorage;
  82 +
  83 + @Autowired
  84 + private UserSetting userSetting;
  85 +
  86 + @Autowired
  87 + private ZLMHttpHookSubscribe subscribe;
  88 +
  89 + @Autowired
  90 + private SipSubscribe sipSubscribe;
  91 +
  92 + @Autowired
  93 + private IMediaServerService mediaServerService;
  94 +
  95 + @Autowired
  96 + private DynamicTask dynamicTask;
  97 +
  98 +
  99 + /**
  100 + * 云台方向放控制,使用配置文件中的默认镜头移动速度
  101 + *
  102 + * @param device 控制设备
  103 + * @param channelId 预览通道
  104 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  105 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  106 + */
  107 + @Override
  108 + public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) {
  109 + return ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0);
  110 + }
  111 +
  112 + /**
  113 + * 云台方向放控制
  114 + *
  115 + * @param device 控制设备
  116 + * @param channelId 预览通道
  117 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  118 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  119 + * @param moveSpeed 镜头移动速度
  120 + */
  121 + @Override
  122 + public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) {
  123 + return ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0);
  124 + }
  125 +
  126 + /**
  127 + * 云台缩放控制,使用配置文件中的默认镜头缩放速度
  128 + *
  129 + * @param device 控制设备
  130 + * @param channelId 预览通道
  131 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  132 + */
  133 + @Override
  134 + public boolean ptzZoomCmd(Device device, String channelId, int inOut) {
  135 + return ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed());
  136 + }
  137 +
  138 + /**
  139 + * 云台缩放控制
  140 + *
  141 + * @param device 控制设备
  142 + * @param channelId 预览通道
  143 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  144 + * @param zoomSpeed 镜头缩放速度
  145 + */
  146 + @Override
  147 + public boolean ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) {
  148 + return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed);
  149 + }
  150 +
  151 + /**
  152 + * 云台指令码计算
  153 + *
  154 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  155 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  156 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  157 + * @param moveSpeed 镜头移动速度 默认 0XFF (0-255)
  158 + * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255)
  159 + */
  160 + public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) {
  161 + int cmdCode = 0;
  162 + if (leftRight == 2) {
  163 + cmdCode|=0x01; // 右移
  164 + } else if(leftRight == 1) {
  165 + cmdCode|=0x02; // 左移
  166 + }
  167 + if (upDown == 2) {
  168 + cmdCode|=0x04; // 下移
  169 + } else if(upDown == 1) {
  170 + cmdCode|=0x08; // 上移
  171 + }
  172 + if (inOut == 2) {
  173 + cmdCode |= 0x10; // 放大
  174 + } else if(inOut == 1) {
  175 + cmdCode |= 0x20; // 缩小
  176 + }
  177 + StringBuilder builder = new StringBuilder("A50F01");
  178 + String strTmp;
  179 + strTmp = String.format("%02X", cmdCode);
  180 + builder.append(strTmp, 0, 2);
  181 + strTmp = String.format("%02X", moveSpeed);
  182 + builder.append(strTmp, 0, 2);
  183 + builder.append(strTmp, 0, 2);
  184 + strTmp = String.format("%X", zoomSpeed);
  185 + builder.append(strTmp, 0, 1).append("0");
  186 + //计算校验码
  187 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100;
  188 + strTmp = String.format("%02X", checkCode);
  189 + builder.append(strTmp, 0, 2);
  190 + return builder.toString();
  191 +}
  192 +
  193 + /**
  194 + * 云台指令码计算
  195 + *
  196 + * @param cmdCode 指令码
  197 + * @param parameter1 数据1
  198 + * @param parameter2 数据2
  199 + * @param combineCode2 组合码2
  200 + */
  201 + public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) {
  202 + StringBuilder builder = new StringBuilder("A50F01");
  203 + String strTmp;
  204 + strTmp = String.format("%02X", cmdCode);
  205 + builder.append(strTmp, 0, 2);
  206 + strTmp = String.format("%02X", parameter1);
  207 + builder.append(strTmp, 0, 2);
  208 + strTmp = String.format("%02X", parameter2);
  209 + builder.append(strTmp, 0, 2);
  210 + strTmp = String.format("%X", combineCode2);
  211 + builder.append(strTmp, 0, 1).append("0");
  212 + //计算校验码
  213 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100;
  214 + strTmp = String.format("%02X", checkCode);
  215 + builder.append(strTmp, 0, 2);
  216 + return builder.toString();
  217 + }
  218 +
  219 + /**
  220 + * 云台控制,支持方向与缩放控制
  221 + *
  222 + * @param device 控制设备
  223 + * @param channelId 预览通道
  224 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移
  225 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移
  226 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大
  227 + * @param moveSpeed 镜头移动速度
  228 + * @param zoomSpeed 镜头缩放速度
  229 + */
  230 + @Override
  231 + public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
  232 + int zoomSpeed) {
  233 + try {
  234 + String cmdStr= cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
  235 + StringBuffer ptzXml = new StringBuffer(200);
  236 + String charset = device.getCharset();
  237 + ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  238 + ptzXml.append("<Control>\r\n");
  239 + ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  240 + ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  241 + ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  242 + ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
  243 + ptzXml.append("<Info>\r\n");
  244 + ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
  245 + ptzXml.append("</Info>\r\n");
  246 + ptzXml.append("</Control>\r\n");
  247 +
  248 + String tm = Long.toString(System.currentTimeMillis());
  249 +
  250 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  251 + : udpSipProvider.getNewCallId();
  252 +
  253 + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);
  254 +
  255 + transmitRequest(device, request);
  256 + return true;
  257 + } catch (SipException | ParseException | InvalidArgumentException e) {
  258 + e.printStackTrace();
  259 + }
  260 + return false;
  261 + }
  262 +
  263 + /**
  264 + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
  265 + *
  266 + * @param device 控制设备
  267 + * @param channelId 预览通道
  268 + * @param cmdCode 指令码
  269 + * @param parameter1 数据1
  270 + * @param parameter2 数据2
  271 + * @param combineCode2 组合码2
  272 + */
  273 + @Override
  274 + public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) {
  275 + try {
  276 + String cmdStr= frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2);
  277 + logger.debug("控制字符串:" + cmdStr);
  278 + StringBuffer ptzXml = new StringBuffer(200);
  279 + String charset = device.getCharset();
  280 + ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  281 + ptzXml.append("<Control>\r\n");
  282 + ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  283 + ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  284 + ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  285 + ptzXml.append("<PTZCmd>" + cmdStr + "</PTZCmd>\r\n");
  286 + ptzXml.append("<Info>\r\n");
  287 + ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
  288 + ptzXml.append("</Info>\r\n");
  289 + ptzXml.append("</Control>\r\n");
  290 +
  291 + String tm = Long.toString(System.currentTimeMillis());
  292 +
  293 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  294 + : udpSipProvider.getNewCallId();
  295 +
  296 + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);
  297 + transmitRequest(device, request);
  298 + return true;
  299 + } catch (SipException | ParseException | InvalidArgumentException e) {
  300 + e.printStackTrace();
  301 + }
  302 + return false;
  303 + }
  304 +
  305 + /**
  306 + * 前端控制指令(用于转发上级指令)
  307 + * @param device 控制设备
  308 + * @param channelId 预览通道
  309 + * @param cmdString 前端控制指令串
  310 + */
  311 + @Override
  312 + public boolean fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) {
  313 + try {
  314 + StringBuffer ptzXml = new StringBuffer(200);
  315 + String charset = device.getCharset();
  316 + ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  317 + ptzXml.append("<Control>\r\n");
  318 + ptzXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  319 + ptzXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  320 + ptzXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  321 + ptzXml.append("<PTZCmd>" + cmdString + "</PTZCmd>\r\n");
  322 + ptzXml.append("<Info>\r\n");
  323 + ptzXml.append("<ControlPriority>5</ControlPriority>\r\n");
  324 + ptzXml.append("</Info>\r\n");
  325 + ptzXml.append("</Control>\r\n");
  326 +
  327 + String tm = Long.toString(System.currentTimeMillis());
  328 +
  329 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  330 + : udpSipProvider.getNewCallId();
  331 +
  332 + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);
  333 + transmitRequest(device, request, errorEvent, okEvent);
  334 + return true;
  335 + } catch (SipException | ParseException | InvalidArgumentException e) {
  336 + e.printStackTrace();
  337 + }
  338 + return false;
  339 + }
  340 +
  341 + /**
  342 + * 请求预览视频流
  343 + * @param device 视频设备
  344 + * @param channelId 预览通道
  345 + * @param event hook订阅
  346 + * @param errorEvent sip错误订阅
  347 + */
  348 + @Override
  349 + public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  350 + ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
  351 + String streamId = ssrcInfo.getStream();
  352 + try {
  353 + if (device == null) {
  354 + return;
  355 + }
  356 + String streamMode = device.getStreamMode().toUpperCase();
  357 +
  358 + logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  359 + // 添加订阅
  360 + JSONObject subscribeKey = new JSONObject();
  361 + subscribeKey.put("app", "rtp");
  362 + subscribeKey.put("stream", streamId);
  363 + subscribeKey.put("regist", true);
  364 + subscribeKey.put("schema", "rtmp");
  365 + subscribeKey.put("mediaServerId", mediaServerItem.getId());
  366 + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
  367 + (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  368 + if (event != null) {
  369 + event.response(mediaServerItemInUse, json);
  370 + }
  371 + });
  372 + //
  373 + StringBuffer content = new StringBuffer(200);
  374 + content.append("v=0\r\n");
  375 + content.append("o="+ channelId+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
  376 + content.append("s=Play\r\n");
  377 + content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
  378 + content.append("t=0 0\r\n");
  379 +
  380 + if (userSetting.isSeniorSdp()) {
  381 + if("TCP-PASSIVE".equals(streamMode)) {
  382 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  383 + }else if ("TCP-ACTIVE".equals(streamMode)) {
  384 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  385 + }else if("UDP".equals(streamMode)) {
  386 + content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
  387 + }
  388 + content.append("a=recvonly\r\n");
  389 + content.append("a=rtpmap:96 PS/90000\r\n");
  390 + content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
  391 + content.append("a=rtpmap:126 H264/90000\r\n");
  392 + content.append("a=rtpmap:125 H264S/90000\r\n");
  393 + content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
  394 + content.append("a=rtpmap:99 H265/90000\r\n");
  395 + content.append("a=rtpmap:98 H264/90000\r\n");
  396 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  397 + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
  398 + content.append("a=setup:passive\r\n");
  399 + content.append("a=connection:new\r\n");
  400 + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  401 + content.append("a=setup:active\r\n");
  402 + content.append("a=connection:new\r\n");
  403 + }
  404 + }else {
  405 + if("TCP-PASSIVE".equals(streamMode)) {
  406 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
  407 + }else if ("TCP-ACTIVE".equals(streamMode)) {
  408 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
  409 + }else if("UDP".equals(streamMode)) {
  410 + content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
  411 + }
  412 + content.append("a=recvonly\r\n");
  413 + content.append("a=rtpmap:96 PS/90000\r\n");
  414 + content.append("a=rtpmap:98 H264/90000\r\n");
  415 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  416 + content.append("a=rtpmap:99 H265/90000\r\n");
  417 + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
  418 + content.append("a=setup:passive\r\n");
  419 + content.append("a=connection:new\r\n");
  420 + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  421 + content.append("a=setup:active\r\n");
  422 + content.append("a=connection:new\r\n");
  423 + }
  424 + }
  425 +
  426 + content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc
  427 + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
  428 +// content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备
  429 +
  430 + String tm = Long.toString(System.currentTimeMillis());
  431 +
  432 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  433 + : udpSipProvider.getNewCallId();
  434 +
  435 + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrcInfo.getSsrc(), callIdHeader);
  436 +
  437 + transmitRequest(device, request, (e -> {
  438 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  439 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  440 + errorEvent.response(e);
  441 + }), e ->{
  442 + // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
  443 + streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
  444 + streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);
  445 + okEvent.response(e);
  446 + });
  447 +
  448 +
  449 + } catch ( SipException | ParseException | InvalidArgumentException e) {
  450 + e.printStackTrace();
  451 + }
  452 + }
  453 +
  454 + /**
  455 + * 请求回放视频流
  456 + *
  457 + * @param device 视频设备
  458 + * @param channelId 预览通道
  459 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  460 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  461 + */
  462 + @Override
  463 + public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  464 + String startTime, String endTime, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  465 + SipSubscribe.Event errorEvent) {
  466 + try {
  467 +
  468 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  469 +
  470 + StringBuffer content = new StringBuffer(200);
  471 + content.append("v=0\r\n");
  472 + content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
  473 + content.append("s=Playback\r\n");
  474 + content.append("u="+channelId+":0\r\n");
  475 + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  476 + content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "
  477 + +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
  478 +
  479 + String streamMode = device.getStreamMode().toUpperCase();
  480 +
  481 + if (userSetting.isSeniorSdp()) {
  482 + if("TCP-PASSIVE".equals(streamMode)) {
  483 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  484 + }else if ("TCP-ACTIVE".equals(streamMode)) {
  485 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  486 + }else if("UDP".equals(streamMode)) {
  487 + content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
  488 + }
  489 + content.append("a=recvonly\r\n");
  490 + content.append("a=rtpmap:96 PS/90000\r\n");
  491 + content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
  492 + content.append("a=rtpmap:126 H264/90000\r\n");
  493 + content.append("a=rtpmap:125 H264S/90000\r\n");
  494 + content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
  495 + content.append("a=rtpmap:99 H265/90000\r\n");
  496 + content.append("a=rtpmap:98 H264/90000\r\n");
  497 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  498 + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
  499 + content.append("a=setup:passive\r\n");
  500 + content.append("a=connection:new\r\n");
  501 + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  502 + content.append("a=setup:active\r\n");
  503 + content.append("a=connection:new\r\n");
  504 + }
  505 + }else {
  506 + if("TCP-PASSIVE".equals(streamMode)) {
  507 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
  508 + }else if ("TCP-ACTIVE".equals(streamMode)) {
  509 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
  510 + }else if("UDP".equals(streamMode)) {
  511 + content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
  512 + }
  513 + content.append("a=recvonly\r\n");
  514 + content.append("a=rtpmap:96 PS/90000\r\n");
  515 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  516 + content.append("a=rtpmap:98 H264/90000\r\n");
  517 + content.append("a=rtpmap:99 H265/90000\r\n");
  518 + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
  519 + content.append("a=setup:passive\r\n");
  520 + content.append("a=connection:new\r\n");
  521 + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  522 + content.append("a=setup:active\r\n");
  523 + content.append("a=connection:new\r\n");
  524 + }
  525 + }
  526 +
  527 + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
  528 +
  529 + String tm = Long.toString(System.currentTimeMillis());
  530 +
  531 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  532 + : udpSipProvider.getNewCallId();
  533 +
  534 + // 添加订阅
  535 + JSONObject subscribeKey = new JSONObject();
  536 + subscribeKey.put("app", "rtp");
  537 + subscribeKey.put("stream", ssrcInfo.getStream());
  538 + subscribeKey.put("regist", true);
  539 + subscribeKey.put("schema", "rtmp");
  540 + subscribeKey.put("mediaServerId", mediaServerItem.getId());
  541 + logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey);
  542 + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
  543 + (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  544 + if (hookEvent != null) {
  545 + InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream());
  546 + hookEvent.call(inviteStreamInfo);
  547 + }
  548 + });
  549 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
  550 +
  551 + transmitRequest(device, request, errorEvent, okEvent -> {
  552 + ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
  553 + streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.playback);
  554 + streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);
  555 + });
  556 + if (inviteStreamCallback != null) {
  557 + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
  558 + }
  559 + } catch ( SipException | ParseException | InvalidArgumentException e) {
  560 + e.printStackTrace();
  561 + }
  562 + }
  563 +
  564 + /**
  565 + * 请求历史媒体下载
  566 + *
  567 + * @param device 视频设备
  568 + * @param channelId 预览通道
  569 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  570 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  571 + * @param downloadSpeed 下载倍速参数
  572 + */
  573 + @Override
  574 + public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  575 + String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  576 + SipSubscribe.Event errorEvent) {
  577 + try {
  578 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  579 +
  580 + StringBuffer content = new StringBuffer(200);
  581 + content.append("v=0\r\n");
  582 + content.append("o="+channelId+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
  583 + content.append("s=Download\r\n");
  584 + content.append("u="+channelId+":0\r\n");
  585 + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
  586 + content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "
  587 + +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
  588 +
  589 + String streamMode = device.getStreamMode().toUpperCase();
  590 +
  591 + if (userSetting.isSeniorSdp()) {
  592 + if("TCP-PASSIVE".equals(streamMode)) {
  593 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  594 + }else if ("TCP-ACTIVE".equals(streamMode)) {
  595 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
  596 + }else if("UDP".equals(streamMode)) {
  597 + content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
  598 + }
  599 + content.append("a=recvonly\r\n");
  600 + content.append("a=rtpmap:96 PS/90000\r\n");
  601 + content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
  602 + content.append("a=rtpmap:126 H264/90000\r\n");
  603 + content.append("a=rtpmap:125 H264S/90000\r\n");
  604 + content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
  605 + content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
  606 + content.append("a=fmtp:99 profile-level-id=3\r\n");
  607 + content.append("a=rtpmap:98 H264/90000\r\n");
  608 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  609 + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
  610 + content.append("a=setup:passive\r\n");
  611 + content.append("a=connection:new\r\n");
  612 + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  613 + content.append("a=setup:active\r\n");
  614 + content.append("a=connection:new\r\n");
  615 + }
  616 + }else {
  617 + if("TCP-PASSIVE".equals(streamMode)) {
  618 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
  619 + }else if ("TCP-ACTIVE".equals(streamMode)) {
  620 + content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 97 98 99\r\n");
  621 + }else if("UDP".equals(streamMode)) {
  622 + content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 97 98 99\r\n");
  623 + }
  624 + content.append("a=recvonly\r\n");
  625 + content.append("a=rtpmap:96 PS/90000\r\n");
  626 + content.append("a=rtpmap:97 MPEG4/90000\r\n");
  627 + content.append("a=rtpmap:98 H264/90000\r\n");
  628 + content.append("a=rtpmap:99 H265/90000\r\n");
  629 + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式
  630 + content.append("a=setup:passive\r\n");
  631 + content.append("a=connection:new\r\n");
  632 + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
  633 + content.append("a=setup:active\r\n");
  634 + content.append("a=connection:new\r\n");
  635 + }
  636 + }
  637 + content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
  638 +
  639 + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
  640 +
  641 + String tm = Long.toString(System.currentTimeMillis());
  642 +
  643 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  644 + : udpSipProvider.getNewCallId();
  645 +
  646 + // 添加订阅
  647 + JSONObject subscribeKey = new JSONObject();
  648 + subscribeKey.put("app", "rtp");
  649 + subscribeKey.put("stream", ssrcInfo.getStream());
  650 + subscribeKey.put("regist", true);
  651 + subscribeKey.put("mediaServerId", mediaServerItem.getId());
  652 + logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
  653 + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
  654 + (MediaServerItem mediaServerItemInUse, JSONObject json)->{
  655 + hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
  656 + subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
  657 + subscribeKey.put("regist", false);
  658 + subscribeKey.put("schema", "rtmp");
  659 + // 添加流注销的订阅,注销了后向设备发送bye
  660 + subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
  661 + (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd)->{
  662 + ClientTransaction transaction = streamSession.getTransaction(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
  663 + if (transaction != null) {
  664 + logger.info("[录像]下载结束, 发送BYE");
  665 + streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
  666 + }
  667 + });
  668 + });
  669 +
  670 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
  671 + if (inviteStreamCallback != null) {
  672 + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
  673 + }
  674 + transmitRequest(device, request, errorEvent, okEvent->{
  675 + ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
  676 + streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.download);
  677 + streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);
  678 + });
  679 +
  680 +
  681 + } catch ( SipException | ParseException | InvalidArgumentException e) {
  682 + e.printStackTrace();
  683 + }
  684 + }
  685 +
  686 + /**
  687 + * 视频流停止, 不使用回调
  688 + */
  689 + @Override
  690 + public void streamByeCmd(String deviceId, String channelId, String stream, String callId) {
  691 + streamByeCmd(deviceId, channelId, stream, callId, null);
  692 + }
  693 +
  694 + /**
  695 + * 视频流停止
  696 + */
  697 + @Override
  698 + public void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent) {
  699 + try {
  700 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, callId, stream);
  701 + ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId, stream, callId);
  702 +
  703 + if (transaction == null ) {
  704 + logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
  705 + SipSubscribe.EventResult<Object> eventResult = new SipSubscribe.EventResult<>();
  706 + if (okEvent != null) {
  707 + okEvent.response(eventResult);
  708 + }
  709 + return;
  710 + }
  711 + SIPDialog dialog;
  712 + if (callId != null) {
  713 + dialog = streamSession.getDialogByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), callId);
  714 + }else {
  715 + if (stream == null && ssrcTransaction == null && ssrcTransaction.getStream() == null) {
  716 + return;
  717 + }
  718 + dialog = streamSession.getDialogByStream(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  719 + }
  720 + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
  721 + mediaServerService.closeRTPServer(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  722 + streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  723 +
  724 + if (dialog == null) {
  725 + logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
  726 + return;
  727 + }
  728 + SipStack sipStack = udpSipProvider.getSipStack();
  729 + SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
  730 + if (dialog != sipDialog) {
  731 + dialog = sipDialog;
  732 + }else {
  733 + dialog.setSipProvider(udpSipProvider);
  734 + try {
  735 + Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
  736 + sipStackField.setAccessible(true);
  737 + sipStackField.set(dialog, sipStack);
  738 + Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
  739 + eventListenersField.setAccessible(true);
  740 + eventListenersField.set(dialog, new HashSet<>());
  741 + } catch (NoSuchFieldException | IllegalAccessException e) {
  742 + e.printStackTrace();
  743 + }
  744 + }
  745 +
  746 + Request byeRequest = dialog.createRequest(Request.BYE);
  747 + SipURI byeURI = (SipURI) byeRequest.getRequestURI();
  748 + SIPRequest request = (SIPRequest)transaction.getRequest();
  749 + byeURI.setHost(request.getRemoteAddress().getHostAddress());
  750 + byeURI.setPort(request.getRemotePort());
  751 + ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME);
  752 + String protocol = viaHeader.getTransport().toUpperCase();
  753 + ClientTransaction clientTransaction = null;
  754 + if("TCP".equals(protocol)) {
  755 + clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
  756 + } else if("UDP".equals(protocol)) {
  757 + clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
  758 + }
  759 +
  760 + CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME);
  761 + if (okEvent != null) {
  762 + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent);
  763 + }
  764 +
  765 + dialog.sendRequest(clientTransaction);
  766 +
  767 + } catch (SipException | ParseException e) {
  768 + e.printStackTrace();
  769 + }
  770 + }
  771 +
  772 + /**
  773 + * 语音广播
  774 + *
  775 + * @param device 视频设备
  776 + * @param channelId 预览通道
  777 + */
  778 + @Override
  779 + public boolean audioBroadcastCmd(Device device, String channelId) {
  780 + // 改为新的实现
  781 + return false;
  782 + }
  783 +
  784 + /**
  785 + * 语音广播
  786 + *
  787 + * @param device 视频设备
  788 + */
  789 + @Override
  790 + public boolean audioBroadcastCmd(Device device) {
  791 + try {
  792 + StringBuffer broadcastXml = new StringBuffer(200);
  793 + String charset = device.getCharset();
  794 + broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  795 + broadcastXml.append("<Notify>\r\n");
  796 + broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
  797 + broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  798 + broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
  799 + broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
  800 + broadcastXml.append("</Notify>\r\n");
  801 +
  802 + String tm = Long.toString(System.currentTimeMillis());
  803 +
  804 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  805 + : udpSipProvider.getNewCallId();
  806 +
  807 + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader);
  808 + transmitRequest(device, request);
  809 + return true;
  810 + } catch (SipException | ParseException | InvalidArgumentException e) {
  811 + e.printStackTrace();
  812 + }
  813 + return false;
  814 + }
  815 + @Override
  816 + public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) {
  817 + try {
  818 + StringBuffer broadcastXml = new StringBuffer(200);
  819 + String charset = device.getCharset();
  820 + broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  821 + broadcastXml.append("<Notify>\r\n");
  822 + broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n");
  823 + broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  824 + broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n");
  825 + broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n");
  826 + broadcastXml.append("</Notify>\r\n");
  827 +
  828 + String tm = Long.toString(System.currentTimeMillis());
  829 +
  830 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  831 + : udpSipProvider.getNewCallId();
  832 +
  833 + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader);
  834 + transmitRequest(device, request, errorEvent);
  835 + } catch (SipException | ParseException | InvalidArgumentException e) {
  836 + e.printStackTrace();
  837 + }
  838 + }
  839 +
  840 +
  841 + /**
  842 + * 音视频录像控制
  843 + *
  844 + * @param device 视频设备
  845 + * @param channelId 预览通道
  846 + * @param recordCmdStr 录像命令:Record / StopRecord
  847 + */
  848 + @Override
  849 + public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) {
  850 + try {
  851 + StringBuffer cmdXml = new StringBuffer(200);
  852 + String charset = device.getCharset();
  853 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  854 + cmdXml.append("<Control>\r\n");
  855 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  856 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  857 + if (StringUtils.isEmpty(channelId)) {
  858 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  859 + } else {
  860 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  861 + }
  862 + cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
  863 + cmdXml.append("</Control>\r\n");
  864 +
  865 + String tm = Long.toString(System.currentTimeMillis());
  866 +
  867 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  868 + : udpSipProvider.getNewCallId();
  869 +
  870 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null, callIdHeader);
  871 + transmitRequest(device, request, errorEvent);
  872 + return true;
  873 + } catch (SipException | ParseException | InvalidArgumentException e) {
  874 + e.printStackTrace();
  875 + return false;
  876 + }
  877 + }
  878 +
  879 + /**
  880 + * 远程启动控制命令
  881 + *
  882 + * @param device 视频设备
  883 + */
  884 + @Override
  885 + public boolean teleBootCmd(Device device) {
  886 + try {
  887 + StringBuffer cmdXml = new StringBuffer(200);
  888 + String charset = device.getCharset();
  889 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  890 + cmdXml.append("<Control>\r\n");
  891 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  892 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  893 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  894 + cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
  895 + cmdXml.append("</Control>\r\n");
  896 +
  897 + String tm = Long.toString(System.currentTimeMillis());
  898 +
  899 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  900 + : udpSipProvider.getNewCallId();
  901 +
  902 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null, callIdHeader);
  903 + transmitRequest(device, request);
  904 + return true;
  905 + } catch (SipException | ParseException | InvalidArgumentException e) {
  906 + e.printStackTrace();
  907 + return false;
  908 + }
  909 + }
  910 +
  911 + /**
  912 + * 报警布防/撤防命令
  913 + *
  914 + * @param device 视频设备
  915 + * @param guardCmdStr "SetGuard"/"ResetGuard"
  916 + */
  917 + @Override
  918 + public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) {
  919 + try {
  920 + StringBuffer cmdXml = new StringBuffer(200);
  921 + String charset = device.getCharset();
  922 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  923 + cmdXml.append("<Control>\r\n");
  924 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  925 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  926 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  927 + cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
  928 + cmdXml.append("</Control>\r\n");
  929 +
  930 + String tm = Long.toString(System.currentTimeMillis());
  931 +
  932 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  933 + : udpSipProvider.getNewCallId();
  934 +
  935 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null, callIdHeader);
  936 + transmitRequest(device, request, errorEvent);
  937 + return true;
  938 + } catch (SipException | ParseException | InvalidArgumentException e) {
  939 + e.printStackTrace();
  940 + return false;
  941 + }
  942 + }
  943 +
  944 + /**
  945 + * 报警复位命令
  946 + *
  947 + * @param device 视频设备
  948 + */
  949 + @Override
  950 + public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) {
  951 + try {
  952 + StringBuffer cmdXml = new StringBuffer(200);
  953 + String charset = device.getCharset();
  954 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  955 + cmdXml.append("<Control>\r\n");
  956 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  957 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  958 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  959 + cmdXml.append("<AlarmCmd>ResetAlarm</AlarmCmd>\r\n");
  960 + if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) {
  961 + cmdXml.append("<Info>\r\n");
  962 + }
  963 + if (!StringUtils.isEmpty(alarmMethod)) {
  964 + cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
  965 + }
  966 + if (!StringUtils.isEmpty(alarmType)) {
  967 + cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
  968 + }
  969 + if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) {
  970 + cmdXml.append("</Info>\r\n");
  971 + }
  972 + cmdXml.append("</Control>\r\n");
  973 +
  974 + String tm = Long.toString(System.currentTimeMillis());
  975 +
  976 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  977 + : udpSipProvider.getNewCallId();
  978 +
  979 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null, callIdHeader);
  980 + transmitRequest(device, request, errorEvent);
  981 + return true;
  982 + } catch (SipException | ParseException | InvalidArgumentException e) {
  983 + e.printStackTrace();
  984 + return false;
  985 + }
  986 + }
  987 +
  988 + /**
  989 + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
  990 + *
  991 + * @param device 视频设备
  992 + * @param channelId 预览通道
  993 + */
  994 + @Override
  995 + public boolean iFrameCmd(Device device, String channelId) {
  996 + try {
  997 + StringBuffer cmdXml = new StringBuffer(200);
  998 + String charset = device.getCharset();
  999 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1000 + cmdXml.append("<Control>\r\n");
  1001 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  1002 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1003 + if (StringUtils.isEmpty(channelId)) {
  1004 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1005 + } else {
  1006 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1007 + }
  1008 + cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
  1009 + cmdXml.append("</Control>\r\n");
  1010 +
  1011 + String tm = Long.toString(System.currentTimeMillis());
  1012 +
  1013 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1014 + : udpSipProvider.getNewCallId();
  1015 +
  1016 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null, callIdHeader);
  1017 + transmitRequest(device, request);
  1018 + return true;
  1019 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1020 + e.printStackTrace();
  1021 + return false;
  1022 + }
  1023 + }
  1024 +
  1025 + /**
  1026 + * 看守位控制命令
  1027 + *
  1028 + * @param device 视频设备
  1029 + * @param enabled 看守位使能:1 = 开启,0 = 关闭
  1030 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
  1031 + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
  1032 + */
  1033 + @Override
  1034 + public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) {
  1035 + try {
  1036 + StringBuffer cmdXml = new StringBuffer(200);
  1037 + String charset = device.getCharset();
  1038 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1039 + cmdXml.append("<Control>\r\n");
  1040 + cmdXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  1041 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1042 + if (StringUtils.isEmpty(channelId)) {
  1043 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1044 + } else {
  1045 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1046 + }
  1047 + cmdXml.append("<HomePosition>\r\n");
  1048 + if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) {
  1049 + cmdXml.append("<Enabled>1</Enabled>\r\n");
  1050 + if (NumericUtil.isInteger(resetTime)) {
  1051 + cmdXml.append("<ResetTime>" + resetTime + "</ResetTime>\r\n");
  1052 + } else {
  1053 + cmdXml.append("<ResetTime>0</ResetTime>\r\n");
  1054 + }
  1055 + if (NumericUtil.isInteger(presetIndex)) {
  1056 + cmdXml.append("<PresetIndex>" + presetIndex + "</PresetIndex>\r\n");
  1057 + } else {
  1058 + cmdXml.append("<PresetIndex>0</PresetIndex>\r\n");
  1059 + }
  1060 + } else {
  1061 + cmdXml.append("<Enabled>0</Enabled>\r\n");
  1062 + }
  1063 + cmdXml.append("</HomePosition>\r\n");
  1064 + cmdXml.append("</Control>\r\n");
  1065 +
  1066 + String tm = Long.toString(System.currentTimeMillis());
  1067 +
  1068 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1069 + : udpSipProvider.getNewCallId();
  1070 +
  1071 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null, callIdHeader);
  1072 + transmitRequest(device, request, errorEvent);
  1073 + return true;
  1074 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1075 + e.printStackTrace();
  1076 + return false;
  1077 + }
  1078 + }
  1079 +
  1080 + /**
  1081 + * 设备配置命令
  1082 + *
  1083 + * @param device 视频设备
  1084 + */
  1085 + @Override
  1086 + public boolean deviceConfigCmd(Device device) {
  1087 + // TODO Auto-generated method stub
  1088 + return false;
  1089 + }
  1090 +
  1091 + /**
  1092 + * 设备配置命令:basicParam
  1093 + *
  1094 + * @param device 视频设备
  1095 + * @param channelId 通道编码(可选)
  1096 + * @param name 设备/通道名称(可选)
  1097 + * @param expiration 注册过期时间(可选)
  1098 + * @param heartBeatInterval 心跳间隔时间(可选)
  1099 + * @param heartBeatCount 心跳超时次数(可选)
  1100 + */
  1101 + @Override
  1102 + public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration,
  1103 + String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) {
  1104 + try {
  1105 + StringBuffer cmdXml = new StringBuffer(200);
  1106 + String charset = device.getCharset();
  1107 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1108 + cmdXml.append("<Control>\r\n");
  1109 + cmdXml.append("<CmdType>DeviceConfig</CmdType>\r\n");
  1110 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1111 + if (StringUtils.isEmpty(channelId)) {
  1112 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1113 + } else {
  1114 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1115 + }
  1116 + cmdXml.append("<BasicParam>\r\n");
  1117 + if (!StringUtils.isEmpty(name)) {
  1118 + cmdXml.append("<Name>" + name + "</Name>\r\n");
  1119 + }
  1120 + if (NumericUtil.isInteger(expiration)) {
  1121 + if (Integer.valueOf(expiration) > 0) {
  1122 + cmdXml.append("<Expiration>" + expiration + "</Expiration>\r\n");
  1123 + }
  1124 + }
  1125 + if (NumericUtil.isInteger(heartBeatInterval)) {
  1126 + if (Integer.valueOf(heartBeatInterval) > 0) {
  1127 + cmdXml.append("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>\r\n");
  1128 + }
  1129 + }
  1130 + if (NumericUtil.isInteger(heartBeatCount)) {
  1131 + if (Integer.valueOf(heartBeatCount) > 0) {
  1132 + cmdXml.append("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>\r\n");
  1133 + }
  1134 + }
  1135 + cmdXml.append("</BasicParam>\r\n");
  1136 + cmdXml.append("</Control>\r\n");
  1137 +
  1138 + String tm = Long.toString(System.currentTimeMillis());
  1139 +
  1140 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1141 + : udpSipProvider.getNewCallId();
  1142 +
  1143 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader);
  1144 + transmitRequest(device, request, errorEvent);
  1145 + return true;
  1146 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1147 + e.printStackTrace();
  1148 + return false;
  1149 + }
  1150 + }
  1151 +
  1152 + /**
  1153 + * 查询设备状态
  1154 + *
  1155 + * @param device 视频设备
  1156 + */
  1157 + @Override
  1158 + public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) {
  1159 + try {
  1160 + String charset = device.getCharset();
  1161 + StringBuffer catalogXml = new StringBuffer(200);
  1162 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1163 + catalogXml.append("<Query>\r\n");
  1164 + catalogXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
  1165 + catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1166 + catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1167 + catalogXml.append("</Query>\r\n");
  1168 +
  1169 + String tm = Long.toString(System.currentTimeMillis());
  1170 +
  1171 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1172 + : udpSipProvider.getNewCallId();
  1173 +
  1174 + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null, callIdHeader);
  1175 +
  1176 + transmitRequest(device, request, errorEvent);
  1177 + return true;
  1178 +
  1179 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1180 + e.printStackTrace();
  1181 + return false;
  1182 + }
  1183 + }
  1184 +
  1185 + /**
  1186 + * 查询设备信息
  1187 + *
  1188 + * @param device 视频设备
  1189 + */
  1190 + @Override
  1191 + public boolean deviceInfoQuery(Device device) {
  1192 + try {
  1193 + StringBuffer catalogXml = new StringBuffer(200);
  1194 + String charset = device.getCharset();
  1195 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1196 + catalogXml.append("<Query>\r\n");
  1197 + catalogXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
  1198 + catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1199 + catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1200 + catalogXml.append("</Query>\r\n");
  1201 +
  1202 + String tm = Long.toString(System.currentTimeMillis());
  1203 +
  1204 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1205 + : udpSipProvider.getNewCallId();
  1206 +
  1207 + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaDeviceInfo-" + tm, "FromDev" + tm, null, callIdHeader);
  1208 +
  1209 + transmitRequest(device, request);
  1210 +
  1211 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1212 + e.printStackTrace();
  1213 + return false;
  1214 + }
  1215 + return true;
  1216 + }
  1217 +
  1218 + /**
  1219 + * 查询目录列表
  1220 + *
  1221 + * @param device 视频设备
  1222 + */
  1223 + @Override
  1224 + public boolean catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) {
  1225 + try {
  1226 + StringBuffer catalogXml = new StringBuffer(200);
  1227 + String charset = device.getCharset();
  1228 + catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1229 + catalogXml.append("<Query>\r\n");
  1230 + catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
  1231 + catalogXml.append("<SN>" + sn + "</SN>\r\n");
  1232 + catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1233 + catalogXml.append("</Query>\r\n");
  1234 +
  1235 + String tm = Long.toString(System.currentTimeMillis());
  1236 +
  1237 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1238 + : udpSipProvider.getNewCallId();
  1239 +
  1240 + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaCatalog-" + tm, "FromCat" + tm, null, callIdHeader);
  1241 +
  1242 + transmitRequest(device, request, errorEvent);
  1243 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1244 + e.printStackTrace();
  1245 + return false;
  1246 + }
  1247 + return true;
  1248 + }
  1249 +
  1250 + /**
  1251 + * 查询录像信息
  1252 + *
  1253 + * @param device 视频设备
  1254 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
  1255 + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
  1256 + */
  1257 + @Override
  1258 + public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
  1259 + if (secrecy == null) {
  1260 + secrecy = 0;
  1261 + }
  1262 + if (type == null) {
  1263 + type = "all";
  1264 + }
  1265 + try {
  1266 + StringBuffer recordInfoXml = new StringBuffer(200);
  1267 + String charset = device.getCharset();
  1268 + recordInfoXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1269 + recordInfoXml.append("<Query>\r\n");
  1270 + recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
  1271 + recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
  1272 + recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1273 + if (startTime != null) {
  1274 + recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
  1275 + }
  1276 + if (endTime != null) {
  1277 + recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
  1278 + }
  1279 + if (secrecy != null) {
  1280 + recordInfoXml.append("<Secrecy> "+ secrecy + " </Secrecy>\r\n");
  1281 + }
  1282 + if (type != null) {
  1283 + // 大华NVR要求必须增加一个值为all的文本元素节点Type
  1284 + recordInfoXml.append("<Type>" + type+"</Type>\r\n");
  1285 + }
  1286 + recordInfoXml.append("</Query>\r\n");
  1287 +
  1288 + String tm = Long.toString(System.currentTimeMillis());
  1289 +
  1290 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1291 + : udpSipProvider.getNewCallId();
  1292 +
  1293 + Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
  1294 + "z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader);
  1295 +
  1296 + transmitRequest(device, request, errorEvent, okEvent);
  1297 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1298 + e.printStackTrace();
  1299 + return false;
  1300 + }
  1301 + return true;
  1302 + }
  1303 +
  1304 + /**
  1305 + * 查询报警信息
  1306 + *
  1307 + * @param device 视频设备
  1308 + * @param startPriority 报警起始级别(可选)
  1309 + * @param endPriority 报警终止级别(可选)
  1310 + * @param alarmMethod 报警方式条件(可选)
  1311 + * @param alarmType 报警类型
  1312 + * @param startTime 报警发生起始时间(可选)
  1313 + * @param endTime 报警发生终止时间(可选)
  1314 + * @return true = 命令发送成功
  1315 + */
  1316 + @Override
  1317 + public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType,
  1318 + String startTime, String endTime, SipSubscribe.Event errorEvent) {
  1319 + try {
  1320 + StringBuffer cmdXml = new StringBuffer(200);
  1321 + String charset = device.getCharset();
  1322 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1323 + cmdXml.append("<Query>\r\n");
  1324 + cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
  1325 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1326 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1327 + if (!StringUtils.isEmpty(startPriority)) {
  1328 + cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
  1329 + }
  1330 + if (!StringUtils.isEmpty(endPriority)) {
  1331 + cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
  1332 + }
  1333 + if (!StringUtils.isEmpty(alarmMethod)) {
  1334 + cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
  1335 + }
  1336 + if (!StringUtils.isEmpty(alarmType)) {
  1337 + cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
  1338 + }
  1339 + if (!StringUtils.isEmpty(startTime)) {
  1340 + cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
  1341 + }
  1342 + if (!StringUtils.isEmpty(endTime)) {
  1343 + cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
  1344 + }
  1345 + cmdXml.append("</Query>\r\n");
  1346 +
  1347 + String tm = Long.toString(System.currentTimeMillis());
  1348 +
  1349 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1350 + : udpSipProvider.getNewCallId();
  1351 +
  1352 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null, callIdHeader);
  1353 + transmitRequest(device, request, errorEvent);
  1354 + return true;
  1355 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1356 + e.printStackTrace();
  1357 + return false;
  1358 + }
  1359 + }
  1360 +
  1361 + /**
  1362 + * 查询设备配置
  1363 + *
  1364 + * @param device 视频设备
  1365 + * @param channelId 通道编码(可选)
  1366 + * @param configType 配置类型:
  1367 + */
  1368 + @Override
  1369 + public boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) {
  1370 + try {
  1371 + StringBuffer cmdXml = new StringBuffer(200);
  1372 + String charset = device.getCharset();
  1373 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1374 + cmdXml.append("<Query>\r\n");
  1375 + cmdXml.append("<CmdType>ConfigDownload</CmdType>\r\n");
  1376 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1377 + if (StringUtils.isEmpty(channelId)) {
  1378 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1379 + } else {
  1380 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1381 + }
  1382 + cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
  1383 + cmdXml.append("</Query>\r\n");
  1384 +
  1385 + String tm = Long.toString(System.currentTimeMillis());
  1386 +
  1387 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1388 + : udpSipProvider.getNewCallId();
  1389 +
  1390 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader);
  1391 + transmitRequest(device, request, errorEvent);
  1392 + return true;
  1393 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1394 + e.printStackTrace();
  1395 + return false;
  1396 + }
  1397 + }
  1398 +
  1399 + /**
  1400 + * 查询设备预置位置
  1401 + *
  1402 + * @param device 视频设备
  1403 + */
  1404 + @Override
  1405 + public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) {
  1406 + try {
  1407 + StringBuffer cmdXml = new StringBuffer(200);
  1408 + String charset = device.getCharset();
  1409 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1410 + cmdXml.append("<Query>\r\n");
  1411 + cmdXml.append("<CmdType>PresetQuery</CmdType>\r\n");
  1412 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1413 + if (StringUtils.isEmpty(channelId)) {
  1414 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1415 + } else {
  1416 + cmdXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1417 + }
  1418 + cmdXml.append("</Query>\r\n");
  1419 +
  1420 + String tm = Long.toString(System.currentTimeMillis());
  1421 +
  1422 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1423 + : udpSipProvider.getNewCallId();
  1424 +
  1425 + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader);
  1426 + transmitRequest(device, request, errorEvent);
  1427 + return true;
  1428 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1429 + e.printStackTrace();
  1430 + return false;
  1431 + }
  1432 + }
  1433 +
  1434 + /**
  1435 + * 查询移动设备位置数据
  1436 + *
  1437 + * @param device 视频设备
  1438 + */
  1439 + @Override
  1440 + public boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) {
  1441 + try {
  1442 + StringBuffer mobilePostitionXml = new StringBuffer(200);
  1443 + String charset = device.getCharset();
  1444 + mobilePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1445 + mobilePostitionXml.append("<Query>\r\n");
  1446 + mobilePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
  1447 + mobilePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1448 + mobilePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1449 + mobilePostitionXml.append("<Interval>60</Interval>\r\n");
  1450 + mobilePostitionXml.append("</Query>\r\n");
  1451 +
  1452 + String tm = Long.toString(System.currentTimeMillis());
  1453 +
  1454 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1455 + : udpSipProvider.getNewCallId();
  1456 +
  1457 + Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, callIdHeader);
  1458 +
  1459 + transmitRequest(device, request, errorEvent);
  1460 +
  1461 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1462 + e.printStackTrace();
  1463 + return false;
  1464 + }
  1465 + return true;
  1466 + }
  1467 +
  1468 + /**
  1469 + * 订阅、取消订阅移动位置
  1470 + *
  1471 + * @param device 视频设备
  1472 + * @return true = 命令发送成功
  1473 + */
  1474 + @Override
  1475 + public boolean mobilePositionSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) {
  1476 + try {
  1477 + StringBuffer subscribePostitionXml = new StringBuffer(200);
  1478 + String charset = device.getCharset();
  1479 + subscribePostitionXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1480 + subscribePostitionXml.append("<Query>\r\n");
  1481 + subscribePostitionXml.append("<CmdType>MobilePosition</CmdType>\r\n");
  1482 + subscribePostitionXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1483 + subscribePostitionXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1484 + if (device.getSubscribeCycleForMobilePosition() > 0) {
  1485 + subscribePostitionXml.append("<Interval>" + String.valueOf(device.getMobilePositionSubmissionInterval()) + "</Interval>\r\n");
  1486 + }
  1487 + subscribePostitionXml.append("</Query>\r\n");
  1488 +
  1489 + Request request;
  1490 + if (dialog != null) {
  1491 + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  1492 + request = dialog.createRequest(Request.SUBSCRIBE);
  1493 + ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
  1494 + request.setExpires(expiresHeader);
  1495 +
  1496 + request.setRequestURI(requestURI);
  1497 +
  1498 + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  1499 + request.setContent(subscribePostitionXml.toString(), contentTypeHeader);
  1500 +
  1501 + CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
  1502 + cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
  1503 + request.removeHeader(CSeqHeader.NAME);
  1504 + request.addHeader(cSeqHeader);
  1505 + }else {
  1506 + String tm = Long.toString(System.currentTimeMillis());
  1507 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1508 + : udpSipProvider.getNewCallId();
  1509 + request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, device.getSubscribeCycleForMobilePosition(), "presence" ,callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
  1510 + }
  1511 + transmitRequest(device, request, errorEvent, okEvent);
  1512 +
  1513 + return true;
  1514 +
  1515 + } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) {
  1516 + e.printStackTrace();
  1517 + return false;
  1518 + }
  1519 + }
  1520 +
  1521 + /**
  1522 + * 订阅、取消订阅报警信息
  1523 + *
  1524 + * @param device 视频设备
  1525 + * @param expires 订阅过期时间(0 = 取消订阅)
  1526 + * @param startPriority 报警起始级别(可选)
  1527 + * @param endPriority 报警终止级别(可选)
  1528 + * @param alarmMethod 报警方式条件(可选)
  1529 + * @param alarmType 报警类型
  1530 + * @param startTime 报警发生起始时间(可选)
  1531 + * @param endTime 报警发生终止时间(可选)
  1532 + * @return true = 命令发送成功
  1533 + */
  1534 + @Override
  1535 + public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) {
  1536 + try {
  1537 + StringBuffer cmdXml = new StringBuffer(200);
  1538 + String charset = device.getCharset();
  1539 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1540 + cmdXml.append("<Query>\r\n");
  1541 + cmdXml.append("<CmdType>Alarm</CmdType>\r\n");
  1542 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1543 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1544 + if (!StringUtils.isEmpty(startPriority)) {
  1545 + cmdXml.append("<StartAlarmPriority>" + startPriority + "</StartAlarmPriority>\r\n");
  1546 + }
  1547 + if (!StringUtils.isEmpty(endPriority)) {
  1548 + cmdXml.append("<EndAlarmPriority>" + endPriority + "</EndAlarmPriority>\r\n");
  1549 + }
  1550 + if (!StringUtils.isEmpty(alarmMethod)) {
  1551 + cmdXml.append("<AlarmMethod>" + alarmMethod + "</AlarmMethod>\r\n");
  1552 + }
  1553 + if (!StringUtils.isEmpty(alarmType)) {
  1554 + cmdXml.append("<AlarmType>" + alarmType + "</AlarmType>\r\n");
  1555 + }
  1556 + if (!StringUtils.isEmpty(startTime)) {
  1557 + cmdXml.append("<StartAlarmTime>" + startTime + "</StartAlarmTime>\r\n");
  1558 + }
  1559 + if (!StringUtils.isEmpty(endTime)) {
  1560 + cmdXml.append("<EndAlarmTime>" + endTime + "</EndAlarmTime>\r\n");
  1561 + }
  1562 + cmdXml.append("</Query>\r\n");
  1563 +
  1564 + String tm = Long.toString(System.currentTimeMillis());
  1565 +
  1566 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1567 + : udpSipProvider.getNewCallId();
  1568 +
  1569 + Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, expires, "presence" , callIdHeader);
  1570 + transmitRequest(device, request);
  1571 +
  1572 + return true;
  1573 +
  1574 + } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) {
  1575 + e.printStackTrace();
  1576 + return false;
  1577 + }
  1578 + }
  1579 +
  1580 + @Override
  1581 + public boolean catalogSubscribe(Device device, Dialog dialog, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) {
  1582 + try {
  1583 + StringBuffer cmdXml = new StringBuffer(200);
  1584 + String charset = device.getCharset();
  1585 + cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1586 + cmdXml.append("<Query>\r\n");
  1587 + cmdXml.append("<CmdType>Catalog</CmdType>\r\n");
  1588 + cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1589 + cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1590 + cmdXml.append("</Query>\r\n");
  1591 +
  1592 +
  1593 + Request request;
  1594 + if (dialog != null) {
  1595 + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
  1596 + request = dialog.createRequest(Request.SUBSCRIBE);
  1597 + ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(device.getSubscribeCycleForCatalog());
  1598 + request.setExpires(expiresHeader);
  1599 +
  1600 + request.setRequestURI(requestURI);
  1601 +
  1602 + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  1603 + request.setContent(cmdXml.toString(), contentTypeHeader);
  1604 +
  1605 + CSeqHeader cSeqHeader = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
  1606 + cSeqHeader.setSeqNumber(redisCatchStorage.getCSEQ(Request.SUBSCRIBE));
  1607 + request.removeHeader(CSeqHeader.NAME);
  1608 + request.addHeader(cSeqHeader);
  1609 +
  1610 + }else {
  1611 + String tm = Long.toString(System.currentTimeMillis());
  1612 +
  1613 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1614 + : udpSipProvider.getNewCallId();
  1615 +
  1616 + // 有效时间默认为60秒以上
  1617 + request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm,
  1618 + "fromTagPos" + tm, null, device.getSubscribeCycleForCatalog(), "Catalog" ,
  1619 + callIdHeader);
  1620 +
  1621 + }
  1622 + transmitRequest(device, request, errorEvent, okEvent);
  1623 + return true;
  1624 +
  1625 + } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) {
  1626 + e.printStackTrace();
  1627 + return false;
  1628 + }
  1629 + }
  1630 +
  1631 + @Override
  1632 + public boolean dragZoomCmd(Device device, String channelId, String cmdString) {
  1633 + try {
  1634 + StringBuffer dragXml = new StringBuffer(200);
  1635 + String charset = device.getCharset();
  1636 + dragXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
  1637 + dragXml.append("<Control>\r\n");
  1638 + dragXml.append("<CmdType>DeviceControl</CmdType>\r\n");
  1639 + dragXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
  1640 + if (StringUtils.isEmpty(channelId)) {
  1641 + dragXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
  1642 + } else {
  1643 + dragXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
  1644 + }
  1645 + dragXml.append(cmdString);
  1646 + dragXml.append("</Control>\r\n");
  1647 + String tm = Long.toString(System.currentTimeMillis());
  1648 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1649 + : udpSipProvider.getNewCallId();
  1650 + Request request = headerProvider.createMessageRequest(device, dragXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);
  1651 + logger.debug("拉框信令: " + request.toString());
  1652 + transmitRequest(device, request);
  1653 + return true;
  1654 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1655 + e.printStackTrace();
  1656 + }
  1657 + return false;
  1658 + }
  1659 +
  1660 +
  1661 + private ClientTransaction transmitRequest(Device device, Request request) throws SipException {
  1662 + return transmitRequest(device, request, null, null);
  1663 + }
  1664 +
  1665 + private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent) throws SipException {
  1666 + return transmitRequest(device, request, errorEvent, null);
  1667 + }
  1668 +
  1669 + private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException {
  1670 + ClientTransaction clientTransaction = null;
  1671 + if("TCP".equals(device.getTransport())) {
  1672 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1673 + } else if("UDP".equals(device.getTransport())) {
  1674 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1675 + }
  1676 + if (request.getHeader(UserAgentHeader.NAME) == null) {
  1677 + List<String> agentParam = new ArrayList<>();
  1678 + agentParam.add("wvp-pro");
  1679 + // TODO 添加版本信息以及日期
  1680 + UserAgentHeader userAgentHeader = null;
  1681 + try {
  1682 + userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);
  1683 + } catch (ParseException e) {
  1684 + throw new RuntimeException(e);
  1685 + }
  1686 + request.addHeader(userAgentHeader);
  1687 + }
  1688 + CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
  1689 + // 添加错误订阅
  1690 + if (errorEvent != null) {
  1691 + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
  1692 + errorEvent.response(eventResult);
  1693 + sipSubscribe.removeErrorSubscribe(eventResult.callId);
  1694 + sipSubscribe.removeOkSubscribe(eventResult.callId);
  1695 + }));
  1696 + }
  1697 + // 添加订阅
  1698 + if (okEvent != null) {
  1699 + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult ->{
  1700 + okEvent.response(eventResult);
  1701 + sipSubscribe.removeOkSubscribe(eventResult.callId);
  1702 + sipSubscribe.removeErrorSubscribe(eventResult.callId);
  1703 + });
  1704 + }
  1705 +
  1706 + clientTransaction.sendRequest();
  1707 + return clientTransaction;
  1708 + }
  1709 +
  1710 + /**
  1711 + * 回放暂停
  1712 + */
  1713 + @Override
  1714 + public void playPauseCmd(Device device, StreamInfo streamInfo) {
  1715 + try {
  1716 + Long cseq = redisCatchStorage.getCSEQ(Request.INFO);
  1717 + StringBuffer content = new StringBuffer(200);
  1718 + content.append("PAUSE RTSP/1.0\r\n");
  1719 + content.append("CSeq: " + cseq + "\r\n");
  1720 + content.append("PauseTime: now\r\n");
  1721 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1722 + if (request == null) {
  1723 + return;
  1724 + }
  1725 + logger.info(request.toString());
  1726 + ClientTransaction clientTransaction = null;
  1727 + if ("TCP".equals(device.getTransport())) {
  1728 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1729 + } else if ("UDP".equals(device.getTransport())) {
  1730 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1731 + }
  1732 + if (clientTransaction != null) {
  1733 + clientTransaction.sendRequest();
  1734 + }
  1735 +
  1736 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1737 + e.printStackTrace();
  1738 + }
  1739 + }
  1740 +
  1741 + /**
  1742 + * 回放恢复
  1743 + */
  1744 + @Override
  1745 + public void playResumeCmd(Device device, StreamInfo streamInfo) {
  1746 + try {
  1747 + Long cseq = redisCatchStorage.getCSEQ(Request.INFO);
  1748 + StringBuffer content = new StringBuffer(200);
  1749 + content.append("PLAY RTSP/1.0\r\n");
  1750 + content.append("CSeq: " + cseq + "\r\n");
  1751 + content.append("Range: npt=now-\r\n");
  1752 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1753 + if (request == null) {
  1754 + return;
  1755 + }
  1756 + logger.info(request.toString());
  1757 + ClientTransaction clientTransaction = null;
  1758 + if ("TCP".equals(device.getTransport())) {
  1759 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1760 + } else if ("UDP".equals(device.getTransport())) {
  1761 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1762 + }
  1763 +
  1764 + clientTransaction.sendRequest();
  1765 +
  1766 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1767 + e.printStackTrace();
  1768 + }
  1769 + }
  1770 +
  1771 + /**
  1772 + * 回放拖动播放
  1773 + */
  1774 + @Override
  1775 + public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) {
  1776 + try {
  1777 + Long cseq = redisCatchStorage.getCSEQ(Request.INFO);
  1778 + StringBuffer content = new StringBuffer(200);
  1779 + content.append("PLAY RTSP/1.0\r\n");
  1780 + content.append("CSeq: " + cseq + "\r\n");
  1781 + content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
  1782 +
  1783 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1784 + if (request == null) {
  1785 + return;
  1786 + }
  1787 + logger.info(request.toString());
  1788 + ClientTransaction clientTransaction = null;
  1789 + if ("TCP".equals(device.getTransport())) {
  1790 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1791 + } else if ("UDP".equals(device.getTransport())) {
  1792 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1793 + }
  1794 +
  1795 + clientTransaction.sendRequest();
  1796 +
  1797 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1798 + e.printStackTrace();
  1799 + }
  1800 + }
  1801 +
  1802 + /**
  1803 + * 回放倍速播放
  1804 + */
  1805 + @Override
  1806 + public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) {
  1807 + try {
  1808 + Long cseq = redisCatchStorage.getCSEQ(Request.INFO);
  1809 + StringBuffer content = new StringBuffer(200);
  1810 + content.append("PLAY RTSP/1.0\r\n");
  1811 + content.append("CSeq: " + cseq + "\r\n");
  1812 + content.append("Scale: " + String.format("%.1f",speed) + "\r\n");
  1813 + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
  1814 + if (request == null) {
  1815 + return;
  1816 + }
  1817 + logger.info(request.toString());
  1818 + ClientTransaction clientTransaction = null;
  1819 + if ("TCP".equals(device.getTransport())) {
  1820 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1821 + } else if ("UDP".equals(device.getTransport())) {
  1822 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1823 + }
  1824 +
  1825 + clientTransaction.sendRequest();
  1826 +
  1827 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1828 + e.printStackTrace();
  1829 + }
  1830 + }
  1831 +
  1832 + @Override
  1833 + public void playbackControlCmd(Device device, StreamInfo streamInfo, String content) {
  1834 + try {
  1835 + Request request = headerProvider.createInfoRequest(device, streamInfo, content);
  1836 + if (request == null) {
  1837 + return;
  1838 + }
  1839 + logger.info(request.toString());
  1840 + ClientTransaction clientTransaction = null;
  1841 + if ("TCP".equals(device.getTransport())) {
  1842 + clientTransaction = tcpSipProvider.getNewClientTransaction(request);
  1843 + } else if ("UDP".equals(device.getTransport())) {
  1844 + clientTransaction = udpSipProvider.getNewClientTransaction(request);
  1845 + }
  1846 +
  1847 + clientTransaction.sendRequest();
  1848 +
  1849 + } catch (SipException | ParseException | InvalidArgumentException e) {
  1850 + e.printStackTrace();
  1851 + }
  1852 + }
  1853 +
  1854 + @Override
  1855 + public boolean sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) {
  1856 + if (device == null) {
  1857 + return false;
  1858 + }
  1859 + logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
  1860 + deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
  1861 + try {
  1862 + String characterSet = device.getCharset();
  1863 + StringBuffer deviceStatusXml = new StringBuffer(600);
  1864 + deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
  1865 + deviceStatusXml.append("<Notify>\r\n");
  1866 + deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
  1867 + deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
  1868 + deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
  1869 + deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
  1870 + deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
  1871 + deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
  1872 + deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
  1873 + deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
  1874 + deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
  1875 + deviceStatusXml.append("<info>\r\n");
  1876 + deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
  1877 + deviceStatusXml.append("</info>\r\n");
  1878 + deviceStatusXml.append("</Notify>\r\n");
  1879 +
  1880 + CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
  1881 + : udpSipProvider.getNewCallId();
  1882 + String tm = Long.toString(System.currentTimeMillis());
  1883 + Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader);
  1884 + transmitRequest(device, request);
  1885 +
  1886 +
  1887 + } catch (SipException | ParseException e) {
  1888 + e.printStackTrace();
  1889 + return false;
  1890 + } catch (InvalidArgumentException e) {
  1891 + throw new RuntimeException(e);
  1892 + }
  1893 + return true;
  1894 + }
  1895 +
  1896 + private void sendNotify(Device device, String catalogXmlContent,
  1897 + SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent )
  1898 + throws NoSuchFieldException, IllegalAccessException, SipException, ParseException {
  1899 + MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
  1900 + String characterSet = device.getCharset();
  1901 + // 设置编码, 防止中文乱码
  1902 + messageFactory.setDefaultContentEncodingCharset(characterSet);
  1903 + Dialog dialog = subscribeInfo.getDialog();
  1904 + if (dialog == null || !dialog.getState().equals(DialogState.CONFIRMED)) {
  1905 + return;
  1906 + }
  1907 + SIPRequest notifyRequest = (SIPRequest)dialog.createRequest(Request.NOTIFY);
  1908 + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
  1909 + notifyRequest.setContent(catalogXmlContent, contentTypeHeader);
  1910 +
  1911 + SubscriptionStateHeader subscriptionState = sipFactory.createHeaderFactory()
  1912 + .createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE);
  1913 + notifyRequest.addHeader(subscriptionState);
  1914 +
  1915 + EventHeader event = sipFactory.createHeaderFactory().createEventHeader(subscribeInfo.getEventType());
  1916 + if (subscribeInfo.getEventId() != null) {
  1917 + event.setEventId(subscribeInfo.getEventId());
  1918 + }
  1919 + notifyRequest.addHeader(event);
  1920 +
  1921 + SipURI sipURI = (SipURI) notifyRequest.getRequestURI();
  1922 + if (subscribeInfo.getTransaction() != null) {
  1923 + SIPRequest request = (SIPRequest) subscribeInfo.getTransaction().getRequest();
  1924 + sipURI.setHost(request.getRemoteAddress().getHostAddress());
  1925 + sipURI.setPort(request.getRemotePort());
  1926 + }else {
  1927 + sipURI.setHost(device.getIp());
  1928 + sipURI.setPort(device.getPort());
  1929 + }
  1930 +
  1931 + ClientTransaction transaction = null;
  1932 + if ("TCP".equals(device.getTransport())) {
  1933 + transaction = tcpSipProvider.getNewClientTransaction(notifyRequest);
  1934 + } else if ("UDP".equals(device.getTransport())) {
  1935 + transaction = udpSipProvider.getNewClientTransaction(notifyRequest);
  1936 + }
  1937 + // 添加错误订阅
  1938 + if (errorEvent != null) {
  1939 + sipSubscribe.addErrorSubscribe(subscribeInfo.getCallId(), errorEvent);
  1940 + }
  1941 + // 添加订阅
  1942 + if (okEvent != null) {
  1943 + sipSubscribe.addOkSubscribe(subscribeInfo.getCallId(), okEvent);
  1944 + }
  1945 + if (transaction == null) {
  1946 + logger.error("平台{}的Transport错误:{}",device.getDeviceId(), device.getTransport());
  1947 + return;
  1948 + }
  1949 + dialog.sendRequest(transaction);
  1950 +
  1951 + }
  1952 +}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java 0 → 100644
  1 +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.gb28181.bean.*;
  6 +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  7 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
  9 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
  11 +import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
  12 +import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  13 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  14 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  15 +import gov.nist.javax.sip.message.SIPRequest;
  16 +import org.dom4j.DocumentException;
  17 +import org.dom4j.Element;
  18 +import org.slf4j.Logger;
  19 +import org.slf4j.LoggerFactory;
  20 +import org.springframework.beans.factory.InitializingBean;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.http.HttpStatus;
  23 +import org.springframework.http.ResponseEntity;
  24 +import org.springframework.stereotype.Component;
  25 +
  26 +import javax.sip.InvalidArgumentException;
  27 +import javax.sip.RequestEvent;
  28 +import javax.sip.SipException;
  29 +import javax.sip.address.SipURI;
  30 +import javax.sip.header.*;
  31 +import javax.sip.message.Response;
  32 +import java.text.ParseException;
  33 +import java.util.Map;
  34 +import java.util.concurrent.ConcurrentHashMap;
  35 +
  36 +@Component
  37 +public class InfoRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
  38 +
  39 + private final static Logger logger = LoggerFactory.getLogger(InfoRequestProcessor.class);
  40 +
  41 + private final String method = "INFO";
  42 +
  43 + @Autowired
  44 + private SIPProcessorObserver sipProcessorObserver;
  45 +
  46 + @Autowired
  47 + private IVideoManagerStorage storage;
  48 +
  49 + @Autowired
  50 + private SipSubscribe sipSubscribe;
  51 +
  52 + @Autowired
  53 + private IRedisCatchStorage redisCatchStorage;
  54 +
  55 + @Autowired
  56 + private IVideoManagerStorage storager;
  57 +
  58 + @Autowired
  59 + private SIPCommander cmder;
  60 +
  61 + @Autowired
  62 + private VideoStreamSessionManager sessionManager;
  63 +
  64 + @Override
  65 + public void afterPropertiesSet() throws Exception {
  66 + // 添加消息处理的订阅
  67 + sipProcessorObserver.addRequestProcessor(method, this);
  68 + }
  69 +
  70 + @Override
  71 + public void process(RequestEvent evt) {
  72 + logger.debug("接收到消息:" + evt.getRequest());
  73 + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
  74 + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
  75 + // 先从会话内查找
  76 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
  77 + if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题
  78 + deviceId = ssrcTransaction.getDeviceId();
  79 + }
  80 + // 查询设备是否存在
  81 + Device device = redisCatchStorage.getDevice(deviceId);
  82 + // 查询上级平台是否存在
  83 + ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
  84 + try {
  85 + if (device != null && parentPlatform != null) {
  86 + logger.warn("[重复]平台与设备编号重复:{}", deviceId);
  87 + SIPRequest request = (SIPRequest) evt.getRequest();
  88 + String hostAddress = request.getRemoteAddress().getHostAddress();
  89 + int remotePort = request.getRemotePort();
  90 + if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) {
  91 + parentPlatform = null;
  92 + }else {
  93 + device = null;
  94 + }
  95 + }
  96 + if (device == null && parentPlatform == null) {
  97 + // 不存在则回复404
  98 + responseAck(evt, Response.NOT_FOUND, "device "+ deviceId +" not found");
  99 + logger.warn("[设备未找到 ]: {}", deviceId);
  100 + if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
  101 + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new DeviceNotFoundEvent(evt.getDialog()));
  102 + sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult);
  103 + };
  104 + }else {
  105 + ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME);
  106 + String contentType = header.getContentType();
  107 + String contentSubType = header.getContentSubType();
  108 + if ("Application".equals(contentType) && "MANSRTSP".equals(contentSubType)) {
  109 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId());
  110 + String streamId = sendRtpItem.getStreamId();
  111 + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
  112 + if (null == streamInfo) {
  113 + responseAck(evt, Response.NOT_FOUND, "stream " + streamId + " not found");
  114 + return;
  115 + }
  116 + Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID());
  117 + cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()));
  118 + }
  119 + }
  120 + } catch (SipException e) {
  121 + logger.warn("SIP 回复错误", e);
  122 + } catch (InvalidArgumentException e) {
  123 + logger.warn("参数无效", e);
  124 + } catch (ParseException e) {
  125 + logger.warn("SIP回复时解析异常", e);
  126 + }
  127 + }
  128 +
  129 +
  130 +}