panlinlin
2021-03-30 56859d09df8d4226882d43934acf32d60a3b51d7
添加推流列表和拉流代理,下一步与国标关联
21个文件已修改
10个文件已添加
5 文件已重命名
1个文件已删除
1634 ■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyDto.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/media/MediaController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/service/IMediaService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/service/IStreamProxyService.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/MediaServiceImpl.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/StreamProxyServiceImpl.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/web/ApiMediaController.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/wvp.sqlite 补丁 | 查看 | 原始文档 | blame | 历史
web_src/package-lock.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/ParentPlatformList.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/PushVideoList.vue 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/StreamProxyList.vue 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/UiHeader.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/channelList.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/StreamProxyEdit.vue 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/chooseChannel.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/chooseChannelForGb.vue 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/devicePlayer.vue 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/platformEdit.vue 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/dialog/player.vue 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/main.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/router/index.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java
@@ -35,6 +35,8 @@
    private String wanIp;
    private long updateTime;
    @JSONField(name = "hls.fileBufSize")
    private String hlsFileBufSize;
@@ -728,4 +730,12 @@
    public void setWanIp(String wanIp) {
        this.wanIp = wanIp;
    }
    public long getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(long updateTime) {
        this.updateTime = updateTime;
    }
}
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.media.zlm;
import java.util.List;
import java.util.UUID;
import com.alibaba.fastjson.JSON;
@@ -272,26 +273,35 @@
        }
        
        String streamId = json.getString("stream");
        String app = json.getString("app");
        StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
        JSONObject ret = new JSONObject();
        ret.put("code", 0);
        ret.put("close", true);
        if (streamInfo != null) {
            if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) {
                ret.put("close", false);
            } else {
        if ("rtp".equals(app)){
            JSONObject ret = new JSONObject();
            ret.put("code", 0);
            ret.put("close", true);
            if (streamInfo != null) {
                if (redisCatchStorage.isChannelSendingRTP(streamInfo.getChannelId())) {
                    ret.put("close", false);
                } else {
                    cmder.streamByeCmd(streamId);
                    redisCatchStorage.stopPlay(streamInfo);
                    storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
                }
            }else{
                cmder.streamByeCmd(streamId);
                redisCatchStorage.stopPlay(streamInfo);
                storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
                streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
                redisCatchStorage.stopPlayback(streamInfo);
            }
        }else{
            cmder.streamByeCmd(streamId);
            streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
            redisCatchStorage.stopPlayback(streamInfo);
            return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
        }else {
            JSONObject ret = new JSONObject();
            ret.put("code", 0);
            ret.put("close", false);
            return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
        }
        return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
    }
    
    /**
@@ -350,10 +360,21 @@
//        String data = json.getString("data");
//        List<MediaServerConfig> mediaServerConfigs = JSON.parseArray(JSON.toJSONString(json), MediaServerConfig.class);
//        MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0);
        List<ZLMHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_started);
        if (subscribes != null && subscribes.size() > 0) {
            for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
                subscribe.response(json);
            }
        }
        MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class);
        mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp);
        mediaServerConfig.setLocalIP(mediaIp);
        redisCatchStorage.updateMediaInfo(mediaServerConfig);
        // 重新发起代理
        JSONObject ret = new JSONObject();
        ret.put("code", 0);
        ret.put("msg", "success");
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
@@ -3,7 +3,9 @@
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -67,4 +69,19 @@
        }
        return event;
    }
    public List<ZLMHttpHookSubscribe.Event> getSubscribes(HookType type) {
        ZLMHttpHookSubscribe.Event event= null;
        Map<JSONObject, Event> eventMap = allSubscribes.get(type);
        if (eventMap == null) {
            return null;
        }
        List<ZLMHttpHookSubscribe.Event> result = new ArrayList<>();
        for (JSONObject key : eventMap.keySet()) {
            result.add(eventMap.get(key));
        }
        return result;
    }
}
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
@@ -32,6 +32,7 @@
    public void updateMediaList() {
        JSONObject mediaList = zlmresTfulUtils.getMediaList();
        if (mediaList == null) return;
        String dataStr = mediaList.getString("data");
        Integer code = mediaList.getInteger("code");
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -131,4 +131,25 @@
    public JSONObject stopSendRtp(Map<String, Object> param) {
        return sendPost("stopSendRtp",param);
    }
    public JSONObject addStreamProxy(String app, String stream, String url, boolean enable_hls, 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("rtp_type", rtp_type);
        return sendPost("addStreamProxy",param);
    }
    public JSONObject closeStreams(String app, String stream) {
        Map<String, Object> param = new HashMap<>();
        param.put("vhost", "__defaultVhost__");
        param.put("app", app);
        param.put("stream", stream);
        param.put("force", 1);
        return sendPost("close_streams",param);
    }
}
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
@@ -4,8 +4,11 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyDto;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
//import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.service.IStreamProxyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -16,6 +19,7 @@
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@@ -24,8 +28,8 @@
    private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
    // @Autowired
    // private IVideoManagerStorager storager;
     @Autowired
     private IVideoManagerStorager storager;
    @Autowired
    private IRedisCatchStorage redisCatchStorage;
@@ -63,18 +67,27 @@
    @Autowired
    private ZLMMediaListManager zlmMediaListManager;
    @Autowired
    private ZLMHttpHookSubscribe hookSubscribe;
    @Autowired
    private IStreamProxyService streamProxyService;
    @Override
    public void run(String... strings) throws Exception {
        JSONObject subscribeKey = new JSONObject();
        // 订阅 zlm启动事件
        hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,subscribeKey,(response)->{
            MediaServerConfig mediaServerConfig = JSONObject.toJavaObject(response, MediaServerConfig.class);
            zLmRunning(mediaServerConfig);
        });
        // 获取zlm信息
        logger.info("等待zlm接入...");
        MediaServerConfig mediaServerConfig = getMediaServerConfig();
        if (mediaServerConfig != null) {
            logger.info("zlm接入成功...");
            if (autoConfig) saveZLMConfig();
            mediaServerConfig = getMediaServerConfig();
            redisCatchStorage.updateMediaInfo(mediaServerConfig);
            // 更新流列表
            zlmMediaListManager.updateMediaList();
            zLmRunning(mediaServerConfig);
        }
    }
@@ -85,8 +98,7 @@
            JSONArray data = responseJSON.getJSONArray("data");
            if (data != null && data.size() > 0) {
                mediaServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), MediaServerConfig.class);
                mediaServerConfig.setLocalIP(mediaIp);
                mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp);
            }
        } else {
            logger.error("getMediaServerConfig失败, 1s后重试");
@@ -136,4 +148,27 @@
        }
    }
    /**
     * zlm 连接成功或者zlm重启后
     */
    private void zLmRunning(MediaServerConfig mediaServerConfig){
        logger.info("zlm接入成功...");
        if (autoConfig) saveZLMConfig();
        MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
        if (System.currentTimeMillis() - mediaInfo.getUpdateTime() < 50){
            logger.info("zlm刚刚更新,忽略这次更新");
            return;
        }
        mediaServerConfig.setLocalIP(mediaIp);
        mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp);
        redisCatchStorage.updateMediaInfo(mediaServerConfig);
        // 更新流列表
        zlmMediaListManager.updateMediaList();
        // 恢复流代理
        List<StreamProxyDto> streamProxyListForEnable = storager.getStreamProxyListForEnable(true);
        for (StreamProxyDto streamProxyDto : streamProxyListForEnable) {
            logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream());
            streamProxyService.addStreamProxyToZlm(streamProxyDto);
        }
    }
}
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyDto.java
New file
@@ -0,0 +1,112 @@
package com.genersoft.iot.vmp.media.zlm.dto;
public class StreamProxyDto {
    private String type;
    private String app;
    private String stream;
    private String url;
    private String src_url;
    private String dst_url;
    private int timeout_ms;
    private String ffmpeg_cmd_key;
    private String rtp_type;
    private boolean enable;
    private boolean enable_hls;
    private boolean enable_mp4;
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getApp() {
        return app;
    }
    public void setApp(String app) {
        this.app = app;
    }
    public String getStream() {
        return stream;
    }
    public void setStream(String stream) {
        this.stream = stream;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getSrc_url() {
        return src_url;
    }
    public void setSrc_url(String src_url) {
        this.src_url = src_url;
    }
    public String getDst_url() {
        return dst_url;
    }
    public void setDst_url(String dst_url) {
        this.dst_url = dst_url;
    }
    public int getTimeout_ms() {
        return timeout_ms;
    }
    public void setTimeout_ms(int timeout_ms) {
        this.timeout_ms = timeout_ms;
    }
    public String getFfmpeg_cmd_key() {
        return ffmpeg_cmd_key;
    }
    public void setFfmpeg_cmd_key(String ffmpeg_cmd_key) {
        this.ffmpeg_cmd_key = ffmpeg_cmd_key;
    }
    public String getRtp_type() {
        return rtp_type;
    }
    public void setRtp_type(String rtp_type) {
        this.rtp_type = rtp_type;
    }
    public boolean isEnable() {
        return enable;
    }
    public void setEnable(boolean enable) {
        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;
    }
    public void setEnable_mp4(boolean enable_mp4) {
        this.enable_mp4 = enable_mp4;
    }
}
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.storager;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.RealVideo;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
@@ -116,5 +117,5 @@
     * 获取当前媒体流列表
     * @return List<RealVideo>
     */
    List<Object> getMediaList(int start, int end);
    JSONObject getMediaList(int start, int end);
}
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
@@ -5,6 +5,7 @@
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyDto;
import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.github.pagehelper.PageInfo;
@@ -261,4 +262,49 @@
     * @param deviceId
     */
    public int clearMobilePositionsByDeviceId(String deviceId);
    /**
     * 新增代理流
     * @param streamProxyDto
     * @return
     */
    public int addStreamProxy(StreamProxyDto streamProxyDto);
    /**
     * 更新代理流
     * @param streamProxyDto
     * @return
     */
    public int updateStreamProxy(StreamProxyDto streamProxyDto);
    /**
     * 移除代理流
     * @param app
     * @param stream
     * @return
     */
    public int deleteStreamProxy(String app, String stream);
    /**
     * 按照是否启用获取代理流
     * @param enable
     * @return
     */
    public List<StreamProxyDto> getStreamProxyListForEnable(boolean enable);
    /**
     * 按照是app和stream获取代理流
     * @param app
     * @param stream
     * @return
     */
    public StreamProxyDto queryStreamProxy(String app, String stream);
    /**
     * 获取代理流
     * @param page
     * @param count
     * @return
     */
    PageInfo<StreamProxyDto> queryStreamProxyList(Integer page, Integer count);
}
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
New file
@@ -0,0 +1,46 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyDto;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface StreamProxyMapper {
    @Insert("INSERT INTO stream_proxy (type, app, stream, url, src_url, dst_url, " +
            "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable) VALUES" +
            "('${type}','${app}', '${stream}', '${url}', '${src_url}', '${dst_url}', " +
            "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable} )")
    int add(StreamProxyDto streamProxyDto);
    @Update("UPDATE stream_proxy " +
            "SET type=#{type}, " +
            "app=#{app}," +
            "stream=#{stream}," +
            "url=#{url}, " +
            "src_url=#{src_url}," +
            "dst_url=#{dst_url}, " +
            "timeout_ms=#{timeout_ms}, " +
            "ffmpeg_cmd_key=#{ffmpeg_cmd_key}, " +
            "rtp_type=#{rtp_type}, " +
            "enable_hls=#{enable_hls}, " +
            "enable=#{enable}, " +
            "enable_mp4=#{enable_mp4} " +
            "WHERE app=#{app} AND stream=#{stream}")
    int update(StreamProxyDto streamProxyDto);
    @Delete("DELETE FROM stream_proxy WHERE app=#{app} AND stream=#{stream}")
    int del(String app, String stream);
    @Select("SELECT * FROM stream_proxy")
    List<StreamProxyDto> selectAll();
    @Select("SELECT * FROM stream_proxy WHERE enable=${enable}")
    List<StreamProxyDto> selectForEnable(boolean enable);
    @Select("SELECT * FROM stream_proxy WHERE app=#{app} AND stream=#{stream}")
    StreamProxyDto selectOne(String app, String stream);
}
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.storager.impl;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.RealVideo;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
@@ -92,6 +93,7 @@
     */
    @Override
    public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) {
        mediaServerConfig.setUpdateTime(System.currentTimeMillis());
        return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX,mediaServerConfig);
    }
