Commit 7d9cc96ef54399795deb5b7fc7682e6323dc1202

Authored by 648540858
1 parent e9586687

优化国标录像下载,添加进度条以及自动合并文件下载,需要结合新版assist服务使用。

Showing 27 changed files with 761 additions and 591 deletions
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
... ... @@ -31,6 +31,9 @@ public class StreamInfo {
31 31 private String rtc;
32 32 private String mediaServerId;
33 33 private Object tracks;
  34 + private String startTime;
  35 + private String endTime;
  36 + private double progress;
34 37  
35 38 public static class TransactionInfo{
36 39 public String callId;
... ... @@ -264,4 +267,29 @@ public class StreamInfo {
264 267 public void setHttps_ts(String https_ts) {
265 268 this.https_ts = https_ts;
266 269 }
  270 +
  271 +
  272 + public String getStartTime() {
  273 + return startTime;
  274 + }
  275 +
  276 + public void setStartTime(String startTime) {
  277 + this.startTime = startTime;
  278 + }
  279 +
  280 + public String getEndTime() {
  281 + return endTime;
  282 + }
  283 +
  284 + public void setEndTime(String endTime) {
  285 + this.endTime = endTime;
  286 + }
  287 +
  288 + public double getProgress() {
  289 + return progress;
  290 + }
  291 +
  292 + public void setProgress(double progress) {
  293 + this.progress = progress;
  294 + }
267 295 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
1 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
  3 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  4 +
3 5 public class SsrcTransaction {
4 6  
5 7 private String deviceId;
... ... @@ -10,6 +12,7 @@ public class SsrcTransaction {
10 12 private byte[] dialog;
11 13 private String mediaServerId;
12 14 private String ssrc;
  15 + private VideoStreamSessionManager.SessionType type;
13 16  
14 17 public String getDeviceId() {
15 18 return deviceId;
... ... @@ -74,4 +77,12 @@ public class SsrcTransaction {
74 77 public void setSsrc(String ssrc) {
75 78 this.ssrc = ssrc;
76 79 }
  80 +
  81 + public VideoStreamSessionManager.SessionType getType() {
  82 + return type;
  83 + }
  84 +
  85 + public void setType(VideoStreamSessionManager.SessionType type) {
  86 + this.type = type;
  87 + }
77 88 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
... ... @@ -156,8 +156,6 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
156 156 List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId);
157 157 if (parentPlatforms != null && parentPlatforms.size() > 0) {
158 158 for (ParentPlatform platform : parentPlatforms) {
159   -// String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_Catalog_" + platform.getServerGBId();
160   -// SubscribeInfo subscribeInfo = redisCatchStorage.getSubscribe(key);
161 159 SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(event.getPlatformId());
162 160 if (subscribeInfo == null) continue;
163 161 logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
... ... @@ -30,6 +30,12 @@ public class VideoStreamSessionManager {
30 30 @Autowired
31 31 private UserSetup userSetup;
32 32  
  33 + public enum SessionType {
  34 + play,
  35 + playback,
  36 + download
  37 + }
  38 +
33 39 /**
34 40 * 添加一个点播/回放的事务信息
35 41 * 后续可以通过流Id/callID
... ... @@ -40,7 +46,7 @@ public class VideoStreamSessionManager {
40 46 * @param mediaServerId 所使用的流媒体ID
41 47 * @param transaction 事务
42 48 */
43   - public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, ClientTransaction transaction){
  49 + public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, ClientTransaction transaction, SessionType type){
44 50 SsrcTransaction ssrcTransaction = new SsrcTransaction();
45 51 ssrcTransaction.setDeviceId(deviceId);
46 52 ssrcTransaction.setChannelId(channelId);
... ... @@ -50,6 +56,7 @@ public class VideoStreamSessionManager {
50 56 ssrcTransaction.setCallId(callId);
51 57 ssrcTransaction.setSsrc(ssrc);
52 58 ssrcTransaction.setMediaServerId(mediaServerId);
  59 + ssrcTransaction.setType(type);
53 60  
54 61 redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId()
55 62 + "_" + deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
... ... @@ -115,7 +115,9 @@ public interface ISIPCommander {
115 115 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
116 116 * @param downloadSpeed 下载倍速参数
117 117 */
118   - void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, InviteStreamCallback event, SipSubscribe.Event errorEvent);
  118 + void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  119 + String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  120 + SipSubscribe.Event errorEvent);
119 121  
120 122 /**
121 123 * 视频流停止
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -428,7 +428,7 @@ public class SIPCommander implements ISIPCommander {
428 428 errorEvent.response(e);
429 429 }), e ->{
430 430 // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值
431   - streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction());
  431 + streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play);
432 432 streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog);
433 433 });
434 434  
... ... @@ -537,7 +537,7 @@ public class SIPCommander implements ISIPCommander {
537 537  
538 538 transmitRequest(device, request, errorEvent, okEvent -> {
539 539 ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
540   - streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction());
  540 + streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.playback);
541 541 streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);
542 542 });
543 543 if (inviteStreamCallback != null) {
... ... @@ -558,8 +558,9 @@ public class SIPCommander implements ISIPCommander {
558 558 * @param downloadSpeed 下载倍速参数
559 559 */
560 560 @Override
561   - public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, InviteStreamCallback event
562   - , SipSubscribe.Event errorEvent) {
  561 + public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  562 + String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  563 + SipSubscribe.Event errorEvent) {
563 564 try {
564 565 logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
565 566  
... ... @@ -572,8 +573,6 @@ public class SIPCommander implements ISIPCommander {
572 573 content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "
573 574 +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
574 575  
575   -
576   -
577 576 String streamMode = device.getStreamMode().toUpperCase();
578 577  
579 578 if (userSetup.isSeniorSdp()) {
... ... @@ -639,15 +638,20 @@ public class SIPCommander implements ISIPCommander {
639 638 logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
640 639 subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
641 640 (MediaServerItem mediaServerItemInUse, JSONObject json)->{
642   - event.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
  641 + hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
643 642 subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
644 643 });
645 644  
646 645 Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc());
  646 + if (inviteStreamCallback != null) {
  647 + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
  648 + }
  649 + transmitRequest(device, request, errorEvent, okEvent->{
  650 + ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
  651 + streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), responseEvent.getClientTransaction(), VideoStreamSessionManager.SessionType.download);
  652 + streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), okEvent.dialog);
  653 + });
647 654  
648   - ClientTransaction transaction = transmitRequest(device, request, errorEvent);
649   - streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), transaction);
650   - streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), transaction);
651 655  
652 656 } catch ( SipException | ParseException | InvalidArgumentException e) {
653 657 e.printStackTrace();
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java
... ... @@ -104,6 +104,7 @@ public class CatalogNotifyMessageHandler extends SIPRequestProcessorParent imple
104 104 DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
105 105 deviceChannel.setParental(0);
106 106 deviceChannel.setParentId(channelReduce.getCatalogId());
  107 + deviceChannel.setCivilCode(parentPlatform.getDeviceGBId().substring(0, 6));
107 108 cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
108 109 // 防止发送过快
109 110 Thread.sleep(100);
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
... ... @@ -62,7 +62,12 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
62 62 if (NotifyType.equals("121")){
63 63 logger.info("媒体播放完毕,通知关流");
64 64 String channelId =getText(rootElement, "DeviceID");
65   - redisCatchStorage.stopPlayback(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
  65 +// redisCatchStorage.stopPlayback(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
  66 +// redisCatchStorage.stopDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
  67 + StreamInfo streamInfo = redisCatchStorage.queryDownload(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
  68 + // 设置进度100%
  69 + streamInfo.setProgress(1);
  70 + redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId());
66 71 cmder.streamByeCmd(device.getDeviceId(), channelId, null, callIdHeader.getCallId());
67 72 // TODO 如果级联播放,需要给上级发送此通知
68 73  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
... ... @@ -107,6 +107,7 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
107 107 DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
108 108 deviceChannel.setParental(0);
109 109 deviceChannel.setParentId(channelReduce.getCatalogId());
  110 + deviceChannel.setCivilCode(parentPlatform.getDeviceGBId().substring(0, 6));
110 111 cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size);
111 112 // 防止发送过快
112 113 Thread.sleep(100);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java 0 → 100644
  1 +package com.genersoft.iot.vmp.media.zlm;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  6 +import okhttp3.*;
  7 +import okhttp3.logging.HttpLoggingInterceptor;
  8 +import org.jetbrains.annotations.NotNull;
  9 +import org.slf4j.Logger;
  10 +import org.slf4j.LoggerFactory;
  11 +import org.springframework.stereotype.Component;
  12 +import org.springframework.util.StringUtils;
  13 +
  14 +import java.io.File;
  15 +import java.io.FileOutputStream;
  16 +import java.io.IOException;
  17 +import java.net.ConnectException;
  18 +import java.util.HashMap;
  19 +import java.util.Map;
  20 +import java.util.Objects;
  21 +import java.util.concurrent.TimeUnit;
  22 +
  23 +@Component
  24 +public class AssistRESTfulUtils {
  25 +
  26 + private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class);
  27 +
  28 + public interface RequestCallback{
  29 + void run(JSONObject response);
  30 + }
  31 +
  32 + private OkHttpClient getClient(){
  33 + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
  34 + if (logger.isDebugEnabled()) {
  35 + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
  36 + logger.debug("http请求参数:" + message);
  37 + });
  38 + logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
  39 + // OkHttp進行添加攔截器loggingInterceptor
  40 + httpClientBuilder.addInterceptor(logging);
  41 + }
  42 + return httpClientBuilder.build();
  43 + }
  44 +
  45 +
  46 + public JSONObject sendGet(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
  47 + OkHttpClient client = getClient();
  48 +
  49 + if (mediaServerItem == null) {
  50 + return null;
  51 + }
  52 + if (StringUtils.isEmpty(mediaServerItem.getRecordAssistPort())) {
  53 + logger.warn("未启用Assist服务");
  54 + return null;
  55 + }
  56 + StringBuffer stringBuffer = new StringBuffer();
  57 + stringBuffer.append(String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api));
  58 + JSONObject responseJSON = null;
  59 +
  60 + if (param != null && param.keySet().size() > 0) {
  61 + stringBuffer.append("?");
  62 + int index = 1;
  63 + for (String key : param.keySet()){
  64 + if (param.get(key) != null) {
  65 + stringBuffer.append(key + "=" + param.get(key));
  66 + if (index < param.size()) {
  67 + stringBuffer.append("&");
  68 + }
  69 + }
  70 + index++;
  71 + }
  72 + }
  73 +
  74 + String url = stringBuffer.toString();
  75 + Request request = new Request.Builder()
  76 + .get()
  77 + .url(url)
  78 + .build();
  79 + if (callback == null) {
  80 + try {
  81 + Response response = client.newCall(request).execute();
  82 + if (response.isSuccessful()) {
  83 + ResponseBody responseBody = response.body();
  84 + if (responseBody != null) {
  85 + String responseStr = responseBody.string();
  86 + responseJSON = JSON.parseObject(responseStr);
  87 + }
  88 + }else {
  89 + response.close();
  90 + Objects.requireNonNull(response.body()).close();
  91 + }
  92 + } catch (ConnectException e) {
  93 + logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
  94 + logger.info("请检查media配置并确认Assist已启动...");
  95 + }catch (IOException e) {
  96 + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
  97 + }
  98 + }else {
  99 + client.newCall(request).enqueue(new Callback(){
  100 +
  101 + @Override
  102 + public void onResponse(@NotNull Call call, @NotNull Response response){
  103 + if (response.isSuccessful()) {
  104 + try {
  105 + String responseStr = Objects.requireNonNull(response.body()).string();
  106 + callback.run(JSON.parseObject(responseStr));
  107 + } catch (IOException e) {
  108 + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
  109 + }
  110 +
  111 + }else {
  112 + response.close();
  113 + Objects.requireNonNull(response.body()).close();
  114 + }
  115 + }
  116 +
  117 + @Override
  118 + public void onFailure(@NotNull Call call, @NotNull IOException e) {
  119 + logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
  120 + logger.info("请检查media配置并确认Assist已启动...");
  121 + }
  122 + });
  123 + }
  124 +
  125 +
  126 +
  127 + return responseJSON;
  128 + }
  129 +
  130 +
  131 + public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){
  132 + Map<String, Object> param = new HashMap<>();
  133 + param.put("app",app);
  134 + param.put("stream",stream);
  135 + param.put("recordIng",true);
  136 + return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
  137 + }
  138 +
  139 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -215,7 +215,16 @@ public class ZLMHttpHookListener {
215 215 if (deviceChannel != null) {
216 216 ret.put("enable_audio", deviceChannel.isHasAudio());
217 217 }
  218 + // 如果是录像下载就设置视频间隔十秒
  219 + if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {
  220 + ret.put("mp4_max_second", 10);
  221 + ret.put("enable_mp4", true);
  222 + ret.put("enable_audio", true);
  223 + }
  224 +
218 225 }
  226 +
  227 +
