Commit 7d888274150e9415ae0fc16e67fefd1cdb4a69cc

Authored by 648540858
1 parent c5ddf598

处理服务重启或设备重新上线时的订阅,优化通道导入重复的处理

src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
... ... @@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.conf;
3 3 import org.springframework.beans.factory.annotation.Autowired;
4 4 import org.springframework.context.annotation.Bean;
5 5 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
6   -import org.springframework.scheduling.support.CronTrigger;
7 6 import org.springframework.stereotype.Component;
8 7  
9 8 import java.util.Map;
... ... @@ -40,4 +39,8 @@ public class DynamicTask {
40 39 }
41 40 }
42 41  
  42 + public boolean contains(String key) {
  43 + return futureMap.get(key) != null;
  44 + }
  45 +
43 46 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java
1 1 package com.genersoft.iot.vmp.conf.runner;
2 2  
3   -import com.genersoft.iot.vmp.common.VideoManagerConstants;
4 3 import com.genersoft.iot.vmp.conf.UserSetup;
  4 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  5 +import com.genersoft.iot.vmp.service.IDeviceService;
5 6 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
6 7 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
7 8 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -28,6 +29,9 @@ public class SipDeviceRunner implements CommandLineRunner {
28 29 @Autowired
29 30 private UserSetup userSetup;
30 31  
  32 + @Autowired
  33 + private IDeviceService deviceService;
  34 +
31 35 @Override
32 36 public void run(String... args) throws Exception {
33 37 // 读取redis没有心跳信息的则设置为离线,等收到下次心跳设置为在线
... ... @@ -36,9 +40,15 @@ public class SipDeviceRunner implements CommandLineRunner {
36 40 List<String> onlineForAll = redisCatchStorage.getOnlineForAll();
37 41 for (String deviceId : onlineForAll) {
38 42 storager.online(deviceId);
  43 + Device device = redisCatchStorage.getDevice(deviceId);
  44 + if (device != null && device.getSubscribeCycleForCatalog() > 0) {
  45 + // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
  46 + deviceService.addCatalogSubscribe(device);
  47 + }
39 48 }
40 49 // 重置cseq计数
41 50 redisCatchStorage.resetAllCSEQ();
42   - // TODO 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
  51 +
  52 +
43 53 }
44 54 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java
... ... @@ -35,6 +35,9 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
35 35  
36 36 @Autowired
37 37 private IVideoManagerStorager storager;
  38 +
  39 + @Autowired
  40 + private IDeviceService deviceService;
38 41  
39 42 @Autowired
40 43 private RedisUtil redis;
... ... @@ -57,6 +60,7 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
57 60 logger.debug("设备上线事件触发,deviceId:" + event.getDevice().getDeviceId() + ",from:" + event.getFrom());
58 61 }
59 62 Device device = event.getDevice();
  63 + if (device == null) return;
60 64 String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + event.getDevice().getDeviceId();
61 65  
62 66 switch (event.getFrom()) {
... ... @@ -84,15 +88,18 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
84 88 }
85 89  
86 90 device.setOnline(1);
87   - Device deviceInstore = storager.queryVideoDevice(device.getDeviceId());
88   - if (deviceInstore != null && deviceInstore.getOnline() == 0) {
  91 + Device deviceInStore = storager.queryVideoDevice(device.getDeviceId());
  92 + if (deviceInStore != null && deviceInStore.getOnline() == 0) {
89 93 List<DeviceChannel> deviceChannelList = storager.queryOnlineChannelsByDeviceId(device.getDeviceId());
90 94 eventPublisher.catalogEventPublish(null, deviceChannelList, CatalogEvent.ON);
91 95 }
92 96 // 处理上线监听
93 97 storager.updateDevice(device);
94 98  
95   - // TODO 上线添加订阅
  99 + // 上线添加订阅
  100 + if (device.getSubscribeCycleForCatalog() > 0) {
  101 + deviceService.addCatalogSubscribe(device);
  102 + }
96 103  
97 104 }
98 105 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
... ... @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory;
18 18 import org.springframework.beans.factory.annotation.Autowired;
19 19 import org.springframework.context.ApplicationListener;
20 20 import org.springframework.stereotype.Component;
  21 +import org.springframework.util.StringUtils;
21 22  
22 23 import java.util.*;
23 24  
... ... @@ -76,12 +77,12 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
76 77 }else if (event.getGbStreams() != null) {
77 78 if (platforms.size() > 0) {
78 79 for (GbStream gbStream : event.getGbStreams()) {
  80 + if (gbStream == null || StringUtils.isEmpty(gbStream.getGbId())) continue;
79 81 List<ParentPlatform> parentPlatformsForGB = storager.queryPlatFormListForStreamWithGBId(gbStream.getApp(),gbStream.getStream(), platforms);
80 82 parentPlatformMap.put(gbStream.getGbId(), parentPlatformsForGB);
81 83 }
82 84 }
83 85 }
84   -
85 86 }
86 87 switch (event.getType()) {
87 88 case CatalogEvent.ON:
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
... ... @@ -41,6 +41,8 @@ public class DeferredResultHolder {
41 41  
42 42 public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
43 43  
  44 + public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL";
  45 +
44 46 public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
45 47  
46 48 public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
... ... @@ -80,17 +80,14 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
80 80 Element rootElement = getRootElement(evt);
81 81 String cmd = XmlUtil.getText(rootElement, "CmdType");
82 82 if (CmdType.MOBILE_POSITION.equals(cmd)) {
83   - logger.info("接收到MobilePosition订阅");
84 83 processNotifyMobilePosition(evt, rootElement);
85 84 // } else if (CmdType.ALARM.equals(cmd)) {
86 85 // logger.info("接收到Alarm订阅");
87 86 // processNotifyAlarm(evt, rootElement);
88 87 } else if (CmdType.CATALOG.equals(cmd)) {
89   - logger.info("接收到Catalog订阅");
90 88 processNotifyCatalogList(evt, rootElement);
91 89 } else {
92 90 logger.info("接收到消息:" + cmd);
93   -// responseAck(evt, Response.OK);
94 91  
95 92 Response response = null;
96 93 response = getMessageFactory().createResponse(200, request);
... ... @@ -132,7 +129,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
132 129 SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId);
133 130 String sn = XmlUtil.getText(rootElement, "SN");
134 131 String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_MobilePosition_" + platformId;
135   -
  132 + logger.info("接收到{}的MobilePosition订阅", platformId);
136 133 StringBuilder resultXml = new StringBuilder(200);
137 134 resultXml.append("<?xml version=\"1.0\" ?>\r\n")
138 135 .append("<Response>\r\n")
... ... @@ -182,7 +179,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
182 179 SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId);
183 180 String sn = XmlUtil.getText(rootElement, "SN");
184 181 String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_Catalog_" + platformId;
185   -
  182 + logger.info("接收到{}的Catalog订阅", platformId);
186 183 StringBuilder resultXml = new StringBuilder(200);
187 184 resultXml.append("<?xml version=\"1.0\" ?>\r\n")
188 185 .append("<Response>\r\n")
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
... ... @@ -30,11 +30,15 @@ public class DeviceServiceImpl implements IDeviceService {
30 30 if (device == null || device.getSubscribeCycleForCatalog() < 0) {
31 31 return false;
32 32 }
  33 + if (dynamicTask.contains(device.getDeviceId())) {
  34 + logger.info("[添加目录订阅] 设备{}的目录订阅以存在", device.getDeviceId());
  35 + return false;
  36 + }
  37 + logger.info("[添加目录订阅] 设备{}", device.getDeviceId());
33 38 // 添加目录订阅
34 39 CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander);
35 40 catalogSubscribeTask.run();
36 41 // 提前开始刷新订阅
37   - // TODO 使用jain sip的当时刷新订阅
38 42 int subscribeCycleForCatalog = device.getSubscribeCycleForCatalog();
39 43 // 设置最小值为30
40 44 subscribeCycleForCatalog = Math.max(subscribeCycleForCatalog, 30);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java
... ... @@ -14,15 +14,23 @@ import java.util.Set;
14 14  
15 15 public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPushExcelDto> {
16 16  
  17 + private ErrorDataHandler errorDataHandler;
17 18 private IStreamPushService pushService;
18 19 private String defaultMediaServerId;
19 20 private List<StreamPushItem> streamPushItems = new ArrayList<>();
20 21 private Set<String> streamPushStreamSet = new HashSet<>();
21 22 private Set<String> streamPushGBSet = new HashSet<>();
  23 + private List<String> errorStreamList = new ArrayList<>();
  24 + private List<String> errorGBList = new ArrayList<>();
22 25  
23   - public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId) {
  26 + public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) {
24 27 this.pushService = pushService;
25 28 this.defaultMediaServerId = defaultMediaServerId;
  29 + this.errorDataHandler = errorDataHandler;
  30 + }
  31 +
  32 + public interface ErrorDataHandler{
  33 + void handle(List<String> streams, List<String> gbId);
26 34 }
27 35  
28 36 @Override
... ... @@ -32,9 +40,16 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener&lt;StreamPus
32 40 || StringUtils.isEmpty(streamPushExcelDto.getGbId())) {
33 41 return;
34 42 }
  43 + if (streamPushGBSet.contains(streamPushExcelDto.getGbId())) {
  44 + errorGBList.add(streamPushExcelDto.getGbId());
  45 + }
  46 + if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream())) {
  47 + errorStreamList.add(streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream());
  48 + }
