648540858
2022-11-23 12fa3b4c8da2af13a710400d64c9fe6d5d6b1e5e
Merge branch 'wvp-28181-2.0'

# Conflicts:
# src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
35个文件已修改
667 ■■■■■ 已修改文件
doc/README.md 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql.sql 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/update.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/bean/GbStreamParam.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/UpdateChannelParam.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/all-application.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/StreamProxyList.vue 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/StreamProxyEdit.vue 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/SyncChannelProgress.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/chooseChannelForGb.vue 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/chooseChannelForStream.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/README.md
@@ -21,22 +21,22 @@
- [X] 实时视音频点播
- [X] 设备控制
  - [X] 云台控制
  - [ ] 远程启动
  - [ ] 录像控制
  - [ ] 报警布防/撤防
  - [ ] 报警复位
  - [X] 远程启动
  - [X] 录像控制
  - [X] 报警布防/撤防
  - [X] 报警复位
  - [X] 强制关键帧
  - [ ] 拉框放大
  - [ ] 拉框缩小
  - [ ] 看守位控制
  - [ ] 设备配置
  - [X] 拉框放大
  - [X] 拉框缩小
  - [X] 看守位控制
  - [X] 设备配置
- [X] 报警事件通知和分发
- [X] 设备目录订阅
- [X] 网络设备信息查询
  - [X] 设备目录查询
  - [X] 设备状态查询
  - [ ] 设备配置查询
  - [ ] 设备预置位查询
  - [X] 设备配置查询
  - [X] 设备预置位查询