219 228 return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
220 229 }
221 230  
... ... @@ -324,7 +333,6 @@ public class ZLMHttpHookListener {
324 333 if (mediaInfo != null) {
325 334 subscribe.response(mediaInfo, json);
326 335 }
327   -
328 336 }
329 337 // 流消失移除redis play
330 338 String app = item.getApp();
... ... @@ -441,6 +449,7 @@ public class ZLMHttpHookListener {
441 449 if ("rtp".equals(app)){
442 450 ret.put("close", true);
443 451 StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(streamId);
  452 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, null, streamId);
444 453 if (streamInfoForPlayCatch != null) {
445 454 // 如果在给上级推流,也不停止。
446 455 if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
... ... @@ -31,6 +31,7 @@ public class ZLMHttpHookSubscribe {
31 31 on_server_keepalive
32 32 }
33 33  
  34 + @FunctionalInterface
34 35 public interface Event{
35 36 void response(MediaServerItem mediaServerItem, JSONObject response);
36 37 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
1 1 package com.genersoft.iot.vmp.service;
2 2  
3 3 import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
4 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
5 6 import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback;
6 7 import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo;
... ... @@ -34,4 +35,9 @@ public interface IPlayService {
34 35 DeferredResult<ResponseEntity<String>> playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack);
35 36  
36 37 void zlmServerOffline(String mediaServerId);
  38 +
  39 + DeferredResult<ResponseEntity<String>> download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack);
  40 + DeferredResult<ResponseEntity<String>> download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack);
  41 +
  42 + StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream);
37 43 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -12,6 +12,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
12 12 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
13 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
14 14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
  15 +import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
  16 +import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
15 17 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
16 18 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
17 19 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
... ... @@ -28,7 +30,6 @@ import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
28 30 import com.genersoft.iot.vmp.service.IMediaService;
29 31 import com.genersoft.iot.vmp.service.IPlayService;
30 32 import gov.nist.javax.sip.stack.SIPDialog;
31   -import jdk.nashorn.internal.ir.RuntimeNode;
32 33 import org.slf4j.Logger;
33 34 import org.slf4j.LoggerFactory;
34 35 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -38,10 +39,8 @@ import org.springframework.stereotype.Service;
38 39 import org.springframework.util.ResourceUtils;
39 40 import org.springframework.web.context.request.async.DeferredResult;
40 41  
41   -import javax.sip.header.CallIdHeader;
42   -import javax.sip.header.Header;
43   -import javax.sip.message.Request;
44 42 import java.io.FileNotFoundException;
  43 +import java.math.BigDecimal;