@@ -280,9 +282,13 @@
     * @return List<RealVideo>
     */
    @Override
    public List<Object> getMediaList(int start, int end) {
    public JSONObject getMediaList(int start, int end) {
        String key = VideoManagerConstants.MEDIA_STREAM_PREFIX;
        Set<Object> realVideos = redis.ZRange(key, start, end);
        return new ArrayList(realVideos);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("list", new ArrayList(realVideos));
        jsonObject.put("total", redis.zSize(key));
        return jsonObject;
    }
}
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
@@ -5,14 +5,11 @@
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyDto;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.storager.dao.DeviceMapper;
import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
import com.genersoft.iot.vmp.storager.dao.PatformChannelMapper;
import com.genersoft.iot.vmp.storager.dao.*;
import com.genersoft.iot.vmp.vmanager.platform.bean.ChannelReduce;
import com.genersoft.iot.vmp.storager.dao.DeviceMobilePositionMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
@@ -48,6 +45,9 @@
    @Autowired
    private PatformChannelMapper patformChannelMapper;
    @Autowired
    private StreamProxyMapper streamProxyMapper;
@@ -233,7 +233,7 @@
    /**
     * 添加Mobile Position设备移动位置
     * @param MobilePosition
     * @param mobilePosition
     */
    @Override
    public synchronized boolean insertMobilePosition(MobilePosition mobilePosition) {
@@ -388,4 +388,68 @@
        return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId);
    }
    /**
     * 新增代理流
     * @param streamProxyDto
     * @return
     */
    @Override
    public int addStreamProxy(StreamProxyDto streamProxyDto) {
        return streamProxyMapper.add(streamProxyDto);
    }
    /**
     * 更新代理流
     * @param streamProxyDto
     * @return
     */
    @Override
    public int updateStreamProxy(StreamProxyDto streamProxyDto) {
        return streamProxyMapper.update(streamProxyDto);
    }
    /**
     * 移除代理流
     * @param id
     * @return
     */
    @Override
    public int deleteStreamProxy(String app, String stream) {
        return streamProxyMapper.del(app, stream);
    }
    /**
     * 根据是否启用获取代理流列表
     * @param enable
     * @return
     */
    @Override
    public List<StreamProxyDto> getStreamProxyListForEnable(boolean enable) {
        return streamProxyMapper.selectForEnable(enable);
    }
    /**
     * 分页查询代理流列表
     * @param page
     * @param count
     * @return
     */
    @Override
    public PageInfo<StreamProxyDto> queryStreamProxyList(Integer page, Integer count) {
        PageHelper.startPage(page, count);
        List<StreamProxyDto> all = streamProxyMapper.selectAll();
        return new PageInfo<>(all);
    }
    /**
     * 按照是app和stream获取代理流
     * @param app
     * @param stream
     * @return
     */
    @Override
    public StreamProxyDto queryStreamProxy(String app, String stream){
        return streamProxyMapper.selectOne(app, stream);
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/media/MediaController.java
New file
@@ -0,0 +1,52 @@
package com.genersoft.iot.vmp.vmanager.media;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.vmanager.service.IMediaService;
import com.genersoft.iot.vmp.vmanager.service.IStreamProxyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@CrossOrigin
@RequestMapping(value = "/api/media")
public class MediaController {
    private final static Logger logger = LoggerFactory.getLogger(MediaController.class);
    @Autowired
    private IRedisCatchStorage redisCatchStorage;
    @Autowired
    private IStreamProxyService streamProxyService;
    @Autowired
    private IMediaService mediaService;
    @RequestMapping(value = "/list")
    @ResponseBody
    public JSONObject list( @RequestParam(required = false)Integer page,
                            @RequestParam(required = false)Integer count,
                            @RequestParam(required = false)String q,
                            @RequestParam(required = false)Boolean online ){
        JSONObject jsonObject = redisCatchStorage.getMediaList(page - 1, page - 1 + count);
        return jsonObject;
    }
    @RequestMapping(value = "/getStreamInfoByAppAndStream")
    @ResponseBody
    public StreamInfo getStreamInfoByAppAndStream(String app, String stream){
        return mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream);
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -8,6 +8,7 @@
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
import com.genersoft.iot.vmp.vmanager.service.IMediaService;
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,6 +55,9 @@
    @Autowired
    private IPlayService playService;
    @Autowired
    private IMediaService mediaService;
    @GetMapping("/play/{deviceId}/{channelId}")
    public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
@@ -159,18 +163,20 @@
                JSONObject data = jsonObject.getJSONObject("data");
                if (data != null) {
                       result.put("key", data.getString("key"));
                    StreamInfo streamInfoResult = new StreamInfo();
                    streamInfoResult.setRtmp(dstUrl);
                    streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
//                    StreamInfo streamInfoResult = new StreamInfo();
//                    streamInfoResult.setRtmp(dstUrl);
//                    streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
//                    streamInfoResult.setStreamId(streamId);
//                    streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
//                    streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
//                    streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
//                    streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
//                    streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
//                    streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
//                    streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
//                    streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStream("convert", streamId);
                    streamInfoResult.setStreamId(streamId);
                    streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
                    result.put("data", streamInfoResult);
                }
            }else {
src/main/java/com/genersoft/iot/vmp/vmanager/service/IMediaService.java
New file
@@ -0,0 +1,25 @@
package com.genersoft.iot.vmp.vmanager.service;
import com.genersoft.iot.vmp.common.StreamInfo;
/**
 * 媒体信息业务
 */
public interface IMediaService {
    /**
     * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在
     * @param app
     * @param stream
     * @return
     */
    StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream);
    /**
     * 根据应用名和流ID获取播放地址, 只是地址拼接
     * @param app
     * @param stream
     * @return
     */
    StreamInfo getStreamInfoByAppAndStream(String app, String stream);
}
src/main/java/com/genersoft/iot/vmp/vmanager/service/IStreamProxyService.java
New file
@@ -0,0 +1,60 @@
package com.genersoft.iot.vmp.vmanager.service;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyDto;
import com.genersoft.iot.vmp.vmanager.streamProxy.StreamProxyController;
import com.github.pagehelper.PageInfo;
public interface IStreamProxyService {
    /**
     * 保存视频代理
     * @param param
     */
    void save(StreamProxyDto param);
    /**
     * 添加视频代理到zlm
     * @param param
     * @return
     */
    JSONObject addStreamProxyToZlm(StreamProxyDto param);
    /**
     * 从zlm移除视频代理
     * @param param
     * @return
     */
    JSONObject removeStreamProxyFromZlm(StreamProxyDto param);
    /**
     * 分页查询
     * @param page
     * @param count
     * @return
     */
    PageInfo<StreamProxyDto> getAll(Integer page, Integer count);
    /**
     * 删除视频代理
     * @param app
     * @param stream
     */
    void del(String app, String stream);
    /**
     * 启用视频代理
     * @param app
     * @param stream
     * @return
     */
    boolean start(String app, String stream);
    /**
     * 停用用视频代理
     * @param app
     * @param stream
     * @return
     */
    boolean stop(String app, String stream);
}
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/MediaServiceImpl.java
New file
@@ -0,0 +1,52 @@
package com.genersoft.iot.vmp.vmanager.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.service.IMediaService;
import com.genersoft.iot.vmp.vmanager.service.IStreamProxyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MediaServiceImpl implements IMediaService {
    @Autowired
    private IRedisCatchStorage redisCatchStorage;
    @Autowired
    private IVideoManagerStorager storager;
    @Autowired
    private ZLMRESTfulUtils zlmresTfulUtils;
    @Override
    public StreamInfo getStreamInfoByAppAndStream(String app, String stream) {
        MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
        StreamInfo streamInfoResult = new StreamInfo();
        streamInfoResult.setRtmp(String.format("rtmp://%s:%s/%s/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), app,  stream));
        streamInfoResult.setRtsp(String.format("rtsp://%s:%s/%s/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), app,  stream));
        streamInfoResult.setFlv(String.format("http://%s:%s/%s/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        streamInfoResult.setWs_flv(String.format("ws://%s:%s/%s/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        streamInfoResult.setHls(String.format("http://%s:%s/%s/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        streamInfoResult.setWs_hls(String.format("ws://%s:%s/%s/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        streamInfoResult.setFmp4(String.format("http://%s:%s/%s/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/%s/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        streamInfoResult.setTs(String.format("http://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        streamInfoResult.setWs_ts(String.format("ws://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app,  stream));
        return streamInfoResult;
    }
    @Override
    public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream) {
        StreamInfo streamInfo = null;
        JSONObject mediaList = zlmresTfulUtils.getMediaList(app, stream);
        if (mediaList != null) {
            streamInfo = getStreamInfoByAppAndStream(app, stream);
        }
        return streamInfo;
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
@@ -15,6 +15,7 @@
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
import com.genersoft.iot.vmp.vmanager.service.IMediaService;
import com.genersoft.iot.vmp.vmanager.service.IPlayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,6 +46,9 @@
    @Autowired
    private ZLMRESTfulUtils zlmresTfulUtils;
    @Autowired
    private IMediaService mediaService;
    @Override
@@ -148,26 +152,27 @@
    public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) {
        String streamId = resonse.getString("id");
        StreamInfo streamInfo = new StreamInfo();
        StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream("rtp", streamId);
//        StreamInfo streamInfo = new StreamInfo();
        streamInfo.setStreamId(streamId);
        streamInfo.setDeviceID(deviceId);
        streamInfo.setChannelId(channelId);
        MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo();
//        MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo();
        streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
        streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtmpPort(), streamId));
        streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtspPort(), streamId));
//        streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//        streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//
//        streamInfo.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//        streamInfo.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//
//        streamInfo.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//        streamInfo.setWs_hls(String.format("ws://%s:%s/rtp/%s/hls.m3u8", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//
//        streamInfo.setTs(String.format("http://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//        streamInfo.setWs_ts(String.format("ws://%s:%s/rtp/%s.live.ts", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId));
//
//        streamInfo.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtmpPort(), streamId));
//        streamInfo.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaServerConfig.getWanIp(), mediaServerConfig.getRtspPort(), streamId));
        return streamInfo;
    }
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/StreamProxyServiceImpl.java
New file
@@ -0,0 +1,123 @@
package com.genersoft.iot.vmp.vmanager.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyDto;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper;
import com.genersoft.iot.vmp.vmanager.service.IStreamProxyService;
import com.genersoft.iot.vmp.vmanager.streamProxy.StreamProxyController;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * 视频代理业务
 */
@Service
public class StreamProxyServiceImpl implements IStreamProxyService {
    @Autowired
    private IVideoManagerStorager videoManagerStorager;
    @Autowired
    private IRedisCatchStorage redisCatchStorage;
    @Autowired
    private ZLMRESTfulUtils zlmresTfulUtils;
    @Autowired
    private StreamProxyMapper streamProxyMapper;
    @Override
    public void save(StreamProxyDto param) {
        MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
        String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(),
                param.getStream() );
        param.setDst_url(dstUrl);
        // 更新
        if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) {
            int result = videoManagerStorager.updateStreamProxy(param);
            if (result > 0 && param.isEnable()) {
                addStreamProxyToZlm(param);
            }
        }else { // 新增
            int result = videoManagerStorager.addStreamProxy(param);
            if (result > 0 && param.isEnable()) {
                addStreamProxyToZlm(param);
            }
        }
    }
    @Override
    public JSONObject addStreamProxyToZlm(StreamProxyDto param) {
        JSONObject result = null;
        if ("default".equals(param.getType())){
            result = zlmresTfulUtils.addStreamProxy(param.getApp(), param.getStream(), param.getUrl(),
                    param.isEnable_hls(), param.isEnable_mp4(), param.getRtp_type());
        }else if ("ffmpeg".equals(param.getType())) {
            result = zlmresTfulUtils.addFFmpegSource(param.getSrc_url(), param.getDst_url(),
                    param.getTimeout_ms() + "");
        }
        return result;
    }
    @Override
    public JSONObject removeStreamProxyFromZlm(StreamProxyDto param) {
        JSONObject result = zlmresTfulUtils.closeStreams(param.getApp(), param.getStream());
        return result;
    }
    @Override
    public PageInfo<StreamProxyDto> getAll(Integer page, Integer count) {
        return videoManagerStorager.queryStreamProxyList(page, count);
    }
    @Override
    public void del(String app, String stream) {
        StreamProxyDto streamProxyDto = new StreamProxyDto();
        streamProxyDto.setApp(app);
        streamProxyDto.setStream(stream);
        JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto);
        if (jsonObject.getInteger("code") == 0) {
            videoManagerStorager.deleteStreamProxy(app, stream);
        }
    }
    @Override
    public boolean start(String app, String stream) {
        boolean result = false;
        StreamProxyDto streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream);
        if (!streamProxyDto.isEnable() &&  streamProxyDto != null) {
            JSONObject jsonObject = addStreamProxyToZlm(streamProxyDto);
            if (jsonObject.getInteger("code") == 0) {
                result = true;
                streamProxyDto.setEnable(true);
                videoManagerStorager.updateStreamProxy(streamProxyDto);
            }
        }
        return result;
    }
    @Override
    public boolean stop(String app, String stream) {
        boolean result = false;
        StreamProxyDto streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream);
        if (streamProxyDto.isEnable() &&  streamProxyDto != null) {
            JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto);
            if (jsonObject.getInteger("code") == 0) {
                result = true;
                streamProxyDto.setEnable(false);
                videoManagerStorager.updateStreamProxy(streamProxyDto);
            }
        }
        return result;
    }
}
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
New file
@@ -0,0 +1,73 @@
package com.genersoft.iot.vmp.vmanager.streamProxy;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyDto;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.vmanager.service.IStreamProxyService;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
 * 拉流代理接口
 */