35 49 if (streamPushGBSet.contains(streamPushExcelDto.getGbId()) || streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream())) {
36 50 return;
37 51 }
  52 +
38 53 StreamPushItem streamPushItem = new StreamPushItem();
39 54 streamPushItem.setApp(streamPushExcelDto.getApp());
40 55 streamPushItem.setStream(streamPushExcelDto.getStream());
... ... @@ -60,8 +75,11 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener&lt;StreamPus
60 75 @Override
61 76 public void doAfterAllAnalysed(AnalysisContext analysisContext) {
62 77 // 这里也要保存数据,确保最后遗留的数据也存储到数据库
63   - pushService.batchAdd(streamPushItems);
  78 + if (streamPushItems.size() > 0) {
  79 + pushService.batchAdd(streamPushItems);
  80 + }
64 81 streamPushGBSet.clear();
65 82 streamPushStreamSet.clear();
  83 + errorDataHandler.handle(errorStreamList, errorGBList);
66 84 }
67 85 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
... ... @@ -50,7 +50,7 @@ public interface StreamPushMapper {
50 50 StreamPushItem selectOne(String app, String stream);
51 51  
52 52 @Insert("<script>" +
53   - "INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
  53 + "REPLACE INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " +
54 54 "createStamp, aliveSecond, mediaServerId) " +
55 55 "VALUES <foreach collection='streamPushItems' item='item' index='index' separator=','>" +
56 56 "( '${item.app}', '${item.stream}', '${item.totalReaderCount}', '${item.originType}', " +
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java
... ... @@ -4,11 +4,14 @@ import com.alibaba.excel.EasyExcel;
4 4 import com.alibaba.excel.ExcelReader;
5 5 import com.alibaba.excel.read.metadata.ReadSheet;
6 6 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
7 9 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
8 10 import com.genersoft.iot.vmp.service.IMediaServerService;
9 11 import com.genersoft.iot.vmp.service.IStreamPushService;
10 12 import com.genersoft.iot.vmp.service.impl.StreamPushUploadFileHandler;
11 13 import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto;
  14 +import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
12 15 import com.github.pagehelper.PageInfo;
13 16 import io.swagger.annotations.Api;
14 17 import io.swagger.annotations.ApiImplicitParam;
... ... @@ -18,12 +21,19 @@ import org.apache.poi.sl.usermodel.Sheet;
18 21 import org.slf4j.Logger;
19 22 import org.slf4j.LoggerFactory;
20 23 import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.springframework.http.HttpStatus;
  25 +import org.springframework.http.ResponseEntity;
21 26 import org.springframework.stereotype.Controller;
22 27 import org.springframework.web.bind.annotation.*;
  28 +import org.springframework.web.context.request.async.DeferredResult;
23 29 import org.springframework.web.multipart.MultipartFile;
24 30  
25 31 import java.io.IOException;
26 32 import java.io.InputStream;
  33 +import java.util.HashMap;
  34 +import java.util.List;
  35 +import java.util.Map;
  36 +import java.util.UUID;
27 37  
28 38 @Api(tags = "推流信息管理")
29 39 @Controller
... ... @@ -39,6 +49,9 @@ public class StreamPushController {
39 49 @Autowired
40 50 private IMediaServerService mediaServerService;
41 51  
  52 + @Autowired
  53 + private DeferredResultHolder resultHolder;
  54 +
42 55 @ApiOperation("推流列表查询")
43 56 @ApiImplicitParams({
44 57 @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class),
... ... @@ -103,10 +116,44 @@ public class StreamPushController {
103 116 }
104 117 @PostMapping(value = "upload")
105 118 @ResponseBody
106   - public String uploadChannelFile(@RequestParam(value = "file") MultipartFile file){
  119 + public DeferredResult<ResponseEntity<WVPResult<Object>>> uploadChannelFile(@RequestParam(value = "file") MultipartFile file){
  120 +
  121 +
  122 + // 最多处理文件一个小时
  123 + DeferredResult<ResponseEntity<WVPResult<Object>>> result = new DeferredResult<>(60*60*1000L);
  124 + // 录像查询以channelId作为deviceId查询
  125 + String key = DeferredResultHolder.UPLOAD_FILE_CHANNEL;
  126 + String uuid = UUID.randomUUID().toString();
  127 +
107 128 if (file.isEmpty()) {
108   - return "fail";
  129 + logger.warn("通道导入文件为空");
  130 + WVPResult<Object> wvpResult = new WVPResult<>();
  131 + wvpResult.setCode(-1);
  132 + wvpResult.setMsg("文件为空");
  133 + result.setResult(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(wvpResult));
  134 + return result;
109 135 }
  136 + // 同时只处理一个文件
  137 + if (resultHolder.exist(key, null)) {
  138 + logger.warn("已有导入任务正在执行");
  139 + WVPResult<Object> wvpResult = new WVPResult<>();
  140 + wvpResult.setCode(-1);
  141 + wvpResult.setMsg("已有导入任务正在执行");
  142 + result.setResult(ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(wvpResult));
  143 + return result;
  144 + }
  145 +
  146 + resultHolder.put(key, uuid, result);
  147 + result.onTimeout(()->{
  148 + logger.warn("通道导入超时,可能文件过大");
  149 + RequestMessage msg = new RequestMessage();
  150 + msg.setKey(key);
  151 + WVPResult<Object> wvpResult = new WVPResult<>();
  152 + wvpResult.setCode(-1);
  153 + wvpResult.setMsg("导入超时,可能文件过大");
  154 + msg.setData(wvpResult);
  155 + resultHolder.invokeAllResult(msg);
  156 + });
110 157 //获取文件流
111 158 InputStream inputStream = null;
112 159 try {
... ... @@ -117,11 +164,29 @@ public class StreamPushController {
117 164 }
118 165 //传入参数
119 166 ExcelReader excelReader = EasyExcel.read(inputStream, StreamPushExcelDto.class,
120   - new StreamPushUploadFileHandler(streamPushService, mediaServerService.getDefaultMediaServer().getId())).build();
  167 + new StreamPushUploadFileHandler(streamPushService, mediaServerService.getDefaultMediaServer().getId(), (errorStreams, errorGBs)->{
  168 + logger.info("通道导入成功,存在重复App+Stream为{}个,存在国标ID为{}个", errorStreams.size(), errorGBs.size());
  169 + RequestMessage msg = new RequestMessage();
  170 + msg.setKey(key);
  171 + WVPResult<Map<String, List<String>>> wvpResult = new WVPResult<>();
  172 + if (errorStreams.size() == 0 && errorGBs.size() == 0) {
  173 + wvpResult.setCode(0);
  174 + wvpResult.setMsg("成功");
  175 + }else {
  176 + wvpResult.setCode(1);
  177 + wvpResult.setMsg("导入成功。但是存在重复数据");
  178 + Map<String, List<String>> errorData = new HashMap<>();
  179 + errorData.put("gbId", errorGBs);
  180 + errorData.put("stream", errorStreams);
  181 + wvpResult.setData(errorData);
  182 + }
  183 + msg.setData(wvpResult);
  184 + resultHolder.invokeAllResult(msg);
  185 + })).build();
121 186 ReadSheet readSheet = EasyExcel.readSheet(0).build();
122 187 excelReader.read(readSheet);
123 188 excelReader.finish();
124   - return "success";
  189 + return result;
125 190 }
126 191  
127 192  
... ...
web_src/src/components/PushVideoList.vue
... ... @@ -127,8 +127,6 @@
127 127 count: that.count
128 128 }
129 129 }).then(function (res) {
130   - console.log(res);
131   - console.log(res.data.list);
132 130 that.total = res.data.total;
133 131 that.pushList = res.data.list;
134 132 that.getDeviceListLoading = false;
... ...
web_src/src/components/dialog/importChannel.vue
... ... @@ -16,6 +16,8 @@
16 16 drag
17 17 :action="uploadUrl"
18 18 name="file"
  19 + :on-success="successHook"
  20 + :on-error="errorHook"
19 21 >
20 22 <i class="el-icon-upload"></i>
21 23 <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
... ... @@ -23,14 +25,19 @@
23 25 </el-upload>
24 26 </div>
25 27 </el-dialog>
  28 + <ShowErrorData ref="showErrorData" :gbIds="errorGBIds" :streams="errorStreams" ></ShowErrorData>
26 29 </div>
27 30 </template>
28 31  
29 32 <script>
30 33  
  34 +import ShowErrorData from './importChannelShowErrorData.vue'
  35 +
31 36 export default {
32 37 name: "importChannel",
33   - computed: {},
  38 + components: {
  39 + ShowErrorData,
  40 + },
34 41 created() {},
35 42 data() {
36 43 return {
... ... @@ -38,6 +45,8 @@ export default {
38 45 showDialog: false,
39 46 isLoging: false,
40 47 isEdit: false,
  48 + errorStreams: null,
  49 + errorGBIds: null,
41 50 uploadUrl: process.env.NODE_ENV === 'development'?`debug/api/push/upload`:`api/push/upload`,
42 51 };
43 52 },
... ... @@ -73,8 +82,35 @@ export default {
73 82 },
74 83 close: function () {
75 84 this.showDialog = false;
76   - this.$refs.form.resetFields();
77 85 },
  86 + successHook: function(response, file, fileList){
  87 + if (response.code === 0) {
  88 + this.$message({
  89 + showClose: true,
  90 + message: response.msg,
  91 + type: "success",
  92 + });
  93 + }else if (response.code === 1) {
  94 + this.errorGBIds = response.data.gbId
  95 + this.errorStreams = response.data.stream
  96 + console.log(this.$refs)
  97 + console.log(this.$refs.showErrorData)
  98 + this.$refs.showErrorData.openDialog()
  99 + }else {
  100 + this.$message({
  101 + showClose: true,
  102 + message: response.msg,
  103 + type: "error",
  104 + });
  105 + }
  106 + },
  107 + errorHook: function (err, file, fileList) {
  108 + this.$message({
  109 + showClose: true,
  110 + message: err,
  111 + type: "error",
  112 + });
  113 + }
78 114 },
79 115 };
80 116 </script>
... ... @@ -82,4 +118,8 @@ export default {
82 118 .upload-box{
83 119 text-align: center;
84 120 }
  121 +.errDataBox{
  122 + max-height: 15rem;
  123 + overflow: auto;
  124 +}
85 125 </style>
... ...
web_src/src/components/dialog/importChannelShowErrorData.vue 0 → 100644
  1 +<template>
  2 + <div id="importChannelShowErrorData" v-loading="isLoging">
  3 + <el-dialog
  4 + title="导入通道数据成功,但数据存在重复"
  5 + width="30rem"
  6 + top="2rem"
  7 + :append-to-body="true"
  8 + :close-on-click-modal="false"
  9 + :visible.sync="showDialog"
  10 + :destroy-on-close="true"
  11 + @close="close()"
  12 + >
  13 + <div >
  14 + 重复国标ID:
  15 + <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="gbIds.join(',')" @success="$message({type:'success', message:'成功拷贝到粘贴板'})">复制</el-button>
  16 + <ul class="errDataBox">
  17 + <li v-for="id in gbIds" >
  18 + {{ id }}
  19 + </li>
  20 + </ul>
  21 + </div>
  22 +
  23 + <div >
  24 + 重复App/stream:
  25 + <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="streams.join(',')" @success="$message({type:'success', message:'成功拷贝到粘贴板'})">复制</el-button>
  26 + <ul class="errDataBox">
  27 + <li v-for="id in streams" >
  28 + {{ id }}
  29 + </li>
  30 + </ul>
  31 + </div>
  32 + </el-dialog>
  33 + </div>
  34 +</template>
  35 +
  36 +<script>
  37 +
  38 +export default {
  39 + name: "importChannelShowErrorData",
  40 + computed: {},
  41 + created() {},
  42 + props: ['gbIds', 'streams'],
  43 + data() {
  44 + return {
  45 + isLoging: false,
  46 + showDialog: false,
  47 + };
  48 + },
  49 + methods: {
  50 + openDialog: function () {
  51 + this.showDialog = true;
  52 + },
  53 + close: function () {
  54 + this.showDialog = false;
  55 + },
  56 + },
  57 +};
  58 +</script>
  59 +<style>
  60 +.errDataBox{
  61 + max-height: 15rem;
  62 + overflow: auto;
  63 +}
  64 +</style>
... ...
web_src/src/components/dialog/jessibuca.vue
... ... @@ -78,7 +78,7 @@ export default {
78 78 this.jessibuca = new window.Jessibuca(Object.assign(
79 79 {
80 80 container: this.$refs.container,
81   - videoBuffer: 0.5, // 最大缓冲时长,单位秒
  81 + videoBuffer: 0.2, // 最大缓冲时长,单位秒
82 82 isResize: true,
83 83 isFlv: true,
84 84 decoder: "./static/js/jessibuca/index.js",
... ...