45 44 import java.util.*;
46 45  
47 46 @SuppressWarnings(value = {"rawtypes", "unchecked"})
... ... @@ -72,6 +71,9 @@ public class PlayServiceImpl implements IPlayService {
72 71 private ZLMRESTfulUtils zlmresTfulUtils;
73 72  
74 73 @Autowired
  74 + private AssistRESTfulUtils assistRESTfulUtils;
  75 +
  76 + @Autowired
75 77 private IMediaService mediaService;
76 78  
77 79 @Autowired
... ... @@ -344,7 +346,7 @@ public class PlayServiceImpl implements IPlayService {
344 346 return result;
345 347 }
346 348  
347   - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId, uuid, result);
  349 + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId, uuid, result);
348 350 RequestMessage msg = new RequestMessage();
349 351 msg.setId(uuid);
350 352 msg.setKey(key);
... ... @@ -406,6 +408,136 @@ public class PlayServiceImpl implements IPlayService {
406 408 }
407 409  
408 410 @Override
  411 + public DeferredResult<ResponseEntity<String>> download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack) {
  412 + Device device = storager.queryVideoDevice(deviceId);
  413 + if (device == null) return null;
  414 + MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
  415 + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true);
  416 +
  417 + return download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed,infoCallBack, hookCallBack);
  418 + }
  419 +
  420 + @Override
  421 + public DeferredResult<ResponseEntity<String>> download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack) {
  422 + if (mediaServerItem == null || ssrcInfo == null) return null;
  423 + String uuid = UUID.randomUUID().toString();
  424 + String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId;
  425 + DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(30000L);
  426 + Device device = storager.queryVideoDevice(deviceId);
  427 + if (device == null) {
  428 + result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
  429 + return result;
  430 + }
  431 +
  432 + resultHolder.put(key, uuid, result);
  433 + RequestMessage msg = new RequestMessage();
  434 + msg.setId(uuid);
  435 + msg.setKey(key);
  436 + WVPResult<StreamInfo> wvpResult = new WVPResult<>();
  437 + msg.setData(wvpResult);
  438 + PlayBackResult<RequestMessage> downloadResult = new PlayBackResult<>();
  439 + downloadResult.setData(msg);
  440 +
  441 + Timer timer = new Timer();
  442 + timer.schedule(new TimerTask() {
  443 + @Override
  444 + public void run() {
  445 + logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId));
  446 + wvpResult.setCode(-1);
  447 + wvpResult.setMsg("录像下载请求超时");
  448 + downloadResult.setCode(-1);
  449 + hookCallBack.call(downloadResult);
  450 + SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream());
  451 + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  452 + if (dialog != null) {
  453 + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  454 + cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
  455 + }else {
  456 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  457 + mediaServerService.closeRTPServer(deviceId, channelId, ssrcInfo.getStream());
  458 + streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
  459 + }
  460 + cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
  461 + // 回复之前所有的点播请求
  462 + hookCallBack.call(downloadResult);
  463 + }
  464 + }, userSetup.getPlayTimeout());
  465 + cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
  466 + inviteStreamInfo -> {
  467 + logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
  468 + timer.cancel();
  469 + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
  470 + streamInfo.setStartTime(startTime);
  471 + streamInfo.setEndTime(endTime);
  472 + if (streamInfo == null) {
  473 + logger.warn("录像下载API调用失败!");
  474 + wvpResult.setCode(-1);
  475 + wvpResult.setMsg("录像下载API调用失败");
  476 + downloadResult.setCode(-1);
  477 + hookCallBack.call(downloadResult);
  478 + return ;
  479 + }
  480 + redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
  481 + wvpResult.setCode(0);
  482 + wvpResult.setMsg("success");
  483 + wvpResult.setData(streamInfo);
  484 + downloadResult.setCode(0);
  485 + downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
  486 + downloadResult.setResponse(inviteStreamInfo.getResponse());
  487 + hookCallBack.call(downloadResult);
  488 + }, event -> {
  489 + timer.cancel();
  490 + downloadResult.setCode(-1);
  491 + wvpResult.setCode(-1);
  492 + wvpResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg));
  493 + downloadResult.setEvent(event);
  494 + hookCallBack.call(downloadResult);
  495 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  496 + });
  497 + return result;
  498 + }
  499 +
  500 + @Override
  501 + public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) {
  502 + StreamInfo streamInfo = redisCatchStorage.queryDownload(deviceId, channelId, stream, null);
  503 + if (streamInfo != null) {
  504 + if (streamInfo.getProgress() == 1) {
  505 + return streamInfo;
  506 + }
  507 +
  508 + // 获取当前已下载时长
  509 + String mediaServerId = streamInfo.getMediaServerId();
  510 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  511 + if (mediaServerItem == null) {
  512 + logger.warn("查询录像信息时发现节点已离线");
  513 + return null;
  514 + }
  515 + if (mediaServerItem.getRecordAssistPort() != 0) {
  516 + JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, streamInfo.getApp(), streamInfo.getStream(), null);
  517 + if (jsonObject != null && jsonObject.getInteger("code") == 0) {
  518 + long duration = jsonObject.getLong("data");
  519 +
  520 + if (duration == 0) {
  521 + streamInfo.setProgress(0);
  522 + }else {
  523 + String startTime = streamInfo.getStartTime();
  524 + String endTime = streamInfo.getEndTime();
  525 + long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  526 + long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  527 +
  528 + BigDecimal currentCount = new BigDecimal(duration/1000);
  529 + BigDecimal totalCount = new BigDecimal(end-start);
  530 + BigDecimal divide = currentCount.divide(totalCount,2, BigDecimal.ROUND_HALF_UP);
  531 + double process = divide.doubleValue();
  532 + streamInfo.setProgress(process);
  533 + }
  534 + }
  535 + }
  536 + }
  537 + return streamInfo;
  538 + }
  539 +
  540 + @Override
