Commit 5749cdd16cd79890c659775de9b1cb7e7ffda5b1

Authored by 648540858
Committed by GitHub
2 parents 1ec70660 9b18c33d

Merge pull request #6 from lawrencehj/master

修正Message Resquest处理的错误等
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -329,7 +329,7 @@ public class SIPCommander implements ISIPCommander {
329 329 streamInfo.setSsrc(ssrc);
330 330 streamInfo.setCahnnelId(channelId);
331 331 streamInfo.setDeviceID(device.getDeviceId());
332   - boolean b = storager.startPlayBlack(streamInfo);
  332 + boolean b = storager.startPlayback(streamInfo);
333 333 return streamInfo;
334 334  
335 335 } catch ( SipException | ParseException | InvalidArgumentException e) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
... ... @@ -35,51 +35,51 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
35 35 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
36 36 import org.springframework.util.StringUtils;
37 37  
38   -/**
  38 +/**
39 39 * @Description:MESSAGE请求处理器
40 40 * @author: swwheihei
41   - * @date: 2020年5月3日 下午5:32:41
  41 + * @date: 2020年5月3日 下午5:32:41
42 42 */
43 43 public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
44   -
  44 +
45 45 private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
46   -
  46 +
47 47 private SIPCommander cmder;
48   -
  48 +
49 49 private IVideoManagerStorager storager;
50   -
  50 +
51 51 private EventPublisher publisher;
52   -
  52 +
53 53 private RedisUtil redis;
54   -
  54 +
55 55 private DeferredResultHolder deferredResultHolder;
56   -
  56 +
57 57 private DeviceOffLineDetector offLineDetector;
58   -
  58 +
59 59 private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";
60   -
  60 +
61 61 private static final String MESSAGE_KEEP_ALIVE = "Keepalive";
62 62 private static final String MESSAGE_CONFIG_DOWNLOAD = "ConfigDownload";
63 63 private static final String MESSAGE_CATALOG = "Catalog";
64 64 private static final String MESSAGE_DEVICE_INFO = "DeviceInfo";
65 65 private static final String MESSAGE_ALARM = "Alarm";
66 66 private static final String MESSAGE_RECORD_INFO = "RecordInfo";
67   -// private static final String MESSAGE_BROADCAST = "Broadcast";
68   -// private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
69   -// private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
70   -// private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval";
71   -
72   - /**
  67 + // private static final String MESSAGE_BROADCAST = "Broadcast";
  68 + // private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
  69 + // private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
  70 + // private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval";
  71 +
  72 + /**
73 73 * 处理MESSAGE请求
74   - *
  74 + *
75 75 * @param evt
76   - */
  76 + */
77 77 @Override
78 78 public void process(RequestEvent evt) {
79 79  
80 80 try {
81 81 Element rootElement = getRootElement(evt);
82   - String cmd = XmlUtil.getText(rootElement,"CmdType");
  82 + String cmd = XmlUtil.getText(rootElement, "CmdType");
83 83  
84 84 if (MESSAGE_KEEP_ALIVE.equals(cmd)) {
85 85 logger.info("接收到KeepAlive消息");
... ... @@ -103,9 +103,10 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
103 103 e.printStackTrace();
104 104 }
105 105 }
106   -
  106 +
107 107 /**
108 108 * 收到deviceInfo设备信息请求 处理
  109 + *
109 110 * @param evt
110 111 */
111 112 private void processMessageDeviceInfo(RequestEvent evt) {
... ... @@ -113,32 +114,38 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
113 114 Element rootElement = getRootElement(evt);
114 115 Element deviceIdElement = rootElement.element("DeviceID");
115 116 String deviceId = deviceIdElement.getText().toString();
116   -
  117 +
117 118 Device device = storager.queryVideoDevice(deviceId);
118 119 if (device == null) {
119 120 return;
120 121 }
121   - device.setName(XmlUtil.getText(rootElement,"DeviceName"));
122   - device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer"));
123   - device.setModel(XmlUtil.getText(rootElement,"Model"));
124   - device.setFirmware(XmlUtil.getText(rootElement,"Firmware"));
125   - if (StringUtils.isEmpty(device.getStreamMode())){
  122 + device.setName(XmlUtil.getText(rootElement, "DeviceName"));
  123 + device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
  124 + device.setModel(XmlUtil.getText(rootElement, "Model"));
  125 + device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
  126 + if (StringUtils.isEmpty(device.getStreamMode())) {
126 127 device.setStreamMode("UDP");
127 128 }
128 129 storager.updateDevice(device);
129   -
  130 +
130 131 RequestMessage msg = new RequestMessage();
131 132 msg.setDeviceId(deviceId);
132 133 msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICEINFO);
133 134 msg.setData(device);
134 135 deferredResultHolder.invokeResult(msg);
135   - } catch (DocumentException e) {
  136 + // 回复200 OK
  137 + responseAck(evt);
  138 + if (offLineDetector.isOnline(deviceId)) {
  139 + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
  140 + }
  141 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
136 142 e.printStackTrace();
137 143 }
138 144 }
139   -
  145 +