@Controller
@CrossOrigin
@RequestMapping(value = "/api/proxy")
public class StreamProxyController {
    private final static Logger logger = LoggerFactory.getLogger(StreamProxyController.class);
    @Autowired
    private IRedisCatchStorage redisCatchStorage;
    @Autowired
    private IStreamProxyService streamProxyService;
    @RequestMapping(value = "/list")
    @ResponseBody
    public PageInfo<StreamProxyDto> list(@RequestParam(required = false)Integer page,
                                         @RequestParam(required = false)Integer count,
                                         @RequestParam(required = false)String q,
                                         @RequestParam(required = false)Boolean online ){
        return streamProxyService.getAll(page, count);
    }
    @RequestMapping(value = "/save")
    @ResponseBody
    public Object save(@RequestBody StreamProxyDto param){
        logger.info("添加代理: " + JSONObject.toJSONString(param));
        streamProxyService.save(param);
        return "success";
    }
    @RequestMapping(value = "/del")
    @ResponseBody
    public Object del(String app, String stream){
        logger.info("移除代理: " + app + "/" + stream);
        streamProxyService.del(app, stream);
        return "success";
    }
    @RequestMapping(value = "/start")
    @ResponseBody
    public Object start(String app, String stream){
        logger.info("启用代理: " + app + "/" + stream);
        boolean result = streamProxyService.start(app, stream);
        return "success";
    }
    @RequestMapping(value = "/stop")
    @ResponseBody
    public Object stop(String app, String stream){
        logger.info("停用代理: " + app + "/" + stream);
        boolean result = streamProxyService.stop(app, stream);
        return "success";
    }
}
src/main/java/com/genersoft/iot/vmp/web/ApiMediaController.java
File was deleted
src/main/resources/wvp.sqlite
Binary files differ
web_src/package-lock.json
@@ -10924,6 +10924,26 @@
        "clipboard": "^2.0.0"
      }
    },
    "vue-clipboards": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/vue-clipboards/-/vue-clipboards-1.3.0.tgz",
      "integrity": "sha512-VMDYHpLQH0EUmqfk9b5XMrkvSu/HjNsLW2EBR4OS6JZHcv/PxmWYdoTBPVlp5eYrhWy07La8nWpRwAh09Mgufw==",
      "requires": {
        "clipboard": "^1.7.1"
      },
      "dependencies": {
        "clipboard": {
          "version": "1.7.1",
          "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
          "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=",
          "requires": {
            "good-listener": "^1.2.2",
            "select": "^1.1.2",
            "tiny-emitter": "^2.0.0"
          }
        }
      }
    },
    "vue-cookies": {
      "version": "1.7.4",
      "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz?cache=0&sync_timestamp=1598941352058&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-cookies%2Fdownload%2Fvue-cookies-1.7.4.tgz",
web_src/package.json
@@ -21,6 +21,7 @@
    "vue": "^2.6.11",
    "vue-baidu-map": "^0.21.22",
    "vue-clipboard2": "^0.3.1",
    "vue-clipboards": "^1.3.0",
    "vue-cookies": "^1.7.4",
    "vue-router": "^3.1.6"
  },
