Browse Source

Merge remote-tracking branch 'origin/wvp-28181-2.0' into wvp-28181-2.0

pull/359/head
chenjialing 4 years ago
parent
commit
0a32f717a1
  1. 19
      pom.xml
  2. 22
      src/main/java/com/genersoft/iot/vmp/common/SystemInfoDto.java
  3. 8
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  4. 34
      src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java
  5. 7
      src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java
  6. 6
      src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java
  7. 4
      src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
  8. 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java
  9. 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  10. 3
      src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java
  11. 10
      src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
  12. 14
      src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
  13. 8
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
  14. 55
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
  15. 81
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java
  16. 6
      src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
  17. 10
      src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
  18. 58
      src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
  19. 26
      src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java
  20. 26
      src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java
  21. 28
      src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
  22. 46
      src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
  23. 13
      src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
  24. 12
      src/main/java/com/genersoft/iot/vmp/utils/CollectionUtil.java
  25. 41
      src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java
  26. 92
      src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java
  27. 54
      src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java
  28. 28
      src/main/java/com/genersoft/iot/vmp/utils/node/ForestNode.java
  29. 68
      src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeManager.java
  30. 51
      src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeMerger.java
  31. 42
      src/main/java/com/genersoft/iot/vmp/utils/node/INode.java
  32. 21
      src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java
  33. 18
      src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
  34. 17
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/BatchGBStreamParam.java
  35. 65
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTree.java
  36. 20
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTreeNode.java
  37. 23
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamPushExcelDto.java
  38. 31
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java
  39. 9
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
  40. 25
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
  41. 45
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
  42. 37
      src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java
  43. 1
      web_src/index.html
  44. 19
      web_src/src/api/deviceApi.js
  45. 108
      web_src/src/components/PushVideoList.vue
  46. 3
      web_src/src/components/UiHeader.vue
  47. 70
      web_src/src/components/channelTree.vue
  48. 74
      web_src/src/components/channelTreeItem.vue
  49. 3
      web_src/src/components/dialog/catalogEdit.vue
  50. 16
      web_src/src/components/dialog/chooseChannel.vue
  51. 41
      web_src/src/components/dialog/chooseChannelForCatalog.vue
  52. 303
      web_src/src/components/dialog/chooseChannelForGb.vue
  53. 265
      web_src/src/components/dialog/chooseChannelForStream.vue
  54. 161
      web_src/src/components/dialog/getCatalog.vue
  55. 4
      web_src/src/components/dialog/importChannel.vue
  56. 317
      web_src/src/components/jessibuca.vue
  57. 357
      web_src/src/components/live.vue
  58. 5
      web_src/src/router/index.js
  59. 14
      web_src/static/css/iconfont.css
  60. BIN
      web_src/static/css/iconfont.woff2
  61. BIN
      web_src/static/favicon.ico
  62. BIN
      web_src/static/file/推流通道导入.zip

19
pom.xml

@ -155,7 +155,6 @@
<version>1.7.35</version>
</dependency>
<!-- xml解析库 -->
<dependency>
<groupId>org.dom4j</groupId>
@ -212,6 +211,13 @@
<version>3.0.4</version>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>6.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
@ -271,5 +277,16 @@
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>