- [X] 状态信息报送
- [X] 设备视音频文件检索
- [X] 历史视音频的回放
sql/mysql.sql
@@ -446,7 +446,7 @@
                                `ffmpeg_cmd_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                                `rtp_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                                `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                                `enable_hls` bit(1) DEFAULT NULL,
                                `enable_audio` bit(1) DEFAULT NULL,
                                `enable_mp4` bit(1) DEFAULT NULL,
                                `enable` bit(1) NOT NULL,
                                `status` bit(1) NOT NULL,
sql/update.sql
@@ -36,3 +36,8 @@
alter table device
    modify hostAddress varchar(50) null;
alter table stream_proxy
    change enable_hls enable_audio bit null;
src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java
@@ -1,12 +1,12 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
@@ -40,8 +40,8 @@
     */
    @ExceptionHandler(ControllerException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public WVPResult<String> exceptionHandler(ControllerException e) {
        return WVPResult.fail(e.getCode(), e.getMsg());
    public ResponseEntity<WVPResult<String>> exceptionHandler(ControllerException e) {
        return new ResponseEntity<>(WVPResult.fail(e.getCode(), e.getMsg()), HttpStatus.OK);
    }
    /**
@@ -51,7 +51,7 @@
     */
    @ExceptionHandler(BadCredentialsException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public WVPResult<String> exceptionHandler(BadCredentialsException e) {
        return WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage());
    public ResponseEntity<WVPResult<String>> exceptionHandler(BadCredentialsException e) {
        return new ResponseEntity<>(WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage()), HttpStatus.OK);
    }
}
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -41,6 +41,8 @@
    private Boolean gbSendStreamStrict = Boolean.FALSE;
    private Boolean syncChannelOnDeviceOnline = Boolean.FALSE;
    private String serverId = "000000";
    private String thirdPartyGBIdReg = "[\\s\\S]*";
@@ -186,4 +188,12 @@
    public void setGbSendStreamStrict(Boolean gbSendStreamStrict) {
        this.gbSendStreamStrict = gbSendStreamStrict;
    }
    public Boolean getSyncChannelOnDeviceOnline() {
        return syncChannelOnDeviceOnline;
    }
    public void setSyncChannelOnDeviceOnline(Boolean syncChannelOnDeviceOnline) {
        this.syncChannelOnDeviceOnline = syncChannelOnDeviceOnline;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -2,7 +2,9 @@
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
@@ -10,10 +12,7 @@
import gov.nist.javax.sip.message.SIPRequest;
import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException;
import javax.sip.SipException;
import javax.sip.message.Message;
import javax.sip.message.Request;
import java.text.ParseException;
import javax.sip.message.Message;
import javax.sip.message.Request;
@@ -234,7 +233,7 @@
     */
    void deviceConfigCmd(Device device);
    
        /**
    /**
     * 设备配置命令:basicParam
     * 
     * @param device              视频设备
@@ -245,7 +244,7 @@
     * @param heartBeatCount    心跳超时次数(可选)
     */  
    void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
    /**
     * 查询设备状态
     * 
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -646,6 +646,7 @@
                    // 修改数据
                    streamProxyService.stop(param.getApp(), param.getStream());
                }else {
                    // 无人观看不做处理
                    ret.put("close", false);
                }
                return ret;
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -239,14 +239,13 @@
    }
    public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms,
                                      boolean enable_hls, boolean enable_mp4, String ffmpeg_cmd_key){
                                      boolean enable_audio, boolean enable_mp4, String ffmpeg_cmd_key){
        logger.info(src_url);
        logger.info(dst_url);
        Map<String, Object> param = new HashMap<>();
        param.put("src_url", src_url);
        param.put("dst_url", dst_url);
        param.put("timeout_ms", timeout_ms);
        param.put("enable_hls", enable_hls);
        param.put("enable_mp4", enable_mp4);
        param.put("ffmpeg_cmd_key", ffmpeg_cmd_key);
        return sendPost(mediaServerItem, "addFFmpegSource",param, null);
@@ -294,19 +293,14 @@
        return sendPost(mediaServerItem, "restartServer",null, null);
    }
    public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
    public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) {
        Map<String, Object> param = new HashMap<>();
        param.put("vhost", "__defaultVhost__");
        param.put("app", app);
        param.put("stream", stream);
        param.put("url", url);
        param.put("enable_hls", enable_hls?1:0);
        param.put("enable_mp4", enable_mp4?1:0);
        param.put("enable_rtmp", 1);
        param.put("enable_fmp4", 1);
        param.put("enable_audio", 1);
        param.put("enable_rtsp", 1);
        param.put("add_mute_audio", 1);
        param.put("enable_audio", enable_audio?1:0);
        param.put("rtp_type", rtp_type);
        return sendPost(mediaServerItem, "addStreamProxy",param, null);
    }
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyItem.java
@@ -31,8 +31,8 @@
    private String rtp_type;
    @Schema(description = "是否启用")
    private boolean enable;
    @Schema(description = "是否启用HLS")
    private boolean enable_hls;
    @Schema(description = "是否启用音频")
    private boolean enable_audio;
    @Schema(description = "是否启用MP4")
    private boolean enable_mp4;
    @Schema(description = "是否 无人观看时删除")
@@ -40,8 +40,6 @@
    @Schema(description = "是否 无人观看时自动停用")
    private boolean enable_disable_none_reader;
    @Schema(description = "上级平台国标ID")
    private String platformGbId;
    @Schema(description = "创建时间")
    private String createTime;
@@ -139,14 +137,6 @@
        this.enable = enable;
    }
    public boolean isEnable_hls() {
        return enable_hls;
    }
    public void setEnable_hls(boolean enable_hls) {
        this.enable_hls = enable_hls;
    }
    public boolean isEnable_mp4() {
        return enable_mp4;
    }
@@ -155,19 +145,12 @@
        this.enable_mp4 = enable_mp4;
    }
    public String getPlatformGbId() {
        return platformGbId;
    }
    public void setPlatformGbId(String platformGbId) {
        this.platformGbId = platformGbId;
    }
    @Override
    public String getCreateTime() {
        return createTime;
    }
    @Override
    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }
@@ -187,4 +170,12 @@
    public void setEnable_disable_none_reader(boolean enable_disable_none_reader) {
        this.enable_disable_none_reader = enable_disable_none_reader;
    }
    public boolean isEnable_audio() {
        return enable_audio;
    }
    public void setEnable_audio(boolean enable_audio) {
        this.enable_audio = enable_audio;
    }
}
src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java
@@ -3,6 +3,7 @@
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import java.util.List;
@@ -38,4 +39,11 @@
     * @return
     */
    ResourceBaceInfo getOverview();
    /**
     * 查询所有未分配的通道
     * @param platformId
     * @return
     */
    List<ChannelReduce> queryAllChannelList(String platformId);
}
src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java
@@ -55,4 +55,18 @@
    int updateGbIdOrName(List<StreamPushItem> streamPushItemForUpdate);
    DeviceChannel getDeviceChannelListByStreamWithStatus(GbStream gbStream, String catalogId, ParentPlatform platform);
    /**
     * 查询所有未分配的通道
     * @param platformId
     * @return
     */
    List<GbStream> getAllGBChannels(String platformId);
    /**
     * 移除所有关联的通道
     * @param platformId
     * @param catalogId
     */
    void delAllPlatformInfo(String platformId, String catalogId);
}
src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java
@@ -19,4 +19,11 @@
     */
    int updateChannelForGB(String platformId, List<ChannelReduce> channelReduces, String catalogId);
    /**
     * 移除目录下的所有通道
     * @param platformId
     * @param catalogId
     * @return
     */
    int delAllChannelForGB(String platformId, String catalogId);
}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java
@@ -10,6 +10,7 @@
import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -168,4 +169,12 @@
    public ResourceBaceInfo getOverview() {
        return channelMapper.getOverview();
    }
    @Override
    public List<ChannelReduce> queryAllChannelList(String platformId) {
        return channelMapper.queryChannelListInAll(null, null, null, platformId, null);
    }
}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.service.impl;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
@@ -12,7 +13,6 @@
import com.genersoft.iot.vmp.service.IDeviceService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
import com.genersoft.iot.vmp.storager.dao.PlatformChannelMapper;
@@ -78,7 +78,7 @@
    TransactionDefinition transactionDefinition;
    @Autowired
    private IVideoManagerStorage storage;
    private UserSetting userSetting;
    @Autowired
    private ISIPCommander commander;
@@ -120,16 +120,18 @@
            if(device.getOnline() == 0){
                device.setOnline(1);
                device.setCreateTime(now);
                logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId());
                deviceMapper.update(device);
                redisCatchStorage.updateDevice(device);
                try {
                    commander.deviceInfoQuery(device);
                } catch (InvalidArgumentException | SipException | ParseException e) {
                    logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
                if (userSetting.getSyncChannelOnDeviceOnline()) {
                    logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId());
                    try {
                        commander.deviceInfoQuery(device);
                    } catch (InvalidArgumentException | SipException | ParseException e) {
                        logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
                    }
                    sync(device);
                    // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台
                }
                sync(device);
                // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台
            }else {
                if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).size() == 0) {
                    logger.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId());
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
@@ -4,11 +4,11 @@
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.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.IGbStreamService;
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
import com.genersoft.iot.vmp.storager.dao.PlatformCatalogMapper;
import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
import com.genersoft.iot.vmp.service.IGbStreamService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
@@ -19,7 +19,6 @@
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@@ -230,4 +229,35 @@
        deviceChannel.setSecrecy("0");
        return deviceChannel;
    }
    @Override
    public List<GbStream> getAllGBChannels(String platformId) {
        return gbStreamMapper.selectAll(platformId, null, null, null);
    }
    @Override
    public void delAllPlatformInfo(String platformId, String catalogId) {
        if (platformId == null) {
            return ;
        }
        ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId);
        if (platform == null) {
            return ;
        }
        if (ObjectUtils.isEmpty(catalogId)) {
            catalogId = platform.getDeviceGBId();
        }
        if (platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId) > 0) {
            List<GbStream> gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId);
            List<DeviceChannel> deviceChannelList = new ArrayList<>();
            for (GbStream gbStream : gbStreams) {
                DeviceChannel deviceChannel = new DeviceChannel();
                deviceChannel.setChannelId(gbStream.getGbId());
                deviceChannelList.add(deviceChannel);
            }
            eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL);
        }
    }
}
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
@@ -16,6 +16,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -105,4 +106,26 @@
        }
        return deviceChannelList;
    }
    @Override
    public int delAllChannelForGB(String platformId, String catalogId) {
        int result;
        if (platformId == null) {
            return 0;
        }
        ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId);
        if (platform == null) {
            return 0;
        }
        if (ObjectUtils.isEmpty(catalogId)) {
           catalogId = platform.getDeviceGBId();
        }
        if ((result = platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId)) > 0) {
            List<DeviceChannel> deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId);
            eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL);
        }
        return result;
    }
}
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -5,23 +5,22 @@
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
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.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
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.hook.OnStreamChangedHookParam;
import com.genersoft.iot.vmp.service.IGbStreamService;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.IMediaService;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
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.StreamProxyMapper;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
@@ -35,7 +34,9 @@
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.ObjectUtils;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 视频代理业务
@@ -101,7 +102,6 @@
                param.getStream() );
        param.setDst_url(dstUrl);
        StringBuffer resultMsg = new StringBuffer();
        boolean streamLive = false;
        param.setMediaServerId(mediaInfo.getId());
        boolean saveResult;
        // 更新
@@ -118,7 +118,6 @@
        if (param.isEnable()) {
            JSONObject jsonObject = addStreamProxyToZlm(param);
            if (jsonObject == null || jsonObject.getInteger("code") != 0) {
                streamLive = false;
                resultMsg.append(", 但是启用失败,请检查流地址是否可用");
                param.setEnable(false);
                // 直接移除
@@ -128,26 +127,10 @@
                    updateStreamProxy(param);
                }
            }else {
                streamLive = true;
                resultForStreamInfo = mediaService.getStreamInfoByAppAndStream(
                        mediaInfo, param.getApp(), param.getStream(), null, null);
            }
        }
        if ( !ObjectUtils.isEmpty(param.getPlatformGbId()) && streamLive) {
            List<GbStream> gbStreams = new ArrayList<>();
            gbStreams.add(param);
            if (gbStreamService.addPlatformInfo(gbStreams, param.getPlatformGbId(), param.getCatalogId())){
                return resultForStreamInfo;
            }else {
                resultMsg.append(",  关联国标平台[ " + param.getPlatformGbId() + " ]失败");
                throw new ControllerException(ErrorCode.ERROR100.getCode(), resultMsg.toString());
            }
        }else {
            if (!streamLive) {
                throw new ControllerException(ErrorCode.ERROR100.getCode(), resultMsg.toString());
            }
        }
        return resultForStreamInfo;
@@ -239,10 +222,10 @@
        }
        if ("default".equals(param.getType())){
            result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(),
                    param.isEnable_hls(), param.isEnable_mp4(), param.getRtp_type());
                    param.isEnable_audio(), param.isEnable_mp4(), param.getRtp_type());
        }else if ("ffmpeg".equals(param.getType())) {
            result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrc_url(), param.getDst_url(),
                    param.getTimeout_ms() + "", param.isEnable_hls(), param.isEnable_mp4(),
                    param.getTimeout_ms() + "", param.isEnable_audio(), param.isEnable_mp4(),
                    param.getFfmpeg_cmd_key());
        }
        return result;
@@ -295,6 +278,9 @@
                result = true;
                streamProxy.setEnable(true);
                updateStreamProxy(streamProxy);
            }else {
                logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"),
                        streamProxy.getSrc_url() == null? streamProxy.getUrl():streamProxy.getSrc_url());
            }
        }
        return result;
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java
@@ -5,7 +5,6 @@
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.StreamPushExcelDto;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
@@ -169,4 +168,5 @@
    @Select("SELECT status FROM stream_push WHERE app=#{app} AND stream=#{stream}")
    Boolean selectStatusForPush(String app, String stream);
}
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
@@ -57,6 +57,9 @@
    @Select("SELECT dc.* FROM platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId WHERE dc.channelId='${channelId}' and pgc.platformId='${platformId}'")
    List<DeviceChannel> queryChannelInParentPlatform(String platformId, String channelId);
    @Select("SELECT dc.* FROM platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId WHERE pgc.platformId='${platformId}' and pgc.catalogId=#{catalogId}")
    List<DeviceChannel> queryAllChannelInCatalog(String platformId, String catalogId);
    @Select(" select dc.channelId as id, dc.name as name, pgc.platformId as platformId, pgc.catalogId as parentId, 0 as childrenCount, 1 as type " +
            " from device_channel dc left join platform_gb_channel pgc on dc.id = pgc.deviceChannelId " +
            " where pgc.platformId=#{platformId} and pgc.catalogId=#{catalogId}")
@@ -99,4 +102,9 @@
           "DELETE FROM platform_gb_channel WHERE platformId=#{serverGBId}"  +
           "</script>")
    void delByPlatformId(String serverGBId);
    @Delete("<script> " +
            "DELETE FROM platform_gb_channel WHERE platformId=#{platformId} and catalogId=#{catalogId}"  +
            "</script>")
    int delChannelForGBByCatalogId(String platformId, String catalogId);
}
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java
@@ -105,4 +105,7 @@
            "</foreach>" +
            "</script>")
    void delByAppAndStreamsByPlatformId(List<GbStream> gbStreams, String platformId);
    @Delete("DELETE FROM platform_gb_stream WHERE platformId=#{platformId} and catalogId=#{catalogId}")
    int delByPlatformAndCatalogId(String platformId, String catalogId);
}
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
@@ -12,9 +12,9 @@
public interface StreamProxyMapper {
    @Insert("INSERT INTO stream_proxy (type, name, app, stream,mediaServerId, url, src_url, dst_url, " +
            "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, createTime) VALUES" +
            "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, createTime) VALUES" +
            "('${type}','${name}', '${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " +
            "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, ${status}, " +
            "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_audio}, ${enable_mp4}, ${enable}, ${status}, " +
            "${enable_remove_none_reader}, ${enable_disable_none_reader}, '${createTime}' )")
    int add(StreamProxyItem streamProxyDto);
@@ -30,7 +30,7 @@
            "timeout_ms=#{timeout_ms}, " +
            "ffmpeg_cmd_key=#{ffmpeg_cmd_key}, " +
            "rtp_type=#{rtp_type}, " +
            "enable_hls=#{enable_hls}, " +
            "enable_audio=#{enable_audio}, " +
            "enable=#{enable}, " +
            "status=#{status}, " +
            "enable_remove_none_reader=#{enable_remove_none_reader}, " +
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
@@ -1,13 +1,11 @@
package com.genersoft.iot.vmp.storager.impl;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
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.dto.StreamProxyItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.genersoft.iot.vmp.service.IGbStreamService;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -28,7 +26,6 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -761,18 +758,6 @@
        return gbStreamMapper.updateStreamGPS(gpsMsgInfos);
    }
    private List<DeviceChannel> getDeviceChannelListByChannelReduceList(List<ChannelReduce> channelReduces, String catalogId) {
        List<DeviceChannel> deviceChannelList = new ArrayList<>();
        if (channelReduces.size() > 0){
            for (ChannelReduce channelReduce : channelReduces) {
                DeviceChannel deviceChannel = queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId());
                deviceChannel.setParental(1);
                deviceChannel.setParentId(catalogId);
                deviceChannelList.add(deviceChannel);
            }
        }
        return deviceChannelList;
    }
    private DeviceChannel getDeviceChannelByCatalog(PlatformCatalog catalog) {
        ParentPlatform platform = platformMapper.getParentPlatByServerGBId(catalog.getPlatformId());
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
@@ -1,9 +1,9 @@
package com.genersoft.iot.vmp.vmanager.gb28181.gbStream;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.service.IGbStreamService;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam;
import com.genersoft.iot.vmp.service.IGbStreamService;
import com.github.pagehelper.PageInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -12,8 +12,9 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name  = "视频流关联到级联平台")
@CrossOrigin
@@ -76,11 +77,14 @@
    @Operation(summary = "移除国标关联")
    @DeleteMapping(value = "/del")
    @ResponseBody
    public Object del(@RequestBody GbStreamParam gbStreamParam){
        if (gbStreamService.delPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getGbStreams())) {
            return "success";
    public void del(@RequestBody GbStreamParam gbStreamParam){
        if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) {
            if (gbStreamParam.isAll()) {
                gbStreamService.delAllPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId());
            }
        }else {
            return "fail";
            gbStreamService.delPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getGbStreams());
        }
    }
@@ -93,11 +97,14 @@
    @Operation(summary = "保存国标关联")
    @PostMapping(value = "/add")
    @ResponseBody
    public Object add(@RequestBody GbStreamParam gbStreamParam){
        if (gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId())) {
            return "success";
    public void add(@RequestBody GbStreamParam gbStreamParam){
        if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) {
            if (gbStreamParam.isAll()) {
                List<GbStream> allGBChannels = gbStreamService.getAllGBChannels(gbStreamParam.getPlatformId());
                gbStreamService.addPlatformInfo(allGBChannels, gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId());
            }
        }else {
            return "fail";
            gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId());
        }
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/bean/GbStreamParam.java
@@ -14,6 +14,9 @@
    @Schema(description = "目录ID")
    private String catalogId;
    @Schema(description = "关联所有通道")
    private boolean all;
    @Schema(description = "流国标信息列表")
    private List<GbStream> gbStreams;
@@ -40,4 +43,12 @@
    public void setGbStreams(List<GbStream> gbStreams) {
        this.gbStreams = gbStreams;
    }
    public boolean isAll() {
        return all;
    }
    public void setAll(boolean all) {
        this.all = all;
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
@@ -10,8 +10,7 @@
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.service.IPlatformChannelService;
import com.genersoft.iot.vmp.service.IPlatformService;
import com.genersoft.iot.vmp.service.*;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
@@ -71,6 +70,12 @@
    @Autowired
    private IPlatformService platformService;
    @Autowired
    private IDeviceChannelService deviceChannelService;
    @Autowired
    private IGbStreamService gbStreamService;
    /**
     * 获取国标服务的配置
@@ -379,7 +384,16 @@
        if (logger.isDebugEnabled()) {
            logger.debug("给上级平台添加国标通道API调用");
        }
        int result = platformChannelService.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId());
        int result = 0;
        if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) {
            if (param.isAll()) {
                logger.info("[国标级联]添加所有通道到上级平台, {}", param.getPlatformId());
                List<ChannelReduce> allChannelForDevice = deviceChannelService.queryAllChannelList(param.getPlatformId());
                result = platformChannelService.updateChannelForGB(param.getPlatformId(), allChannelForDevice, param.getCatalogId());
            }
        }else {
            result = platformChannelService.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId());
        }
        if (result <= 0) {
            throw new ControllerException(ErrorCode.ERROR100);
        }
@@ -399,8 +413,15 @@
        if (logger.isDebugEnabled()) {
            logger.debug("给上级平台删除国标通道API调用");
        }
        int result = storager.delChannelForGB(param.getPlatformId(), param.getChannelReduces());
        int result = 0;
        if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) {
            if (param.isAll()) {
                logger.info("[国标级联]移除所有通道,上级平台, {}", param.getPlatformId());
                result = platformChannelService.delAllChannelForGB(param.getPlatformId(), param.getCatalogId());
            }
        }else {
            result = storager.delChannelForGB(param.getPlatformId(), param.getChannelReduces());
        }
        if (result <= 0) {
            throw new ControllerException(ErrorCode.ERROR100);
        }
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/UpdateChannelParam.java
@@ -17,6 +17,9 @@
    @Schema(description = "目录的国标编号")
    private String catalogId;
    @Schema(description = "处理所有通道")
    private boolean all;
    @Schema(description = "")
    private List<ChannelReduce> channelReduces;
@@ -43,4 +46,12 @@
    public void setCatalogId(String catalogId) {
        this.catalogId = catalogId;
    }
    public boolean isAll() {
        return all;
    }
    public void setAll(boolean all) {
        this.all = all;
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
@@ -111,7 +111,7 @@
            resultHolder.invokeResult(msg);
        });
        if (userSetting.isUsePushingAsStatus()) {
        if (userSetting.getUseSourceIpAsStreamIp()) {
            // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误
            deferredResultEx.setFilter(result1 -> {
                WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>)result1;
@@ -130,7 +130,6 @@
            });
        }
        // 录像查询以channelId作为deviceId查询
        resultHolder.put(key, uuid, deferredResultEx);
@@ -139,7 +138,6 @@
        }
        return result;
    }
    @Operation(summary = "停止点播")
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
@@ -177,7 +175,6 @@
        json.put("deviceId", deviceId);
        json.put("channelId", channelId);
        return json;
    }
    /**
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
@@ -109,7 +109,6 @@
        logger.info("启用代理: " + app + "/" + stream);
        boolean result = streamProxyService.start(app, stream);
        if (!result) {
            logger.info("启用代理失败: " + app + "/" + stream);
            throw new ControllerException(ErrorCode.ERROR100);
        }
    }
src/main/resources/all-application.yml
@@ -195,6 +195,8 @@
    # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能,
    # 非严格模式使用随机端口发流,性能更好, 默认关闭
    gb-send-stream-strict: false
    # 设备上线时是否自动同步通道
    sync-channel-on-device-online: false
# 关闭在线文档(生产环境建议关闭)
springdoc:
web_src/src/components/StreamProxyList.vue
@@ -32,7 +32,7 @@
      <el-table-column label="类型" width="100" >
        <template slot-scope="scope">
          <div slot="reference" class="name-wrapper">
            <el-tag size="medium">{{scope.row.type}}</el-tag>
            <el-tag size="medium">{{scope.row.type === "default"? "直接代理":"FFMPEG代理"}}</el-tag>
          </div>
        </template>
      </el-table-column>
@@ -55,15 +55,15 @@
        </template>
      </el-table-column>
      <el-table-column prop="createTime" label="创建时间"  min-width="150" show-overflow-tooltip/>
      <el-table-column label="转HLS" min-width="120" >
      <el-table-column label="音频" min-width="120" >
        <template slot-scope="scope">
          <div slot="reference" class="name-wrapper">
            <el-tag size="medium" v-if="scope.row.enable_hls">已启用</el-tag>
            <el-tag size="medium" type="info" v-if="!scope.row.enable_hls">未启用</el-tag>
            <el-tag size="medium" v-if="scope.row.enable_audio">已启用</el-tag>
            <el-tag size="medium" type="info" v-if="!scope.row.enable_audio">未启用</el-tag>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="MP4录制" min-width="120" >
      <el-table-column label="录制" min-width="120" >
        <template slot-scope="scope">
          <div slot="reference" class="name-wrapper">
            <el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag>
@@ -71,11 +71,12 @@
          </div>
        </template>
      </el-table-column>
      <el-table-column label="无人观看自动删除" min-width="160" >
      <el-table-column label="无人观看" min-width="160" >
        <template slot-scope="scope">
          <div slot="reference" class="name-wrapper">
            <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">已启用</el-tag>
            <el-tag size="medium" type="info" v-if="!scope.row.enable_remove_none_reader">未启用</el-tag>
            <el-tag size="medium" v-if="scope.row.enable_remove_none_reader">移除</el-tag>
            <el-tag size="medium" v-if="scope.row.enable_disable_none_reader">停用</el-tag>
            <el-tag size="medium" type="info" v-if="!scope.row.enable_remove_none_reader && !scope.row.enable_disable_none_reader">不做处理</el-tag>
          </div>
        </template>
      </el-table-column>
@@ -131,7 +132,6 @@
                currentPage:1,
                count:15,
                total:0,
                getListLoading: false,
        startBtnLoading: false
            };
        },
@@ -139,7 +139,7 @@
        },
        mounted() {
            this.initData();
            this.updateLooper = setInterval(this.initData, 1000);
            this.startUpdateList()
        },
        destroyed() {
            this.$destroy('videojs');
@@ -149,6 +149,12 @@
            initData: function() {
                this.getStreamProxyList();
            },
      stopUpdateList: function (){
        window.clearInterval(this.updateLooper)
      },
      startUpdateList: function (){
        this.updateLooper = setInterval(this.initData, 1000);
      },
            currentChange: function(val){
                this.currentPage = val;
                this.getStreamProxyList();
@@ -159,7 +165,6 @@
            },
            getStreamProxyList: function() {
                let that = this;
                this.getListLoading = true;
                this.$axios({
                    method: 'get',
                    url:`/api/proxy/list`,
@@ -175,23 +180,18 @@
            }
            that.streamProxyList = res.data.data.list;
          }
          that.getListLoading = false;
                }).catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            },
            addStreamProxy: function(){
                this.$refs.streamProxyEdit.openDialog(null, this.initData)
            },
      addOnvif: function(){
        this.getListLoading = true;
        this.getListLoading = true;
        this.$axios({
          method: 'get',
          url:`/api/onvif/search?timeout=3000`,
        }).then((res) =>{
          this.getListLoading = false;
          if (res.data.code === 0 ){
            if (res.data.data.length > 0) {
              this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{
@@ -208,7 +208,6 @@
          }
        }).catch((error)=> {
          this.getListLoading = false;
          this.$message.error(error.response.data.msg);
        });
@@ -217,7 +216,6 @@
            },
            play: function(row){
                let that = this;
                this.getListLoading = true;
                this.$axios({
                    method: 'get',
                    url:`/api/push/getPlayUrl`,
@@ -227,7 +225,6 @@
            mediaServerId: row.mediaServerId
                    }
                }).then(function (res) {
                    that.getListLoading = false;
                    if (res.data.code === 0) {
            that.$refs.devicePlayer.openDialog("streamPlay", null, null, {
              streamInfo: res.data.data,
@@ -243,13 +240,11 @@
                }).catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            },
            deleteStreamProxy: function(row){
                let that = this;
                this.getListLoading = true;
                that.$axios({
                    method:"delete",
                    url:"/api/proxy/del",
@@ -258,16 +253,13 @@
                      stream: row.stream
                    }
                }).then((res)=>{
                    that.getListLoading = false;
                              that.initData()
                }).catch(function (error) {
                    console.log(error);
                              that.getListLoading = false;
                });
            },
            start: function(row){
                let that = this;
                this.getListLoading = true;
        this.stopUpdateList()
        this.$set(row, 'startBtnLoading', true)
                this.$axios({
                    method: 'get',
@@ -276,28 +268,31 @@
                        app: row.app,
                        stream: row.stream
                    }
                }).then(function (res) {
          that.getListLoading = false;
          that.$set(row, 'startBtnLoading', false)
                }).then((res)=> {
                  if (res.data.code === 0){
            that.initData()
            this.initData()
          }else {
            that.$message({
            this.$message({
              showClose: true,
              message: "保存失败,请检查地址是否可用!",
              message: "启动失败,请检查地址是否可用!",
              type: "error",
            });
          }
                }).catch(function (error) {
          this.$set(row, 'startBtnLoading', false)
          this.startUpdateList()
                }).catch((error)=> {
                    console.log(error);
                    that.getListLoading = false;
          that.$set(row, 'startBtnLoading', false)
          this.$message({
            showClose: true,
            message: "启动失败,请检查地址是否可用!",
            type: "error",
          });
          this.$set(row, 'startBtnLoading', false)
          this.startUpdateList()
                });
            },
            stop: function(row){
                let that = this;
                this.getListLoading = true;
                this.$axios({
                    method: 'get',
                    url:`/api/proxy/stop`,
@@ -306,11 +301,9 @@
                        stream: row.stream
                    }
                }).then(function (res) {
                    that.getListLoading = false;
                    that.initData()
                }).catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            },
      refresh: function (){
web_src/src/components/dialog/StreamProxyEdit.vue
@@ -83,31 +83,23 @@
                  <el-option label="组播" value="2"></el-option>
                </el-select>
              </el-form-item>
              <el-form-item label="国标平台">
                <el-select
                  v-model="proxyParam.platformGbId"
                  style="width: 100%"
                  placeholder="请选择国标平台"
                >
                  <el-option
                    v-for="item in platformList"
                    :key="item.name"
                    :label="item.name"
                    :value="item.serverGBId">
                    <span style="float: left">{{ item.name }}</span>
                    <span style="float: right; color: #8492a6; font-size: 13px">{{ item.serverGBId }}</span>
                  </el-option>
                </el-select>
              </el-form-item>
            <el-form-item label="无人观看" prop="rtp_type" >
              <el-select
                @change="noneReaderHandler"
                v-model="proxyParam.none_reader"
                style="width: 100%"
                placeholder="请选择无人观看的处理方式"
              >
                <el-option label="不做处理" value="0"></el-option>
                <el-option label="停用" value="1"></el-option>
                <el-option label="移除" value="2"></el-option>
              </el-select>
            </el-form-item>
              <el-form-item label="其他选项">
                <div style="float: left;">
                  <el-checkbox label="启用" v-model="proxyParam.enable" ></el-checkbox>
                  <el-checkbox label="转HLS" v-model="proxyParam.enable_hls" ></el-checkbox>
                  <el-checkbox label="MP4录制" v-model="proxyParam.enable_mp4" ></el-checkbox>
                  <el-checkbox label="无人观看自动删除" v-model="proxyParam.enable_remove_none_reader" @change="removeNoneReader"></el-checkbox>
                  <el-checkbox label="无人观看停止拉流" v-model="proxyParam.enable_disable_none_reader" @change="disableNoneReaderHandType"></el-checkbox>
                  <el-checkbox label="开启音频" v-model="proxyParam.enable_audio" ></el-checkbox>
                  <el-checkbox label="录制" v-model="proxyParam.enable_mp4" ></el-checkbox>
                </div>
              </el-form-item>
@@ -169,10 +161,11 @@
          gbId: null,
          rtp_type: null,
          enable: true,
          enable_hls: true,
          enable_audio: true,
          enable_mp4: false,
          none_reader: null,
          enable_remove_none_reader: false,
          enable_disable_none_reader: true,
          enable_disable_none_reader: false,
          platformGbId: null,
          mediaServerId: null,
      },
@@ -196,6 +189,7 @@
      this.listChangeCallback = callback;
      if (proxyParam != null) {
        this.proxyParam = proxyParam;
        this.proxyParam.none_reader = null;
      }
      let that = this;
@@ -233,26 +227,26 @@
    },
    onSubmit: function () {
      this.dialogLoading = true;
      var that = this;
      that.$axios({
      this.noneReaderHandler();
      this.$axios({
        method: 'post',
        url:`/api/proxy/save`,
        data: that.proxyParam
      }).then(function (res) {
        that.dialogLoading = false;
        data: this.proxyParam
      }).then((res)=> {
        this.dialogLoading = false;
        if (typeof (res.data.code) != "undefined" && res.data.code === 0) {
          that.$message({
          this.$message({
            showClose: true,
            message: res.data.msg,
            type: "success",
          });
          that.showDialog = false;
          if (that.listChangeCallback != null) {
            that.listChangeCallback();
            that.dialogLoading = false;
          this.showDialog = false;
          if (this.listChangeCallback != null) {
            this.listChangeCallback();
            this.dialogLoading = false;
          }
        }
      }).catch(function (error) {
      }).catch((error) =>{
        console.log(error);
        this.dialogLoading = false;
      });
@@ -280,12 +274,18 @@
        this.platform.expires = "300";
      }
    },
    removeNoneReader: function(checked) {
      this.proxyParam.enable_disable_none_reader = !checked;
    noneReaderHandler: function() {
      if (this.proxyParam.none_reader === null || this.proxyParam.none_reader === "0") {
        this.proxyParam.enable_disable_none_reader = false;
        this.proxyParam.enable_remove_none_reader = false;
      }else if (this.proxyParam.none_reader === "1"){
        this.proxyParam.enable_disable_none_reader = true;
        this.proxyParam.enable_remove_none_reader = false;
      }else if (this.proxyParam.none_reader ==="2"){
        this.proxyParam.enable_disable_none_reader = false;
        this.proxyParam.enable_remove_none_reader = true;
      }
    },
    disableNoneReaderHandType: function(checked) {
      this.proxyParam.enable_remove_none_reader = !checked;
    }
  },
};
</script>
web_src/src/components/dialog/SyncChannelProgress.vue
@@ -63,37 +63,29 @@
          }
          if (res.data.data != null) {
            if (res.data.syncIng) {
            if (res.data.data.syncIng) {
              if (res.data.data.total == 0) {
                if (res.data.data.errorMsg !== null ){
                  this.msg = res.data.data.errorMsg;
                  this.syncStatus = "exception"
                }else {
                  this.msg = `等待同步中`;
                  this.timmer = setTimeout(this.getProgress, 300)
                }
              }else  {
                if (res.data.data.total == res.data.data.current) {
                  this.syncStatus = "success"
                  this.percentage = 100;
                  this.msg = '同步成功';
                }else {
                  if (res.data.data.errorMsg !== null ){
                    this.msg = res.data.data.errorMsg;
                    this.syncStatus = "exception"
                  }else {
                    this.total = res.data.data.total;
                    this.current = res.data.data.current;
                    this.percentage = Math.floor(Number(res.data.data.current)/Number(res.data.data.total)* 10000)/100;
                    this.msg = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
                    this.timmer = setTimeout(this.getProgress, 300)
                  }
                }
                this.msg = `等待同步中`;
                this.timmer = setTimeout(this.getProgress, 300)
              }else {
                this.total = res.data.data.total;
                this.current = res.data.data.current;
                this.percentage = Math.floor(Number(res.data.data.current)/Number(res.data.data.total)* 10000)/100;
                this.msg = `同步中...[${res.data.data.current}/${res.data.data.total}]`;
                this.timmer = setTimeout(this.getProgress, 300)
              }
            }else {
              this.syncStatus = "success"
              this.percentage = 100;
              this.msg = '同步成功';
              if (res.data.data.errorMsg){
                this.msg = res.data.data.errorMsg;
                this.syncStatus = "exception"
              }else {
                this.syncStatus = "success"
                this.percentage = 100;
                this.msg = '同步成功';
                setTimeout(()=>{
                  this.showDialog = false;
                }, 3000)
              }
            }
          }
        }else {
web_src/src/components/dialog/chooseChannelForGb.vue
@@ -18,8 +18,10 @@
            <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="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>
     <el-button v-if="catalogId !== null" icon="el-icon-delete" size="mini" :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" :disabled="gbChannels.length === 0 || multipleSelection.length === 0" @click="batchAdd">批量添加</el-button>
     <el-button v-if="catalogId === null" icon="el-icon-plus" size="mini" @click="add()">全部添加</el-button>
     <el-button v-if="catalogId !== null" type="danger" icon="el-icon-delete" size="mini" @click="remove()">全部移除</el-button>
    </div>
    <el-table ref="gbChannelsTable" :data="gbChannels" border style="width: 100%" :height="winHeight" :row-key="(row)=> row.deviceId + row.channelId" @selection-change="handleSelectionChange">
@@ -115,13 +117,15 @@
            this.initData();
        },
        add: function (row) {
          let all = typeof(row) === "undefined"
          this.getCatalogFromUser((catalogId)=> {
            this.$axios({
              method:"post",
              url:"/api/platform/update_channel_for_gb",
              data:{
                platformId:  this.platformId,
                channelReduces: [row],
                all: all,
                channelReduces: all?[]:[row],
                catalogId: catalogId
              }
            }).then((res)=>{
@@ -134,21 +138,34 @@
        },
        remove: function (row) {
          console.log(row)
          let all = typeof(row) === "undefined"
          this.$confirm(`确定移除${all?"所有通道":""}吗?`, '提示', {
            dangerouslyUseHTMLString: true,
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            console.log(row)
          this.$axios({
            method:"delete",
            url:"/api/platform/del_channel_for_gb",
            data:{
              platformId:  this.platformId,
              channelReduces: [row]
            }
          }).then((res)=>{
            console.log("移除成功")
            this.getChannelList();
          }).catch(function (error) {
            console.log(error);
            this.$axios({
              method:"delete",
              url:"/api/platform/del_channel_for_gb",
              data:{
                platformId:  this.platformId,
                all: all,
                channelReduces: all?[]:[row],
              }
            }).then((res)=>{
              console.log("移除成功")
              this.getChannelList();
            }).catch(function (error) {
              console.log(error);
            });
          }).catch(() => {
          });
        },
        // checkedChange: function (val) {
        //     let that = this;
web_src/src/components/dialog/chooseChannelForStream.vue
@@ -24,6 +24,8 @@
      </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>
      <el-button v-if="catalogId === null"  icon="el-icon-plus" size="mini" style="margin-right: 1rem;" @click="add()">全部添加</el-button>
      <el-button v-if="catalogId !== null" type="danger" icon="el-icon-delete" size="mini" style="margin-right: 1rem;" @click="remove()">全部移除</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">
@@ -128,6 +130,7 @@
        },
        add: function (row, scope) {
          let all = typeof(row) === "undefined"
          this.getCatalogFromUser((catalogId)=>{
            this.$axios({
              method:"post",
@@ -135,7 +138,8 @@
              data:{
                platformId: this.platformId,
                catalogId: catalogId,
                gbStreams:  [row],
                all: all,
                gbStreams: all?[]:[row],
              }
            }).then((res)=>{
              console.log("保存成功")
@@ -149,20 +153,33 @@
        },
        remove: function (row, scope) {
          this.$axios({
            method:"delete",
            url:"/api/gbStream/del",
            data:{
              platformId: this.platformId,
              gbStreams:  [row],
            }
          }).then((res)=>{
            console.log("移除成功")
            // this.gbStreams.splice(scope.$index,1)
            this.getChannelList();
          }).catch(function (error) {
            console.log(error);
          let all = typeof(row) === "undefined"
          this.$confirm(`确定移除${all?"所有通道":""}吗?`, '提示', {
            dangerouslyUseHTMLString: true,
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            this.$axios({
              method:"delete",
              url:"/api/gbStream/del",
              data:{
                platformId: this.platformId,
                all: all,
                gbStreams: all?[]:[row],
              }
            }).then((res)=>{
              console.log("移除成功")
              // this.gbStreams.splice(scope.$index,1)
              this.getChannelList();
            }).catch(function (error) {
              console.log(error);
            });
          }).catch(() => {
          });
        },
        getChannelList: function () {
            let that = this;