140 146 /***
141 147 * 收到catalog设备目录列表请求 处理
  148 + *
142 149 * @param evt
143 150 */
144 151 private void processMessageCatalogList(RequestEvent evt) {
... ... @@ -171,34 +178,43 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
171 178 DeviceChannel deviceChannel = new DeviceChannel();
172 179 deviceChannel.setName(channelName);
173 180 deviceChannel.setChannelId(channelDeviceId);
174   - if(status.equals("ON") || status.equals("On")) {
  181 + if (status.equals("ON") || status.equals("On")) {
175 182 deviceChannel.setStatus(1);
176 183 }
177   - if(status.equals("OFF") || status.equals("Off")) {
  184 + if (status.equals("OFF") || status.equals("Off")) {
178 185 deviceChannel.setStatus(0);
179 186 }
180 187  
181   - deviceChannel.setManufacture(XmlUtil.getText(itemDevice,"Manufacturer"));
182   - deviceChannel.setModel(XmlUtil.getText(itemDevice,"Model"));
183   - deviceChannel.setOwner(XmlUtil.getText(itemDevice,"Owner"));
184   - deviceChannel.setCivilCode(XmlUtil.getText(itemDevice,"CivilCode"));
185   - deviceChannel.setBlock(XmlUtil.getText(itemDevice,"Block"));
186   - deviceChannel.setAddress(XmlUtil.getText(itemDevice,"Address"));
187   - deviceChannel.setParental(itemDevice.element("Parental") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Parental")));
188   - deviceChannel.setParentId(XmlUtil.getText(itemDevice,"ParentID"));
189   - deviceChannel.setSafetyWay(itemDevice.element("SafetyWay") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"SafetyWay")));
190   - deviceChannel.setRegisterWay(itemDevice.element("RegisterWay") == null? 1:Integer.parseInt(XmlUtil.getText(itemDevice,"RegisterWay")));
191   - deviceChannel.setCertNum(XmlUtil.getText(itemDevice,"CertNum"));
192   - deviceChannel.setCertifiable(itemDevice.element("Certifiable") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Certifiable")));
193   - deviceChannel.setErrCode(itemDevice.element("ErrCode") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"ErrCode")));
194   - deviceChannel.setEndTime(XmlUtil.getText(itemDevice,"EndTime"));
195   - deviceChannel.setSecrecy(XmlUtil.getText(itemDevice,"Secrecy"));
196   - deviceChannel.setIpAddress(XmlUtil.getText(itemDevice,"IPAddress"));
197   - deviceChannel.setPort(itemDevice.element("Port") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Port")));
198   - deviceChannel.setPassword(XmlUtil.getText(itemDevice,"Password"));
199   - deviceChannel.setLongitude(itemDevice.element("Longitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Longitude")));
200   - deviceChannel.setLatitude(itemDevice.element("Latitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Latitude")));
201   - deviceChannel.setPTZType(itemDevice.element("PTZType") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"PTZType")));
  188 + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
  189 + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
  190 + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
  191 + deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
  192 + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
  193 + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
  194 + deviceChannel.setParental(itemDevice.element("Parental") == null ? 0
  195 + : Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
  196 + deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
  197 + deviceChannel.setSafetyWay(itemDevice.element("SafetyWay") == null ? 0
  198 + : Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
  199 + deviceChannel.setRegisterWay(itemDevice.element("RegisterWay") == null ? 1
  200 + : Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
  201 + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
  202 + deviceChannel.setCertifiable(itemDevice.element("Certifiable") == null ? 0
  203 + : Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
  204 + deviceChannel.setErrCode(itemDevice.element("ErrCode") == null ? 0
  205 + : Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
  206 + deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
  207 + deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
  208 + deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
  209 + deviceChannel.setPort(itemDevice.element("Port") == null ? 0
  210 + : Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
  211 + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
  212 + deviceChannel.setLongitude(itemDevice.element("Longitude") == null ? 0.00
  213 + : Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
  214 + deviceChannel.setLatitude(itemDevice.element("Latitude") == null ? 0.00
  215 + : Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
  216 + deviceChannel.setPTZType(itemDevice.element("PTZType") == null ? 0
  217 + : Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
202 218 deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
203 219 storager.updateChannel(device.getDeviceId(), deviceChannel);
204 220 }
... ... @@ -208,21 +224,20 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
208 224 msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
209 225 msg.setData(device);
210 226 deferredResultHolder.invokeResult(msg);
211   -
212   - // 回复200
  227 + // 回复200 OK
  228 + responseAck(evt);
213 229 if (offLineDetector.isOnline(deviceId)) {
214   - responseAck(evt);
215 230 publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
216 231 }
217   -
218 232 }
219 233 } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
220 234 e.printStackTrace();
221 235 }
222 236 }
223   -
  237 +
224 238 /***
225 239 * 收到alarm设备报警信息 处理
  240 + *
226 241 * @param evt
227 242 */
228 243 private void processMessageAlarm(RequestEvent evt) {
... ... @@ -230,37 +245,45 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
230 245 Element rootElement = getRootElement(evt);
231 246 Element deviceIdElement = rootElement.element("DeviceID");
232 247 String deviceId = deviceIdElement.getText().toString();
233   -
  248 +
234 249 Device device = storager.queryVideoDevice(deviceId);
235 250 if (device == null) {
236 251 // TODO 也可能是通道
237   -// storager.queryChannel(deviceId)
  252 + // storager.queryChannel(deviceId)
238 253 return;
239 254 }
240   - device.setName(XmlUtil.getText(rootElement,"DeviceName"));
241   - device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer"));
242   - device.setModel(XmlUtil.getText(rootElement,"Model"));
243   - device.setFirmware(XmlUtil.getText(rootElement,"Firmware"));
244   - if (StringUtils.isEmpty(device.getStreamMode())){
  255 + device.setName(XmlUtil.getText(rootElement, "DeviceName"));
  256 + device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
  257 + device.setModel(XmlUtil.getText(rootElement, "Model"));
  258 + device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
  259 + if (StringUtils.isEmpty(device.getStreamMode())) {
245 260 device.setStreamMode("UDP");
246 261 }
247 262 storager.updateDevice(device);
248 263 cmder.catalogQuery(device);
249   - } catch (DocumentException e) {
  264 + // 回复200 OK
  265 + responseAck(evt);
  266 + if (offLineDetector.isOnline(deviceId)) {
  267 + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
  268 + }
  269 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  270 + // } catch (DocumentException e) {
250 271 e.printStackTrace();
251 272 }
252 273 }
253   -
  274 +
254 275 /***
255 276 * 收到keepalive请求 处理
  277 + *
256 278 * @param evt
257 279 */
258   - private void processMessageKeepAlive(RequestEvent evt){
  280 + private void processMessageKeepAlive(RequestEvent evt) {
259 281 try {
260 282 Element rootElement = getRootElement(evt);
261   - String deviceId = XmlUtil.getText(rootElement,"DeviceID");
  283 + String deviceId = XmlUtil.getText(rootElement, "DeviceID");
  284 + // 回复200 OK
  285 + responseAck(evt);
262 286 if (offLineDetector.isOnline(deviceId)) {
263   - responseAck(evt);
264 287 publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
265 288 } else {
266 289 }
... ... @@ -268,58 +291,66 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
268 291 e.printStackTrace();
269 292 }
270 293 }
271   -
  294 +
272 295 /***
273   - * 收到catalog设备目录列表请求 处理
274   - * TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
  296 + * 收到catalog设备目录列表请求 处理 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
  297 + *
275 298 * @param evt
276 299 */
277 300 private void processMessageRecordInfo(RequestEvent evt) {
278 301 try {
  302 + // 回复200 OK
  303 + responseAck(evt);
279 304 RecordInfo recordInfo = new RecordInfo();
280 305 Element rootElement = getRootElement(evt);
281 306 Element deviceIdElement = rootElement.element("DeviceID");
282 307 String deviceId = deviceIdElement.getText().toString();
283 308 recordInfo.setDeviceId(deviceId);
284   - recordInfo.setName(XmlUtil.getText(rootElement,"Name"));
285   - recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement,"SumNum")));
286   - String sn = XmlUtil.getText(rootElement,"SN");
  309 + recordInfo.setName(XmlUtil.getText(rootElement, "Name"));
  310 + recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement, "SumNum")));
  311 + String sn = XmlUtil.getText(rootElement, "SN");
287 312 Element recordListElement = rootElement.element("RecordList");
288 313 if (recordListElement == null) {
  314 + logger.info("无录像数据");
  315 + // responseAck(evt);
289 316 return;
290 317 }
291   -
  318 +
292 319 Iterator<Element> recordListIterator = recordListElement.elementIterator();
293 320 List<RecordItem> recordList = new ArrayList<RecordItem>();
294 321 if (recordListIterator != null) {
295 322 RecordItem record = new RecordItem();
  323 + logger.info("处理录像列表数据...");
296 324 // 遍历DeviceList
297 325 while (recordListIterator.hasNext()) {
298 326 Element itemRecord = recordListIterator.next();
299 327 Element recordElement = itemRecord.element("DeviceID");
300 328 if (recordElement == null) {
  329 + logger.info("记录为空,下一个...");
301 330 continue;
302 331 }
303 332 record = new RecordItem();
304   - record.setDeviceId(XmlUtil.getText(itemRecord,"DeviceID"));
305   - record.setName(XmlUtil.getText(itemRecord,"Name"));
306   - record.setFilePath(XmlUtil.getText(itemRecord,"FilePath"));
307   - record.setAddress(XmlUtil.getText(itemRecord,"Address"));
308   - record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord,"StartTime")));
309   - record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord,"EndTime")));
310   - record.setSecrecy(itemRecord.element("Secrecy") == null? 0:Integer.parseInt(XmlUtil.getText(itemRecord,"Secrecy")));
311   - record.setType(XmlUtil.getText(itemRecord,"Type"));
312   - record.setRecorderId(XmlUtil.getText(itemRecord,"RecorderID"));
  333 + record.setDeviceId(XmlUtil.getText(itemRecord, "DeviceID"));
  334 + record.setName(XmlUtil.getText(itemRecord, "Name"));
  335 + record.setFilePath(XmlUtil.getText(itemRecord, "FilePath"));
  336 + record.setAddress(XmlUtil.getText(itemRecord, "Address"));
  337 + record.setStartTime(
  338 + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "StartTime")));
  339 + record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "EndTime")));
  340 + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
  341 + : Integer.parseInt(XmlUtil.getText(itemRecord, "Secrecy")));
  342 + record.setType(XmlUtil.getText(itemRecord, "Type"));
  343 + record.setRecorderId(XmlUtil.getText(itemRecord, "RecorderID"));
313 344 recordList.add(record);
314 345 }
315   -// recordList.sort(Comparator.naturalOrder());
  346 + // recordList.sort(Comparator.naturalOrder());
316 347 recordInfo.setRecordList(recordList);
317 348 }
318   -
  349 +
319 350 // 存在录像且如果当前录像明细个数小于总条数,说明拆包返回,需要组装,暂不返回
320 351 if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
321 352 // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分
322   - String cacheKey = CACHE_RECORDINFO_KEY+deviceId+sn;
  353 + String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