22
src/main/java/com/genersoft/iot/vmp/common/SystemInfoDto.java

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.common;
public class SystemInfoDto<T> {
private String time;
private T data;
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

8
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@ -60,7 +60,13 @@ public class VideoManagerConstants {
public static final String SIP_SN_PREFIX = "VMP_SIP_SN_";
public static final String SIP_SUBSCRIBE_PREFIX = "SIP_SUBSCRIBE_";
public static final String SIP_SUBSCRIBE_PREFIX = "VMP_SIP_SUBSCRIBE_";
public static final String SYSTEM_INFO_CPU_PREFIX = "VMP_SYSTEM_INFO_CPU_";
public static final String SYSTEM_INFO_MEM_PREFIX = "VMP_SYSTEM_INFO_MEM_";
public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_";
//************************** redis 消息*********************************
public static final String WVP_MSG_STREAM_CHANGE_PREFIX = "WVP_MSG_STREAM_CHANGE_";

34
src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java

@ -0,0 +1,34 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 获取系统信息写入redis
*/
@Component
public class SystemInfoTimerTask {
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Scheduled(fixedRate = 1000) //每1秒执行一次
public void execute(){
try {
double cpuInfo = SystemInfoUtils.getCpuInfo();
redisCatchStorage.addCpuInfo(cpuInfo);
double memInfo = SystemInfoUtils.getMemInfo();
redisCatchStorage.addMemInfo(memInfo);
Map<String, String> networkInterfaces = SystemInfoUtils.getNetworkInterfaces();
redisCatchStorage.addNetInfo(networkInterfaces);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

7
src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java

@ -131,7 +131,7 @@ public class EventPublisher {
}
@Async
public void catalogEventPublishForStream(String platformId, List<GbStream> gbStreams, String type) {
public void catalogEventPublishForStream(String platformId, GbStream[] gbStreams, String type) {
CatalogEvent outEvent = new CatalogEvent(this);
outEvent.setGbStreams(gbStreams);
outEvent.setType(type);
@ -141,8 +141,7 @@ public class EventPublisher {
@Async
public void catalogEventPublishForStream(String platformId, GbStream gbStream, String type) {
List<GbStream> gbStreamList = new ArrayList<>();
gbStreamList.add(gbStream);
catalogEventPublishForStream(platformId, gbStreamList, type);
GbStream[] gbStreams = {gbStream};
catalogEventPublishForStream(platformId, gbStreams, type);
}
}

6
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java

@ -20,7 +20,7 @@ public class CatalogEvent extends ApplicationEvent {
public static final String UPDATE = "UPDATE"; // 更新
private List<DeviceChannel> deviceChannels;
private List<GbStream> gbStreams;
private GbStream[] gbStreams;
private String type;
private String platformId;
@ -48,11 +48,11 @@ public class CatalogEvent extends ApplicationEvent {
this.platformId = platformId;
}
public List<GbStream> getGbStreams() {
public GbStream[] getGbStreams() {
return gbStreams;
}
public void setGbStreams(List<GbStream> gbStreams) {
public void setGbStreams(GbStream[] gbStreams) {
this.gbStreams = gbStreams;
}
}

4
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java

@ -94,7 +94,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
if (event.getDeviceChannels() != null) {
deviceChannelList.addAll(event.getDeviceChannels());
}
if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
if (event.getGbStreams() != null && event.getGbStreams().length > 0){
for (GbStream gbStream : event.getGbStreams()) {
DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform.getDeviceGBId());
deviceChannelList.add(deviceChannelByStream);
@ -134,7 +134,7 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
if (event.getDeviceChannels() != null) {
deviceChannelList.addAll(event.getDeviceChannels());
}
if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
if (event.getGbStreams() != null && event.getGbStreams().length > 0){
for (GbStream gbStream : event.getGbStreams()) {
DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform.getDeviceGBId());
deviceChannelList.add(deviceChannelByStream);

1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java

@ -96,7 +96,6 @@ public class CatalogNotifyMessageHandler extends SIPRequestProcessorParent imple
if (channelReduces.size() > 0) {
for (ChannelReduce channelReduce : channelReduces) {
DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
// TODO 目前暂时认为这里只用通道没有目录
deviceChannel.setParental(0);
deviceChannel.setParentId(channelReduce.getCatalogId());

2
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@ -371,7 +371,7 @@ public class ZLMHttpHookListener {
}
}
if (gbStreams.size() > 0) {
eventPublisher.catalogEventPublishForStream(null, gbStreams, CatalogEvent.ON);
eventPublisher.catalogEventPublishForStream(null, gbStreams.toArray(new GbStream[0]), CatalogEvent.ON);
}
}else {

3
src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java

@ -17,7 +17,7 @@ public interface IGbStreamService {
* @param count
* @return
*/
PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId);
PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId, String catalogId,String query,Boolean pushing,String mediaServerId);
/**
@ -43,4 +43,5 @@ public interface IGbStreamService {
DeviceChannel getDeviceChannelListByStream(GbStream gbStream, String catalogId, String deviceGBId);
void sendCatalogMsg(GbStream gbStream, String type);
void sendCatalogMsgs(List<GbStream> gbStreams, String type);
}

10
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java

@ -30,11 +30,9 @@ public interface IStreamPushService {
/**
* 获取
* @param page
* @param count
* @return
*/
PageInfo<StreamPushItem> getPushList(Integer page, Integer count);
PageInfo<StreamPushItem> getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId);
List<StreamPushItem> getPushList(String mediaSererId);
StreamPushItem transform(MediaItem item);
@ -68,4 +66,8 @@ public interface IStreamPushService {
boolean saveToRandomGB();
void batchAdd(List<StreamPushItem> streamPushExcelDtoList);
boolean batchStop(List<GbStream> streamPushItems);
void batchAddForUpload(String platformId, String catalogId, List<StreamPushItem> streamPushItems);
}

14
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java

@ -20,6 +20,7 @@ import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@ -51,9 +52,9 @@ public class GbStreamServiceImpl implements IGbStreamService {
private EventPublisher eventPublisher;
@Override
public PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId) {
public PageInfo<GbStream> getAll(Integer page, Integer count, String platFormId, String catalogId, String query, Boolean pushing, String mediaServerId) {
PageHelper.startPage(page, count);
List<GbStream> all = gbStreamMapper.selectAll(platFormId);
List<GbStream> all = gbStreamMapper.selectAll(platFormId, catalogId, query, pushing, mediaServerId);
return new PageInfo<>(all);
}
@ -69,6 +70,7 @@ public class GbStreamServiceImpl implements IGbStreamService {
boolean result = false;
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
ParentPlatform parentPlatform = platformMapper.getParentPlatByServerGBId(platformId);
if (catalogId == null) catalogId = parentPlatform.getCatalogId();
try {
List<DeviceChannel> deviceChannelList = new ArrayList<>();
for (GbStream gbStream : gbStreams) {
@ -144,8 +146,16 @@ public class GbStreamServiceImpl implements IGbStreamService {
gbStreams.add(streamProxyItem);
}
}
sendCatalogMsgs(gbStreams, type);
}
@Override
public void sendCatalogMsgs(List<GbStream> gbStreams, String type) {
if (gbStreams.size() > 0) {
for (GbStream gs : gbStreams) {
if (StringUtils.isEmpty(gs.getGbId())){
continue;
}
List<ParentPlatform> parentPlatforms = platformGbStreamMapper.selectByAppAndStream(gs.getApp(), gs.getStream());
if (parentPlatforms.size() > 0) {
for (ParentPlatform parentPlatform : parentPlatforms) {

8
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java

@ -86,8 +86,8 @@ public class MediaServiceImpl implements IMediaService {
if (mediaInfo.getRtspSSLPort() != 0) {
streamInfoResult.setRtsps(String.format("rtsps://%s:%s/%s/%s", addr, mediaInfo.getRtspSSLPort(), app, stream));
}
streamInfoResult.setFlv(String.format("http://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpPort(), app, stream));
streamInfoResult.setWs_flv(String.format("ws://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpPort(), app, stream));
streamInfoResult.setFlv(String.format("http://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpPort(), app, stream));
streamInfoResult.setWs_flv(String.format("ws://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpPort(), app, stream));
streamInfoResult.setHls(String.format("http://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpPort(), app, stream));
streamInfoResult.setWs_hls(String.format("ws://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpPort(), app, stream));
streamInfoResult.setFmp4(String.format("http://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpPort(), app, stream));
@ -95,8 +95,8 @@ public class MediaServiceImpl implements IMediaService {
streamInfoResult.setTs(String.format("http://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpPort(), app, stream));
streamInfoResult.setWs_ts(String.format("ws://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpPort(), app, stream));
if (mediaInfo.getHttpSSlPort() != 0) {
streamInfoResult.setHttps_flv(String.format("https://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpSSlPort(), app, stream));
streamInfoResult.setWss_flv(String.format("wss://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpSSlPort(), app, stream));
streamInfoResult.setHttps_flv(String.format("https://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpSSlPort(), app, stream));
streamInfoResult.setWss_flv(String.format("wss://%s:%s/%s/%s.live.flv", addr, mediaInfo.getHttpSSlPort(), app, stream));
streamInfoResult.setHttps_hls(String.format("https://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpSSlPort(), app, stream));
streamInfoResult.setWss_hls(String.format("wss://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpSSlPort(), app, stream));
streamInfoResult.setHttps_fmp4(String.format("https://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpSSlPort(), app, stream));

55
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java

@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.UserSetup;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
@ -18,10 +19,7 @@ import com.genersoft.iot.vmp.service.IGbStreamService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.StreamPushMapper;
import com.genersoft.iot.vmp.storager.dao.*;
import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
@ -43,6 +41,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
@Autowired
private ParentPlatformMapper parentPlatformMapper;
@Autowired
private PlatformCatalogMapper platformCatalogMapper;
@Autowired
private PlatformGbStreamMapper platformGbStreamMapper;
@ -95,13 +96,12 @@ public class StreamPushServiceImpl implements IStreamPushService {
streamPushItem.setMediaServerId(item.getMediaServerId());
streamPushItem.setStream(item.getStream());
streamPushItem.setAliveSecond(item.getAliveSecond());
streamPushItem.setCreateStamp(item.getCreateStamp());
streamPushItem.setOriginSock(item.getOriginSock());
streamPushItem.setTotalReaderCount(item.getTotalReaderCount());
streamPushItem.setOriginType(item.getOriginType());
streamPushItem.setOriginTypeStr(item.getOriginTypeStr());
streamPushItem.setOriginUrl(item.getOriginUrl());
streamPushItem.setCreateStamp(item.getCreateStamp());
streamPushItem.setCreateStamp(item.getCreateStamp() * 1000);
streamPushItem.setAliveSecond(item.getAliveSecond());
streamPushItem.setStatus(true);
streamPushItem.setStreamType("push");
@ -110,9 +110,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
}
@Override
public PageInfo<StreamPushItem> getPushList(Integer page, Integer count) {
public PageInfo<StreamPushItem> getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId) {
PageHelper.startPage(page, count);
List<StreamPushItem> all = streamPushMapper.selectAll();
List<StreamPushItem> all = streamPushMapper.selectAllForList(query, pushing, mediaServerId);
return new PageInfo<>(all);
}
@ -355,8 +355,47 @@ public class StreamPushServiceImpl implements IStreamPushService {
}
}
}
}
}
@Override
public void batchAddForUpload(String platformId, String catalogId, List<StreamPushItem> streamPushItems) {
streamPushMapper.addAll(streamPushItems);
gbStreamMapper.batchAdd(streamPushItems);
if (platformId != null) {
ParentPlatform platform = parentPlatformMapper.getParentPlatByServerGBId(platformId);
if (platform != null) {
if (catalogId == null) {
catalogId = platform.getCatalogId();
}else {
PlatformCatalog catalog = platformCatalogMapper.select(catalogId);
if (catalog == null) {
return;
}
}
platformGbStreamMapper.batchAdd(platformId, catalogId, streamPushItems);
eventPublisher.catalogEventPublishForStream(platformId, streamPushItems.toArray(new GbStream[0]), CatalogEvent.ADD);
}
}
}
@Override
public boolean batchStop(List<GbStream> gbStreams) {
if (gbStreams == null || gbStreams.size() == 0) {
return false;
}
gbStreamService.sendCatalogMsgs(gbStreams, CatalogEvent.DEL);
int delStream = streamPushMapper.delAllForGbStream(gbStreams);
gbStreamMapper.batchDelForGbStream(gbStreams);
platformGbStreamMapper.delByGbStreams(gbStreams);
if (delStream > 0) {
for (GbStream gbStream : gbStreams) {
MediaServerItem mediaServerItem = mediaServerService.getOne(gbStream.getMediaServerId());
zlmresTfulUtils.closeStreams(mediaServerItem, gbStream.getApp(), gbStream.getStream());
}
}
return true;
}
}

81
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java

@ -7,10 +7,7 @@ import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPushExcelDto> {
@ -18,10 +15,13 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus
private IStreamPushService pushService;
private String defaultMediaServerId;
private List<StreamPushItem> streamPushItems = new ArrayList<>();
private Map<String, UploadData> streamPushItemsForPlatform = new HashMap<>();
private Set<String> streamPushStreamSet = new HashSet<>();
private Set<String> streamPushGBSet = new HashSet<>();
private List<String> errorStreamList = new ArrayList<>();
private List<String> errorGBList = new ArrayList<>();
// 读取数量计数器
private int loadedSize = 0;
public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) {
this.pushService = pushService;
@ -33,6 +33,16 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus
void handle(List<String> streams, List<String> gbId);
}
private class UploadData{
public String platformId;
public Map<String, List<StreamPushItem>> catalogData = new HashMap<>();
public List<StreamPushItem> streamPushItems = new ArrayList<>();
public UploadData(String platformId) {
this.platformId = platformId;
}
}
@Override
public void invoke(StreamPushExcelDto streamPushExcelDto, AnalysisContext analysisContext) {
if (StringUtils.isEmpty(streamPushExcelDto.getApp())
@ -43,10 +53,10 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus
if (streamPushGBSet.contains(streamPushExcelDto.getGbId())) {
errorGBList.add(streamPushExcelDto.getGbId());
}
if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream())) {
if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId())) {
errorStreamList.add(streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream());
}
if (streamPushGBSet.contains(streamPushExcelDto.getGbId()) || streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream())) {
if (streamPushGBSet.contains(streamPushExcelDto.getGbId()) || streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId())) {
return;
}
@ -62,24 +72,69 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus
streamPushItem.setOriginType(2);
streamPushItem.setOriginTypeStr("rtsp_push");
streamPushItem.setTotalReaderCount("0");
streamPushItems.add(streamPushItem);
streamPushItem.setPlatformId(streamPushExcelDto.getPlatformId());
streamPushItem.setCatalogId(streamPushExcelDto.getCatalogId());
if (StringUtils.isEmpty(streamPushExcelDto.getPlatformId())) {
streamPushItems.add(streamPushItem);
}else {
UploadData uploadData = streamPushItemsForPlatform.get(streamPushExcelDto.getPlatformId());
if (uploadData == null) {
uploadData = new UploadData(streamPushExcelDto.getPlatformId());
streamPushItemsForPlatform.put(streamPushExcelDto.getPlatformId(), uploadData);
}
if (!StringUtils.isEmpty(streamPushExcelDto.getCatalogId())) {
List<StreamPushItem> streamPushItems = uploadData.catalogData.get(streamPushExcelDto.getCatalogId());
if (streamPushItems == null) {
streamPushItems = new ArrayList<>();
uploadData.catalogData.put(streamPushExcelDto.getCatalogId(), streamPushItems);
}
streamPushItems.add(streamPushItem);
}else {
uploadData.streamPushItems.add(streamPushItem);
}
}
streamPushGBSet.add(streamPushExcelDto.getGbId());
streamPushStreamSet.add(streamPushExcelDto.getApp()+streamPushExcelDto.getStream());
if (streamPushItems.size() > 300) {
pushService.batchAdd(streamPushItems);
// 存储完成清理 list
loadedSize ++;
if (loadedSize > 1000) {
saveData();
streamPushItems.clear();
streamPushItemsForPlatform.clear();
loadedSize = 0;
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
if (streamPushItems.size() > 0) {
pushService.batchAdd(streamPushItems);
}
saveData();
streamPushGBSet.clear();
streamPushStreamSet.clear();
errorDataHandler.handle(errorStreamList, errorGBList);
}
private void saveData(){
if (streamPushItems.size() > 0) {
pushService.batchAddForUpload(null, null, streamPushItems);
}
// 处理已分配到平台的流
if (streamPushItemsForPlatform.size() > 0){
for (String platformId : streamPushItemsForPlatform.keySet()) {
UploadData uploadData = streamPushItemsForPlatform.get(platformId);
if (uploadData.streamPushItems.size() > 0) {
pushService.batchAddForUpload(platformId, null, uploadData.streamPushItems);
}
if (uploadData.catalogData.size() > 0) {
for (String catalogId : uploadData.catalogData.keySet()) {
if (uploadData.catalogData.get(catalogId).size() > 0) {
pushService.batchAddForUpload(platformId, catalogId, uploadData.catalogData.get(catalogId));
}
}
}
}
}
}
}

6
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java

@ -214,4 +214,10 @@ public interface IRedisCatchStorage {
List<SubscribeInfo> getAllSubscribe();
List<String> getAllSubscribePlatform();
void addCpuInfo(double cpuInfo);
void addMemInfo(double memInfo);
void addNetInfo(Map<String, String> networkInterfaces);
}

10
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java

@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import com.github.pagehelper.PageInfo;
@ -93,6 +94,13 @@ public interface IVideoManagerStorager {
public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit);
/**
* 获取某个设备的通道树
* @param deviceId 设备ID
* @return
*/
List<DeviceChannelTree> tree(String deviceId);
/**
* 获取某个设备的通道列表
*
@ -231,7 +239,7 @@ public interface IVideoManagerStorager {
/**
* 查询通道信息不区分设备(已关联平台或全部)
*/
PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, Boolean inPlatform);
PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, String catalogId);
/**
* 查询设备的通道信息

58
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
@ -92,28 +93,31 @@ public interface DeviceChannelMapper {
void startPlay(String deviceId, String channelId, String streamId);
@Select(value = {" <script>" +
"SELECT * FROM ( "+
" SELECT dc.channelId, dc.deviceId, dc.name, de.manufacturer, de.hostAddress, " +
"(SELECT count(0) FROM device_channel WHERE parentId=dc.channelId) as subCount, " +
"(SELECT pc.platformId FROM platform_gb_channel pc WHERE pc.deviceId=dc.deviceId AND pc.channelId = dc.channelId AND pc.platformId = #{platformId}) as platformId, " +
"(SELECT pc.catalogId FROM platform_gb_channel pc WHERE pc.deviceId=dc.deviceId AND pc.channelId = dc.channelId AND pc.platformId = #{platformId} ) as catalogId " +
"FROM device_channel dc " +
"LEFT JOIN device de ON dc.deviceId = de.deviceId " +
" WHERE 1=1 " +
" <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
" <if test='online == true' > AND dc.status=1</if> " +
" <if test='online == false' > AND dc.status=0</if> " +
") dcr" +
"SELECT dc.channelId, "+
"dc.deviceId, " +
"dc.name, " +
"de.manufacturer, " +
"de.hostAddress, " +
"(SELECT count(0) FROM device_channel WHERE parentId = dc.channelId) as subCount, " +
"pgc.platformId as platformId, " +
"pgc.catalogId as catalogId " +
"FROM device_channel dc " +
"LEFT JOIN device de ON dc.deviceId = de.deviceId " +
"LEFT JOIN platform_gb_channel pgc on de.deviceId = pgc.deviceId and pgc.channelId = dc.channelId " +
"LEFT JOIN device_channel dc2 ON dc2.deviceId = de.deviceId AND dc2.parentId = dc.channelId " +
" WHERE 1=1 " +
" <if test='hasSubChannel!= null and hasSubChannel == true' > AND subCount >0</if> " +
" <if test='hasSubChannel!= null and hasSubChannel == false' > AND subCount=0</if> " +
" <if test='platformId != null and inPlatform == true ' > AND platformId='${platformId}'</if> " +
" <if test='platformId != null and inPlatform == false ' > AND (platformId != '${platformId}' OR platformId is NULL ) </if> " +
" ORDER BY deviceId, channelId ASC" +
" <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
" <if test='online == true' > AND dc.status=1</if> " +
" <if test='online == false' > AND dc.status=0</if> " +
" <if test='hasSubChannel!= null and hasSubChannel == true' > AND dc2.channelId is not null</if> " +
" <if test='hasSubChannel!= null and hasSubChannel == false' > AND dc2.channelId is null</if> " +
" <if test='catalogId == null ' > AND pgc.platformId is null AND pgc.catalogId is null</if> " +
" <if test='catalogId != null ' > AND pgc.platformId =#{platformId} AND pgc.catalogId = #{catalogId}</if> " +
" ORDER BY dc.deviceId, dc.channelId ASC" +
" </script>"})
List<ChannelReduce> queryChannelListInAll(String query, Boolean online, Boolean hasSubChannel, String platformId, Boolean inPlatform);
List<ChannelReduce> queryChannelListInAll(String query, Boolean online, Boolean hasSubChannel, String platformId, String catalogId);
@Select("SELECT * FROM device_channel WHERE channelId=#{channelId}")
List<DeviceChannel> queryChannelByChannelId( String channelId);
@ -201,4 +205,20 @@ public interface DeviceChannelMapper {
@Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId);
@Select(" SELECT\n" +
" channelId,\n" +
" channelId as id,\n" +
" deviceId,\n" +
" parentId,\n" +
" status,\n" +
" name as title,\n" +
" channelId as \"value\",\n" +
" channelId as \"key\",\n" +
" channelId,\n" +
" longitude,\n" +
" latitude\n" +
" from device_channel\n" +
" where deviceId = #{deviceId}")
List<DeviceChannelTree> tree(String deviceId);
}

26
src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java

@ -37,10 +37,20 @@ public interface GbStreamMapper {
@Delete("DELETE FROM gb_stream WHERE app=#{app} AND stream=#{stream}")
int del(String app, String stream);
@Select("SELECT gs.*, pgs.platformId AS platformId, pgs.catalogId AS catalogId FROM gb_stream gs " +
"LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream AND (pgs.platformId = #{platformId} OR pgs.platformId is null)" +
"order by gs.id asc ")
List<GbStream> selectAll(String platformId);
@Select("<script> "+
"SELECT gs.*, pgs.platformId AS platformId, pgs.catalogId AS catalogId FROM gb_stream gs " +
"LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream " +
"WHERE " +
"1=1 " +
" <if test='catalogId != null'> AND pgs.platformId = #{platformId} AND pgs.catalogId = #{catalogId}</if> " +
" <if test='catalogId == null'> AND pgs.platformId is null AND pgs.catalogId is null</if> " +
" <if test='query != null'> AND (gs.app LIKE '%${query}%' OR gs.stream LIKE '%${query}%' OR gs.gbId LIKE '%${query}%' OR gs.name LIKE '%${query}%')</if> " +
" <if test='pushing == true' > AND gs.status=1</if>" +
" <if test='pushing == false' > AND gs.status=0</if>" +
" <if test='mediaServerId != null' > AND gs.mediaServerId=#{mediaServerId} </if>" +
" order by gs.id asc " +
"</script>")
List<GbStream> selectAll(String platformId, String catalogId, String query, Boolean pushing, String mediaServerId);
@Select("SELECT * FROM gb_stream WHERE app=#{app} AND stream=#{stream}")
StreamProxyItem selectOne(String app, String stream);
@ -84,6 +94,14 @@ public interface GbStreamMapper {
"</script>")
void batchDel(List<StreamProxyItem> streamProxyItemList);
@Delete("<script> "+
"DELETE FROM gb_stream where " +
"<foreach collection='gbStreams' item='item' separator='or'>" +
"(app=#{item.app} and stream=#{item.stream}) " +
"</foreach>" +
"</script>")
void batchDelForGbStream(List<GbStream> gbStreams);
@Insert("<script> " +
"REPLACE into gb_stream " +
"(app, stream, gbId, name, " +

26
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java

@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
import com.genersoft.iot.vmp.gb28181.bean.PlatformGbStream;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
@ -19,6 +20,17 @@ public interface PlatformGbStreamMapper {
"('${app}', '${stream}', '${platformId}', '${catalogId}')")
int add(PlatformGbStream platformGbStream);
@Insert("<script> " +
"REPLACE into platform_gb_stream " +
"(app, stream, platformId, catalogId) " +
"values " +
"<foreach collection='streamPushItems' index='index' item='item' separator=','> " +
"('${item.app}', '${item.stream}', '${platformId}', '${catalogId}')" +
"</foreach> " +
"</script>")
int batchAdd(String platformId, String catalogId, List<StreamPushItem> streamPushItems);
@Delete("DELETE FROM platform_gb_stream WHERE app=#{app} AND stream=#{stream}")
int delByAppAndStream(String app, String stream);
@ -32,8 +44,7 @@ public interface PlatformGbStreamMapper {
"LEFT JOIN parent_platform pp ON pp.serverGBId = pgs.platformId " +
"WHERE " +
"pgs.app =#{app} " +
"AND pgs.stream =#{stream} " +
"GROUP BY pp.serverGBId")
"AND pgs.stream =#{stream} ")
List<ParentPlatform> selectByAppAndStream(String app, String stream);
@Select("SELECT pgs.*, gs.gbId FROM platform_gb_stream pgs " +
@ -75,4 +86,15 @@ public interface PlatformGbStreamMapper {
@Delete("DELETE FROM platform_gb_stream WHERE app=#{app} AND stream=#{stream} AND platformId=#{platformId}")
int delByAppAndStreamAndPlatform(String app, String stream, String platformId);
@Delete("<script> "+
"DELETE FROM platform_gb_stream where " +
"<foreach collection='gbStreams' item='item' separator='or'>" +
"(app=#{item.app} and stream=#{item.stream}) " +
"</foreach>" +
"</script>")
void delByGbStreams(List<GbStream> gbStreams);
}

28
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
@ -48,6 +49,33 @@ public interface StreamPushMapper {
"</script>")
int delAll(List<StreamPushItem> streamPushItems);
@Delete("<script> "+
"DELETE FROM stream_push where " +
"<foreach collection='gbStreams' item='item' separator='or'>" +
"(app=#{item.app} and stream=#{item.stream}) " +
"</foreach>" +
"</script>")
int delAllForGbStream(List<GbStream> gbStreams);
@Select(value = {" <script>" +
"SELECT " +
"st.*, " +
"pgs.gbId, pgs.status, pgs.name, pgs.longitude, pgs.latitude " +
"from " +
"stream_push st " +
"LEFT JOIN gb_stream pgs " +
"on st.app = pgs.app AND st.stream = pgs.stream " +
"WHERE " +
"1=1 " +
" <if test='query != null'> AND (st.app LIKE '%${query}%' OR st.stream LIKE '%${query}%' OR pgs.gbId LIKE '%${query}%' OR pgs.name LIKE '%${query}%')</if> " +
" <if test='pushing == true' > AND (pgs.gbId is null OR pgs.status=1)</if>" +
" <if test='pushing == false' > AND pgs.status=0</if>" +
" <if test='mediaServerId != null' > AND st.mediaServerId=#{mediaServerId} </if>" +
"order by st.createStamp desc" +
" </script>"})
List<StreamPushItem> selectAllForList(String query, Boolean pushing, String mediaServerId);
@Select("SELECT st.*, pgs.gbId, pgs.status, pgs.name, pgs.longitude, pgs.latitude FROM stream_push st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.createStamp desc")
List<StreamPushItem> selectAll();

46
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.storager.impl;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.SystemInfoDto;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.UserSetup;
import com.genersoft.iot.vmp.gb28181.bean.*;
@ -534,4 +535,49 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
}
return result;
}
@Override
public void addCpuInfo(double cpuInfo) {
String key = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetup.getServerId();
SystemInfoDto<Double> systemInfoDto = new SystemInfoDto<>();
systemInfoDto.setTime(format.format(System.currentTimeMillis()));
systemInfoDto.setData(cpuInfo);
redis.lSet(key, systemInfoDto);
// 每秒一个,最多只存30个
if (redis.lGetListSize(key) > 30) {
for (int i = 0; i < redis.lGetListSize(key) - 30; i++) {
redis.lLeftPop(key);
}
}
}
@Override
public void addMemInfo(double memInfo) {
String key = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetup.getServerId();
SystemInfoDto<Double> systemInfoDto = new SystemInfoDto<>();
systemInfoDto.setTime(format.format(System.currentTimeMillis()));
systemInfoDto.setData(memInfo);
redis.lSet(key, systemInfoDto);
// 每秒一个,最多只存30个
if (redis.lGetListSize(key) > 30) {
for (int i = 0; i < redis.lGetListSize(key) - 30; i++) {
redis.lLeftPop(key);
}
}
}
@Override
public void addNetInfo(Map<String, String> networkInterfaces) {
String key = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetup.getServerId();
SystemInfoDto<Map<String, String>> systemInfoDto = new SystemInfoDto<>();
systemInfoDto.setTime(format.format(System.currentTimeMillis()));
systemInfoDto.setData(networkInterfaces);
redis.lSet(key, systemInfoDto);
// 每秒一个,最多只存30个
if (redis.lGetListSize(key) > 30) {
for (int i = 0; i < redis.lGetListSize(key) - 30; i++) {
redis.lLeftPop(key);
}
}
}
}

13
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java

@ -13,6 +13,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.storager.dao.*;
import com.genersoft.iot.vmp.utils.node.ForestNodeMerger;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
@ -328,6 +330,11 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit);
}
@Override
public List<DeviceChannelTree> tree(String deviceId) {
return ForestNodeMerger.merge(deviceChannelMapper.tree(deviceId));
}
@Override
public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) {
return deviceChannelMapper.queryChannels(deviceId, null,null, null, null);
@ -568,16 +575,16 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
@Override
public PageInfo<ChannelReduce> queryAllChannelList(int page, int count, String query, Boolean online,
Boolean channelType, String platformId, Boolean inPlatform) {
Boolean channelType, String platformId, String catalogId) {
PageHelper.startPage(page, count);
List<ChannelReduce> all = deviceChannelMapper.queryChannelListInAll(query, online, channelType, platformId, inPlatform);
List<ChannelReduce> all = deviceChannelMapper.queryChannelListInAll(query, online, channelType, platformId, catalogId);
return new PageInfo<>(all);
}
@Override
public List<ChannelReduce> queryChannelListInParentPlatform(String platformId) {
return deviceChannelMapper.queryChannelListInAll(null, null, null, platformId, true);
return deviceChannelMapper.queryChannelListInAll(null, null, null, platformId, platformId);
}
@Override

12
src/main/java/com/genersoft/iot/vmp/utils/CollectionUtil.java

@ -0,0 +1,12 @@
package com.genersoft.iot.vmp.utils;
import java.util.Arrays;
public class CollectionUtil {
public static <T> boolean contains(T[] array, final T element) {
return array != null && Arrays.stream(array).anyMatch((x) -> {
return ObjectUtils.nullSafeEquals(x, element);
});
}
}

41
src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java

@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.utils;
import java.util.Arrays;
public class ObjectUtils {
public static boolean nullSafeEquals(Object o1, Object o2) {
if (o1 == o2) {
return true;
} else if (o1 != null && o2 != null) {
if (o1.equals(o2)) {
return true;
} else {
return o1.getClass().isArray() && o2.getClass().isArray() && arrayEquals(o1, o2);
}
} else {
return false;
}
}
private static boolean arrayEquals(Object o1, Object o2) {
if (o1 instanceof Object[] && o2 instanceof Object[]) {
return Arrays.equals((Object[])((Object[])o1), (Object[])((Object[])o2));
} else if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
return Arrays.equals((boolean[])((boolean[])o1), (boolean[])((boolean[])o2));
} else if (o1 instanceof byte[] && o2 instanceof byte[]) {
return Arrays.equals((byte[])((byte[])o1), (byte[])((byte[])o2));
} else if (o1 instanceof char[] && o2 instanceof char[]) {
return Arrays.equals((char[])((char[])o1), (char[])((char[])o2));
} else if (o1 instanceof double[] && o2 instanceof double[]) {
return Arrays.equals((double[])((double[])o1), (double[])((double[])o2));
} else if (o1 instanceof float[] && o2 instanceof float[]) {
return Arrays.equals((float[])((float[])o1), (float[])((float[])o2));
} else if (o1 instanceof int[] && o2 instanceof int[]) {
return Arrays.equals((int[])((int[])o1), (int[])((int[])o2));
} else if (o1 instanceof long[] && o2 instanceof long[]) {
return Arrays.equals((long[])((long[])o1), (long[])((long[])o2));
} else {
return o1 instanceof short[] && o2 instanceof short[] && Arrays.equals((short[]) ((short[]) o1), (short[]) ((short[]) o2));
}
}
}

92
src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java

@ -0,0 +1,92 @@
package com.genersoft.iot.vmp.utils;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.software.os.OperatingSystem;
import oshi.util.FormatUtil;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 实现参考自xiaozhangnomoney原创文章
* 版权声明本文为xiaozhangnomoney原创文章遵循 CC 4.0 BY-SA 版权协议转载请附上原文出处链接和本声明
* 原文出处链接https://blog.csdn.net/xiaozhangnomoney/article/details/107769147
*/
public class SystemInfoUtils {
/**
* 获取cpu信息
* @return
* @throws InterruptedException
*/
public static double getCpuInfo() throws InterruptedException {
SystemInfo systemInfo = new SystemInfo();
CentralProcessor processor = systemInfo.getHardware().getProcessor();
long[] prevTicks = processor.getSystemCpuLoadTicks();
// 睡眠1s
TimeUnit.SECONDS.sleep(1);
long[] ticks = processor.getSystemCpuLoadTicks();
long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
return 1.0-(idle * 1.0 / totalCpu);
}
/**
* 获取内存使用率
* @return
*/
public static double getMemInfo(){
SystemInfo systemInfo = new SystemInfo();
GlobalMemory memory = systemInfo.getHardware().getMemory();
//总内存
long totalByte = memory.getTotal();
//剩余
long acaliableByte = memory.getAvailable();
return (totalByte-acaliableByte)*1.0/totalByte;
}
/**
* 获取网络上传和下载
* @return
*/
public static Map<String,String> getNetworkInterfaces() {
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
List<NetworkIF> networkIFs = hal.getNetworkIFs();
int i= networkIFs.size() -1;
NetworkIF net= networkIFs.get(i);
String in = FormatUtil.formatBytes(net.getBytesRecv());
String out = FormatUtil.formatBytes(net.getBytesSent());
HashMap<String, String> map = new HashMap<>();
map.put("in",in);
map.put("out",out);
return map;
}
/**
* 获取进程数
* @return
*/
public static int getProcessesCount(){
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
int processCount = os.getProcessCount();
return processCount;
}
}

54
src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java

@ -0,0 +1,54 @@
package com.genersoft.iot.vmp.utils.node;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 节点基类
*
*/
@Data
public class BaseNode<T> implements INode<T> {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
protected String id;
/**
* 父节点ID
*/
protected String parentId;
/**
* 子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
protected List<T> children = new ArrayList<T>();
/**
* 是否有子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Boolean hasChildren;
/**
* 是否有子孙节点
*
* @return Boolean
*/
@Override
public Boolean getHasChildren() {
if (children.size() > 0) {
return true;
} else {
return this.hasChildren;
}
}
}

28
src/main/java/com/genersoft/iot/vmp/utils/node/ForestNode.java

@ -0,0 +1,28 @@
package com.genersoft.iot.vmp.utils.node;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 森林节点类
*
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ForestNode extends BaseNode<ForestNode> {
private static final long serialVersionUID = 1L;
/**
* 节点内容
*/
private Object content;
public ForestNode(String id, String parentId, Object content) {
this.id = id;
this.parentId = parentId;
this.content = content;
}
}

68
src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeManager.java

@ -0,0 +1,68 @@
package com.genersoft.iot.vmp.utils.node;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 森林管理类
*
* @author smallchill
*/
public class ForestNodeManager<T extends INode<T>> {
/**
* 森林的所有节点
*/
private final ImmutableMap<String, T> nodeMap;
/**
* 森林的父节点ID
*/
private final Map<String, Object> parentIdMap = Maps.newHashMap();
public ForestNodeManager(List<T> nodes) {
nodeMap = Maps.uniqueIndex(nodes, INode::getId);
}
/**
* 根据节点ID获取一个节点
*
* @param id 节点ID
* @return 对应的节点对象
*/
public INode<T> getTreeNodeAt(String id) {
if (nodeMap.containsKey(id)) {
return nodeMap.get(id);
}
return null;
}
/**
* 增加父节点ID
*
* @param parentId 父节点ID
*/
public void addParentId(String parentId) {
parentIdMap.put(parentId, "");
}
/**
* 获取树的根节点(一个森林对应多颗树)
*
* @return 树的根节点集合
*/
public List<T> getRoot() {
List<T> roots = new ArrayList<>();
nodeMap.forEach((key, node) -> {
if (node.getParentId() == null || parentIdMap.containsKey(node.getId())) {
roots.add(node);
}
});
return roots;
}
}

51
src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeMerger.java

@ -0,0 +1,51 @@
package com.genersoft.iot.vmp.utils.node;
import com.genersoft.iot.vmp.utils.CollectionUtil;
import java.util.List;
/**
* 森林节点归并类
*
*/
public class ForestNodeMerger {
/**
* 将节点数组归并为一个森林多棵树填充节点的children域
* 时间复杂度为O(n^2)
*
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static <T extends INode<T>> List<T> merge(List<T> items) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getParentId() != null) {
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
if (node != null) {
node.getChildren().add(forestNode);
} else {
forestNodeManager.addParentId(forestNode.getId());
}
}
});
return forestNodeManager.getRoot();
}
public static <T extends INode<T>> List<T> merge(List<T> items, String[] parentIds) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getParentId() != null) {
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
if (CollectionUtil.contains(parentIds, forestNode.getId())){
forestNodeManager.addParentId(forestNode.getId());
} else {
if (node != null){
node.getChildren().add(forestNode);
}
}
}
});
return forestNodeManager.getRoot();
}
}

42
src/main/java/com/genersoft/iot/vmp/utils/node/INode.java

@ -0,0 +1,42 @@
package com.genersoft.iot.vmp.utils.node;
import java.io.Serializable;
import java.util.List;
/**
*
* 节点
*/
public interface INode<T> extends Serializable {
/**
* 主键
*
* @return String
*/
String getId();
/**
* 父主键
*
* @return String
*/
String getParentId();
/**
* 子孙节点
*
* @return List<T>
*/
List<T> getChildren();
/**
* 是否有子孙节点
*
* @return Boolean
*/
default Boolean getHasChildren() {
return false;
}
}

21
src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java

@ -0,0 +1,21 @@
package com.genersoft.iot.vmp.utils.node;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 树型节点类
*
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class TreeNode extends BaseNode<TreeNode> {
private static final long serialVersionUID = 1L;
private String title;
private String key;
private String value;
}

18
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java

@ -660,6 +660,24 @@ public class RedisUtil {
}
}
/**
* 在键为 key list中移除第一个元素
* @param key
* @return
*/
public Object lLeftPop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
/**
* 在键为 key list中移除最后一个元素
* @param key
* @return
*/
public Object lrightPop(String key) {
return redisTemplate.opsForList().rightPop(key);
}
/**
* 模糊查询
* @param key

17
src/main/java/com/genersoft/iot/vmp/vmanager/bean/BatchGBStreamParam.java

@ -0,0 +1,17 @@
package com.genersoft.iot.vmp.vmanager.bean;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import java.util.List;
public class BatchGBStreamParam {
private List<GbStream> gbStreams;
public List<GbStream> getGbStreams() {
return gbStreams;
}
public void setGbStreams(List<GbStream> gbStreams) {
this.gbStreams = gbStreams;
}
}

65
src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTree.java

@ -0,0 +1,65 @@
package com.genersoft.iot.vmp.vmanager.bean;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.utils.node.INode;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "DeviceChannelTree对象", description = "DeviceChannelTree对象")
public class DeviceChannelTree extends DeviceChannel implements INode<DeviceChannelTree> {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private String id;
/**
* 父节点ID
*/
private String parentId;
private String parentName;
private String title;
private String key;
private String value;
/**
* 子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<DeviceChannelTree> children;
/**
* 是否有子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Boolean hasChildren;
@Override
public List<DeviceChannelTree> getChildren() {
if (this.children == null) {
this.children = new ArrayList<>();
}
return this.children;
}
@Override
public Boolean getHasChildren() {
if (children.size() > 0) {
return true;
} else {
return this.hasChildren;
}
}
}

20
src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTreeNode.java

@ -0,0 +1,20 @@
package com.genersoft.iot.vmp.vmanager.bean;
import com.genersoft.iot.vmp.utils.node.TreeNode;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class DeviceChannelTreeNode extends TreeNode {
private Integer status;
private String deviceId;
private String channelId;
private Double lng;
private Double lat;
}

23
src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamPushExcelDto.java

@ -16,6 +16,12 @@ public class StreamPushExcelDto {
@ExcelProperty("国标ID")
private String gbId;
@ExcelProperty("平台ID")
private String platformId;
@ExcelProperty("目录ID")
private String catalogId;
public String getName() {
return name;
}
@ -47,4 +53,21 @@ public class StreamPushExcelDto {
public void setGbId(String gbId) {
this.gbId = gbId;
}
public String getPlatformId() {
return platformId;
}
public void setPlatformId(String platformId) {
this.platformId = platformId;
}
public String getCatalogId() {
return catalogId;
}
public void setCatalogId(String catalogId) {
this.catalogId = catalogId;
}
}

31
src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java

@ -1,32 +1,35 @@
package com.genersoft.iot.vmp.vmanager.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WVPResult<T> {
private int code;
private String msg;
private T data;
public int getCode() {
return code;
}
private static final Integer SUCCESS = 200;
private static final Integer FAILED = 400;
public void setCode(int code) {
this.code = code;
public static <T> WVPResult<T> Data(T t, String msg) {
return new WVPResult<>(SUCCESS, msg, t);
}
public String getMsg() {
return msg;
public static <T> WVPResult<T> Data(T t) {
return Data(t, "成功");
}
public void setMsg(String msg) {
this.msg = msg;
public static <T> WVPResult<T> fail(int code, String msg) {
return new WVPResult<>(code, msg, null);
}
public T getData() {
return data;
public static <T> WVPResult<T> fail(String msg) {
return fail(FAILED, msg);
}
public void setData(T data) {
this.data = data;
}
}

9
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java

@ -10,8 +10,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@ -25,6 +27,7 @@ import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import java.util.List;
import java.util.UUID;
@Api(tags = "国标设备查询", value = "国标设备查询")
@ -431,5 +434,9 @@ public class DeviceQuery {
return result;
}
@GetMapping("/{deviceId}/tree")
@ApiOperation(value = "通道树形结构", notes = "通道树形结构")
public WVPResult<List<DeviceChannelTree>> tree(@PathVariable String deviceId) {
return WVPResult.Data(storager.tree(deviceId));
}
}

25
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java

@ -12,6 +12,7 @@ import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
@Api(tags = "视频流关联到级联平台")
@ -40,15 +41,33 @@ public class GbStreamController {
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "当前页", required = true , dataTypeClass = Integer.class),
@ApiImplicitParam(name = "count", value = "每页条数", required = true , dataTypeClass = Integer.class),
@ApiImplicitParam(name = "platformId", value = "平台ID", required = true , dataTypeClass = Integer.class),
@ApiImplicitParam(name = "platformId", value = "平台ID", required = true , dataTypeClass = String.class),
@ApiImplicitParam(name = "catalogId", value = "目录ID", required = false , dataTypeClass = String.class),
@ApiImplicitParam(name="query", value = "查询内容", required = false , dataTypeClass = String.class),
@ApiImplicitParam(name="pushing", value = "是否正在推流", required = false , dataTypeClass = Boolean.class),
@ApiImplicitParam(name="mediaServerId", value = "流媒体ID", required = false , dataTypeClass = String.class),
})
@GetMapping(value = "/list")
@ResponseBody
public PageInfo<GbStream> list(@RequestParam(required = true)Integer page,
@RequestParam(required = true)Integer count,
@RequestParam(required = true)String platformId){
@RequestParam(required = true)String platformId,
@RequestParam(required = false)String catalogId,
@RequestParam(required = false)String query,
@RequestParam(required = false)Boolean pushing,
@RequestParam(required = false)String mediaServerId){
if (StringUtils.isEmpty(catalogId)) {
catalogId = null;
}
if (StringUtils.isEmpty(query)) {
query = null;
}
if (StringUtils.isEmpty(mediaServerId)) {
mediaServerId = null;
}
return gbStreamService.getAll(page, count, platformId);
return gbStreamService.getAll(page, count, platformId, catalogId, query, pushing, mediaServerId);
}

45
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java

@ -65,6 +65,25 @@ public class PlatformController {
result.put("password", sipConfig.getPassword());
return new ResponseEntity<>(result, HttpStatus.OK);
}
/**
* 获取级联服务器信息
* @return
*/
@ApiOperation("获取国标服务的配置")
@GetMapping("/info/{id}")
public ResponseEntity<WVPResult<ParentPlatform>> getPlatform(@PathVariable String id) {
ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(id);
WVPResult<ParentPlatform> wvpResult = new WVPResult<>();
if (parentPlatform != null) {
wvpResult.setCode(0);
wvpResult.setMsg("success");
wvpResult.setData(parentPlatform);
}else {
wvpResult.setCode(-1);
wvpResult.setMsg("未查询到此平台");
}
return new ResponseEntity<>(wvpResult, HttpStatus.OK);
}
/**
* 分页查询级联平台
@ -282,29 +301,33 @@ public class PlatformController {
@ApiImplicitParam(name = "page", value = "当前页", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "count", value = "每页条数", dataTypeClass = Integer.class),
@ApiImplicitParam(name = "platformId", value = "上级平台ID", dataTypeClass = String.class),
@ApiImplicitParam(name = "catalogId", value = "目录ID", dataTypeClass = String.class),
@ApiImplicitParam(name = "query", value = "查询内容", dataTypeClass = String.class),
@ApiImplicitParam(name = "online", value = "是否在线", dataTypeClass = Boolean.class),
@ApiImplicitParam(name = "choosed", value = "是否已选中", dataTypeClass = Boolean.class),
@ApiImplicitParam(name = "channelType", value = "通道类型", dataTypeClass = Boolean.class),
})
@GetMapping("/channel_list")
@ResponseBody
public PageInfo<ChannelReduce> channelList(int page, int count,
@RequestParam(required = false) String platformId,
@RequestParam(required = false) String catalogId,
@RequestParam(required = false) String query,
@RequestParam(required = false) Boolean online,
@RequestParam(required = false) Boolean choosed,
@RequestParam(required = false) Boolean channelType){
// if (logger.isDebugEnabled()) {
// logger.debug("查询所有所有通道API调用");
// }
PageInfo<ChannelReduce> channelReduces = null;
if (platformId != null ) {
channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, platformId, choosed);
}else {
channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, null, false);
if(StringUtils.isEmpty(platformId)) {
platformId = null;
}
if(StringUtils.isEmpty(query)) {
query = null;
}
if(StringUtils.isEmpty(platformId) || StringUtils.isEmpty(catalogId)) {
catalogId = null;
}
PageInfo<ChannelReduce> channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, platformId, catalogId);
return channelReduces;
}
@ -371,11 +394,11 @@ public class PlatformController {
}
List<PlatformCatalog> platformCatalogList = storager.getChildrenCatalogByPlatform(platformId, parentId);
// 查询下属的国标通道
List<PlatformCatalog> catalogsForChannel = storager.queryChannelInParentPlatformAndCatalog(platformId, parentId);
// List<PlatformCatalog> catalogsForChannel = storager.queryChannelInParentPlatformAndCatalog(platformId, parentId);
// 查询下属的直播流通道
List<PlatformCatalog> catalogsForStream = storager.queryStreamInParentPlatformAndCatalog(platformId, parentId);
platformCatalogList.addAll(catalogsForChannel);
platformCatalogList.addAll(catalogsForStream);
// List<PlatformCatalog> catalogsForStream = storager.queryStreamInParentPlatformAndCatalog(platformId, parentId);
// platformCatalogList.addAll(catalogsForChannel);
// platformCatalogList.addAll(catalogsForStream);
WVPResult<List<PlatformCatalog>> result = new WVPResult<>();
result.setCode(0);
result.setMsg("success");

37
src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java

@ -10,6 +10,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.service.impl.StreamPushUploadFileHandler;
import com.genersoft.iot.vmp.vmanager.bean.BatchGBStreamParam;
import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo;
@ -24,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.multipart.MultipartFile;
@ -57,16 +59,24 @@ public class StreamPushController {
@ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class),
@ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class),
@ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class),
@ApiImplicitParam(name="online", value = "是否在线", dataTypeClass = Boolean.class),
@ApiImplicitParam(name="pushing", value = "是否正在推流", dataTypeClass = Boolean.class),
@ApiImplicitParam(name="mediaServerId", value = "流媒体ID", dataTypeClass = String.class),
})
@GetMapping(value = "/list")
@ResponseBody
public PageInfo<StreamPushItem> list(@RequestParam(required = false)Integer page,
@RequestParam(required = false)Integer count,
@RequestParam(required = false)String query,
@RequestParam(required = false)Boolean online ){
@RequestParam(required = false)Boolean pushing,
@RequestParam(required = false)String mediaServerId ){
PageInfo<StreamPushItem> pushList = streamPushService.getPushList(page, count);
if (StringUtils.isEmpty(query)) {
query = null;
}
if (StringUtils.isEmpty(mediaServerId)) {
mediaServerId = null;
}
PageInfo<StreamPushItem> pushList = streamPushService.getPushList(page, count, query, pushing, mediaServerId);
return pushList;
}
@ -107,13 +117,32 @@ public class StreamPushController {
})
@PostMapping(value = "/stop")
@ResponseBody
public Object removeFormGB(@RequestParam(required = true)String app, @RequestParam(required = true)String streamId){
public Object stop(String app, String streamId){
if (streamPushService.stop(app, streamId)){
return "success";
}else {
return "fail";
}
}
@ApiOperation("中止多个推流")
@ApiImplicitParams({
@ApiImplicitParam(name = "app", value = "应用名", required = true, dataTypeClass = String.class),
@ApiImplicitParam(name = "streamId", value = "流ID", required = true, dataTypeClass = String.class),
})
@DeleteMapping(value = "/batchStop")
@ResponseBody
public Object batchStop(@RequestBody BatchGBStreamParam batchGBStreamParam){
if (batchGBStreamParam.getGbStreams().size() == 0) {
return "fail";
}
if (streamPushService.batchStop(batchGBStreamParam.getGbStreams())){
return "success";
}else {
return "fail";
}
}
@PostMapping(value = "upload")
@ResponseBody
public DeferredResult<ResponseEntity<WVPResult<Object>>> uploadChannelFile(@RequestParam(value = "file") MultipartFile file){

1
web_src/index.html

@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>国标28181</title>
<link rel="shortcut icon" href="static/favicon.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="./static/css/iconfont.css">
<link rel="stylesheet" type="text/css" href="./static/css/login.css">
</head>

19
web_src/src/api/deviceApi.js

@ -0,0 +1,19 @@
import axios from 'axios';
export const tree = (deviceId) => {
return axios({
url: `/api/device/query/${deviceId}/tree`,
method: 'get'
})
}
export const deviceList = (page, count) => {
return axios({
method: 'get',
url:`/api/device/query/devices`,
params: {
page,
count
}
})
}

108
web_src/src/components/PushVideoList.vue

@ -9,31 +9,52 @@
<span style="font-size: 1rem; font-weight: bold;">推流列表</span>
</div>
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;">
搜索: <el-input @input="getPushList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" prefix-icon="el-icon-search" v-model="searchSrt" clearable> </el-input>
流媒体: <el-select size="mini" @change="getPushList" style="margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" default-first-option>
<el-option label="全部" value=""></el-option>
<el-option
v-for="item in mediaServerList"
:key="item.id"
:label="item.id"
:value="item.id">
</el-option>
</el-select>
推流状态: <el-select size="mini" style="margin-right: 1rem;" @change="getPushList" v-model="pushing" placeholder="请选择" default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="推流进行中" value="true"></el-option>
<el-option label="推流未进行" value="false"></el-option>
</el-select>
<el-button icon="el-icon-upload2" size="mini" style="margin-right: 1rem;" type="primary" @click="importChannel">通道导入</el-button>
<el-button icon="el-icon-download" size="mini" style="margin-right: 1rem;" type="primary" >
<a style="color: #FFFFFF; text-align: center; text-decoration: none" href="/static/file/推流通道导入.zip" download='推流通道导入.zip' >下载模板</a>
</el-button>
<el-button icon="el-icon-delete" size="mini" style="margin-right: 1rem;" :disabled="multipleSelection.length === 0" type="danger" @click="batchDel">批量移除</el-button>
</div>
<devicePlayer ref="devicePlayer"></devicePlayer>
<addStreamTOGB ref="addStreamTOGB"></addStreamTOGB>
<el-table :data="pushList" border style="width: 100%" :height="winHeight">
<el-table-column prop="app" label="APP" width="180" align="center">
<el-table ref="pushListTable" :data="pushList" border style="width: 100%" :height="winHeight" @selection-change="handleSelectionChange" :row-key="(row)=> row.app + row.stream">
<el-table-column align="center" type="selection" :reserve-selection="true" width="55">
</el-table-column>
<el-table-column prop="name" label="名称" align="center">
</el-table-column>
<el-table-column prop="stream" label="流ID" width="240" align="center">
<el-table-column prop="app" label="APP" align="center">
</el-table-column>
<el-table-column prop="gbId" label="国标编码" width="150" align="center">
<el-table-column prop="stream" label="流ID" align="center">
</el-table-column>
<el-table-column prop="mediaServerId" label="流媒体" width="150" align="center">
<el-table-column prop="gbId" label="国标编码" width="200" align="center">
</el-table-column>
<el-table-column label="开始时间" align="center" >
<el-table-column prop="mediaServerId" label="流媒体" width="200" align="center">
</el-table-column>
<el-table-column label="开始时间" align="center" width="200">
<template slot-scope="scope">
<el-button-group>
{{dateFormat(parseInt(scope.row.createStamp))}}
</el-button-group>
</template>
</el-table-column>
<el-table-column label="正在推流" align="center" >
<el-table-column label="正在推流" align="center" width="100">
<template slot-scope="scope">
{{(scope.row.status == false && scope.row.gbId == null) || scope.row.status ?'是':'否'}}
</template>
@ -42,8 +63,8 @@
<el-table-column label="操作" width="360" align="center" fixed="right">
<template slot-scope="scope">
<el-button-group>
<el-button size="mini" icon="el-icon-video-play" v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status" @click="playPuhsh(scope.row)">播放</el-button>
<el-button size="mini" icon="el-icon-switch-button" type="danger" @click="stopPuhsh(scope.row)">移除</el-button>
<el-button size="mini" icon="el-icon-video-play" v-if="(scope.row.status == false && scope.row.gbId == null) || scope.row.status" @click="playPush(scope.row)">播放</el-button>
<el-button size="mini" icon="el-icon-delete" type="danger" @click="stopPush(scope.row)">移除</el-button>
<el-button size="mini" icon="el-icon-position" type="primary" v-if="!!!scope.row.gbId" @click="addToGB(scope.row)">加入国标</el-button>
<el-button size="mini" icon="el-icon-position" type="primary" v-if="!!scope.row.gbId" @click="removeFromGB(scope.row)">移出国标</el-button>
</el-button-group>
@ -73,6 +94,7 @@
import addStreamTOGB from './dialog/addStreamTOGB.vue'
import uiHeader from './UiHeader.vue'
import importChannel from './dialog/importChannel.vue'
import MediaServer from './service/MediaServer'
export default {
name: 'pushVideoList',
components: {
@ -88,10 +110,16 @@
currentPusher: {}, //
updateLooper: 0, //
currentDeviceChannelsLenth:0,
winHeight: window.innerHeight - 200,
winHeight: window.innerHeight - 250,
mediaServerObj : new MediaServer(),
currentPage:1,
count:15,
total:0,
searchSrt: "",
pushing: "",
mediaServerId: "",
mediaServerList: [],
multipleSelection: [],
getDeviceListLoading: false
};
},
@ -99,13 +127,16 @@
},
mounted() {
this.initData();
this.updateLooper = setInterval(this.initData, 2000);
this.updateLooper = setInterval(this.getPushList, 2000);
},
destroyed() {
clearTimeout(this.updateLooper);
},
methods: {
initData: function() {
this.mediaServerObj.getOnlineMediaServerList((data)=>{
this.mediaServerList = data.data;
})
this.getPushList();
},
currentChange: function(val){
@ -124,19 +155,22 @@
url:`/api/push/list`,
params: {
page: that.currentPage,
count: that.count
count: that.count,
query: that.searchSrt,
pushing: that.pushing,
mediaServerId: that.mediaServerId,
}
}).then(function (res) {
that.total = res.data.total;
that.pushList = res.data.list;
that.getDeviceListLoading = false;
}).catch(function (error) {
console.log(error);
console.error(error);
that.getDeviceListLoading = false;
});
},
playPuhsh: function(row){
playPush: function(row){
let that = this;
this.getListLoading = true;
this.$axios({
@ -154,12 +188,12 @@
hasAudio: true
});
}).catch(function (error) {
console.log(error);
console.error(error);
that.getListLoading = false;
});
},
stopPuhsh: function(row){
var that = this;
stopPush: function(row){
let that = this;
that.$axios({
method:"post",
url:"/api/push/stop",
@ -172,14 +206,14 @@
that.initData()
}
}).catch(function (error) {
console.log(error);
console.error(error);
});
},
addToGB: function(row){
this.$refs.addStreamTOGB.openDialog({app: row.app, stream: row.stream, mediaServerId: row.mediaServerId}, this.initData);
},
removeFromGB: function(row){
var that = this;
let that = this;
that.$axios({
method:"delete",
url:"/api/push/remove_form_gb",
@ -189,13 +223,13 @@
that.initData()
}
}).catch(function (error) {
console.log(error);
console.error(error);
});
},
dateFormat: function(/** timestamp=0 **/) {
var ts = arguments[0] || 0;
var t,y,m,d,h,i,s;
t = ts ? new Date(ts*1000) : new Date();
let ts = arguments[0] || 0;
let t,y,m,d,h,i,s;
t = ts ? new Date(ts) : new Date();
y = t.getFullYear();
m = t.getMonth()+1;
d = t.getDate();
@ -209,6 +243,32 @@
this.$refs.importChannel.openDialog(()=>{
})
},
batchDel: function () {
this.$confirm(`确定删除选中的${this.multipleSelection.length}个通道?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let that = this;
that.$axios({
method:"delete",
url:"/api/push/batchStop",
data: {
gbStreams: this.multipleSelection
}
}).then((res)=>{
this.initData();
this.$refs.pushListTable.clearSelection();
}).catch(function (error) {
console.error(error);
});
}).catch(() => {
});
},
handleSelectionChange: function (val) {
this.multipleSelection = val;
},
}
};

3
web_src/src/components/UiHeader.vue

@ -2,7 +2,8 @@
<div id="UiHeader">
<el-menu router :default-active="activeIndex" menu-trigger="click" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" mode="horizontal">
<el-menu-item index="/">控制台</el-menu-item>
<el-menu-item index="/deviceList">设备列表</el-menu-item>
<el-menu-item index="/live">实时监控</el-menu-item>
<el-menu-item index="/deviceList">国标设备</el-menu-item>
<el-menu-item index="/pushVideoList">推流列表</el-menu-item>
<el-menu-item index="/streamProxyList">拉流代理</el-menu-item>
<el-menu-item index="/cloudRecord">云端录像</el-menu-item>

70
web_src/src/components/channelTree.vue

@ -0,0 +1,70 @@
<template>
<div>
<el-tree :data="channelList" :props="props" @node-click="sendDevicePush">
<span slot-scope="{ node }">
<span v-if="node.isLeaf">
<i class="el-icon-video-camera" :style="{color:node.disabled==1?'#67C23A':'#F56C6C'}"></i>
</span>
<span v-else>
<i class="el-icon-folder"></i>
</span>
<span>
{{ node.label }}
</span>
</span>
</el-tree>
</div>
</template>
<script>
import ChannelTreeItem from "@/components/channelTreeItem"
import {tree} from '@/api/deviceApi'
export default {
components: {
ChannelTreeItem,
},
props:{
device: {
type: Object,
required: true
}
},
data() {
return {
loading: false,
channelList: [],
props: {
label: 'title',
children: 'children',
isLeaf: 'hasChildren',
disabled: 'status'
},
}
},
computed: {
},
mounted() {
this.leafs = []
this.getTree()
},
methods: {
getTree() {
this.loading = true
var that = this
tree(this.device.deviceId).then(function (res) {
console.log(res.data.data);
that.channelList = res.data.data;
that.loading = false;
}).catch(function (error) {
console.log(error);
that.loading = false;
});
},
sendDevicePush(c) {
if(c.hasChildren) return
this.$emit('sendDevicePush',c)
}
}
}
</script>

74
web_src/src/components/channelTreeItem.vue

@ -0,0 +1,74 @@
<template>
<div>
<!-- <div :index="item.key" v-for="(item,i) in list" :key="i+'-'">
<el-submenu v-if="item.hasChildren">
<template slot="title">
<i class="el-icon-video-camera"></i>
<span slot="title">{{item.title || item.deviceId}}</span>
</template>
<channel-list :list="item.children" @sendDevicePush="sendDevicePush"></channel-list>
</el-submenu>
<el-menu-item v-else :index="item.key" @click="sendDevicePush(item)">
<template slot="title" >
<i class="el-icon-switch-button" :style="{color:item.status==1?'#67C23A':'#F56C6C'}"></i>
<span slot="title">{{item.title}}</span>
</template>
</el-menu-item>
</div> -->
<div >
<template v-if="!item.hasChildren">
<el-menu-item :index="item.key" @click="sendDevicePush(item)">
<i class="el-icon-video-camera" :style="{color:item.status==1?'#67C23A':'#F56C6C'}"></i>
{{item.title}}
</el-menu-item>
</template>
<el-submenu v-else :index="item.key">
<template slot="title" >
<i class="el-icon-location-outline"></i>
{{item.title}}
</template>
<template v-for="child in item.children">
<channel-item
v-if="child.hasChildren"
:item="child"
:key="child.key"
@sendDevicePush="sendDevicePush"/>
<el-menu-item v-else :key="child.key" :index="child.key" @click="sendDevicePush(child)">
<i class="el-icon-video-camera" :style="{color:child.status==1?'#67C23A':'#F56C6C'}"></i>
{{child.title}}
</el-menu-item>
</template>
</el-submenu>
</div>
</div>
</template>
<script>
export default {
name:'ChannelItem',
props:{
list:Array,
channelId: String,
item: {
type: Object,
required: true
}
},
data () {
return {
}
},
watch: {
channelId(val) {
console.log(val);
}
},
methods: {
sendDevicePush(c) {
this.$emit('sendDevicePush',c)
}
}
}
</script>

3
web_src/src/components/dialog/catalogEdit.vue

@ -77,8 +77,7 @@ export default {
})
.then((res)=> {
if (res.data.code === 0) {
console.log("添加/修改成功")
if (this.submitCallback)this.submitCallback()
if (this.submitCallback)this.submitCallback(this.form)
}else {
this.$message({
showClose: true,

16
web_src/src/components/dialog/chooseChannel.vue

@ -8,7 +8,7 @@
<el-tab-pane label="目录结构" name="catalog">
<el-container>
<el-main v-bind:style="{backgroundColor: '#FFF', maxHeight: winHeight + 'px'}">
<chooseChannelForCatalog ref="chooseChannelForCatalog" :platformId=platformId :platformName=platformName :defaultCatalogId=defaultCatalogId :catalogIdChange="catalogIdChange"></chooseChannelForCatalog>
<chooseChannelForCatalog ref="chooseChannelForCatalog" :platformId=platformId :platformName=platformName :defaultCatalogId=defaultCatalogId :catalogIdChange="catalogIdChange" ></chooseChannelForCatalog>
</el-main>
</el-container>
</el-tab-pane>
@ -20,14 +20,14 @@
<el-tab-pane label="国标通道" name="gbChannel">
<el-container>
<el-main style="background-color: #FFF;">
<chooseChannelForGb ref="chooseChannelForGb" :catalogId="catalogId" :platformId=platformId :updateChoosedCallback="updateChooseChannelCallback"></chooseChannelForGb>
<chooseChannelForGb ref="chooseChannelForGb" :catalogId="catalogId" :catalogName="catalogName" :platformId=platformId ></chooseChannelForGb>
</el-main>
</el-container>
</el-tab-pane>
<el-tab-pane label="直播流通道" name="streamchannel">
<el-container>
<el-main style="background-color: #FFF;">
<chooseChannelFoStream ref="chooseChannelFoStream" :catalogId="catalogId" :platformId=platformId :updateChoosedCallback="updateChooseChannelCallback"></chooseChannelFoStream>
<chooseChannelFoStream ref="chooseChannelFoStream" :catalogId="catalogId" :catalogName="catalogName" :currentCatalogId="currentCatalogId" :platformId=platformId ></chooseChannelFoStream>
</el-main>
</el-container>
</el-tab-pane>
@ -61,6 +61,8 @@ export default {
catalogTabActiveName: "catalog",
platformId: "",
catalogId: "",
catalogName: "",
currentCatalogId: "",
platformName: "",
defaultCatalogId: "",
showDialog: false,
@ -108,14 +110,10 @@ export default {
console.log(error);
});
},
catalogIdChange: function (id) {
console.log("中间模块收到: " + id)
catalogIdChange: function (id, name) {
this.catalogId = id;
this.catalogName = name;
},
updateChooseChannelCallback (id){
console.log("中间模块收到选择通道变化: " + id)
this.$refs.chooseChannelForCatalog.refreshCatalogById(id)
}
}
};
</script>

41
web_src/src/components/dialog/chooseChannelForCatalog.vue

@ -7,19 +7,21 @@
empty-text="未知节点"
node-key="id"
default-expand-all
:highlight-current="true"
:highlight-current="false"
:expand-on-click-node="false"
:props="props"
:load="loadNode"
@node-contextmenu="contextmenuEventHandler"
@node-click="nodeClickHandler"
lazy>
<span class="custom-tree-node" slot-scope="{ node, data }" style="width: 100%">
<el-radio v-if="node.data.type === 0" style="margin-right: 0" v-model="chooseId" :label="node.data.id">{{''}}</el-radio>
<el-radio v-if="node.data.type === 0 || node.data.type === -1" style="margin-right: 0" v-model="chooseId" :label="node.data.id">{{''}}</el-radio>
<span v-if="node.data.type === -1 && node.level === 1" style="font-size: 12px" class="iconfont icon-ziyuan"></span>
<span v-if="node.data.type === 0 && node.level === 1" class="el-icon-s-home"></span>
<span v-if="node.data.type === 0 && node.level > 1" class="el-icon-folder-opened"></span>
<span v-if="node.data.type === 1" class="iconfont icon-shexiangtou"></span>
<span v-if="node.data.type === 2" class="iconfont icon-zhibo"></span>
<span style="padding-left: 1px">{{ node.label }}</span>
<span style=" padding-left: 1px">{{ node.label }}</span>
<span>
<i style="margin-left: 5rem; color: #9d9d9d; padding-right: 20px" v-if="node.data.id === defaultCatalogIdSign">默认</i>
</span>
@ -42,7 +44,7 @@ export default {
this.defaultCatalogIdSign = this.defaultCatalogId;
this.initData();
setTimeout(()=>{
if (this.catalogIdChange)this.catalogIdChange(this.defaultCatalogId);
if (this.catalogIdChange)this.catalogIdChange(this.defaultCatalogId, this.platformName);
}, 100)
},
@ -59,6 +61,7 @@ export default {
defaultCatalogIdSign: null,
chooseNode: null,
chooseId: "",
chooseName: "",
catalogTree: null,
contextmenuShow: false
@ -69,10 +72,6 @@ export default {
console.log(newData)
this.initData()
},
chooseId(newData, oldData){
console.log("发送: " + newData)
if (this.catalogIdChange)this.catalogIdChange(newData);
},
},
methods: {
initData: function () {
@ -123,9 +122,12 @@ export default {
editCatalog: function (data, node){
let that = this;
//
that.$refs.catalogEdit.openDialog(true, data.id, data.name, data.parentId, (data)=>{
that.$refs.catalogEdit.openDialog(true, data.id, data.name, data.parentId, (newData)=>{
node.parent.loaded = false
node.parent.expand();
if (data.id === this.chooseId && newData.name !== data.name) {
if (this.catalogIdChange)this.catalogIdChange(this.chooseId, newData.name);
}
});
},
@ -172,11 +174,17 @@ export default {
},
loadNode: function(node, resolve){
if (node.level === 0) {
resolve([{
name: this.platformName,
id: this.platformId,
type: 0
}]);
resolve([
{
name: "未分配",
id: null,
type: -1
},{
name: this.platformName,
id: this.platformId,
type: 0
}
]);
}
if (node.level >= 1){
this.getCatalog(node.data.id, resolve)
@ -291,6 +299,11 @@ export default {
return false;
},
nodeClickHandler: function (data, node, tree){
this.chooseId = data.id;
this.chooseName = data.name;
if (this.catalogIdChange)this.catalogIdChange(this.chooseId, this.chooseName);
}
}
};
</script>

303
web_src/src/components/dialog/chooseChannelForGb.vue

@ -1,6 +1,10 @@
<template>
<div id="chooseChannelForGb" >
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;">
<div style="font-size: 17px; color: #606060; white-space: nowrap; line-height: 30px; font-family: monospace;">
<span v-if="catalogId == null">{{catalogName}}的直播流</span>
<span v-if="catalogId != null">{{catalogName}}({{catalogId}})的直播流</span>
</div>
<div style="background-color: #FFFFFF; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;">
搜索: <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" prefix-icon="el-icon-search" v-model="searchSrt" clearable> </el-input>
通道类型: <el-select size="mini" @change="search" style="margin-right: 1rem; width:6rem" v-model="channelType" placeholder="请选择" default-first-option>
@ -9,21 +13,18 @@
<el-option label="子目录" value="true"></el-option>
</el-select>
选择状态: <el-select size="mini" style="margin-right: 1rem; width:6rem" v-model="choosed" @change="search" placeholder="请选择" default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="已选择" value="true"></el-option>
<el-option label="未选择" value="false"></el-option>
</el-select>
在线状态: <el-select size="mini" style="margin-right: 1rem; width:6rem" @change="search" v-model="online" placeholder="请选择" default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="在线" value="true"></el-option>
<el-option label="离线" value="false"></el-option>
</el-select>
<!-- <el-checkbox @change="shareAllCheckedChange">全部共享</el-checkbox>-->
<el-button v-if="catalogId !== null" icon="el-icon-delete" size="mini" style="margin-right: 1rem;" :disabled="gbChannels.length === 0 || multipleSelection.length === 0" type="danger" @click="batchDel">批量移除</el-button>
<el-button v-if="catalogId === null" icon="el-icon-plus" size="mini" style="margin-right: 1rem;" :disabled="gbChannels.length === 0 || multipleSelection.length === 0" @click="batchAdd">批量添加</el-button>
</div>
<el-table ref="gbChannelsTable" :data="gbChannels" border style="width: 100%" :height="winHeight">
<el-table ref="gbChannelsTable" :data="gbChannels" border style="width: 100%" :height="winHeight" :row-key="(row)=> row.deviceId + row.channelId" @selection-change="handleSelectionChange">
<el-table-column align="center" type="selection" :reserve-selection="true" width="55">
</el-table-column>
<el-table-column prop="channelId" label="通道编号" width="180" align="center">
</el-table-column>
<el-table-column prop="name" label="通道名称" show-overflow-tooltip align="center">
@ -50,10 +51,12 @@
</el-table>
<el-pagination style="float: right;margin-top: 1rem;" @size-change="handleSizeChange" @current-change="currentChange" :current-page="currentPage" :page-size="count" :page-sizes="[10, 20, 30, 50]" layout="total, sizes, prev, pager, next" :total="total">
</el-pagination>
<getCatalog ref="getCatalog" :platformId="platformId" ></getCatalog>
</div>
</template>
<script>
import getCatalog from './getCatalog'
export default {
name: 'chooseChannelForGb',
computed: {
@ -65,10 +68,13 @@ export default {
// };
// }
},
props: ['platformId','catalogId', 'updateChoosedCallback'],
props: ['platformId','catalogId', 'catalogName'],
created() {
this.initData();
},
components: {
getCatalog,
},
data() {
return {
gbChannels: [],
@ -80,7 +86,8 @@ export default {
currentPage: 1,
count: 10,
total: 0,
eventEnanle: false,
eventEnable: false,
multipleSelection: [],
winHeight: window.innerHeight - 400,
};
@ -88,8 +95,10 @@ export default {
watch:{
platformId(newData, oldData){
console.log(newData)
this.initData()
this.getChannelList()
},
catalogId(newData, oldData){
this.getChannelList()
},
},
methods: {
@ -105,28 +114,24 @@ export default {
console.log(val)
this.initData();
},
rowcheckedChange: function (val, row) {
console.log(val)
console.log(row)
},
add: function (row) {
console.log(row)
row.catalogId = this.catalogId
row.platformId = this.platformId
this.$axios({
method:"post",
url:"/api/platform/update_channel_for_gb",
data:{
platformId: this.platformId,
channelReduces: [row],
catalogId: this.catalogId
}
}).then((res)=>{
console.log("保存成功")
if(this.updateChoosedCallback)this.updateChoosedCallback(this.catalogId)
}).catch(function (error) {
console.log(error);
});
this.getCatalogFromUser((catalogId)=> {
this.$axios({
method:"post",
url:"/api/platform/update_channel_for_gb",
data:{
platformId: this.platformId,
channelReduces: [row],
catalogId: catalogId
}
}).then((res)=>{
console.log("保存成功")
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
})
},
remove: function (row) {
console.log(row)
@ -140,91 +145,87 @@ export default {
}
}).then((res)=>{
console.log("移除成功")
if(this.updateChoosedCallback)this.updateChoosedCallback(row.catalogId)
row.platformId = null;
row.catalogId = null
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
},
checkedChange: function (val) {
let that = this;
if (!that.eventEnanle) {
return;
}
let newData = {};
let addData = [];
let delData = [];
if (val.length > 0) {
for (let i = 0; i < val.length; i++) {
const element = val[i];
let key = element.deviceId + "_" + element.channelId;
newData[key] = element;
if (!!!that.gbChoosechannel[key]){
addData.push(element)
}else{
delete that.gbChoosechannel[key]
}
}
let oldKeys = Object.keys(that.gbChoosechannel);
if (oldKeys.length > 0) {
for (let i = 0; i < oldKeys.length; i++) {
const key = oldKeys[i];
delData.push(that.gbChoosechannel[key])
}
}
}else{
let oldKeys = Object.keys(that.gbChoosechannel);
if (oldKeys.length > 0) {
for (let i = 0; i < oldKeys.length; i++) {
const key = oldKeys[i];
delData.push(that.gbChoosechannel[key])
}
}
}
that.gbChoosechannel = newData;
if (Object.keys(addData).length >0) {
that.$axios({
method:"post",
url:"/api/platform/update_channel_for_gb",
data:{
platformId: that.platformId,
channelReduces: addData,
catalogId: that.catalogId
}
}).then((res)=>{
console.log("保存成功")
if(that.updateChoosedCallback)that.updateChoosedCallback(that.catalogId)
}).catch(function (error) {
console.log(error);
});
}
if (delData.length >0) {
that.$axios({
method:"delete",
url:"/api/platform/del_channel_for_gb",
data:{
platformId: that.platformId,
channelReduces: delData
}
}).then((res)=>{
console.log("移除成功")
let nodeIds = new Array();
for (let i = 0; i < delData.length; i++) {
nodeIds.push(delData[i].channelId)
}
if(that.updateChoosedCallback)that.updateChoosedCallback(null, nodeIds)
}).catch(function (error) {
console.log(error);
});
}
},
shareAllCheckedChange: function (val) {
},
// checkedChange: function (val) {
// let that = this;
// if (!that.eventEnable) {
// return;
// }
// let newData = {};
// let addData = [];
// let delData = [];
// if (val.length > 0) {
// for (let i = 0; i < val.length; i++) {
// const element = val[i];
// let key = element.deviceId + "_" + element.channelId;
// newData[key] = element;
// if (!!!that.gbChoosechannel[key]){
// addData.push(element)
// }else{
// delete that.gbChoosechannel[key]
// }
// }
//
// let oldKeys = Object.keys(that.gbChoosechannel);
// if (oldKeys.length > 0) {
// for (let i = 0; i < oldKeys.length; i++) {
// const key = oldKeys[i];
// delData.push(that.gbChoosechannel[key])
// }
// }
//
// }else{
// let oldKeys = Object.keys(that.gbChoosechannel);
// if (oldKeys.length > 0) {
// for (let i = 0; i < oldKeys.length; i++) {
// const key = oldKeys[i];
// delData.push(that.gbChoosechannel[key])
// }
// }
// }
//
// that.gbChoosechannel = newData;
// if (Object.keys(addData).length >0) {
// that.$axios({
// method:"post",
// url:"/api/platform/update_channel_for_gb",
// data:{
// platformId: that.platformId,
// channelReduces: addData,
// catalogId: that.catalogId
// }
// }).then((res)=>{
// console.log("")
// }).catch(function (error) {
// console.log(error);
// });
// }
// if (delData.length >0) {
// that.$axios({
// method:"delete",
// url:"/api/platform/del_channel_for_gb",
// data:{
// platformId: that.platformId,
// channelReduces: delData
// }
// }).then((res)=>{
// console.log("")
// let nodeIds = new Array();
// for (let i = 0; i < delData.length; i++) {
// nodeIds.push(delData[i].channelId)
// }
// }).catch(function (error) {
// console.log(error);
// });
// }
// },
// shareAllCheckedChange: function (val) {
//
// },
getChannelList: function () {
let that = this;
@ -236,7 +237,7 @@ export default {
count: that.count,
query: that.searchSrt,
online: that.online,
choosed: that.choosed,
catalogId: that.catalogId,
platformId: that.platformId,
channelType: that.channelType
}
@ -248,20 +249,7 @@ export default {
//
that.$nextTick(() => {
that.$refs.gbChannelsTable.doLayout();
//
var chooseGBS = [];
for (let i = 0; i < res.data.list.length; i++) {
const row = res.data.list[i];
console.log(row.platformId)
if (row.platformId == that.platformId) {
that.$refs.gbChannelsTable.toggleRowSelection(row, true);
chooseGBS.push(row)
that.gbChoosechannel[row.deviceId+ "_" + row.channelId] = row;
}
}
that.eventEnanle = true;
// that.checkedChange(chooseGBS)
that.eventEnable = true;
})
console.log(that.gbChoosechannel)
})
@ -276,10 +264,55 @@ export default {
handleGBSelectionChange: function() {
this.initData();
},
// catalogIdChange: function(id) {
// this.catalogId = id;
// console.log(" " + id)
// },
batchDel: function() {
this.$confirm(`确认这${this.multipleSelection.length}个通道吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios({
method:"delete",
url:"/api/platform/del_channel_for_gb",
data:{
platformId: this.platformId,
channelReduces: this.multipleSelection
}
}).then((res)=>{
console.log("移除成功")
this.$refs.gbChannelsTable.clearSelection()
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
}).catch(() => {
});
},
batchAdd: function() {
this.getCatalogFromUser((catalogId)=> {
this.$axios({
method: "post",
url: "/api/platform/update_channel_for_gb",
data: {
platformId: this.platformId,
channelReduces: this.multipleSelection,
catalogId: catalogId,
}
}).then((res) => {
console.log("保存成功")
this.$refs.gbChannelsTable.clearSelection()
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
});
},
handleSelectionChange: function (val) {
this.multipleSelection = val;
},
getCatalogFromUser(callback){
this.$refs.getCatalog.openDialog(callback)
},
}
};
</script>

265
web_src/src/components/dialog/chooseChannelForStream.vue

@ -1,6 +1,33 @@
<template>
<div id="chooseChannelFoStream" >
<el-table ref="gbStreamsTable" :data="gbStreams" border style="width: 100%" :height="winHeight">
<div style="font-size: 17px; color: #606060; white-space: nowrap; line-height: 30px; font-family: monospace;">
<span v-if="catalogId == null">{{catalogName}}的直播流</span>
<span v-if="catalogId != null">{{catalogName}}({{catalogId}})的直播流</span>
</div>
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;">
搜索: <el-input @input="getChannelList" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字" prefix-icon="el-icon-search" v-model="searchSrt" clearable> </el-input>
<!-- 流媒体: <el-select size="mini" @change="getChannelList" style="margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" default-first-option>-->
<!-- <el-option label="全部" value=""></el-option>-->
<!-- <el-option-->
<!-- v-for="item in mediaServerList"-->
<!-- :key="item.id"-->
<!-- :label="item.id"-->
<!-- :value="item.id">-->
<!-- </el-option>-->
<!-- </el-select>-->
推流状态: <el-select size="mini" style="margin-right: 1rem;" @change="getChannelList" v-model="pushing" placeholder="请选择" default-first-option>
<el-option label="全部" value=""></el-option>
<el-option label="推流进行中" value="true"></el-option>
<el-option label="推流未进行" value="false"></el-option>
</el-select>
<el-button v-if="catalogId !== null" icon="el-icon-delete" size="mini" style="margin-right: 1rem;" :disabled="gbStreams.length === 0 || multipleSelection.length === 0" type="danger" @click="batchDel">批量移除</el-button>
<el-button v-if="catalogId === null" icon="el-icon-plus" size="mini" style="margin-right: 1rem;" :disabled="gbStreams.length === 0 || multipleSelection.length === 0" @click="batchAdd">批量添加</el-button>
</div>
<el-table ref="gbStreamsTable" :data="gbStreams" border style="width: 100%" :height="winHeight" :row-key="(row)=> row.app + row.stream" @selection-change="handleSelectionChange">
<el-table-column align="center" type="selection" :reserve-selection="true" width="55">
</el-table-column>
<el-table-column prop="name" label="名称" show-overflow-tooltip align="center">
</el-table-column>
<el-table-column prop="app" label="应用名" show-overflow-tooltip align="center">
@ -20,18 +47,21 @@
<el-table-column label="操作" width="100" align="center" fixed="right" >
<template slot-scope="scope">
<el-button-group>
<el-button size="mini" icon="el-icon-plus" v-if="!scope.row.platformId" @click="add(scope.row)">添加</el-button>
<el-button size="mini" icon="el-icon-delete" v-if="scope.row.platformId" type="danger" @click="remove(scope.row)">移除</el-button>
<el-button size="mini" icon="el-icon-plus" v-if="!scope.row.platformId" @click="add(scope.row, scope)">添加</el-button>
<el-button size="mini" icon="el-icon-delete" v-if="scope.row.platformId" type="danger" @click="remove(scope.row, scope)">移除</el-button>
</el-button-group>
</template>
</el-table-column>
</el-table>
<el-pagination style="float: right;margin-top: 1rem;" @size-change="handleSizeChange" @current-change="currentChange" :current-page="currentPage" :page-size="count" :page-sizes="[10, 20, 30, 50]" layout="total, sizes, prev, pager, next" :total="total">
</el-pagination>
<getCatalog ref="getCatalog" :platformId="platformId" ></getCatalog>
</div>
</template>
<script>
import MediaServer from './../service/MediaServer'
import getCatalog from './getCatalog'
export default {
name: 'chooseChannelFoStream',
computed: {
@ -43,73 +73,81 @@ export default {
// };
// }
},
props: ['platformId', 'catalogId', 'updateChoosedCallback'],
props: ['platformId', 'catalogId', 'catalogName'],
created() {
this.initData();
},
components: {
getCatalog,
},
data() {
return {
gbStreams: [],
gbChoosechannel:{},
searchSrt: "",
channelType: "",
online: "",
choosed: "",
currentPage: 1,
count: 10,
total: 0,
eventEnanle: false,
searchSrt: "",
pushing: "",
mediaServerId: "",
mediaServerList: [],
mediaServerObj : new MediaServer(),
eventEnable: false,
multipleSelection: [],
winHeight: window.innerHeight - 350,
};
},
watch:{
platformId(newData, oldData){
console.log(newData)
this.initData()
this.getChannelList()
},
catalogId(newData, oldData){
this.getChannelList()
},
},
methods: {
initData: function() {
this.mediaServerObj.getOnlineMediaServerList((data)=>{
this.mediaServerList = data.data;
})
this.getChannelList();
},
currentChange: function (val) {
this.currentPage = val;
this.initData();
this.getChannelList();
},
handleSizeChange: function (val) {
this.count = val;
console.log(val)
this.initData();
this.getChannelList();
},
rowcheckedChanage: function (val, row) {
console.log(val)
console.log(row)
},
add: function (row) {
console.log(row)
row.catalogId = this.catalogId
row.platformId = this.platformId
this.$axios({
method:"post",
url:"/api/gbStream/add",
data:{
platformId: this.platformId,
catalogId: this.catalogId,
gbStreams: [row],
}
}).then((res)=>{
console.log("保存成功")
if(this.updateChoosedCallback)this.updateChoosedCallback(this.catalogId)
}).catch(function (error) {
console.log(error);
});
},
remove: function (row) {
console.log(row)
add: function (row, scope) {
this.getCatalogFromUser((catalogId)=>{
this.$axios({
method:"post",
url:"/api/gbStream/add",
data:{
platformId: this.platformId,
catalogId: catalogId,
gbStreams: [row],
}
}).then((res)=>{
console.log("保存成功")
// this.gbStreams.splice(scope.$index,1)
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
})
},
remove: function (row, scope) {
this.$axios({
method:"delete",
url:"/api/gbStream/del",
@ -119,92 +157,12 @@ export default {
}
}).then((res)=>{
console.log("移除成功")
if(this.updateChoosedCallback)this.updateChoosedCallback(row.catalogId)
row.platformId = null;
row.catalogId = null
// this.gbStreams.splice(scope.$index,1)
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
},
checkedChanage: function (val) {
var that = this;
if (!that.eventEnanle) {
return;
}
var newData = {};
var addData = [];
var delData = [];
if (val.length > 0) {
for (let i = 0; i < val.length; i++) {
const element = val[i];
var key = element.app + "_" + element.stream;
newData[key] = element;
if (!!!that.gbChoosechannel[key]){
addData.push(element)
}else{
delete that.gbChoosechannel[key]
}
}
var oldKeys = Object.keys(that.gbChoosechannel);
if (oldKeys.length > 0) {
for (let i = 0; i < oldKeys.length; i++) {
const key = oldKeys[i];
delData.push(that.gbChoosechannel[key])
}
}
}else{
var oldKeys = Object.keys(that.gbChoosechannel);
if (oldKeys.length > 0) {
for (let i = 0; i < oldKeys.length; i++) {
const key = oldKeys[i];
delData.push(that.gbChoosechannel[key])
}
}
}
that.gbChoosechannel = newData;
if (Object.keys(addData).length >0) {
console.log(addData)
that.$axios({
method:"post",
url:"/api/gbStream/add",
data:{
platformId: that.platformId,
catalogId: that.catalogId,
gbStreams: addData,
}
}).then((res)=>{
console.log("保存成功")
if(this.updateChoosedCallback)this.updateChoosedCallback(this.catalogId)
}).catch(function (error) {
console.log(error);
});
}
if (Object.keys(delData).length >0) {
console.log(delData)
that.$axios({
method:"delete",
url:"/api/gbStream/del",
data:{
platformId: that.platformId,
gbStreams: delData,
}
}).then((res)=>{
console.log("移除成功")
if(this.updateChoosedCallback)this.updateChoosedCallback(this.catalogId)
}).catch(function (error) {
console.log(error);
});
}
},
shareAllCheckedChanage: function (val) {
this.chooseChanage(null, val)
},
getChannelList: function () {
let that = this;
@ -215,10 +173,10 @@ export default {
page: that.currentPage,
count: that.count,
query: that.searchSrt,
online: that.online,
choosed: that.choosed,
pushing: that.online,
platformId: that.platformId,
channelType: that.channelType
catalogId: that.catalogId,
mediaServerId: that.mediaServerId
}
})
.then(function (res) {
@ -229,29 +187,62 @@ export default {
that.$nextTick(() => {
that.$refs.gbStreamsTable.doLayout();
//
var chooseGBS = [];
for (let i = 0; i < res.data.list.length; i++) {
const row = res.data.list[i];
console.log(row.platformId)
if (row.platformId == that.platformId) {
that.$refs.gbStreamsTable.toggleRowSelection(row, true);
chooseGBS.push(row)
that.gbChoosechannel[row.app+ "_" + row.stream] = row;
}
}
that.eventEnanle = true;
// that.checkedChanage(chooseGBS)
that.eventEnable = true;
})
console.log(that.gbChoosechannel)
})
.catch(function (error) {
console.log(error);
});
},
handleGBSelectionChange: function() {
this.initData();
batchDel: function() {
this.$confirm(`确认这${this.multipleSelection.length}个通道吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios({
method:"delete",
url:"/api/gbStream/del",
data:{
platformId: this.platformId,
gbStreams: this.multipleSelection,
}
}).then((res)=>{
console.log("移除成功")
this.$refs.gbStreamsTable.clearSelection()
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
}).catch(() => {
});
},
batchAdd: function() {
this.getCatalogFromUser((catalogId)=>{
this.$axios({
method:"post",
url:"/api/gbStream/add",
data:{
platformId: this.platformId,
catalogId: catalogId,
gbStreams: this.multipleSelection,
}
}).then((res)=>{
console.log("保存成功")
this.$refs.gbStreamsTable.clearSelection()
this.getChannelList();
}).catch(function (error) {
console.log(error);
});
})
},
getCatalogFromUser(callback){
this.$refs.getCatalog.openDialog(callback)
},
handleSelectionChange: function (val) {
this.multipleSelection = val;
},
}
};

161
web_src/src/components/dialog/getCatalog.vue

@ -0,0 +1,161 @@
<template>
<div id="getCatalog" >
<el-dialog title="选择要添加到的节点" v-if="showDialog" width="50%" :append-to-body="true" :close-on-click-modal="false" :visible.sync="showDialog" :destroy-on-close="true" @close="close()" center>
<div>
<el-tree class="el-scrollbar"
ref="tree"
id="catalogTree"
empty-text="未知节点"
node-key="id"
default-expand-all
:highlight-current="false"
:expand-on-click-node="false"
:props="props"
:load="loadNode"
@node-click="nodeClickHandler"
lazy>
<span class="custom-tree-node" slot-scope="{ node, data }" style="width: 100%">
<el-radio v-if="node.data.type === 0 || node.data.type === -1" style="margin-right: 0" v-model="chooseId" :label="node.data.id">{{''}}</el-radio>
<span v-if="node.data.type === -1 && node.level === 1" style="font-size: 12px" class="iconfont icon-ziyuan"></span>
<span v-if="node.data.type === 0 && node.level === 1" class="el-icon-s-home"></span>
<span v-if="node.data.type === 0 && node.level > 1" class="el-icon-folder-opened"></span>
<span v-if="node.data.type === 1" class="iconfont icon-shexiangtou"></span>
<span v-if="node.data.type === 2" class="iconfont icon-zhibo"></span>
<span style=" padding-left: 1px">{{ node.label }}</span>
<span>
<i style="margin-left: 5rem; color: #9d9d9d; padding-right: 20px" v-if="node.data.id === defaultCatalogIdSign">默认</i>
</span>
</span>
</el-tree>
</div>
<div style="float: right; height: 13rem">
<el-button type="primary" size="mini" @click="submit()" >确认</el-button>
<el-button @click="close()" size="mini">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'getCatalog',
beforeCreate(){
},
created() {
this.chooseId = this.defaultCatalogId;
this.defaultCatalogIdSign = this.defaultCatalogId;
this.initData();
setTimeout(()=>{
if (this.catalogIdChange)this.catalogIdChange(this.defaultCatalogId);
}, 100)
},
props: ['platformId'],
data() {
return {
props: {
label: 'name',
children: 'children',
isLeaf: 'leaf'
},
platformName: null,
defaultCatalogId: null,
catalogIdResult: null,
showDialog: false,
defaultCatalogIdSign: null,
chooseNode: null,
chooseId: "",
catalogTree: null,
contextmenuShow: false,
};
},
methods: {
openDialog(catalogIdResult) {
this.showDialog = true
this.catalogIdResult = catalogIdResult
},
initData: function () {
this.getCatalog();
},
getCatalog: function(parentId, callback) {
let that = this;
this.$axios({
method:"get",
url:`/api/platform/catalog`,
params: {
platformId: that.platformId,
parentId: parentId
}
})
.then((res)=> {
if (res.data.code === 0) {
if (typeof(callback) === 'function') {
callback(res.data.data)
}
}
})
.catch(function (error) {
console.log(error);
});
},
loadNode: function(node, resolve){
if (node.level === 0) {
this.$axios({
method:"get",
url:`/api/platform/info/` + this.platformId,
})
.then((res)=> {
if (res.data.code === 0) {
this.platformName = res.data.data.name;
this.defaultCatalogId = res.data.data.catalogId;
resolve([
{
name: "未分配",
id: null,
type: -1
},{
name: this.platformName,
id: this.platformId,
type: 0
}
]);
}
})
.catch(function (error) {
console.log(error);
});
}
if (node.level >= 1){
this.getCatalog(node.data.id, resolve)
}
},
nodeClickHandler: function (data, node, tree){
this.chooseId = data.id;
},
close: function() {
this.showDialog = false;
},
submit: function() {
if (this.catalogIdResult)this.catalogIdResult(this.chooseId)
this.showDialog = false;
},
}
};
</script>
<style>
#catalogTree{
display: inline-block;
}
</style>

4
web_src/src/components/dialog/importChannel.vue

@ -45,8 +45,8 @@ export default {
showDialog: false,
isLoging: false,
isEdit: false,
errorStreams: null,
errorGBIds: null,
errorStreams: [],
errorGBIds: [],
uploadUrl: process.env.NODE_ENV === 'development'?`debug/api/push/upload`:`api/push/upload`,
};
},

317
web_src/src/components/jessibuca.vue

@ -0,0 +1,317 @@
<template>
<div :id="'jessibuca'+idx" style="width: 100%; height: 100%">
<div :id="'container'+idx" ref="container" style="width: 100%; height: 100%; background-color: #000" @dblclick="fullscreenSwich">
<div class="buttons-box" :id="'buttonsBox'+idx">
<div class="buttons-box-left">
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick"></i>
<i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause"></i>
<i class="iconfont icon-stop jessibuca-btn" @click="destroyButton"></i>
<i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="jessibuca.mute()"></i>
<i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="jessibuca.cancelMute()"></i>
</div>
<div class="buttons-box-right">
<span class="jessibuca-btn">{{kBps}} kb/s</span>
<!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>-->
<!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>-->
<i class="iconfont icon-camera1196054easyiconnet jessibuca-btn" @click="screenshot" style="font-size: 1rem !important"></i>
<i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick"></i>
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich"></i>
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich"></i>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'jessibuca',
data() {
return {
jessibuca: null,
playing: false,
isNotMute: false,
quieting: false,
fullscreen: false,
loaded: false, // mute
speed: 0,
performance: "", //
kBps: 0,
btnDom: null,
videoInfo: null,
volume: 1,
rotate: 0,
vod: true, //
forceNoOffscreen: false,
};
},
props: ['videoUrl', 'error', 'hasAudio', 'height','idx'],
mounted () {
window.onerror = (msg) => {
// console.error(msg)
};
let paramUrl = decodeURIComponent(this.$route.params.url)
this.$nextTick(() =>{
let dom = document.getElementById("container"+this.idx);
// dom.style.height = (9/16 ) * dom.clientWidth + "px"
if (typeof (this.videoUrl) == "undefined") {
this.videoUrl = paramUrl;
}
this.btnDom = document.getElementById("buttonsBox"+this.idx);
console.log("初始化时的地址为: " + this.videoUrl)
this.play(this.videoUrl)
})
},
watch:{
videoUrl(newData, oldData){
this.play(newData)
},
immediate:true
},
methods: {
create(){
let options = {};
console.log(this.$refs.container)
console.log("hasAudio " + !!this.hasAudio)
this.jessibuca = new window.Jessibuca(Object.assign(
{
container: this.$refs.container,
videoBuffer: 0.2, //
isResize: true,
decoder: "./static/js/jessibuca/index.js",
// text: "WVP-PRO",
// background: "bg.jpg",
loadingText: "加载中",
hasAudio: !!this.hasAudio,
debug: false,
timeout:5,
supportDblclickFullscreen: false, //
operateBtns: {
fullscreen: false,
screenshot: false,
play: false,
audio: false,
},
record: "record",
vod: this.vod,
forceNoOffscreen: this.forceNoOffscreen,
isNotMute: this.isNotMute,
},
options
));
let _this = this;
this.jessibuca.on("load", function () {
console.log("on load init");
});
this.jessibuca.on("log", function (msg) {
console.log("on log", msg);
});
this.jessibuca.on("record", function (msg) {
console.log("on record:", msg);
});
this.jessibuca.on("pause", function () {
_this.playing = false;
});
this.jessibuca.on("play", function () {
_this.playing = true;
});
this.jessibuca.on("fullscreen", function (msg) {
console.log("on fullscreen", msg);
_this.fullscreen = msg
});
this.jessibuca.on("mute", function (msg) {
console.log("on mute", msg);
_this.isNotMute = !msg;
});
this.jessibuca.on("audioInfo", function (msg) {
// console.log("audioInfo", msg);
});
this.jessibuca.on("videoInfo", function (msg) {
this.videoInfo = msg;
// console.log("videoInfo", msg);
});
this.jessibuca.on("bps", function (bps) {
// console.log('bps', bps);
});
let _ts = 0;
this.jessibuca.on("timeUpdate", function (ts) {
// console.log('timeUpdate,old,new,timestamp', _ts, ts, ts - _ts);
_ts = ts;
});
this.jessibuca.on("videoInfo", function (info) {
console.log("videoInfo", info);
});
this.jessibuca.on("error", (error) =>{
console.log("error", error);
this.pause()
});
this.jessibuca.on("timeout", ()=> {
console.log("timeout");
// this.pause()
this.play(this.videoUrl)
});
this.jessibuca.on('start', function () {
console.log('start');
})
this.jessibuca.on("performance", function (performance) {
let show = "卡顿";
if (performance === 2) {
show = "非常流畅";
} else if (performance === 1) {
show = "流畅";
}
_this.performance = show;
});
this.jessibuca.on('buffer', function (buffer) {
// console.log('buffer', buffer);
})
this.jessibuca.on('stats', function (stats) {
// console.log('stats', stats);
})
this.jessibuca.on('kBps', function (kBps) {
_this.kBps = Math.round(kBps);
});
// PTS
this.jessibuca.on('videoFrame', function () {
})
//
this.jessibuca.on('metadata', function () {
});
},
playBtnClick: function (event){
this.play(this.videoUrl)
},
play: function (url) {
console.log(url)
if (this.jessibuca) {
this.destroy();
}
if(!url){
return
}
this.create();
this.jessibuca.on("play", () => {
this.playing = true;
this.loaded = true;
this.quieting = this.jessibuca.quieting;
});
if (this.jessibuca.hasLoaded()) {
this.jessibuca.play(url);
} else {
this.jessibuca.on("load", () => {
console.log("load 播放")
this.jessibuca.play(url);
});
}
},
pause: function () {
if (this.jessibuca) {
this.jessibuca.pause();
}
this.playing = false;
this.err = "";
this.performance = "";
},
destroy: function () {
if (this.jessibuca) {
this.jessibuca.destroy();
}
if (document.getElementById("buttonsBox"+this.idx) == null) {
document.getElementById("container"+this.idx).appendChild(this.btnDom)
}
this.jessibuca = null;
this.playing = false;
this.err = "";
this.performance = "";
},
eventcallbacK: function(type, message) {
// console.log("player ")
// console.log(type)
// console.log(message)
},
fullscreenSwich: function (){
let isFull = this.isFullscreen()
this.jessibuca.setFullscreen(!isFull)
this.fullscreen = !isFull;
},
isFullscreen: function (){
return document.fullscreenElement ||
document.msFullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement || false;
},
resize(){
this.jessibuca.resize()
},
screenshot(){
this.jessibuca.screenshot('截图','png',0.5)
// let base64 = this.jessibuca.screenshot("shot","jpeg",0.5,'base64')
// this.$emit('screenshot',base64)
},
destroyButton() {
this.$emit('destroy', this.idx)
this.destroy()
}
},
destroyed() {
if (this.jessibuca) {
this.jessibuca.destroy();
}
this.playing = false;
this.loaded = false;
this.performance = "";
},
}
</script>
<style>
.buttons-box{
width: 100%;
height: 28px;
background-color: rgba(43, 51, 63, 0.7);
position: absolute;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
left: 0;
bottom: 0;
user-select: none;
z-index: 10;
}
.jessibuca-btn{
width: 20px;
color: rgb(255, 255, 255);
line-height: 27px;
margin: 0px 10px;
padding: 0px 2px;
cursor: pointer;
text-align: center;
font-size: 0.8rem !important;
}
.buttons-box-right {
position: absolute;
right: 0;
}
</style>

357
web_src/src/components/live.vue

@ -0,0 +1,357 @@
<template>
<div id="devicePosition" style="height: 100%">
<el-container style="height: 100%">
<el-header>
<uiHeader></uiHeader>
</el-header>
<el-container v-loading="loading" element-loading-text="拼命加载中">
<el-aside width="300px" style="background-color: #ffffff">
<div style="text-align: center;padding-top: 20px;">设备列表</div>
<el-menu v-loading="loading">
<el-submenu v-for="device in deviceList" :key="device.deviceId" :index="device.deviceId" @click="sendDevicePush(item)">
<template slot="title" >
<i class="el-icon-location-outline"></i>
{{device.name}}
</template>
<ChannelTree :device="device" @sendDevicePush="sendDevicePush"></ChannelTree>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<!-- <LivePlay></LivePlay> -->
<el-header height="40px" style="text-align: left;font-size: 17px;line-height: 40px;">
分屏:
<i class="el-icon-full-screen btn" :class="{active:spilt==1}" @click="spilt=1"/>
<i class="el-icon-menu btn" :class="{active:spilt==4}" @click="spilt=4"/>
<i class="el-icon-s-grid btn" :class="{active:spilt==9}" @click="spilt=9"/>
</el-header>
<el-main>
<div style="width: 100%;height: calc( 100vh - 110px );display: flex;flex-wrap: wrap;background-color: #000;">
<div v-for="i in spilt" :key="i" class="play-box"
:style="liveStyle" :class="{redborder:playerIdx == (i-1)}"
@click="playerIdx = (i-1)"
>
<div v-if="!videoUrl[i-1]" style="color: #ffffff;font-size: 30px;font-weight: bold;">{{i}}</div>
<player v-else :ref="'player'+i" :videoUrl="videoUrl[i-1]" fluent autoplay :height="true"
:idx="'player'+i" @screenshot="shot" @destroy="destroy"></player>
<!-- <player v-else ref="'player'+i" :idx="'player'+i" :visible.sync="showVideoDialog" :videoUrl="videoUrl[i-1]" :height="true" :hasAudio="hasAudio" fluent autoplay live ></player> -->
</div>
</div>
</el-main>
</el-container>
</el-container>
</el-container>
</div>
</template>
<script>
import uiHeader from "./UiHeader.vue";
import player from './jessibuca.vue'
import ChannelTree from './channelTree.vue'
export default {
name: "live",
components: {
uiHeader, player, ChannelTree
},
data() {
return {
showVideoDialog: true,
hasAudio: false,
videoUrl:[''],
spilt:1,//
playerIdx:0,//
deviceList: [], //
currentDevice: {}, //
videoComponentList: [],
updateLooper: 0, //
currentDeviceChannelsLenth:0,
winHeight: window.innerHeight - 200,
currentPage:1,
count:15,
total:0,
getDeviceListLoading: false,
//channel
searchSrt: "",
channelType: "",
online: "",
channelTotal:0,
deviceChannelList:[],
loading:false
};
},
mounted() {
this.initData();
},
created(){
this.checkPlayByParam()
},
computed:{
liveStyle(){
if(this.spilt==1){
return {width:'100%',height:'100%'}
}else if(this.spilt==4){
return {width:'49%',height:'49%'}
}else if(this.spilt==9){
return {width:'32%',height:'32%'}
}
}
},
watch:{
spilt(newValue){
console.log("切换画幅;"+newValue)
let that = this
for (let i = 1; i <= newValue; i++) {
if(!that.$refs['player'+i]){
continue
}
this.$nextTick(()=>{
if(that.$refs['player'+i] instanceof Array){
that.$refs['player'+i][0].resize()
}else {
that.$refs['player'+i].resize()
}
})
}
window.localStorage.setItem('split',newValue)
},
'$route.fullPath':'checkPlayByParam'
},
destroyed() {
clearTimeout(this.updateLooper);
},
methods: {
initData: function () {
this.getDeviceList();
},
destroy(idx) {
console.log(idx);
this.clear(idx.substring(idx.length-1))
},
getDeviceList: function() {
let that = this;
this.$axios({
method: 'get',
url:`/api/device/query/devices`,
params: {
page: that.currentPage,
count: that.count
}
}).then(function (res) {
console.log(res.data.list);
that.total = res.data.total;
that.deviceList = res.data.list.map(item=>{return {deviceChannelList:[],...item}});
that.getDeviceListLoading = false;
}).catch(function (error) {
console.log(error);
that.getDeviceListLoading = false;
});
},
//
sendDevicePush: function (itemData) {
if(itemData.status===0){
this.$message.error('设备离线!');
return
}
this.save(itemData)
let deviceId = itemData.deviceId;
// this.isLoging = true;
let channelId = itemData.channelId;
console.log("通知设备推流1:" + deviceId + " : " + channelId );
let idxTmp = this.playerIdx
let that = this;
this.loading = true
this.$axios({
method: 'get',
url: '/api/play/start/' + deviceId + '/' + channelId
}).then(function (res) {
// that.isLoging = false;
console.log('=====----=====')
console.log(res)
if (res.data.code == 0 && res.data.data) {
itemData.playUrl = res.data.data.httpsFlv
that.setPlayUrl(res.data.data.ws_flv,idxTmp)
}else {
that.$message.error(res.data.msg);
}
}).catch(function (e) {
}).finally(()=>{
that.loading = false
});
},
setPlayUrl(url,idx){
this.$set(this.videoUrl,idx,url)
let _this = this
setTimeout(()=>{
window.localStorage.setItem('videoUrl',JSON.stringify(_this.videoUrl))
},100)
},
checkPlayByParam(){
let {deviceId,channelId} = this.$route.query
if(deviceId && channelId){
this.sendDevicePush({deviceId,channelId})
}
},
convertImageToCanvas(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
},
shot(e){
// console.log(e)
// send({code:'image',data:e})
var base64ToBlob = function(code) {
let parts = code.split(';base64,');
let contentType = parts[0].split(':')[1];
let raw = window.atob(parts[1]);
let rawLength = raw.length;
let uInt8Array = new Uint8Array(rawLength);
for(let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {
type: contentType
});
};
let aLink = document.createElement('a');
let blob = base64ToBlob(e); //new Blob([content]);
let evt = document.createEvent("HTMLEvents");
evt.initEvent("click", true, true); //initEvent FF
aLink.download = '截图';
aLink.href = URL.createObjectURL(blob);
aLink.click();
},
save(item){
let dataStr = window.localStorage.getItem('playData') || '[]'
let data = JSON.parse(dataStr);
data[this.playerIdx] = item
window.localStorage.setItem('playData',JSON.stringify(data))
},
clear(idx) {
let dataStr = window.localStorage.getItem('playData') || '[]'
let data = JSON.parse(dataStr);
data[idx-1] = null;
console.log(data);
window.localStorage.setItem('playData',JSON.stringify(data))
},
loadAndPlay(){
let dataStr = window.localStorage.getItem('playData') || '[]'
let data = JSON.parse(dataStr);
data.forEach((item,i)=>{
if(item){
this.playerIdx = i
this.sendDevicePush(item)
}
})
}
}
};
</script>
<style>
.btn{
margin: 0 10px;
}
.btn:hover{
color: #409EFF;
}
.btn.active{
color: #409EFF;
}
.redborder{
border: 2px solid red !important;
}
.play-box{
background-color: #000000;
border: 2px solid #505050;
display: flex;
align-items: center;
justify-content: center;
}
</style>
<style>
.videoList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.video-item {
position: relative;
width: 15rem;
height: 10rem;
margin-right: 1rem;
background-color: #000000;
}
.video-item-img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100%;
height: 100%;
}
.video-item-img:after {
content: "";
display: inline-block;
position: absolute;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 3rem;
height: 3rem;
background-image: url("../assets/loading.png");
background-size: cover;
background-color: #000000;
}
.video-item-title {
position: absolute;
bottom: 0;
color: #000000;
background-color: #ffffff;
line-height: 1.5rem;
padding: 0.3rem;
width: 14.4rem;
}
.baidumap {
width: 100%;
height: 100%;
border: none;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
/* 去除百度地图版权那行字 和 百度logo */
.baidumap > .BMap_cpyCtrl {
display: none !important;
}
.baidumap > .anchorBL {
display: none !important;
}
</style>

5
web_src/src/router/index.js

@ -15,6 +15,7 @@ import test from '../components/test.vue'
import web from '../components/setting/Web.vue'
import sip from '../components/setting/Sip.vue'
import media from '../components/setting/Media.vue'
import live from '../components/live.vue'
import wasmPlayer from '../components/dialog/jessibuca.vue'
import rtcPlayer from '../components/dialog/rtcPlayer.vue'
@ -34,6 +35,10 @@ export default new VueRouter({
path: '/',
component: control,
},
{
path: '/live',
component: live,
},
{
path: '/deviceList',
component: deviceList,

14
web_src/static/css/iconfont.css

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 1291092 */
src: url('iconfont.woff2?t=1640922722742') format('woff2'),
url('iconfont.woff?t=1640922722742') format('woff'),
url('iconfont.ttf?t=1640922722742') format('truetype');
src: url('iconfont.woff2?t=1644809302709') format('woff2'),
url('iconfont.woff?t=1644809302709') format('woff'),
url('iconfont.ttf?t=1644809302709') format('truetype');
}
.iconfont {
@ -13,6 +13,14 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-ziyuan:before {
content: "\e7d5";
}
.icon-shexiangtou1:before {
content: "\e7d4";
}
.icon-wxbzhuye:before {
content: "\e7d1";
}

BIN
web_src/static/css/iconfont.woff2

Binary file not shown.

BIN
web_src/static/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
web_src/static/file/推流通道导入.zip

Binary file not shown.
Loading…
Cancel
Save