web_src/src/components/ParentPlatformList.vue
@@ -68,9 +68,9 @@
</template>
<script>
import platformEdit from './platformEdit.vue'
import platformEdit from './dialog/platformEdit.vue'
import uiHeader from './UiHeader.vue'
import chooseChannelDialog from './gb28181/chooseChannel.vue'
import chooseChannelDialog from './dialog/chooseChannel.vue'
export default {
  name: 'app',
  components: {
web_src/src/components/PushVideoList.vue
@@ -1,5 +1,5 @@
<template>
    <div id="app">
    <div id="pushVideoList">
        <el-container>
            <el-header>
                <uiHeader></uiHeader>
@@ -7,28 +7,27 @@
            <el-main>
                <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;">
                    <span style="font-size: 1rem; font-weight: bold;">推流列表</span>
                    <div style="position: absolute; right: 1rem; top: 0.3rem;">
                        <el-button icon="el-icon-refresh-right" circle size="mini" :loading="getDeviceListLoading" @click="getDeviceList()"></el-button>
                    </div>
                </div>
                <!-- <devicePlayer ref="devicePlayer"></devicePlayer> -->
                <el-table :data="deviceList" border style="width: 100%" :height="winHeight">
                    <el-table-column prop="schema" label="协议" width="180" align="center">
                <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;">
                    <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button>
                </div>
                <devicePlayer ref="devicePlayer"></devicePlayer>
                <el-table :data="pushList" border style="width: 100%" :height="winHeight">
                    <el-table-column prop="app" label="APP" width="180" align="center">
                    </el-table-column>
                    <el-table-column prop="streamUrl" label="流地址" width="240" align="center">
                    <el-table-column prop="stream" label="流ID" width="240" align="center">
                    </el-table-column>
                    <el-table-column prop="online" label="在线人数" width="240" align="center">
                    <el-table-column prop="totalReaderCount" label="在线人数" width="240" align="center">
                    </el-table-column>
                    <el-table-column prop="startTime" label="开始时间" align="center">
                    <el-table-column prop="createStamp" label="开始时间" align="center">
                    </el-table-column>
                    
                    <el-table-column label="操作" width="360" align="center" fixed="right">
                        <template slot-scope="scope">
                            <el-button size="mini" :ref="scope.row.deviceId + 'refbtn' " icon="el-icon-refresh"  @click="refDevice(scope.row)">刷新</el-button>
                            <el-button-group>
                            <el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">播放</el-button>
                            <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId" @click="stopDevicePush(scope.row)">停止</el-button>
                                <el-button size="mini" icon="el-icon-video-play" @click="playPuhsh(scope.row)">播放</el-button>
                                <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId" @click="stopPuhsh(scope.row)">停止</el-button>
                            </el-button-group>
                            </template>
                    </el-table-column>
@@ -43,25 +42,27 @@
                    layout="total, sizes, prev, pager, next"
                    :total="total">
                </el-pagination>
            <streamProxyEdit ref="streamProxyEdit" ></streamProxyEdit>
            </el-main>
        </el-container>
    </div>
</template>
<script>
    import streamProxyEdit from './dialog/StreamProxyEdit.vue'
    import devicePlayer from './dialog/devicePlayer.vue'
    import uiHeader from './UiHeader.vue'
    export default {
        name: 'app',
        name: 'pushVideoList',
        components: {
            devicePlayer,
            streamProxyEdit,
            uiHeader
        },
        data() {
            return {
                deviceList: [], //设备列表
                currentDevice: {}, //当前操作设备对象
                videoComponentList: [],
                pushList: [], //设备列表
                currentPusher: {}, //当前操作设备对象
                updateLooper: 0, //数据刷新轮训标志
                currentDeviceChannelsLenth:0,
                winHeight: window.innerHeight - 200,
@@ -72,23 +73,10 @@
            };
        },
        computed: {
            getcurrentDeviceChannels: function() {
                let data = this.currentDevice['channelMap'];
                let channels = null;
                if (data) {
                    channels = Object.keys(data).map(key => {
                        return data[key];
                    });
                    this.currentDeviceChannelsLenth = channels.length;
                }
                console.log("数据:" + JSON.stringify(channels));
                return channels;
            }
        },
        mounted() {
            this.initData();
            this.updateLooper = setInterval(this.initData, 10000);
            // this.updateLooper = setInterval(this.initData, 10000);
        },
        destroyed() {
            this.$destroy('videojs');
@@ -96,20 +84,20 @@
        },
        methods: {
            initData: function() {
                this.getDeviceList();
                this.getPushList();
            },
            currentChange: function(val){
                this.currentPage = val;
                this.getDeviceList();
                this.getPushList();
            },
            handleSizeChange: function(val){
                this.count = val;
                this.getDeviceList();
                this.getPushList();
            },
            getDeviceList: function() {
            getPushList: function() {
                let that = this;
                this.getDeviceListLoading = true;
                this.$axios.get(`/api/devices`,{
                this.$axios.get(`/api/media/list`,{
                    params: {
                        page: that.currentPage,
                        count: that.count
@@ -119,83 +107,44 @@
                    console.log(res);
                    console.log(res.data.list);
                    that.total = res.data.total;
                    that.deviceList = res.data.list;
                    that.pushList = res.data.list;
                    that.getDeviceListLoading = false;
                })
                .catch(function (error) {
                    console.log(error);
                    that.getDeviceListLoading = false;
                });
            },
            showChannelList: function(row) {
                console.log(JSON.stringify(row))
                this.$router.push(`/channelList/${row.deviceId}/0/15/1`);
            addStreamProxy: function(){
                console.log(2222)
                this.$refs.streamProxyEdit.openDialog(null, this.initData)
            },
            showDevicePosition: function(row) {
                console.log(JSON.stringify(row))
                this.$router.push(`/devicePosition/${row.deviceId}/0/15/1`);
            saveStreamProxy: function(){
            },
            //gb28181平台对接
            //刷新设备信息
            refDevice: function(itemData) {
                ///api/devices/{deviceId}/sync
                console.log("刷新对应设备:" + itemData.deviceId);
                var that = this;
                that.$refs[itemData.deviceId + 'refbtn' ].loading = true;
                this.$axios({
                    method: 'post',
                    url: '/api/devices/' + itemData.deviceId + '/sync'
                }).then(function(res) {
                    console.log("刷新设备结果:"+JSON.stringify(res));
                    if (!res.data.deviceId) {
                        that.$message({
                            showClose: true,
                            message: res.data,
                            type: 'error'
                        });
                    }else{
                        that.$message({
                            showClose: true,
                            message: '请求成功',
                            type: 'success'
                        });
            playPuhsh: function(row){
                let that = this;
                this.getListLoading = true;
                this.$axios.get(`/api/media/getStreamInfoByAppAndStream`,{
                    params: {
                        app: row.app,
                        stream: row.stream
                    }
                    that.initData()
                    that.$refs[itemData.deviceId + 'refbtn' ].loading = false;
                }).catch(function(e) {
                    console.error(e)
                    that.$refs[itemData.deviceId + 'refbtn' ].loading = false;
                });;
                })
                .then(function (res) {
                    that.getListLoading = false;
                    that.$refs.devicePlayer.openDialog("streamPlay", null, null, {
                        streamInfo: res.data,
                        hasAudio: true
                    });
                })
                .catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            },
            //通知设备上传媒体流
            sendDevicePush: function(itemData) {
                // let deviceId = this.currentDevice.deviceId;
                // let channelId = itemData.channelId;
                // console.log("通知设备推流1:" + deviceId + " : " + channelId);
                // let that = this;
                // this.$axios({
                //     method: 'get',
                //     url: '/api/play/' + deviceId + '/' + channelId
                // }).then(function(res) {
                //     let ssrc = res.data.ssrc;
                //     that.$refs.devicePlayer.play(ssrc,deviceId,channelId);
                // }).catch(function(e) {
                // });
            },
      transportChange: function (row) {
        console.log(row);
        console.log(`修改传输方式为 ${row.streamMode}:${row.deviceId} `);
        let that = this;
        this.$axios({
          method: 'get',
          url: '/api/devices/' + row.deviceId + '/transport/' + row.streamMode
        }).then(function(res) {
        }).catch(function(e) {
        });
      }
            stopPuhsh: function(row){
                console.log(row)
            }
        }
    };
web_src/src/components/StreamProxyList.vue
New file
@@ -0,0 +1,297 @@
<template>
    <div id="streamProxyList">
        <el-container>
            <el-header>
                <uiHeader></uiHeader>
            </el-header>
            <el-main>
                <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;">
                    <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-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button>
                </div>
                <devicePlayer ref="devicePlayer"></devicePlayer>
                <el-table :data="streamProxyList" border style="width: 100%" :height="winHeight">
                    <el-table-column prop="app" label="应用名" align="center" show-overflow-tooltip/>
                    <el-table-column prop="stream" label="流ID" align="center" show-overflow-tooltip/>
                    <el-table-column label="流地址" width="400" align="center" show-overflow-tooltip >
                        <template slot-scope="scope">
                        <div slot="reference" class="name-wrapper">
                            <el-tag size="medium" v-if="scope.row.type == 'default'">
                                <i class="cpoy-btn el-icon-document-copy"  title="点击拷贝" v-clipboard="scope.row.url" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
                                {{scope.row.url}}
                            </el-tag>
                            <el-tag size="medium" v-if="scope.row.type != 'default'">
                                <i class="cpoy-btn el-icon-document-copy"  title="点击拷贝" v-clipboard="scope.row.src_url" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
                                {{scope.row.src_url}}
                            </el-tag>
                        </div>
                        </template>
                    </el-table-column>
                    <el-table-column label="转HLS" width="120" align="center">
                        <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>
                        </div>
                        </template>
                    </el-table-column>
                    <el-table-column label="MP4录制" width="120" align="center">
                        <template slot-scope="scope">
                        <div slot="reference" class="name-wrapper">
                            <el-tag size="medium" v-if="scope.row.enable_mp4">已启用</el-tag>
                            <el-tag size="medium" type="info" v-if="!scope.row.enable_mp4">未启用</el-tag>
                        </div>
                        </template>
                    </el-table-column>
                    <el-table-column label="启用" width="120" align="center">
                        <template slot-scope="scope">
                        <div slot="reference" class="name-wrapper">
                            <el-tag size="medium" v-if="scope.row.enable">已启用</el-tag>
                            <el-tag size="medium" type="info" v-if="!scope.row.enable">未启用</el-tag>
                        </div>
                        </template>
                    </el-table-column>
                    <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.enable" @click="play(scope.row)">播放</el-button>
                                <el-button size="mini" icon="el-icon-close" type="success" v-if="scope.row.enable" @click="stop(scope.row)">停用</el-button>
                                <el-button size="mini" icon="el-icon-check" type="primary" v-if="!scope.row.enable" @click="start(scope.row)">启用</el-button>
                                <el-button size="mini" icon="el-icon-delete" type="danger"  @click="deleteStreamProxy(scope.row)">删除</el-button>
                            </el-button-group>
                            </template>
                    </el-table-column>
                </el-table>
                <el-pagination
                    style="float: right"
                    @size-change="handleSizeChange"
                    @current-change="currentChange"
                    :current-page="currentPage"
                    :page-size="count"
                    :page-sizes="[15, 25, 35, 50]"
                    layout="total, sizes, prev, pager, next"
                    :total="total">
                </el-pagination>
            <streamProxyEdit ref="streamProxyEdit" ></streamProxyEdit>
            </el-main>
        </el-container>
    </div>
</template>
<script>
    import streamProxyEdit from './dialog/StreamProxyEdit.vue'
    import devicePlayer from './dialog/devicePlayer.vue'
    import uiHeader from './UiHeader.vue'
    export default {
        name: 'streamProxyList',
        components: {
            devicePlayer,
            streamProxyEdit,
            uiHeader
        },
        data() {
            return {
                streamProxyList: [],
                currentPusher: {}, //当前操作设备对象
                updateLooper: 0, //数据刷新轮训标志
                currentDeviceChannelsLenth:0,
                winHeight: window.innerHeight - 200,
                currentPage:1,
                count:15,
                total:0,
                getListLoading: false
            };
        },
        computed: {
        },
        mounted() {
            this.initData();
            // this.updateLooper = setInterval(this.initData, 10000);
        },
        destroyed() {
            this.$destroy('videojs');
            clearTimeout(this.updateLooper);
        },
        methods: {
            initData: function() {
                this.getStreamProxyList();
            },
            currentChange: function(val){
                this.currentPage = val;
                this.getStreamProxyList();
            },
            handleSizeChange: function(val){
                this.count = val;
                this.getStreamProxyList();
            },
            getStreamProxyList: function() {
                let that = this;
                this.getListLoading = true;
                this.$axios.get(`/api/proxy/list`,{
                    params: {
                        page: that.currentPage,
                        count: that.count
                    }
                } )
                .then(function (res) {
                    console.log(res);
                    console.log(res.data.list);
                    that.total = res.data.total;
                    that.streamProxyList = res.data.list;
                    that.getListLoading = false;
                })
                .catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            },
            addStreamProxy: function(){
                this.$refs.streamProxyEdit.openDialog(null, this.initData)
            },
            saveStreamProxy: function(){
            },
            play: function(row){
                let that = this;
                this.getListLoading = true;
                this.$axios.get(`/api/media/getStreamInfoByAppAndStream`,{
                    params: {
                        app: row.app,
                        stream: row.stream
                    }
                })
                .then(function (res) {
                    that.getListLoading = false;
                    that.$refs.devicePlayer.openDialog("streamPlay", null, null, {
                        streamInfo: res.data,
                        hasAudio: true
                    });
                })
                .catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            },
            deleteStreamProxy: function(row){
                console.log(1111)
                let that = this;
                this.getListLoading = true;
                this.$axios.get(`/api/proxy/del`,{
                    params: {
                        app: row.app,
                        stream: row.stream
                    }
                })
                .then(function (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.$axios.get(`/api/proxy/start`,{
                    params: {
                        app: row.app,
                        stream: row.stream
                    }
                })
                .then(function (res) {
                    that.getListLoading = false;
                    that.initData()
                })
                .catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            },
            stop: function(row){
                let that = this;
                this.getListLoading = true;
                this.$axios.get(`/api/proxy/stop`,{
                    params: {
                        app: row.app,
                        stream: row.stream
                    }
                })
                .then(function (res) {
                    that.getListLoading = false;
                    that.initData()
                })
                .catch(function (error) {
                    console.log(error);
                    that.getListLoading = false;
                });
            }
        }
    };
</script>
<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;
    }
    .cpoy-btn {
        cursor: pointer;
        margin-right: 10px;
    }
</style>
web_src/src/components/UiHeader.vue
@@ -4,6 +4,7 @@
            <el-menu-item index="/">控制台</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="/parentPlatformList/15/1">国标级联</el-menu-item>
            <el-switch v-model="alarmNotify"  active-text="报警信息推送" style="display: block float: right" @change="sseControl"></el-switch>
            <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>
web_src/src/components/channelList.vue
@@ -75,7 +75,7 @@
</template>
<script>
import devicePlayer from './gb28181/devicePlayer.vue'
import devicePlayer from './dialog/devicePlayer.vue'
import uiHeader from './UiHeader.vue'
import moment from "moment";
export default {
web_src/src/components/dialog/StreamProxyEdit.vue
New file
@@ -0,0 +1,186 @@
<template>
  <div id="addStreamProxy" v-loading="isLoging">
    <el-dialog
      title="添加代理"
      width="40%"
      top="2rem"
      :close-on-click-modal="false"
      :visible.sync="showDialog"
      :destroy-on-close="true"
      @close="close()"
    >
      <div id="shared" style="margin-top: 1rem;margin-right: 100px;">
        <el-form ref="streamProxy" :rules="rules" :model="proxyParam" label-width="140px">
              <el-form-item label="类型" prop="type">
                <el-select
                  v-model="proxyParam.type"
                  style="width: 100%"
                  placeholder="请选择代理类型"
                >
                  <el-option label="默认" value="default"></el-option>
                  <el-option label="FFmpeg" value="ffmpeg"></el-option>
                </el-select>
              </el-form-item>
              <el-form-item label="应用名" prop="app">
                <el-input v-model="proxyParam.app" clearable></el-input>
              </el-form-item>
              <el-form-item label="流ID" prop="stream">
                <el-input v-model="proxyParam.stream" clearable></el-input>
              </el-form-item>
              <el-form-item label="拉流地址" prop="url" v-if="proxyParam.type=='default'">
                <el-input v-model="proxyParam.url" clearable></el-input>
              </el-form-item>
              <el-form-item label="拉流地址" prop="src_url" v-if="proxyParam.type=='ffmpeg'">
                <el-input v-model="proxyParam.src_url" clearable></el-input>
              </el-form-item>
              <el-form-item label="超时时间" prop="timeout_ms" v-if="proxyParam.type=='ffmpeg'">
                <el-input v-model="proxyParam.timeout_ms" clearable></el-input>
              </el-form-item>
              <el-form-item label="FFmpeg命令模板" prop="ffmpeg_cmd_key" v-if="proxyParam.type=='ffmpeg'">
                <el-input v-model="proxyParam.ffmpeg_cmd_key" clearable></el-input>
              </el-form-item>
              <el-form-item label="拉流方式" prop="rtp_type" v-if="proxyParam.type=='default'">
                <el-select
                  v-model="proxyParam.rtp_type"
                  style="width: 100%"
                  placeholder="请选择拉流方式"
                >
                  <el-option label="TCP" value="0"></el-option>
                  <el-option label="UDP" 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>
                </div>
              </el-form-item>
              <el-form-item>
                <div style="float: right;">
                  <el-button type="primary" @click="onSubmit">{{onSubmit_text}}</el-button>
                  <el-button @click="close">取消</el-button>
                </div>
              </el-form-item>
            </el-form>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: "streamProxyEdit",
  props: {},
  computed: {},
  created() {},
  data() {
    // var deviceGBIdRules = async (rule, value, callback) => {
    //   console.log(value);
    //   if (value === "") {
    //     callback(new Error("请输入设备国标编号"));
    //   } else {
    //     var exit = await this.deviceGBIdExit(value);
    //     console.log(exit);
    //     console.log(exit == "true");
    //     console.log(exit === "true");
    //     if (exit) {
    //       callback(new Error("设备国标编号已存在"));
    //     } else {
    //       callback();
    //     }
    //   }
    // };
    return {
      listChangeCallback: null,
      showDialog: false,
      isLoging: false,
      onSubmit_text: "立即创建",
      proxyParam: {
          type: "default",
          app: null,
          stream: null,
          url: "rtmp://58.200.131.2:1935/livetv/hunantv",
          src_url: null,
          timeout_ms: null,
          ffmpeg_cmd_key: null,
          rtp_type: null,
          enable: true,
          enable_hls: true,
          enable_mp4: false,
      },
      rules: {
        app: [{ required: true, message: "请输入应用名", trigger: "blur" }],
        stream: [{ required: true, message: "请输入流ID", trigger: "blur" }],
        url: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],
        src_url: [{ required: true, message: "请输入要代理的流", trigger: "blur" }],
        timeout_ms: [{ required: true, message: "请输入FFmpeg推流成功超时时间", trigger: "blur" }],
        ffmpeg_cmd_key: [{ required: false, message: "请输入FFmpeg命令参数模板(可选)", trigger: "blur" }],
      },
    };
  },
  methods: {
    openDialog: function (proxyParam, callback) {
      this.showDialog = true;
      this.listChangeCallback = callback;
      if (proxyParam != null) {
        this.proxyParam = proxyParam;
        this.onSubmit_text = "保存";
      } else {
        this.onSubmit_text = "立即创建";
      }
    },
    onSubmit: function () {
      console.log("onSubmit");
      var that = this;
      that.$axios
        .post(`/api/proxy/save`, that.proxyParam)
        .then(function (res) {
          console.log(res);
          console.log(res.data == "success");
          if (res.data == "success") {
            that.$message({
              showClose: true,
              message: "保存成功",
              type: "success",
            });
            that.showDialog = false;
            if (that.listChangeCallback != null) {
              that.listChangeCallback();
            }
          }
        })
        .catch(function (error) {
          console.log(error);
        });
    },
    close: function () {
      console.log("关闭添加视频平台");
      this.showDialog = false;
      this.$refs.streamProxy.resetFields();
    },
    deviceGBIdExit: async function (deviceGbId) {
      var result = false;
      var that = this;
      await that.$axios
        .post(`/api/platforms/exit/${deviceGbId}`)
        .then(function (res) {
          result = res.data;
        })
        .catch(function (error) {
          console.log(error);
        });
      return result;
    },
    checkExpires: function() {
      if (this.platform.enable && this.platform.expires == "0") {
        this.platform.expires = "300";
      }
    }
  },
};
</script>
web_src/src/components/dialog/chooseChannel.vue
File was renamed from web_src/src/components/gb28181/chooseChannel.vue
@@ -21,7 +21,7 @@
</template>
<script>
import chooseChannelForGb from './chooseChannelForGb.vue'
import chooseChannelForGb from '../dialog/chooseChannelForGb.vue'
export default {
    name: 'chooseChannel',
    props: {},
web_src/src/components/dialog/chooseChannelForGb.vue
web_src/src/components/dialog/devicePlayer.vue
File was renamed from web_src/src/components/gb28181/devicePlayer.vue
@@ -26,7 +26,7 @@
                    </div>
                </el-tab-pane>
                <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
                <el-tab-pane label="录像查询" name="record">
                <el-tab-pane label="录像查询" name="record" v-if="showRrecord">
                    <el-date-picker size="mini" v-model="videoHistory.date" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="queryRecords()"></el-date-picker>
                    <el-table :data="videoHistory.searchHistoryResult" height="150" v-loading="recordsLoading">
                        <el-table-column label="名称" prop="name"></el-table-column>
@@ -42,7 +42,7 @@
                    </el-table>
                </el-tab-pane>
                <!--遥控界面-->
                <el-tab-pane label="云台控制" name="control">
                <el-tab-pane label="云台控制" name="control" v-if="showPtz">
                    <div style="display: flex; justify-content: left;">
                        <div class="control-wrapper">
                            <div class="control-btn control-top" @mousedown="ptzCamera(0, 2, 0)" @mouseup="ptzCamera(0, 0, 0)">
@@ -136,7 +136,7 @@
</template>
<script>
import player from './player.vue'
import player from '../dialog/player.vue'
export default {
    name: 'devicePlayer',
    props: {},
@@ -184,7 +184,9 @@
            tracks: [],
            coverPlaying:false,
            tracksLoading: false,
            recordPlay: ""
            recordPlay: "",
            showPtz: true,
            showRrecord: true,
        };
    },
    methods: {
@@ -230,6 +232,11 @@
                    this.videoHistory.date = param.date;
                    this.queryRecords()
                    break;
                case "streamPlay":
                    this.showRrecord = false,
                    this.showPtz = false,
                    this.play(param.streamInfo, param.hasAudio)
                    break;
                case "control":
                    break;
            }
web_src/src/components/dialog/platformEdit.vue
web_src/src/components/dialog/player.vue
web_src/src/main.js
@@ -11,6 +11,8 @@
import VueClipboard from 'vue-clipboard2';
import { Notification } from 'element-ui';
import Fingerprint2 from 'fingerprintjs2';
import VueClipboards from 'vue-clipboards';
// 生成唯一ID
Fingerprint2.get(function(components) {
@@ -32,6 +34,7 @@
Vue.use(VueClipboard);
Vue.use(ElementUI);
Vue.use(VueCookies);
Vue.use(VueClipboards);
Vue.prototype.$axios = axios;
Vue.prototype.$notify = Notification;
web_src/src/router/index.js
@@ -5,6 +5,7 @@
import deviceList from '../components/DeviceList.vue'
import channelList from '../components/channelList.vue'
import pushVideoList from '../components/PushVideoList.vue'
import streamProxyList from '../components/StreamProxyList.vue'
import devicePosition from  '../components/devicePosition.vue'
import login from '../components/Login.vue'
import parentPlatformList from '../components/ParentPlatformList.vue'
@@ -33,6 +34,10 @@
      component: pushVideoList,
    },
    {
      path: '/streamProxyList',
      component: streamProxyList,
    },
    {
      path: '/login',
      name: '登录',
      component: login,