323 354 // TODO 暂时直接操作redis存储,后续封装专用缓存接口,改为本地内存缓存
324 355 if (redis.hasKey(cacheKey)) {
325 356 List<RecordItem> previousList = (List<RecordItem>) redis.get(cacheKey);
... ... @@ -328,24 +359,28 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
328 359 }
329 360 // 本分支表示录像列表被拆包,且加上之前的数据还是不够,保存缓存返回,等待下个包再处理
330 361 if (recordList.size() < recordInfo.getSumNum()) {
331   - redis.set(cacheKey, recordList, 180);
  362 + logger.info("已获取" + recordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");
  363 + redis.set(cacheKey, recordList, 90);
332 364 return;
333 365 } else {
334 366 // 本分支表示录像被拆包,但加上之前的数据够足够,返回响应
335 367 // 因设备心跳有监听redis过期机制,为提高性能,此处手动删除
  368 + logger.info("录像数据已全部获取");
336 369 redis.del(cacheKey);
337 370 }
338 371 } else {
339 372 // 本分支有两种可能:1、录像列表被拆包,且是第一个包,直接保存缓存返回,等待下个包再处理
340   - // 2、之前有包,但超时清空了,那么这次sn批次的响应数据已经不完整,等待过期时间后redis自动清空数据
341   - redis.set(cacheKey, recordList, 180);
  373 + // 2、之前有包,但超时清空了,那么这次sn批次的响应数据已经不完整,等待过期时间后redis自动清空数据
  374 + logger.info("等待后续的包...");
  375 +
  376 + redis.set(cacheKey, recordList, 90);
342 377 return;
343 378 }
344   -
  379 +
345 380 }
346 381 // 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作
347   - // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作
348   - // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据
  382 + // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作
  383 + // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据
349 384  
350 385 // 对记录进行排序
351 386 RequestMessage msg = new RequestMessage();
... ... @@ -355,16 +390,17 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
355 390 recordInfo.getRecordList().sort(Comparator.naturalOrder());
356 391 msg.setData(recordInfo);
357 392 deferredResultHolder.invokeResult(msg);
358   - } catch (DocumentException e) {
  393 + logger.info("处理完成,返回结果");
  394 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
359 395 e.printStackTrace();
360 396 }
361 397 }
362   -
  398 +
363 399 private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
364   - Response response = getMessageFactory().createResponse(Response.OK,evt.getRequest());
  400 + Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
365 401 getServerTransaction(evt).sendResponse(response);
366 402 }
367   -
  403 +
368 404 private Element getRootElement(RequestEvent evt) throws DocumentException {
369 405 Request request = evt.getRequest();
370 406 SAXReader reader = new SAXReader();
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -142,7 +142,7 @@ public class ZLMHttpHookListener {
142 142 storager.startPlay(streamInfoForPlay);
143 143 }
144 144  
145   - StreamInfo streamInfoForPlayBack = storager.queryPlayBlackBySSRC(ssrc);
  145 + StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc);
146 146 if ("rtp".equals(app) && streamInfoForPlayBack != null ) {
147 147 MediaServerConfig mediaInfo = storager.getMediaInfo();
148 148 streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId));
... ... @@ -150,7 +150,7 @@ public class ZLMHttpHookListener {
150 150 streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId));
151 151 streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId));
152 152 streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId));
153   - storager.startPlayBlack(streamInfoForPlayBack);
  153 + storager.startPlayback(streamInfoForPlayBack);
154 154 }
155 155  
156 156 // TODO Auto-generated method stub
... ... @@ -261,7 +261,12 @@ public class ZLMHttpHookListener {
261 261 String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16));
262 262 StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc);
263 263 if ("rtp".equals(app) && !regist ) {
264   - storager.stopPlay(streamInfo);
  264 + if (streamInfo!=null){
  265 + storager.stopPlay(streamInfo);
  266 + }else{
  267 + streamInfo = storager.queryPlaybackBySSRC(ssrc);
  268 + storager.stopPlayback(streamInfo);
  269 + }
265 270 }
266 271  
267 272  
... ... @@ -288,6 +293,13 @@ public class ZLMHttpHookListener {
288 293 String ssrc = String.format("%010d", numb);
289 294  
290 295 cmder.streamByeCmd(ssrc);
  296 + StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc);
  297 + if (streamInfo!=null){
  298 + storager.stopPlay(streamInfo);
  299 + }else{
  300 + streamInfo = storager.queryPlaybackBySSRC(ssrc);
  301 + storager.stopPlayback(streamInfo);
  302 + }