409 541 public void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String uuid) {
410 542 RequestMessage msg = new RequestMessage();
411 543 msg.setKey(DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
... ... @@ -91,7 +91,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
91 91 MediaServerItem mediaInfo;
92 92 WVPResult<StreamInfo> wvpResult = new WVPResult<>();
93 93 wvpResult.setCode(0);
94   - if ("auto".equals(param.getMediaServerId())){
  94 + if (param.getMediaServerId() == null || "auto".equals(param.getMediaServerId())){
95 95 mediaInfo = mediaServerService.getMediaServerForMinimumLoad();
96 96 }else {
97 97 mediaInfo = mediaServerService.getOne(param.getMediaServerId());
... ...
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
... ... @@ -169,6 +169,8 @@ public interface IRedisCatchStorage {
169 169  
170 170 StreamInfo queryDownload(String deviceId, String channelId, String stream, String callId);
171 171  
  172 + boolean stopDownload(String deviceId, String channelId, String stream, String callId);
  173 +
172 174 /**
173 175 * 查找第三方系统留下的国标预设值
174 176 * @param queryKey
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -166,8 +166,42 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
166 166  
167 167 @Override
168 168 public boolean startDownload(StreamInfo stream, String callId) {
169   - return redis.set(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
170   - userSetup.getServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream);
  169 + boolean result;
  170 + if (stream.getProgress() == 1) {
  171 + result = redis.set(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
  172 + userSetup.getServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream);
  173 + }else {
  174 + result = redis.set(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
  175 + userSetup.getServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream, 60*60);
  176 + }
  177 + return result;
  178 + }
  179 + @Override
  180 + public boolean stopDownload(String deviceId, String channelId, String stream, String callId) {
  181 + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId);
  182 + if (deviceChannel != null) {
  183 + deviceChannel.setStreamId(null);
  184 + deviceChannel.setDeviceId(deviceId);
  185 + deviceChannelMapper.update(deviceChannel);
  186 + }
  187 + if (deviceId == null) deviceId = "*";
  188 + if (channelId == null) channelId = "*";
  189 + if (stream == null) stream = "*";
  190 + if (callId == null) callId = "*";
  191 + String key = String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
  192 + userSetup.getServerId(),
  193 + deviceId,
  194 + channelId,
  195 + stream,
  196 + callId
  197 + );
  198 + List<Object> scan = redis.scan(key);
  199 + if (scan.size() > 0) {
  200 + for (Object keyObj : scan) {
  201 + redis.del((String) keyObj);
  202 + }
  203 + }
  204 + return true;
171 205 }
172 206  
173 207 @Override
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.vmanager.gb28181.playback;
2   -
3   -import com.genersoft.iot.vmp.common.StreamInfo;
4   -import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo;
5   -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
6   -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
7   -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
8   -import com.genersoft.iot.vmp.service.IMediaServerService;
9   -import com.genersoft.iot.vmp.service.bean.SSRCInfo;
10   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
11   -import com.genersoft.iot.vmp.service.IPlayService;
12   -import io.swagger.annotations.Api;
13   -import io.swagger.annotations.ApiImplicitParam;
14   -import io.swagger.annotations.ApiImplicitParams;
15   -import io.swagger.annotations.ApiOperation;
16   -import org.slf4j.Logger;
17   -import org.slf4j.LoggerFactory;
18   -import org.springframework.beans.factory.annotation.Autowired;
19   -import org.springframework.http.HttpStatus;
20   -import org.springframework.http.ResponseEntity;
21   -import org.springframework.web.bind.annotation.CrossOrigin;
22   -import org.springframework.web.bind.annotation.GetMapping;
23   -import org.springframework.web.bind.annotation.PathVariable;
24   -import org.springframework.web.bind.annotation.RequestMapping;
25   -import org.springframework.web.bind.annotation.RestController;
26   -
27   -import com.alibaba.fastjson.JSONObject;
28   -import com.genersoft.iot.vmp.gb28181.bean.Device;
29   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
30   -import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
31   -import org.springframework.web.context.request.async.DeferredResult;
32   -
33   -import javax.sip.message.Response;
34   -import java.util.UUID;
35   -
36   -@Api(tags = "历史媒体下载")
37   -@CrossOrigin
38   -@RestController
39   -@RequestMapping("/api/download")
40   -public class DownloadController {
41   -
42   - private final static Logger logger = LoggerFactory.getLogger(DownloadController.class);
43   -
44   - @Autowired
45   - private SIPCommander cmder;
46   -
47   - @Autowired
48   - private IVideoManagerStorager storager;
49   -
50   - @Autowired
51   - private IRedisCatchStorage redisCatchStorage;
52   -
53   - // @Autowired
54   - // private ZLMRESTfulUtils zlmresTfulUtils;
55   -
56   - @Autowired
57   - private IPlayService playService;
58   -
59   - @Autowired
60   - private DeferredResultHolder resultHolder;
61   -
62   - @Autowired
63   - private IMediaServerService mediaServerService;
64   -
65   - @ApiOperation("开始历史媒体下载")
66   - @ApiImplicitParams({
67   - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
68   - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class),
69   - @ApiImplicitParam(name = "startTime", value = "开始时间", dataTypeClass = String.class),
70   - @ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class),
71   - @ApiImplicitParam(name = "downloadSpeed", value = "下载倍速", dataTypeClass = String.class),
72   - })
73   - @GetMapping("/start/{deviceId}/{channelId}")
74   - public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId, @PathVariable String channelId,
75   - String startTime, String endTime, String downloadSpeed) {
76   -
77   - if (logger.isDebugEnabled()) {
78   - logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed));
79   - }
80   - String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId;
81   - String uuid = UUID.randomUUID().toString();
82   - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
83   - // 超时处理
84   - result.onTimeout(()->{
85   - logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId));
86   - RequestMessage msg = new RequestMessage();
87   - msg.setId(uuid);
88   - msg.setKey(key);
89   - msg.setData("Timeout");
90   - resultHolder.invokeAllResult(msg);
91   - });
92   - if(resultHolder.exist(key, null)) {
93   - return result;
94   - }
95   - resultHolder.put(key, uuid, result);
96   - Device device = storager.queryVideoDevice(deviceId);
97   -
98   - MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
99   - if (newMediaServerItem == null) {
100   - logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId));
101   - RequestMessage msg = new RequestMessage();
102   - msg.setId(uuid);
103   - msg.setKey(key);
104   - msg.setData("Timeout");
105   - resultHolder.invokeAllResult(msg);
106   - return result;
107   - }
108   -
109   - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true);
110   -
111   - cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (InviteStreamInfo inviteStreamInfo) -> {
112   - logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
113   - playService.onPublishHandlerForDownload(inviteStreamInfo, deviceId, channelId, uuid);
114   - }, event -> {
115   - RequestMessage msg = new RequestMessage();
116   - msg.setId(uuid);
117   - msg.setKey(key);
118   - msg.setData(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg));
119   - resultHolder.invokeAllResult(msg);
120   - });
121   -
122   - return result;
123   - }
124   -
125   - @ApiOperation("停止历史媒体下载")
126   - @ApiImplicitParams({
127   - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
128   - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class),
129   - @ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class),
130   - })
131   - @GetMapping("/stop/{deviceId}/{channelId}/{stream}")
132   - public ResponseEntity<String> playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
133   -
134   - cmder.streamByeCmd(deviceId, channelId, stream, null);
135   -
136   - if (logger.isDebugEnabled()) {
137   - logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId));
138   - }
139   -
140   - if (deviceId != null && channelId != null) {
141   - JSONObject json = new JSONObject();
142   - json.put("deviceId", deviceId);
143   - json.put("channelId", channelId);
144   - return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
145   - } else {
146   - logger.warn("设备历史媒体下载停止API调用失败!");
147   - return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
148   - }
149   - }
150   -}
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.record;
2 2  
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.genersoft.iot.vmp.common.StreamInfo;
  5 +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo;
3 6 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  7 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  8 +import com.genersoft.iot.vmp.service.IMediaServerService;
  9 +import com.genersoft.iot.vmp.service.IPlayService;
  10 +import com.genersoft.iot.vmp.service.bean.SSRCInfo;
4 11 import io.swagger.annotations.Api;
5 12 import io.swagger.annotations.ApiImplicitParam;
6 13 import io.swagger.annotations.ApiImplicitParams;
... ... @@ -8,6 +15,7 @@ import io.swagger.annotations.ApiOperation;
8 15 import org.slf4j.Logger;
9 16 import org.slf4j.LoggerFactory;
10 17 import org.springframework.beans.factory.annotation.Autowired;
  18 +import org.springframework.http.HttpStatus;
11 19 import org.springframework.http.ResponseEntity;
12 20 import org.springframework.web.bind.annotation.CrossOrigin;
13 21 import org.springframework.web.bind.annotation.GetMapping;
... ... @@ -41,6 +49,12 @@ public class GBRecordController {
41 49 @Autowired
42 50 private DeferredResultHolder resultHolder;
43 51  
  52 + @Autowired
  53 + private IPlayService playService;
  54 +
  55 + @Autowired
  56 + private IMediaServerService mediaServerService;
  57 +
44 58 @ApiOperation("录像查询")
45 59 @ApiImplicitParams({
46 60 @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
... ... @@ -77,4 +91,111 @@ public class GBRecordController {
77 91 });
78 92 return result;
79 93 }
  94 +
  95 + @ApiOperation("开始历史媒体下载")
  96 + @ApiImplicitParams({
  97 + @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
  98 + @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class),
  99 + @ApiImplicitParam(name = "startTime", value = "开始时间", dataTypeClass = String.class),
  100 + @ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class),
  101 + @ApiImplicitParam(name = "downloadSpeed", value = "下载倍速", dataTypeClass = String.class),
  102 + })
  103 + @GetMapping("/download/start/{deviceId}/{channelId}")
  104 + public DeferredResult<ResponseEntity<String>> download(@PathVariable String deviceId, @PathVariable String channelId,
  105 + String startTime, String endTime, String downloadSpeed) {
  106 +
  107 + if (logger.isDebugEnabled()) {
  108 + logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed));
  109 + }
  110 +// String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId;
  111 +// String uuid = UUID.randomUUID().toString();
  112 +// DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
  113 +// // 超时处理
  114 +// result.onTimeout(()->{
  115 +// logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId));
  116 +// RequestMessage msg = new RequestMessage();
  117 +// msg.setId(uuid);
  118 +// msg.setKey(key);
  119 +// msg.setData("Timeout");
  120 +// resultHolder.invokeAllResult(msg);
  121 +// });
  122 +// if(resultHolder.exist(key, null)) {
  123 +// return result;
  124 +// }
  125 +// resultHolder.put(key, uuid, result);
  126 +// Device device = storager.queryVideoDevice(deviceId);
  127 +//
  128 +// MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
  129 +// if (newMediaServerItem == null) {
  130 +// logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId));
  131 +// RequestMessage msg = new RequestMessage();
  132 +// msg.setId(uuid);
  133 +// msg.setKey(key);
  134 +// msg.setData("Timeout");
  135 +// resultHolder.invokeAllResult(msg);
  136 +// return result;
  137 +// }
  138 +//
  139 +// SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true);
  140 +//
  141 +// cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (InviteStreamInfo inviteStreamInfo) -> {
  142 +// logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
  143 +// playService.onPublishHandlerForDownload(inviteStreamInfo, deviceId, channelId, uuid);
  144 +// }, event -> {
  145 +// RequestMessage msg = new RequestMessage();
  146 +// msg.setId(uuid);
  147 +// msg.setKey(key);
  148 +// msg.setData(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg));
  149 +// resultHolder.invokeAllResult(msg);
  150 +// });
  151 +
  152 + if (logger.isDebugEnabled()) {
  153 + logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
  154 + }
  155 +
  156 + DeferredResult<ResponseEntity<String>> result = playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed), null, hookCallBack->{
  157 + resultHolder.invokeResult(hookCallBack.getData());
  158 + });
  159 +
  160 + return result;
  161 + }
  162 +
  163 + @ApiOperation("停止历史媒体下载")
  164 + @ApiImplicitParams({
  165 + @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
  166 + @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class),
  167 + @ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class),
  168 + })
  169 + @GetMapping("/download/stop/{deviceId}/{channelId}/{stream}")
  170 + public ResponseEntity<String> playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
  171 +
  172 + cmder.streamByeCmd(deviceId, channelId, stream, null);
  173 +
  174 + if (logger.isDebugEnabled()) {
  175 + logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId));
  176 + }
  177 +
  178 + if (deviceId != null && channelId != null) {
  179 + JSONObject json = new JSONObject();
  180 + json.put("deviceId", deviceId);
  181 + json.put("channelId", channelId);
  182 + return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
  183 + } else {
  184 + logger.warn("设备历史媒体下载停止API调用失败!");
  185 + return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
  186 + }
  187 + }
  188 +
  189 + @ApiOperation("获取历史媒体下载进度")
  190 + @ApiImplicitParams({
  191 + @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
  192 + @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class),
  193 + @ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class),
  194 + })
  195 + @GetMapping("/download/progress/{deviceId}/{channelId}/{stream}")
  196 + public ResponseEntity<StreamInfo> getProgress(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) {
  197 +
  198 + StreamInfo streamInfo = playService.getDownLoadInfo(deviceId, channelId, stream);
  199 + return new ResponseEntity<>(streamInfo, HttpStatus.OK);
  200 + }
80 201 }
... ...
web_src/build/webpack.dev.conf.js
... ... @@ -32,6 +32,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
32 32 contentBase: false, // since we use CopyWebpackPlugin.
33 33 compress: true,
34 34 host: HOST || config.dev.host,
  35 + // host:'127.0.0.1',
35 36 port: PORT || config.dev.port,
36 37 open: config.dev.autoOpenBrowser,
37 38 overlay: config.dev.errorOverlay
... ...
web_src/config/index.js
... ... @@ -29,11 +29,13 @@ module.exports = {
29 29 },
30 30  
31 31 // Various Dev Server settings
32   - host: 'localhost', // can be overwritten by process.env.HOST
  32 + host:"127.0.0.1",
  33 + useLocalIp: false, // can be overwritten by process.env.HOST
33 34 port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
34 35 autoOpenBrowser: false,
35 36 errorOverlay: true,
36 37 notifyOnErrors: true,
  38 + hot: true,//自动保存
37 39 poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
38 40  
39 41  
... ...
web_src/package-lock.json
... ... @@ -57,7 +57,7 @@
57 57 "vue-template-compiler": "^2.5.2",
58 58 "webpack": "^3.6.0",
59 59 "webpack-bundle-analyzer": "^2.9.0",
60   - "webpack-dev-server": "^2.9.1",
  60 + "webpack-dev-server": "^2.11.5",
61 61 "webpack-merge": "^4.1.0"
62 62 },
63 63 "engines": {
... ... @@ -13382,7 +13382,7 @@
13382 13382 },
13383 13383 "node_modules/webpack-dev-server": {
13384 13384 "version": "2.11.5",
13385   - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz",
  13385 + "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz",
13386 13386 "integrity": "sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==",
13387 13387 "dev": true,
13388 13388 "dependencies": {
... ... @@ -25569,7 +25569,7 @@
25569 25569 },
25570 25570 "webpack-dev-server": {
25571 25571 "version": "2.11.5",
25572   - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz",
  25572 + "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz",
25573 25573 "integrity": "sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==",
25574 25574 "dev": true,
25575 25575 "requires": {
... ...
web_src/src/components/dialog/devicePlayer.vue
... ... @@ -175,6 +175,7 @@
175 175 </el-tabs>
176 176 </div>
177 177 </el-dialog>
  178 + <recordDownload ref="recordDownload"></recordDownload>
178 179 </div>
179 180 </template>
180 181  
... ... @@ -183,15 +184,15 @@
183 184 // import LivePlayer from '@liveqing/liveplayer'
184 185 // import player from '../dialog/easyPlayer.vue'
185 186 import player from '../dialog/jessibuca.vue'
  187 +import recordDownload from '../dialog/recordDownload.vue'
186 188 export default {
187 189 name: 'devicePlayer',
188 190 props: {},
189 191 components: {
190   - player,
  192 + player,recordDownload,
191 193 },
192 194 computed: {
193 195 getPlayerShared: function () {
194   -
195 196 return {
196 197 sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl),
197 198 sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>',
... ... @@ -250,7 +251,7 @@ export default {
250 251 that.tracks = [];
251 252 that.tracksLoading = true;
252 253 that.tracksNotLoaded = false;
253   - if (tab.name == "codec") {
  254 + if (tab.name === "codec") {
254 255 this.$axios({
255 256 method: 'get',
256 257 url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtmp&app='+ this.app +'&stream='+ this.streamId
... ... @@ -340,7 +341,7 @@ export default {
340 341 this.$refs.videoPlayer.pause()
341 342 that.$axios({
342 343 method: 'post',
343   - url: '/api/play/convert/' + that.streamId
  344 + url: '/api/gb_record/convert/' + that.streamId
344 345 }).then(function (res) {
345 346 if (res.data.code == 0) {
346 347 that.convertKey = res.data.key;
... ... @@ -474,8 +475,8 @@ export default {
474 475 console.log(this.seekTime)
475 476 if (that.streamId != "") {
476 477 that.stopPlayRecord(function () {
477   - that.streamId = "",
478   - that.playRecord(row);
  478 + that.streamId = "";
  479 + that.playRecord(row);
479 480 })
480 481 } else {
481 482 this.$axios({
... ... @@ -506,22 +507,36 @@ export default {
506 507 downloadRecord: function (row) {
507 508 let that = this;
508 509 if (that.streamId != "") {
509   - that.stopDownloadRecord(function () {
510   - that.streamId = "",
511   - that.downloadRecord(row);
  510 + that.stopDownloadRecord(function (res) {
  511 + if (res.code == 0) {
  512 + that.streamId = "";
  513 + that.downloadRecord(row);
  514 + }else {
  515 + this.$message({
  516 + showClose: true,
  517 + message: res.data.msg,
  518 + type: "error",
  519 + });
  520 + }
  521 +
512 522 })
513 523 } else {
514 524 this.$axios({
515 525 method: 'get',
516   - url: '/api/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
  526 + url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
517 527 row.endTime + '&downloadSpeed=4'
518 528 }).then(function (res) {
519   - var streamInfo = res.data;
520   - that.app = streamInfo.app;
521   - that.streamId = streamInfo.stream;
522   - that.mediaServerId = streamInfo.mediaServerId;
523   - that.videoUrl = that.getUrlByStreamInfo(streamInfo);
524   - that.recordPlay = true;
  529 + if (res.data.code == 0) {
  530 + let streamInfo = res.data.data;
  531 + that.recordPlay = false;
  532 + that.$refs.recordDownload.openDialog(that.deviceId, that.channelId, streamInfo.app, streamInfo.stream, streamInfo.mediaServerId);
  533 + }else {
  534 + that.$message({
  535 + showClose: true,
  536 + message: res.data.msg,
  537 + type: "error",
  538 + });
  539 + }
525 540 });
526 541 }
527 542 },
... ... @@ -530,9 +545,9 @@ export default {
530 545 this.videoUrl = '';
531 546 this.$axios({
532 547 method: 'get',
533   - url: '/api/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
534   - }).then(function (res) {
535   - if (callback) callback()
  548 + url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
  549 + }).then((res)=> {
  550 + if (callback) callback(res)
536 551 });
537 552 },
538 553 ptzCamera: function (command) {
... ...
web_src/src/components/dialog/recordDownload.vue 0 → 100644
  1 +<template>
  2 +<div id="recordDownload" >
  3 + <el-dialog :title="title" v-if="showDialog" width="45rem" :append-to-body="true" :close-on-click-modal="false" :visible.sync="showDialog" :destroy-on-close="true" @close="close()" center>
  4 + <el-row>
  5 + <el-col :span="18" style="padding-top: 7px;">
  6 + <el-progress :percentage="percentage"></el-progress>
  7 + </el-col>
  8 + <el-col :span="6" >
  9 +<!-- <el-dropdown size="mini" title="播放倍速" style="margin-left: 1px;" @command="gbScale">-->
  10 +<!-- <el-button-group>-->
  11 +<!-- <el-button size="mini" style="width: 100%">-->
  12 +<!-- {{scale}}倍速 <i class="el-icon-arrow-down el-icon&#45;&#45;right"></i>-->
  13 +<!-- </el-button>-->
  14 +<!-- </el-button-group>-->
  15 +<!-- <el-dropdown-menu slot="dropdown">-->
  16 +<!-- <el-dropdown-item command="1">1倍速</el-dropdown-item>-->
  17 +<!-- <el-dropdown-item command="2">2倍速</el-dropdown-item>-->
  18 +<!-- <el-dropdown-item command="4">4倍速</el-dropdown-item>-->
  19 +<!-- </el-dropdown-menu>-->
  20 +<!-- </el-dropdown>-->
  21 + <el-button icon="el-icon-download" v-if="percentage < 100" size="mini" title="点击下载可将以缓存部分下载到本地" @click="download()">停止缓存并下载</el-button>
  22 + </el-col>
  23 + </el-row>
  24 + </el-dialog>
  25 +</div>
  26 +</template>
  27 +
  28 +
  29 +<script>
  30 +
  31 +import moment from "moment";
  32 +
  33 +export default {
  34 + name: 'recordDownload',
  35 + created() {
  36 +
  37 +
  38 + },
  39 + data() {
  40 + return {
  41 + title: "四倍速下载中...",
  42 + deviceId: "",
  43 + channelId: "",
  44 + app: "",
  45 + stream: "",
  46 + mediaServerId: "",
  47 + showDialog: false,
  48 + scale: 1,
  49 + percentage: 0.00,
  50 + streamInfo: null,
  51 + taskId: null,
  52 + getProgressRun: false,
  53 + getProgressForFileRun: false,
  54 +
  55 + };
  56 + },
  57 + methods: {
  58 + openDialog: function (deviceId, channelId, app, stream, mediaServerId) {
  59 + this.deviceId = deviceId;
  60 + this.channelId = channelId;
  61 + this.app = app;
  62 + this.stream = stream;
  63 + this.mediaServerId = mediaServerId;
  64 + this.showDialog = true;
  65 + this.getProgressRun = true;
  66 + this.percentage = 0.0;
  67 + this.getProgressTimer()
  68 + },
  69 + getProgressTimer(){
  70 + if (!this.getProgressRun) {
  71 + return;
  72 + }
  73 + if (this.percentage == 100 ) {
  74 + this.getFileDownload();
  75 + return;
  76 + }
  77 + setTimeout( ()=>{
  78 + if (!this.showDialog) return;
  79 + this.getProgress(this.getProgressTimer())
  80 + }, 5000)
  81 + },
  82 + getProgress: function (callback){
  83 + this.$axios({
  84 + method: 'get',
  85 + url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
  86 + }).then((res)=> {
  87 + console.log(res)
  88 + console.log(res.data.progress)
  89 + this.streamInfo = res.data;
  90 + if (parseFloat(res.data.progress) == 1) {
  91 + this.percentage = 100;
  92 + }else {
  93 + this.percentage = (res.data.progress*100).toFixed(1);
  94 + }
  95 + if (callback)callback();
  96 + }).catch((e) =>{
  97 +
  98 + });
  99 + },
  100 + close: function (){
  101 + if (this.streamInfo.progress < 100) {
  102 + this.stopDownloadRecord();
  103 + }
  104 + this.showDialog=false;
  105 + this.getProgressRun = false;
  106 + this.getProgressForFileRun = false;
  107 + },
  108 + gbScale: function (scale){
  109 + this.scale = scale;
  110 + },
  111 + download: function (){
  112 + this.getProgressRun = false;
  113 + if (this.streamInfo != null ) {
  114 + if (this.streamInfo.progress < 1) {
  115 + // 发送停止缓存
  116 + this.stopDownloadRecord((res)=>{
  117 + this.getFileDownload()
  118 + })
  119 + }else {
  120 + this.getFileDownload()
  121 + }
  122 + }
  123 + },
  124 + stopDownloadRecord: function (callback) {
  125 + this.$axios({
  126 + method: 'get',
  127 + url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.stream
  128 + }).then((res)=> {
  129 + if (callback) callback(res)
  130 + });
  131 + },
  132 + getFileDownload: function (){
  133 + this.$axios({
  134 + method: 'get',
  135 + url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/add`,
  136 + params: {
  137 + app: this.app,
  138 + stream: this.stream,
  139 + startTime: null,
  140 + endTime: null,
  141 + }
  142 + }).then((res) =>{
  143 + if (res.data.code === 0 && res.data.msg === "success") {
  144 + // 查询进度
  145 + this.title = "录像文件处理中..."
  146 + this.taskId = res.data.data;
  147 + this.percentage = 0.0;
  148 + this.getProgressForFileRun = true;
  149 + this.getProgressForFileTimer();
  150 + }
  151 + }).catch(function (error) {
  152 + console.log(error);
  153 + });
  154 + },
  155 + getProgressForFileTimer: function (){
  156 + if (!this.getProgressForFileRun || this.percentage == 100) {
  157 + return;
  158 + }
  159 + setTimeout( ()=>{
  160 + if (!this.showDialog) return;
  161 + this.getProgressForFile(this.getProgressForFileTimer())
  162 + }, 1000)
  163 + },
  164 + getProgressForFile: function (callback){
  165 + this.$axios({
  166 + method: 'get',
  167 + url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/list`,
  168 + params: {
  169 + app: this.app,
  170 + stream: this.stream,
  171 + taskId: this.taskId,
  172 + isEnd: true,
  173 + }
  174 + }).then((res) => {
  175 + if (res.data.code == 0) {
  176 + this.percentage = parseFloat(res.data.data.percentage)*100
  177 + if (res.data.data[0].percentage === '1') {
  178 + this.getProgressForFileRun = false;
  179 + window.open(res.data.data[0].downloadFile)
  180 + this.close();
  181 + }else {
  182 + if (callback)callback()
  183 + }
  184 + }
  185 + }).catch(function (error) {
  186 + console.log(error);
  187 + });
  188 + }
  189 + }
  190 +};
  191 +</script>
  192 +
  193 +<style>
  194 +
  195 +</style>
... ...
web_src/src/components/test.vue deleted 100644 → 0
1   -<template>
2   -<div id="test">
3   - <div class="timeQuery" id="timeQuery">
4   - <div class="timeQuery-background" ></div>
5   - <div class="timeQuery-pointer">
6   - <div class="timeQuery-pointer-content" id="timeQueryPointer">
7   - <div class="timeQuery-pointer-handle" @mousedown.left="mousedownHandler" ></div>
8   - </div>
9   - </div>
10   -
11   - <div class="timeQuery-data" >
12   -
13   - <div class="timeQuery-data-cell" v-for="item of recordData" :style="'width:' + getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'" ></div>
14   - <!-- <div class="timeQuery-data-cell" style="width: 30%; left: 20%" @click="timeChoose"></div>-->
15   - <!-- <div class="timeQuery-data-cell" style="width: 60%; left: 20%" @click="timeChoose"></div>-->
16   - </div>
17   -
18   - <div class="timeQuery-label" >
19   - <div class="timeQuery-label-cell" style="left: 0%">
20   - <div class="timeQuery-label-cell-label">0</div>
21   - </div>
22   - <div v-for="index of timeNode" class="timeQuery-label-cell" :style="'left:' + (100.0/timeNode*index).toFixed(4) + '%'">
23   - <div class="timeQuery-label-cell-label">{{24/timeNode * index}}</div>
24   - </div>
25   - </div>
26   - </div>
27   -
28   -</div>
29   -</template>
30   -
31   -<script>
32   -export default {
33   - name: "test",
34   - data() {
35   - return {
36   - mouseDown: false,
37   - timeNode: 24,
38   - recordData:[
39   - {
40   - startTime: "2021-04-18 00:00:00",
41   - endTime: "2021-04-18 00:00:09",
42   - },
43   - {
44   - startTime: "2021-04-18 00:00:09",
45   - endTime: "2021-04-18 01:00:05",
46   - },
47   - {
48   - startTime: "2021-04-18 02:00:01",
49   - endTime: "2021-04-18 04:25:05",
50   - },
51   - {
52   - startTime: "2021-04-18 05:00:01",
53   - endTime: "2021-04-18 20:00:05",
54   - },
55   - ]
56   - };
57   - },
58   - mounted() {
59   - document.body.addEventListener("mouseup", this.mouseupHandler, false)
60   - document.body.addEventListener("mousemove", this.mousemoveHandler, false)
61   - },
62   - methods:{
63   - getTimeNode(){
64   - let mine = 20
65   - let width = document.getElementById("timeQuery").offsetWidth
66   - if (width/20 > 24){
67   - return 24
68   - }else if (width/20 > 12) {
69   - return 12
70   - }else if (width/20 > 6) {
71   - return 6
72   - }
73   - },
74   - timeChoose(event){
75   - console.log(event)
76   - },
77   - getDataWidth(item){
78   - let startTime = new Date(item.startTime);
79   - let endTime = new Date(item.endTime);
80   - let result = parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10))
81   - // console.log(result)
82   - return parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10))
83   - },
84   - getDataLeft(item){
85   - let startTime = new Date(item.startTime);
86   - let differenceTime = startTime.getTime() - new Date(item.startTime.substr(0,10) + " 00:00:00").getTime()
87   - let result = differenceTime/(24*60*60*10)
88   - console.log(differenceTime)
89   - console.log(result)
90   - return parseFloat(differenceTime/(24*60*60*10));
91   - },
92   - mousedownHandler(event){
93   - this.mouseDown = true
94   - },
95   - mousemoveHandler(event){
96   - if (this.mouseDown){
97   - document.getElementById("timeQueryPointer").style.left = (event.clientX - 20)+ "px"
98   - }
99   - },
100   - mouseupHandler(event){
101   - this.mouseDown = false
102   - }
103   - }
104   -}
105   -</script>
106   -
107   -<style scoped>
108   - .timeQuery{
109   - width: 96%;
110   - margin-left: 2%;
111   - margin-right: 2%;
112   - margin-top: 20%;
113   - position: absolute;
114   - }
115   - .timeQuery-background{
116   - height: 16px;
117   - width: 100%;
118   - background-color: #ececec;
119   - position: absolute;
120   - left: 0;
121   - top: 0;
122   - z-index: 10;
123   - box-shadow: #9d9d9d 0px 0px 10px inset;
124   - }
125   - .timeQuery-data{
126   - height: 16px;
127   - width: 100%;
128   - position: absolute;
129   - left: 0;
130   - top: 0;
131   - z-index: 11;
132   - }
133   - .timeQuery-data-cell{
134   - height: 10px;
135   - background-color: #888787;
136   - position: absolute;
137   - z-index: 11;
138   - -webkit-box-shadow: #9d9d9d 0px 0px 10px inset;
139   - margin-top: 3px;
140   - top: 100%;
141   - }
142   - .timeQuery-label{
143   - height: 16px;
144   - width: 100%;
145   - position: absolute;
146   - pointer-events: none;
147   - left: 0;
148   - top: 0;
149   - z-index: 11;
150   - }
151   - .timeQuery-label-cell{
152   - height: 16px;
153   - position: absolute;
154   - z-index: 12;
155   - width: 0px;
156   - border-right: 1px solid #b7b7b7;
157   - }
158   - .timeQuery-label-cell-label {
159   - width: 23px;
160   - text-align: center;
161   - height: 18px;
162   - margin-left: -10px;
163   - margin-top: -30px;
164   - color: #444;
165   - }
166   - .timeQuery-pointer{
167   - width: 0px;
168   - height: 18px;
169   - position: absolute;
170   - left: 0;
171   - }
172   - .timeQuery-pointer-content{
173   - width: 0px;
174   - height: 70px;
175   - position: absolute;
176   - border-right: 2px solid #f60303;
177   - z-index: 14;
178   - top: -30px;
179   - }
180   - .timeQuery-pointer-handle {
181   - width: 0;
182   - height: 0;
183   - border-top: 12px solid transparent;
184   - border-right: 12px solid transparent;
185   - border-bottom: 20px solid #ff0909;
186   - border-left: 12px solid transparent;
187   - cursor: no-drop;
188   - position: absolute;
189   - left: -11px;
190   - top: 50px;
191   - }
192   - /*.timeQuery-cell:after{*/
193   - /* content: "";*/
194   - /* height: 14px;*/
195   - /* border: 1px solid #e70303;*/
196   - /* position: absolute;*/
197   - /*}*/
198   -</style>
web_src/src/components/test2.vue deleted 100644 → 0
1   -<template>
2   -<div id="test2">
3   - <div class="timeQuery" style="width: 100%; height: 300px" id="timeQuery">
4   - </div>
5   -</div>
6   -</template>
7   -
8   -<script>
9   -
10   -import * as echarts from 'echarts';
11   -
12   -export default {
13   - name: "test2",
14   - data() {
15   - return {
16   - };
17   - },
18   - mounted() {
19   - var base = +new Date("2021-02-02 00:00:00");
20   - var oneDay = 24 * 3600 * 1000;
21   -
22   - var data = [[base, 10]];
23   -
24   - for (var i = 1; i < 24; i++) {
25   - var now = new Date(base += oneDay);
26   - data.push([
27   - new Date("2021-02-02 " + i+":00:00"), 10
28   - ]);
29   - }
30   - // 基于准备好的dom,初始化echarts实例
31   - var myChart = echarts.init(document.getElementById('timeQuery'));
32   - let option = {
33   -
34   - toolbox: {
35   - feature: {
36   - dataZoom: {
37   - yAxisIndex: 'none'
38   - },
39   - restore: {},
40   - saveAsImage: {}
41   - }
42   - },
43   - xAxis: {
44   - type: 'time',
45   - boundaryGap: false
46   - },
47   - yAxis: {
48   - type: 'value',
49   - show: false,
50   - splitLine:{show: false}, //去除网格线
51   - boundaryGap: [0, '100%']
52   - },
53   - dataZoom: [{
54   - type: 'inside',
55   - start: 0,
56   - end: 20
57   - }, {
58   - start: 0,
59   - end: 20
60   - }],
61   - series: [
62   - {
63   - name: '模拟数据',
64   - type: 'line',
65   - smooth: false,
66   - symbol: 'none',
67   - areaStyle: {},
68   - data: data
69   - }
70   - ]
71   - };
72   - // 绘制图表
73   - myChart.setOption(option);
74   - },
75   - methods:{
76   - getTimeNode(){
77   - let mine = 20
78   - let width = document.getElementById("timeQuery").offsetWidth
79   - if (width/20 > 24){
80   - return 24
81   - }else if (width/20 > 12) {
82   - return 12
83   - }else if (width/20 > 6) {
84   - return 6
85   - }
86   - },
87   - hoveEvent(event){
88   - console.log(2222222)
89   - console.log(event)
90   - },
91   - timeChoose(event){
92   - console.log(event)
93   - },
94   - getDataWidth(item){
95   - let startTime = new Date(item.startTime);
96   - let endTime = new Date(item.endTime);
97   - let result = parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10))
98   - // console.log(result)
99   - return parseFloat((endTime.getTime() - startTime.getTime())/(24*60*60*10))
100   - },
101   - getDataLeft(item){
102   - let startTime = new Date(item.startTime);
103   - let differenceTime = startTime.getTime() - new Date(item.startTime.substr(0,10) + " 00:00:00").getTime()
104   - let result = differenceTime/(24*60*60*10)
105   - console.log(differenceTime)
106   - console.log(result)
107   - return parseFloat(differenceTime/(24*60*60*10));
108   - }
109   - }
110   -}
111   -</script>
112   -
113   -<style scoped>
114   - .timeQuery{
115   - width: 96%;
116   - margin-left: 2%;
117   - margin-right: 2%;
118   - margin-top: 20%;
119   - position: absolute;
120   - }
121   - .timeQuery-background{
122   - height: 16px;
123   - width: 100%;
124   - background-color: #ececec;
125   - position: absolute;
126   - left: 0;
127   - top: 0;
128   - z-index: 10;
129   - box-shadow: #9d9d9d 0px 0px 10px inset;
130   - }
131   - .timeQuery-data{
132   - height: 16px;
133   - width: 100%;
134   - position: absolute;
135   - left: 0;
136   - top: 0;
137   - z-index: 11;
138   - }
139   - .timeQuery-data-cell{
140   - height: 10px;
141   - background-color: #888787;
142   - position: absolute;
143   - z-index: 11;
144   - -webkit-box-shadow: #9d9d9d 0px 0px 10px inset;
145   - margin-top: 3px;
146   - }
147   - .timeQuery-label{
148   - height: 16px;
149   - width: 100%;
150   - position: absolute;
151   - pointer-events: none;
152   - left: 0;
153   - top: 0;
154   - z-index: 11;
155   - }
156   - .timeQuery-label-cell{
157   - height: 16px;
158   - position: absolute;
159   - z-index: 12;
160   - width: 0px;
161   - border-right: 1px solid #b7b7b7;
162   - }
163   - .timeQuery-label-cell-label {
164   - width: 23px;
165   - text-align: center;
166   - height: 18px;
167   - margin-left: -10px;
168   - margin-top: -30px;
169   - color: #444;
170   - }
171   - .timeQuery-pointer{
172   - width: 0px;
173   - height: 18px;
174   - position: absolute;
175   - left: 0;
176   - }
177   - .timeQuery-pointer-content{
178   - width: 0px;
179   - height: 16px;
180   - position: absolute;
181   - border-right: 3px solid #f60303;
182   - z-index: 14;
183   - }
184   - /*.timeQuery-cell:after{*/
185   - /* content: "";*/
186   - /* height: 14px;*/
187   - /* border: 1px solid #e70303;*/
188   - /* position: absolute;*/
189   - /*}*/
190   -</style>
web_src/src/router/index.js
... ... @@ -11,7 +11,6 @@ import login from &#39;../components/Login.vue&#39;
11 11 import parentPlatformList from '../components/ParentPlatformList.vue'
12 12 import cloudRecord from '../components/CloudRecord.vue'
13 13 import mediaServerManger from '../components/MediaServerManger.vue'
14   -import test from '../components/test.vue'
15 14 import web from '../components/setting/Web.vue'
16 15 import sip from '../components/setting/Sip.vue'
17 16 import media from '../components/setting/Media.vue'
... ... @@ -97,11 +96,6 @@ export default new VueRouter({
97 96 component: media,
98 97 },
99 98 {
100   - path: '/test',
101   - name: 'test',
102   - component: test,
103   - },
104   - {
105 99 path: '/play/wasm/:url',
106 100 name: 'wasmPlayer',
107 101 component: wasmPlayer,
... ...