Commit 7d9cc96ef54399795deb5b7fc7682e6323dc1202
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
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--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 '../components/Login.vue' |
| 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, | ... | ... |