291 303  
292 304 JSONObject ret = new JSONObject();
293 305 ret.put("code", 0);
... ...
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
... ... @@ -184,11 +184,11 @@ public interface IVideoManagerStorager {
184 184  
185 185 Map<String, StreamInfo> queryPlayByDeviceId(String deviceId);
186 186  
187   - boolean startPlayBlack(StreamInfo streamInfo);
  187 + boolean startPlayback(StreamInfo streamInfo);
188 188  
189   - boolean stopPlayBlack(StreamInfo streamInfo);
  189 + boolean stopPlayback(StreamInfo streamInfo);
190 190  
191   - StreamInfo queryPlayBlackByDevice(String deviceId, String channelId);
  191 + StreamInfo queryPlaybackByDevice(String deviceId, String channelId);
192 192  
193   - StreamInfo queryPlayBlackBySSRC(String ssrc);
  193 + StreamInfo queryPlaybackBySSRC(String ssrc);
194 194 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java
... ... @@ -195,22 +195,22 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager {
195 195 }
196 196  
197 197 @Override
198   - public boolean startPlayBlack(StreamInfo streamInfo) {
  198 + public boolean startPlayback(StreamInfo streamInfo) {
199 199 return false;
200 200 }
201 201  
202 202 @Override
203   - public boolean stopPlayBlack(StreamInfo streamInfo) {
  203 + public boolean stopPlayback(StreamInfo streamInfo) {
204 204 return false;
205 205 }
206 206  
207 207 @Override
208   - public StreamInfo queryPlayBlackByDevice(String deviceId, String channelId) {
  208 + public StreamInfo queryPlaybackByDevice(String deviceId, String channelId) {
209 209 return null;
210 210 }
211 211  
212 212 @Override
213   - public StreamInfo queryPlayBlackBySSRC(String ssrc) {
  213 + public StreamInfo queryPlaybackBySSRC(String ssrc) {
214 214 return null;
215 215 }
216 216 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java
... ... @@ -409,7 +409,7 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
409 409 }
410 410  
411 411 @Override
412   - public StreamInfo queryPlayBlackBySSRC(String ssrc) {
  412 + public StreamInfo queryPlaybackBySSRC(String ssrc) {
413 413 // List<Object> playLeys = redis.keys(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc));
414 414 List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, ssrc));
415 415 if (playLeys == null || playLeys.size() == 0) return null;
... ... @@ -505,14 +505,14 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
505 505  
506 506  
507 507 @Override
508   - public boolean startPlayBlack(StreamInfo stream) {
  508 + public boolean startPlayback(StreamInfo stream) {
509 509 return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getSsrc(),stream.getDeviceID(), stream.getCahnnelId()),
510 510 stream);
511 511 }
512 512  
513 513  
514 514 @Override
515   - public boolean stopPlayBlack(StreamInfo streamInfo) {
  515 + public boolean stopPlayback(StreamInfo streamInfo) {
516 516 if (streamInfo == null) return false;
517 517 DeviceChannel deviceChannel = queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId());
518 518 if (deviceChannel != null) {
... ... @@ -527,7 +527,7 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
527 527 }
528 528  
529 529 @Override
530   - public StreamInfo queryPlayBlackByDevice(String deviceId, String code) {
  530 + public StreamInfo queryPlaybackByDevice(String deviceId, String code) {
531 531 String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX,
532 532 deviceId,
533 533 code);
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
... ... @@ -26,32 +26,32 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
26 26 @RestController
27 27 @RequestMapping("/api")
28 28 public class PlayController {
29   -
  29 +
30 30 private final static Logger logger = LoggerFactory.getLogger(PlayController.class);
31   -
  31 +
32 32 @Autowired
33 33 private SIPCommander cmder;
34   -
  34 +
35 35 @Autowired
36 36 private IVideoManagerStorager storager;
37 37  
38 38 @Autowired
39 39 private ZLMRESTfulUtils zlmresTfulUtils;
40   -
  40 +
41 41 @GetMapping("/play/{deviceId}/{channelId}")
42   - public ResponseEntity<String> play(@PathVariable String deviceId,@PathVariable String channelId){
43   -
  42 + public ResponseEntity<String> play(@PathVariable String deviceId, @PathVariable String channelId) {
  43 +
44 44 Device device = storager.queryVideoDevice(deviceId);
45 45 StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId);
46 46  
47 47 if (streamInfo == null) {
48 48 streamInfo = cmder.playStreamCmd(device, channelId);
49   - }else {
  49 + } else {
50 50 String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
51 51 JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
52 52 if (rtpInfo.getBoolean("exist")) {
53   - return new ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK);
54   - }else {
  53 + return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK);
  54 + } else {
55 55 storager.stopPlay(streamInfo);
56 56 streamInfo = cmder.playStreamCmd(device, channelId);
57 57 }
... ... @@ -60,34 +60,40 @@ public class PlayController {
60 60 String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase();
61 61 // 等待推流, TODO 默认超时30s
62 62 boolean lockFlag = true;
  63 + boolean rtpPushed = false;
63 64 long startTime = System.currentTimeMillis();
  65 + JSONObject rtpInfo = null;
64 66  
65 67 while (lockFlag) {
66 68 try {
67   - if (System.currentTimeMillis() - startTime > 30 * 1000) {
  69 + if (System.currentTimeMillis() - startTime > 60 * 1000) {
68 70 storager.stopPlay(streamInfo);
69 71 logger.info("播放等待超时");
70   - return new ResponseEntity<String>("timeout",HttpStatus.OK);
71   - }else {
  72 + return new ResponseEntity<String>("timeout", HttpStatus.OK);
  73 + } else {
72 74 streamInfo = storager.queryPlayByDevice(deviceId, channelId);
73   - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
74   - if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null && streamInfo.getFlv() != null){
75   - logger.info("RTP已推流,查询编码信息:"+streamInfo.getFlv());
  75 + if (!rtpPushed) {
  76 + logger.info("查询RTP推流信息...");
  77 + rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
  78 + }
  79 + if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null && streamInfo.getFlv() != null) {
  80 + logger.info("查询流编码信息:" + streamInfo.getFlv());
  81 + rtpPushed = true;
76 82 Thread.sleep(2000);
77 83 JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId);
78 84 if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) {
79 85 lockFlag = false;
80   - logger.info("媒体编码信息已获取");
  86 + logger.info("编码信息已获取");
81 87 JSONArray tracks = mediaInfo.getJSONArray("tracks");
82 88 streamInfo.setTracks(tracks);
83 89 storager.startPlay(streamInfo);
84   - }else {
85   - logger.info("媒体编码信息未获取,2秒后重试...");
  90 + } else {
  91 + logger.info("流编码信息未获取,2秒后重试...");
86 92 }
87   - }else {
  93 + } else {
88 94 Thread.sleep(2000);
89 95 continue;
90   - };
  96 + }
91 97 }
92 98 } catch (InterruptedException e) {
93 99 e.printStackTrace();
... ... @@ -95,33 +101,35 @@ public class PlayController {
95 101 }
96 102  
97 103 if (logger.isDebugEnabled()) {
98   - logger.debug(String.format("设备预览 API调用,deviceId:%s ,channelId:%s",deviceId, channelId));
99   - logger.debug("设备预览 API调用,ssrc:"+streamInfo.getSsrc()+",ZLMedia streamId:"+Integer.toHexString(Integer.parseInt(streamInfo.getSsrc())));
  104 + logger.debug(String.format("设备预览 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
  105 + logger.debug("设备预览 API调用,ssrc:" + streamInfo.getSsrc() + ",ZLMedia streamId:"
  106 + + Integer.toHexString(Integer.parseInt(streamInfo.getSsrc())));
100 107 }
101   -
102   - if(streamInfo!=null) {
103   - return new ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK);
  108 +
  109 + if (streamInfo != null) {
  110 + return new ResponseEntity<String>(JSON.toJSONString(streamInfo), HttpStatus.OK);
104 111 } else {
105 112 logger.warn("设备预览API调用失败!");
106 113 return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
107 114 }
108 115 }
109   -
  116 +
110 117 @PostMapping("/play/{ssrc}/stop")
111   - public ResponseEntity<String> playStop(@PathVariable String ssrc){
112   -
  118 + public ResponseEntity<String> playStop(@PathVariable String ssrc) {
  119 +
113 120 cmder.streamByeCmd(ssrc);
114 121 StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc);
115   - if (streamInfo == null) return new ResponseEntity<String>(HttpStatus.PAYMENT_REQUIRED);
  122 + if (streamInfo == null)
  123 + return new ResponseEntity<String>(HttpStatus.PAYMENT_REQUIRED);
116 124 storager.stopPlay(streamInfo);
117 125 if (logger.isDebugEnabled()) {
118 126 logger.debug(String.format("设备预览停止API调用,ssrc:%s", ssrc));
119 127 }
120   -
121   - if(ssrc!=null) {
  128 +
  129 + if (ssrc != null) {
122 130 JSONObject json = new JSONObject();
123 131 json.put("ssrc", ssrc);
124   - return new ResponseEntity<String>(json.toString(),HttpStatus.OK);
  132 + return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
125 133 } else {
126 134 logger.warn("设备预览停止API调用失败!");
127 135 return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
... ... @@ -52,7 +52,7 @@ public class PlaybackController {
52 52 }
53 53  
54 54 Device device = storager.queryVideoDevice(deviceId);
55   - StreamInfo streamInfo = storager.queryPlayBlackByDevice(deviceId, channelId);
  55 + StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, channelId);
56 56  
57 57 if (streamInfo != null) {
58 58 cmder.streamByeCmd(streamInfo.getSsrc());
... ... @@ -64,7 +64,7 @@ public class PlaybackController {
64 64 // if (rtpInfo.getBoolean("exist")) {
65 65 // return new ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK);
66 66 // }else {
67   -// storager.stopPlayBlack(streamInfo);
  67 +// storager.stopPlayback(streamInfo);
68 68 // streamInfo = cmder.playbackStreamCmd(device, channelId, startTime, endTime);
69 69 // }
70 70 // }
... ... @@ -77,29 +77,40 @@ public class PlaybackController {
77 77 }
78 78 // 等待推流, TODO 默认超时15s
79 79 boolean lockFlag = true;
  80 + boolean rtpPushed = false;
80 81 long lockStartTime = System.currentTimeMillis();
  82 + JSONObject rtpInfo = null;
  83 +
81 84 while (lockFlag) {
82 85 try {
83 86 if (System.currentTimeMillis() - lockStartTime > 75 * 1000) {
84   - storager.stopPlayBlack(streamInfo);
  87 + storager.stopPlayback(streamInfo);
  88 + logger.info("播放等待超时");
85 89 return new ResponseEntity<String>("timeout",HttpStatus.OK);
86 90 }else {
87   - streamInfo = storager.queryPlayBlackByDevice(deviceId, channelId);
88   - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
89   - if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo.getFlv() != null){
  91 + streamInfo = storager.queryPlaybackByDevice(deviceId, channelId);
  92 + if (!rtpPushed) {
  93 + logger.info("查询RTP推流信息...");
  94 + rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
  95 + }
  96 + if (rtpInfo != null && rtpInfo.getBoolean("exist") && streamInfo != null && streamInfo.getFlv() != null){
  97 + logger.info("查询流编码信息:"+streamInfo.getFlv());
  98 + rtpPushed = true;
  99 + Thread.sleep(2000);
90 100 JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo("rtp", "rtmp", streamId);
91 101 if (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")) {
92 102 lockFlag = false;
  103 + logger.info("流编码信息已获取");
93 104 JSONArray tracks = mediaInfo.getJSONArray("tracks");
94 105 streamInfo.setTracks(tracks);
95   - storager.startPlayBlack(streamInfo);
  106 + storager.startPlayback(streamInfo);
96 107 }else {
97   -
  108 + logger.info("流编码信息未获取,2秒后重试...");
98 109 }
99 110 }else {
100 111 Thread.sleep(2000);
101 112 continue;
102   - };
  113 + }
103 114 }
104 115 } catch (InterruptedException e) {
105 116 e.printStackTrace();
... ...
web_src/src/components/gb28181/devicePlayer.vue
1 1 <template>
2   - <div id="devicePlayer">
3   - <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
4   - <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live ></LivePlayer>
5   - <div id="shared" style="text-align: right; margin-top: 1rem;">
6   - <el-tabs v-model="tabActiveName">
7   - <el-tab-pane label="实时视频" name="media">
8   - <div style="margin-bottom: 0.5rem;">
9   -<!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>-->
10   -<!-- <el-button type="primary" size="small" @click="startRecord()">录制</el-button>-->
11   -<!-- <el-button type="primary" size="small" @click="stopRecord()">停止录制</el-button>-->
12   - </div>
13   - <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
14   - <span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址:</span>
15   - <el-input v-model="getPlayerShared.sharedUrl" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedUrl)"></el-input>
16   - </div>
17   - <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
18   - <span style="width: 5rem; line-height: 2.5rem; text-align: right;">iframe:</span>
19   - <el-input v-model="getPlayerShared.sharedIframe" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedIframe)"></el-input>
20   - </div>
21   - <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
22   - <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span>
23   - <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedRtmp)"></el-input>
24   - </div>
25   - </el-tab-pane>
26   - <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
27   - <el-tab-pane label="录像查询" name="record">
28   - <el-date-picker size="mini" v-model="videoHistory.date" type="date" value-format="yyyy-MM-dd" placeholder="日期"
29   - @change="queryRecords()"></el-date-picker>
30   -<!-- <el-slider style="margin: 0 1rem 1rem 1rem;"-->
31   -<!-- v-model="timeVal"-->
32   -<!-- :min="timeMin"-->
33   -<!-- :max="timeMax"-->
34   -<!-- :step="5"-->
35   -<!-- :marks="getTimeMakrs()"-->
36   -<!-- :format-tooltip="formatTooltip">-->
37   -<!-- </el-slider>-->
38   -<!-- <range-slider :min="timeMin"-->
39   -<!-- :max="timeMax"-->
40   -<!-- :step="5"></range-slider>-->
  2 +<div id="devicePlayer">
  3 + <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
  4 + <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer>
  5 + <div id="shared" style="text-align: right; margin-top: 1rem;">
  6 + <el-tabs v-model="tabActiveName">
  7 + <el-tab-pane label="实时视频" name="media">
  8 + <div style="margin-bottom: 0.5rem;">
  9 + <!-- <el-button type="primary" size="small" @click="playRecord(true, '')">播放</el-button>-->
  10 + <!-- <el-button type="primary" size="small" @click="startRecord()">录制</el-button>-->
  11 + <!-- <el-button type="primary" size="small" @click="stopRecord()">停止录制</el-button>-->
  12 + </div>
  13 + <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  14 + <span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址:</span>
  15 + <el-input v-model="getPlayerShared.sharedUrl" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedUrl)"></el-input>
  16 + </div>
  17 + <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  18 + <span style="width: 5rem; line-height: 2.5rem; text-align: right;">iframe:</span>
  19 + <el-input v-model="getPlayerShared.sharedIframe" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedIframe)"></el-input>
  20 + </div>
  21 + <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  22 + <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span>
  23 + <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedRtmp)"></el-input>
  24 + </div>
  25 + </el-tab-pane>
  26 + <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
  27 + <el-tab-pane label="录像查询" name="record">
  28 + <el-date-picker size="mini" v-model="videoHistory.date" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="queryRecords()"></el-date-picker>
  29 + <!-- <el-slider style="margin: 0 1rem 1rem 1rem;"-->
  30 + <!-- v-model="timeVal"-->
  31 + <!-- :min="timeMin"-->
  32 + <!-- :max="timeMax"-->
  33 + <!-- :step="5"-->
  34 + <!-- :marks="getTimeMakrs()"-->
  35 + <!-- :format-tooltip="formatTooltip">-->
  36 + <!-- </el-slider>-->
  37 + <!-- <range-slider :min="timeMin"-->
  38 + <!-- :max="timeMax"-->
  39 + <!-- :step="5"></range-slider>-->
41 40  
42   -<!-- <el-date-picker v-model="videoHistory.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="结束时间"-->
43   -<!-- @change="recordList()"></el-date-picker>-->
44   - <el-table :data="videoHistory.searchHistoryResult" height="150" v-load="recordsLoading">
45   - <el-table-column label="名称" prop="name"></el-table-column>
46   - <el-table-column label="文件" prop="filePath"></el-table-column>
47   - <el-table-column label="开始时间" prop="startTime" :formatter="timeFormatter"></el-table-column>
48   - <el-table-column label="结束时间" prop="endTime" :formatter="timeFormatter"></el-table-column>
  41 + <!-- <el-date-picker v-model="videoHistory.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="结束时间"-->
  42 + <!-- @change="recordList()"></el-date-picker>-->
  43 + <el-table :data="videoHistory.searchHistoryResult" height="150" v-load="recordsLoading">
  44 + <el-table-column label="名称" prop="name"></el-table-column>
  45 + <el-table-column label="文件" prop="filePath"></el-table-column>
  46 + <el-table-column label="开始时间" prop="startTime" :formatter="timeFormatter"></el-table-column>
  47 + <el-table-column label="结束时间" prop="endTime" :formatter="timeFormatter"></el-table-column>
49 48  
50   - <el-table-column label="操作" >
51   - <template slot-scope="scope">
52   - <el-button icon="el-icon-video-play" size="mini" @click="playRecord(scope.row)">播放</el-button>
53   - </template>
54   - </el-table-column>
55   - </el-table>
56   - </el-tab-pane>
57   - <!--遥控界面-->
58   - <el-tab-pane label="云台控制" name="control">
59   - <div style="display: flex; justify-content: center;">
60   - <div class="control-wrapper">
61   - <div class="control-btn control-top" @mousedown="ptzCamera(0, 1, 0)" @mouseup="ptzCamera(0, 0, 0)">
62   - <i class="el-icon-caret-top"></i>
63   - <div class="control-inner-btn control-inner"></div>
64   - </div>
65   - <div class="control-btn control-left" @mousedown="ptzCamera(1, 0, 0)" @mouseup="ptzCamera(0, 0, 0)">
66   - <i class="el-icon-caret-left"></i>
67   - <div class="control-inner-btn control-inner"></div>
68   - </div>
69   - <div class="control-btn control-bottom" @mousedown="ptzCamera(0, 2, 0)" @mouseup="ptzCamera(0, 0, 0)">
70   - <i class="el-icon-caret-bottom"></i>
71   - <div class="control-inner-btn control-inner"></div>
72   - </div>
73   - <div class="control-btn control-right" @mousedown="ptzCamera(2, 0, 0)" @mouseup="ptzCamera(0, 0, 0)">
74   - <i class="el-icon-caret-right"></i>
75   - <div class="control-inner-btn control-inner"></div>
76   - </div>
77   - <div class="control-round">
78   - <div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
79   - </div>
80   - <div style="position: absolute; left: 7.25rem; top: 1.25rem" @mousedown="ptzCamera(0, 0, 2)" @mouseup="ptzCamera(0, 0, 0)"><i
81   - class="el-icon-zoom-in" style="font-size: 1.875rem;"></i></div>
82   - <div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;" @mousedown="ptzCamera(0, 0, 1)"
83   - @mouseup="ptzCamera(0, 0, 0)"><i class="el-icon-zoom-out"></i></div>
84   - </div>
85   - </div>
  49 + <el-table-column label="操作">
  50 + <template slot-scope="scope">
  51 + <el-button icon="el-icon-video-play" size="mini" @click="playRecord(scope.row)">播放</el-button>
  52 + </template>
  53 + </el-table-column>
  54 + </el-table>
  55 + </el-tab-pane>
  56 + <!--遥控界面-->
  57 + <el-tab-pane label="云台控制" name="control">
  58 + <div style="display: flex; justify-content: center;">
  59 + <div class="control-wrapper">
  60 + <div class="control-btn control-top" @mousedown="ptzCamera(0, 1, 0)" @mouseup="ptzCamera(0, 0, 0)">
  61 + <i class="el-icon-caret-top"></i>
  62 + <div class="control-inner-btn control-inner"></div>
  63 + </div>
  64 + <div class="control-btn control-left" @mousedown="ptzCamera(1, 0, 0)" @mouseup="ptzCamera(0, 0, 0)">
  65 + <i class="el-icon-caret-left"></i>
  66 + <div class="control-inner-btn control-inner"></div>
  67 + </div>
  68 + <div class="control-btn control-bottom" @mousedown="ptzCamera(0, 2, 0)" @mouseup="ptzCamera(0, 0, 0)">
  69 + <i class="el-icon-caret-bottom"></i>
  70 + <div class="control-inner-btn control-inner"></div>
  71 + </div>
  72 + <div class="control-btn control-right" @mousedown="ptzCamera(2, 0, 0)" @mouseup="ptzCamera(0, 0, 0)">
  73 + <i class="el-icon-caret-right"></i>
  74 + <div class="control-inner-btn control-inner"></div>
  75 + </div>
  76 + <div class="control-round">
  77 + <div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
  78 + </div>
  79 + <div style="position: absolute; left: 7.25rem; top: 1.25rem" @mousedown="ptzCamera(0, 0, 2)" @mouseup="ptzCamera(0, 0, 0)"><i class="el-icon-zoom-in" style="font-size: 1.875rem;"></i></div>
  80 + <div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;" @mousedown="ptzCamera(0, 0, 1)" @mouseup="ptzCamera(0, 0, 0)"><i class="el-icon-zoom-out"></i></div>
  81 + </div>
  82 + </div>
86 83  
87   - </el-tab-pane>
88   - </el-tabs>
89   - </div>
90   - </el-dialog>
91   - </div>
  84 + </el-tab-pane>
  85 + </el-tabs>
  86 + </div>
  87 + </el-dialog>
  88 +</div>
92 89 </template>
93 90  
94 91 <script>
95   - import LivePlayer from '@liveqing/liveplayer'
96   - export default {
97   - name: 'devicePlayer',
98   - props: {},
99   - components: {
100   - LivePlayer
101   - },
102   - computed: {
103   - getPlayerShared: function() {
  92 +import LivePlayer from '@liveqing/liveplayer'
  93 +export default {
  94 + name: 'devicePlayer',
  95 + props: {},
  96 + components: {
  97 + LivePlayer
  98 + },
  99 + computed: {
  100 + getPlayerShared: function () {
  101 + return {
  102 + sharedUrl: window.location.host + '/' + this.videoUrl,
  103 + sharedIframe: '<iframe src="' + window.location.host + '/' + this.videoUrl + '"></iframe>',
  104 + sharedRtmp: this.videoUrl
  105 + };
  106 + }
  107 + },
  108 + created() {},
  109 + data() {
104 110 return {
105   - sharedUrl: window.location.host + '/' + this.videoUrl,
106   - sharedIframe: '<iframe src="' + window.location.host + '/' + this.videoUrl + '"></iframe>',
107   - sharedRtmp: this.videoUrl
108   - };
109   - }
110   - },
111   - created() {
112   - },
113   - data() {
114   - return {
115   - video:'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4',
116   - videoUrl: '',
117   - videoHistory: {
118   - date: '',
119   - searchHistoryResult: [] //媒体流历史记录搜索结果
120   - },
121   - timeMakrs:{
122   - // 0 : "0:00",
123   - // // 60 : "1:00",
124   - // 120 : "2:00",
125   - // // 180 : "3:00",
126   - // 240 : "4:00",
127   - // // 300 : "5:00",
128   - // 360 : "6:00",
129   - // // 420 : "7:00",
130   - // 480 : "8:00",
131   - // 540 : "9:00",
132   - 600: "10:00",
133   - // 660 : "11:00",
134   - 720 : "12:00",
135   - // 780 : "13:00",
136   - 840 : "14:00",
137   - // 900 : "15:00",
138   - 960 : "16:00",
139   - // 1020 : "17:00",
140   - 1080 : "18:00",
141   - // 1140 : "19:00",
142   - // 1200 : "20:00",
143   - // // 1260 : "21:00",
144   - // 1320 : "22:00",
145   - // // 1380 : "23:00",
146   - // 1440 : "24:00"
147   - },
148   - showVideoDialog: false,
149   - ssrc: '',
150   - deviceId: '',
151   - channelId: '',
152   - tabActiveName: 'media',
153   - hasaudio: false,
154   - loadingRecords: false,
155   - recordsLoading: false,
156   - timeVal: 0,
157   - timeMin: 0,
158   - timeMax: 1440,
  111 + video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4',
  112 + videoUrl: '',
  113 + videoHistory: {
  114 + date: '',
  115 + searchHistoryResult: [] //媒体流历史记录搜索结果
  116 + },
  117 + timeMakrs: {
  118 + // 0 : "0:00",
  119 + // // 60 : "1:00",
  120 + // 120 : "2:00",
  121 + // // 180 : "3:00",
  122 + // 240 : "4:00",
  123 + // // 300 : "5:00",
  124 + // 360 : "6:00",
  125 + // // 420 : "7:00",
  126 + // 480 : "8:00",
  127 + // 540 : "9:00",
  128 + 600: "10:00",
  129 + // 660 : "11:00",
  130 + 720: "12:00",
  131 + // 780 : "13:00",
  132 + 840: "14:00",
  133 + // 900 : "15:00",
  134 + 960: "16:00",
  135 + // 1020 : "17:00",
  136 + 1080: "18:00",
  137 + // 1140 : "19:00",
  138 + // 1200 : "20:00",
  139 + // // 1260 : "21:00",
  140 + // 1320 : "22:00",
  141 + // // 1380 : "23:00",
  142 + // 1440 : "24:00"
  143 + },
  144 + showVideoDialog: false,
  145 + ssrc: '',
  146 + deviceId: '',
  147 + channelId: '',
  148 + tabActiveName: 'media',
  149 + hasaudio: false,
  150 + loadingRecords: false,
  151 + recordsLoading: false,
  152 + timeVal: 0,
  153 + timeMin: 0,
  154 + timeMax: 1440,
159 155  
160   - };
161   - },
162   - methods: {
163   - openDialog: function (tab, deviceId, channelId, param) {
164   - this.tabActiveName = tab;
165   - this.channelId = channelId;
166   - this.deviceId = deviceId;
167   - this.ssrc = "";
168   - this.videoUrl = ""
169   - if (!!this.$refs.videoPlayer) {
170   - this.$refs.videoPlayer.pause();
171   - }
  156 + };
  157 + },
  158 + methods: {
  159 + openDialog: function (tab, deviceId, channelId, param) {
  160 + this.tabActiveName = tab;
  161 + this.channelId = channelId;
  162 + this.deviceId = deviceId;
  163 + this.ssrc = "";
  164 + this.videoUrl = ""
  165 + if (!!this.$refs.videoPlayer) {
  166 + this.$refs.videoPlayer.pause();
  167 + }
172 168  
173   - switch(tab) {
174   - case "media":
175   - this.play(param.streamInfo, param.hasAudio)
176   - break;
177   - case "record":
178   - this.showVideoDialog = true;
  169 + switch (tab) {
  170 + case "media":
  171 + this.play(param.streamInfo, param.hasAudio)
  172 + break;
  173 + case "record":
  174 + this.showVideoDialog = true;
179 175  
180   - this.videoHistory.date = param.date;
181   - this.queryRecords()
182   - break;
183   - case "control":
184   - break;
185   - }
186   - },
187   - timeAxisSelTime:function (val) {
188   - console.log(val)
189   - },
190   - getTimeMakrs(){
191   - return this.timeMakrs;
192   - },
  176 + this.videoHistory.date = param.date;
  177 + this.queryRecords()
  178 + break;
  179 + case "control":
  180 + break;
  181 + }
  182 + },
  183 + timeAxisSelTime: function (val) {
  184 + console.log(val)
  185 + },
  186 + getTimeMakrs() {
  187 + return this.timeMakrs;
  188 + },
193 189 play: function (streamInfo, hasAudio) {
194 190 this.hasaudio = hasAudio;
195 191 // 根据媒体流信息二次判断
... ... @@ -236,276 +232,278 @@
236 232 );
237 233 },
238 234  
239   - queryRecords: function() {
240   - if (!this.videoHistory.date) {
241   - return;
242   - }
243   - this.recordsLoading = true;
244   - let that = this;
245   - var startTime = this.videoHistory.date + " 00:00:00";
246   - var endTime = this.videoHistory.date + " 23:59:59";
247   - this.$axios({
248   - method: 'get',
249   - url: '/api/record/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime
250   - }).then(function(res) {
251   - // 处理时间信息
252   - that.videoHistory.searchHistoryResult = res.data.recordList;
253   - that.recordsLoading = false;
254   - }).catch(function(e) {
255   - // that.videoHistory.searchHistoryResult = falsificationData.recordData;
256   - });
  235 + queryRecords: function () {
  236 + if (!this.videoHistory.date) {
  237 + return;
  238 + }
  239 + this.recordsLoading = true;
  240 + this.videoHistory.searchHistoryResult = [];
  241 + let that = this;
  242 + var startTime = this.videoHistory.date + " 00:00:00";
  243 + var endTime = this.videoHistory.date + " 23:59:59";
  244 + this.$axios({
  245 + method: 'get',
  246 + url: '/api/record/' + this.deviceId + '/' + this.channelId + '?startTime=' + startTime + '&endTime=' + endTime
  247 + }).then(function (res) {
  248 + // 处理时间信息
  249 + that.videoHistory.searchHistoryResult = res.data.recordList;
  250 + that.recordsLoading = false;
  251 + }).catch(function (e) {
  252 + console.log(e.message);
  253 + // that.videoHistory.searchHistoryResult = falsificationData.recordData;
  254 + });
257 255  
258   - },
259   - onTimeChange: function (video) {
260   - // this.queryRecords()
261   - },
262   - playRecord: function(row) {
263   - let that = this;
264   - if (that.ssrc != "") {
265   - that.stopPlayRecord(function (){
266   - that.ssrc = "",
267   - that.playRecord(row);
268   - })
269   - }else {
270   - this.$axios({
271   - method: 'get',
272   - url: '/api/playback/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
273   - row.endTime
274   - }).then(function(res) {
275   - var streamInfo = res.data;
276   - that.ssrc = streamInfo.ssrc;
277   - that.videoUrl = streamInfo.ws_flv;
278   - });
279   - }
280   - },
281   - stopPlayRecord: function (callback) {
282   - this.$refs.videoPlayer.pause();
283   - this.videoUrl = '';
284   - this.$axios({
285   - method: 'get',
286   - url: '/api/playback/' + this.ssrc + '/stop'
287   - }).then(function(res) {
288   - if (callback) callback()
289   - });
290   - },
291   - ptzCamera: function(leftRight, upDown, zoom) {
292   - console.log('云台控制:' + leftRight + ' : ' + upDown + " : " + zoom);
293   - let that = this;
294   - this.$axios({
295   - method: 'post',
296   - url: '/api/ptz/' + this.deviceId + '/' + this.channelId + '?leftRight=' + leftRight + '&upDown=' + upDown +
297   - '&inOut=' + zoom + '&moveSpeed=50&zoomSpeed=50'
298   - }).then(function(res) {});
299   - },
300   - //////////////////////播放器事件处理//////////////////////////
301   - videoError:function(e){
302   - console.log("播放器错误:"+JSON.stringify(e));
303   - },
304   - formatTooltip:function(val) {
305   - var h = parseInt(val/60);
306   - var hStr = h< 10 ? ("0" + h):h;
307   - var s = val%60;
308   - var sStr = s< 10 ? ("0" + s):s;
309   - return h + ":" + sStr;
310   - },
311   - timeFormatter: function (row, column, cellValue, index) {
312   - return cellValue.split( " ")[1];
313   - },
314   - mergeTime: function (timeArray) {
315   - var resultArray = [];
316   - for (let i = 0; i < timeArray.length; i++) {
317   - var startTime = new Date(timeArray[i].startTime);
318   - var endTime = new Date(timeArray[i].endTime);
319   - if (i ==0) {
320   - resultArray[0] = {
321   - startTime: startTime,
322   - endTime: endTime
  256 + },
  257 + onTimeChange: function (video) {
  258 + // this.queryRecords()
  259 + },
  260 + playRecord: function (row) {
  261 + let that = this;
  262 + if (that.ssrc != "") {
  263 + that.stopPlayRecord(function () {
  264 + that.ssrc = "",
  265 + that.playRecord(row);
  266 + })
  267 + } else {
  268 + this.$axios({
  269 + method: 'get',
  270 + url: '/api/playback/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
  271 + row.endTime
  272 + }).then(function (res) {
  273 + var streamInfo = res.data;
  274 + that.ssrc = streamInfo.ssrc;
  275 + that.videoUrl = streamInfo.ws_flv;
  276 + });
323 277 }
324   - }
325   - for (let j = 0; j < resultArray.length; j++) {
326   - if (startTime > resultArray[j].endTime ) { // 合并
327   - if (startTime - resultArray[j].endTime <= 1000){
328   - resultArray[j].endTime = endTime;
329   - }else {
330   - resultArray[resultArray.length] = {
331   - startTime: startTime,
332   - endTime: endTime
  278 + },
  279 + stopPlayRecord: function (callback) {
  280 + this.$refs.videoPlayer.pause();
  281 + this.videoUrl = '';
  282 + this.$axios({
  283 + method: 'get',
  284 + url: '/api/playback/' + this.ssrc + '/stop'
  285 + }).then(function (res) {
  286 + if (callback) callback()
  287 + });
  288 + },
  289 + ptzCamera: function (leftRight, upDown, zoom) {
  290 + console.log('云台控制:' + leftRight + ' : ' + upDown + " : " + zoom);
  291 + let that = this;
  292 + this.$axios({
  293 + method: 'post',
  294 + url: '/api/ptz/' + this.deviceId + '/' + this.channelId + '?leftRight=' + leftRight + '&upDown=' + upDown +
  295 + '&inOut=' + zoom + '&moveSpeed=50&zoomSpeed=50'
  296 + }).then(function (res) {});
  297 + },
  298 + //////////////////////播放器事件处理//////////////////////////
  299 + videoError: function (e) {
  300 + console.log("播放器错误:" + JSON.stringify(e));
  301 + },
  302 + formatTooltip: function (val) {
  303 + var h = parseInt(val / 60);
  304 + var hStr = h < 10 ? ("0" + h) : h;
  305 + var s = val % 60;
  306 + var sStr = s < 10 ? ("0" + s) : s;
  307 + return h + ":" + sStr;
  308 + },
  309 + timeFormatter: function (row, column, cellValue, index) {
  310 + return cellValue.split(" ")[1];
  311 + },
  312 + mergeTime: function (timeArray) {
  313 + var resultArray = [];
  314 + for (let i = 0; i < timeArray.length; i++) {
  315 + var startTime = new Date(timeArray[i].startTime);
  316 + var endTime = new Date(timeArray[i].endTime);
  317 + if (i == 0) {
  318 + resultArray[0] = {
  319 + startTime: startTime,
  320 + endTime: endTime
  321 + }
333 322 }
334   - }
335   - }else if(resultArray[j].startTime > endTime ) { // 合并
336   - if (resultArray[j].startTime - endTime <= 1000) {
337   - resultArray[j].startTime = startTime;
338   - }else {
339   - resultArray[resultArray.length] = {
340   - startTime: startTime,
341   - endTime: endTime
  323 + for (let j = 0; j < resultArray.length; j++) {
  324 + if (startTime > resultArray[j].endTime) { // 合并
  325 + if (startTime - resultArray[j].endTime <= 1000) {
  326 + resultArray[j].endTime = endTime;
  327 + } else {
  328 + resultArray[resultArray.length] = {
  329 + startTime: startTime,
  330 + endTime: endTime
  331 + }
  332 + }
  333 + } else if (resultArray[j].startTime > endTime) { // 合并
  334 + if (resultArray[j].startTime - endTime <= 1000) {
  335 + resultArray[j].startTime = startTime;
  336 + } else {
  337 + resultArray[resultArray.length] = {
  338 + startTime: startTime,
  339 + endTime: endTime
  340 + }
  341 + }
  342 + }
342 343 }
343   - }
344 344 }
345   - }
  345 + console.log(resultArray)
  346 + return resultArray;
346 347 }
347   - console.log(resultArray)
348   - return resultArray;
349   - }
350   - }
351   - };
  348 + }
  349 +};
352 350 </script>
353 351  
354 352 <style>
355   - .control-wrapper {
356   - position: relative;
357   - width: 6.25rem;
358   - height: 6.25rem;
359   - max-width: 6.25rem;
360   - max-height: 6.25rem;
361   - margin: 0 auto;
362   - border-radius: 100%;
363   - float: left;
364   - }
  353 +.control-wrapper {
  354 + position: relative;
  355 + width: 6.25rem;
  356 + height: 6.25rem;
  357 + max-width: 6.25rem;
  358 + max-height: 6.25rem;
  359 + margin: 0 auto;
  360 + border-radius: 100%;
  361 + float: left;
  362 +}
365 363  
366   - .control-btn {
367   - display: flex;
368   - justify-content: center;
369   - position: absolute;
370   - width: 44%;
371   - height: 44%;
372   - border-radius: 5px;
373   - border: 1px solid #78aee4;
374   - box-sizing: border-box;
375   - transition: all 0.3s linear;
376   - }
  364 +.control-btn {
  365 + display: flex;
  366 + justify-content: center;
  367 + position: absolute;
  368 + width: 44%;
  369 + height: 44%;
  370 + border-radius: 5px;
  371 + border: 1px solid #78aee4;
  372 + box-sizing: border-box;
  373 + transition: all 0.3s linear;
  374 +}
377 375  
378   - .control-btn i {
379   - font-size: 20px;
380   - color: #78aee4;
381   - display: flex;
382   - justify-content: center;
383   - align-items: center;
384   - }
  376 +.control-btn i {
  377 + font-size: 20px;
  378 + color: #78aee4;
  379 + display: flex;
  380 + justify-content: center;
  381 + align-items: center;
  382 +}
385 383  
386   - .control-round {
387   - position: absolute;
388   - top: 21%;
389   - left: 21%;
390   - width: 58%;
391   - height: 58%;
392   - background: #fff;
393   - border-radius: 100%;
394   - }
  384 +.control-round {
  385 + position: absolute;
  386 + top: 21%;
  387 + left: 21%;
  388 + width: 58%;
  389 + height: 58%;
  390 + background: #fff;
  391 + border-radius: 100%;
  392 +}
395 393  
396   - .control-round-inner {
397   - position: absolute;
398   - left: 15%;
399   - top: 15%;
400   - display: flex;
401   - justify-content: center;
402   - align-items: center;
403   - width: 70%;
404   - height: 70%;
405   - font-size: 40px;
406   - color: #78aee4;
407   - border: 1px solid #78aee4;
408   - border-radius: 100%;
409   - transition: all 0.3s linear;
410   - }
  394 +.control-round-inner {
  395 + position: absolute;
  396 + left: 15%;
  397 + top: 15%;
  398 + display: flex;
  399 + justify-content: center;
  400 + align-items: center;
  401 + width: 70%;
  402 + height: 70%;
  403 + font-size: 40px;
  404 + color: #78aee4;
  405 + border: 1px solid #78aee4;
  406 + border-radius: 100%;
  407 + transition: all 0.3s linear;
  408 +}
411 409  
412   - .control-inner-btn {
413   - position: absolute;
414   - width: 60%;
415   - height: 60%;
416   - background: #fafafa;
417   - }
  410 +.control-inner-btn {
  411 + position: absolute;
  412 + width: 60%;
  413 + height: 60%;
  414 + background: #fafafa;
  415 +}
418 416  
419   - .control-top {
420   - top: -8%;
421   - left: 27%;
422   - transform: rotate(-45deg);
423   - border-radius: 5px 100% 5px 0;
424   - }
  417 +.control-top {
  418 + top: -8%;
  419 + left: 27%;
  420 + transform: rotate(-45deg);
  421 + border-radius: 5px 100% 5px 0;
  422 +}
425 423  
426   - .control-top i {
427   - transform: rotate(45deg);
428   - border-radius: 5px 100% 5px 0;
429   - }
  424 +.control-top i {
  425 + transform: rotate(45deg);
  426 + border-radius: 5px 100% 5px 0;
  427 +}
430 428  
431   - .control-top .control-inner {
432   - left: -1px;
433   - bottom: 0;
434   - border-top: 1px solid #78aee4;
435   - border-right: 1px solid #78aee4;
436   - border-radius: 0 100% 0 0;
437   - }
  429 +.control-top .control-inner {
  430 + left: -1px;
  431 + bottom: 0;
  432 + border-top: 1px solid #78aee4;
  433 + border-right: 1px solid #78aee4;
  434 + border-radius: 0 100% 0 0;
  435 +}
438 436  
439   - .control-top .fa {
440   - transform: rotate(45deg) translateY(-7px);
441   - }
  437 +.control-top .fa {
  438 + transform: rotate(45deg) translateY(-7px);
  439 +}
442 440  
443   - .control-left {
444   - top: 27%;
445   - left: -8%;
446   - transform: rotate(45deg);
447   - border-radius: 5px 0 5px 100%;
448   - }
  441 +.control-left {
  442 + top: 27%;
  443 + left: -8%;
  444 + transform: rotate(45deg);
  445 + border-radius: 5px 0 5px 100%;
  446 +}
449 447  
450   - .control-left i {
451   - transform: rotate(-45deg);
452   - }
  448 +.control-left i {
  449 + transform: rotate(-45deg);
  450 +}
453 451  
454   - .control-left .control-inner {
455   - right: -1px;
456   - top: -1px;
457   - border-bottom: 1px solid #78aee4;
458   - border-left: 1px solid #78aee4;
459   - border-radius: 0 0 0 100%;
460   - }
  452 +.control-left .control-inner {
  453 + right: -1px;
  454 + top: -1px;
  455 + border-bottom: 1px solid #78aee4;
  456 + border-left: 1px solid #78aee4;
  457 + border-radius: 0 0 0 100%;
  458 +}
461 459  
462   - .control-left .fa {
463   - transform: rotate(-45deg) translateX(-7px);
464   - }
  460 +.control-left .fa {
  461 + transform: rotate(-45deg) translateX(-7px);
  462 +}
465 463  
466   - .control-right {
467   - top: 27%;
468   - right: -8%;
469   - transform: rotate(45deg);
470   - border-radius: 5px 100% 5px 0;
471   - }
  464 +.control-right {
  465 + top: 27%;
  466 + right: -8%;
  467 + transform: rotate(45deg);
  468 + border-radius: 5px 100% 5px 0;
  469 +}
472 470  
473   - .control-right i {
474   - transform: rotate(-45deg);
475   - }
  471 +.control-right i {
  472 + transform: rotate(-45deg);
  473 +}
476 474  
477   - .control-right .control-inner {
478   - left: -1px;
479   - bottom: -1px;
480   - border-top: 1px solid #78aee4;
481   - border-right: 1px solid #78aee4;
482   - border-radius: 0 100% 0 0;
483   - }
  475 +.control-right .control-inner {
  476 + left: -1px;
  477 + bottom: -1px;
  478 + border-top: 1px solid #78aee4;
  479 + border-right: 1px solid #78aee4;
  480 + border-radius: 0 100% 0 0;
  481 +}
484 482  
485   - .control-right .fa {
486   - transform: rotate(-45deg) translateX(7px);
487   - }
  483 +.control-right .fa {
  484 + transform: rotate(-45deg) translateX(7px);
  485 +}
488 486  
489   - .control-bottom {
490   - left: 27%;
491   - bottom: -8%;
492   - transform: rotate(45deg);
493   - border-radius: 0 5px 100% 5px;
494   - }
  487 +.control-bottom {
  488 + left: 27%;
  489 + bottom: -8%;
  490 + transform: rotate(45deg);
  491 + border-radius: 0 5px 100% 5px;
  492 +}
495 493  
496   - .control-bottom i {
497   - transform: rotate(-45deg);
498   - }
  494 +.control-bottom i {
  495 + transform: rotate(-45deg);
  496 +}
499 497  
500   - .control-bottom .control-inner {
501   - top: -1px;
502   - left: -1px;
503   - border-bottom: 1px solid #78aee4;
504   - border-right: 1px solid #78aee4;
505   - border-radius: 0 0 100% 0;
506   - }
  498 +.control-bottom .control-inner {
  499 + top: -1px;
  500 + left: -1px;
  501 + border-bottom: 1px solid #78aee4;
  502 + border-right: 1px solid #78aee4;
  503 + border-radius: 0 0 100% 0;
  504 +}
507 505  
508   - .control-bottom .fa {
509   - transform: rotate(-45deg) translateY(7px);
510   - }
  506 +.control-bottom .fa {
  507 + transform: rotate(-45deg) translateY(7px);
  508 +}
511 509 </style>
... ...