From 56859d09df8d4226882d43934acf32d60a3b51d7 Mon Sep 17 00:00:00 2001
From: panlinlin <648540858@qq.com>
Date: 星期二, 30 三月 2021 18:46:34 +0800
Subject: [PATCH] 添加推流列表和拉流代理,下一步与国标关联

---
 src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java                 |   28 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java                    |   21 
 web_src/src/components/PushVideoList.vue                                              |  157 +---
 web_src/package-lock.json                                                             |   20 
 web_src/src/components/dialog/chooseChannelForGb.vue                                  |    0 
 web_src/src/main.js                                                                   |    3 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java               |   17 
 src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java          |   10 
 web_src/src/components/dialog/devicePlayer.vue                                        |   15 
 src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java        |   37 
 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyDto.java                 |  112 ++++
 src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java                  |    3 
 web_src/src/components/UiHeader.vue                                                   |    1 
 web_src/src/components/dialog/StreamProxyEdit.vue                                     |  186 ++++++
 src/main/java/com/genersoft/iot/vmp/vmanager/service/IStreamProxyService.java         |   60 ++
 web_src/src/components/dialog/player.vue                                              |    0 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java                          |   55 +
 web_src/src/components/dialog/platformEdit.vue                                        |    0 
 src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java                       |   10 
 web_src/src/components/StreamProxyList.vue                                            |  297 ++++++++++
 web_src/src/components/ParentPlatformList.vue                                         |    4 
 src/main/java/com/genersoft/iot/vmp/vmanager/media/MediaController.java               |   52 +
 src/main/resources/wvp.sqlite                                                         |    0 
 web_src/package.json                                                                  |    1 
 src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java   |   73 ++
 src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java       |   76 ++
 src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java               |   46 +
 /dev/null                                                                             |   45 -
 web_src/src/components/dialog/chooseChannel.vue                                       |    2 
 web_src/src/components/channelList.vue                                                |    2 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java                |   49 +
 src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/StreamProxyServiceImpl.java |  123 ++++
 src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java               |   46 +
 src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/MediaServiceImpl.java       |   52 +
 web_src/src/router/index.js                                                           |    5 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java                |    1 
 src/main/java/com/genersoft/iot/vmp/vmanager/service/IMediaService.java               |   25 
 37 files changed, 1,417 insertions(+), 217 deletions(-)

diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java
index 76390e4..eba9a5e 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java
+++ b/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;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
index 51c6e1b..b2c1597 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
+++ b/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);
+
+		// 閲嶆柊鍙戣捣浠g悊
+
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
index 995f916..129f8a3 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
+++ b/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;
+    }
+
+
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
index b17151a..5ffa467 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
+++ b/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");
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
index ad17fee..21752a0 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
+++ b/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);
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
index e65fae0..15fa957 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
+++ b/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 杩炴帴鎴愬姛鎴栬�厇lm閲嶅惎鍚�
+     */
+    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);
+        }
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyDto.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyDto.java
new file mode 100644
index 0000000..8f03602
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamProxyDto.java
@@ -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;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
index 2866611..ca4ebae 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
+++ b/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);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
index 10360b0..7656898 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
+++ b/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);
+
+	/**
+	 * 鏂板浠g悊娴�
+	 * @param streamProxyDto
+	 * @return
+	 */
+	public int addStreamProxy(StreamProxyDto streamProxyDto);
+
+	/**
+	 * 鏇存柊浠g悊娴�
+	 * @param streamProxyDto
+	 * @return
+	 */
+	public int updateStreamProxy(StreamProxyDto streamProxyDto);
+
+	/**
+	 * 绉婚櫎浠g悊娴�
+	 * @param app
+	 * @param stream
+	 * @return
+	 */
+	public int deleteStreamProxy(String app, String stream);
+
+	/**
+	 * 鎸夌収鏄惁鍚敤鑾峰彇浠g悊娴�
+	 * @param enable
+	 * @return
+	 */
+	public List<StreamProxyDto> getStreamProxyListForEnable(boolean enable);
+
+	/**
+	 * 鎸夌収鏄痑pp鍜宻tream鑾峰彇浠g悊娴�
+	 * @param app
+	 * @param stream
+	 * @return
+	 */
+	public StreamProxyDto queryStreamProxy(String app, String stream);
+
+	/**
+	 * 鑾峰彇浠g悊娴�
+	 * @param page
+	 * @param count
+	 * @return
+	 */
+	PageInfo<StreamProxyDto> queryStreamProxyList(Integer page, Integer count);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
new file mode 100644
index 0000000..414125b
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java
@@ -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);
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
index 9871083..7b7f599 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
+++ b/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;
     }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
index ac2ed5b..071bf68 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
+++ b/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);
 	}
 
+	/**
+	 * 鏂板浠g悊娴�
+	 * @param streamProxyDto
+	 * @return
+	 */
+	@Override
+	public int addStreamProxy(StreamProxyDto streamProxyDto) {
+		return streamProxyMapper.add(streamProxyDto);
+	}
+
+	/**
+	 * 鏇存柊浠g悊娴�
+	 * @param streamProxyDto
+	 * @return
+	 */
+	@Override
+	public int updateStreamProxy(StreamProxyDto streamProxyDto) {
+		return streamProxyMapper.update(streamProxyDto);
+	}
+
+	/**
+	 * 绉婚櫎浠g悊娴�
+	 * @param id
+	 * @return
+	 */
+	@Override
+	public int deleteStreamProxy(String app, String stream) {
+		return streamProxyMapper.del(app, stream);
+	}
+
+	/**
+	 * 鏍规嵁鏄惁鍚敤鑾峰彇浠g悊娴佸垪琛�
+	 * @param enable
+	 * @return
+	 */
+	@Override
+	public List<StreamProxyDto> getStreamProxyListForEnable(boolean enable) {
+		return streamProxyMapper.selectForEnable(enable);
+	}
+
+	/**
+	 * 鍒嗛〉鏌ヨ浠g悊娴佸垪琛�
+	 * @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);
+	}
+
+
+	/**
+	 * 鎸夌収鏄痑pp鍜宻tream鑾峰彇浠g悊娴�
+	 * @param app
+	 * @param stream
+	 * @return
+	 */
+	@Override
+	public StreamProxyDto queryStreamProxy(String app, String stream){
+		return streamProxyMapper.selectOne(app, stream);
+	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/media/MediaController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/media/MediaController.java
new file mode 100644
index 0000000..115e840
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/media/MediaController.java
@@ -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);
+    }
+
+
+
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
index 0c773e6..67045b4 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
+++ b/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 {
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IMediaService.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IMediaService.java
new file mode 100644
index 0000000..757a7ac
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IMediaService.java
@@ -0,0 +1,25 @@
+package com.genersoft.iot.vmp.vmanager.service;
+
+import com.genersoft.iot.vmp.common.StreamInfo;
+
+/**
+ * 濯掍綋淇℃伅涓氬姟
+ */
+public interface IMediaService {
+
+    /**
+     * 鏍规嵁搴旂敤鍚嶅拰娴両D鑾峰彇鎾斁鍦板潃, 閫氳繃zlm鎺ュ彛妫�鏌ユ槸鍚﹀瓨鍦�
+     * @param app
+     * @param stream
+     * @return
+     */
+    StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream);
+
+    /**
+     * 鏍规嵁搴旂敤鍚嶅拰娴両D鑾峰彇鎾斁鍦板潃, 鍙槸鍦板潃鎷兼帴
+     * @param app
+     * @param stream
+     * @return
+     */
+    StreamInfo getStreamInfoByAppAndStream(String app, String stream);
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/IStreamProxyService.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IStreamProxyService.java
new file mode 100644
index 0000000..4a93cd2
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/IStreamProxyService.java
@@ -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 {
+
+    /**
+     * 淇濆瓨瑙嗛浠g悊
+     * @param param
+     */
+    void save(StreamProxyDto param);
+
+    /**
+     * 娣诲姞瑙嗛浠g悊鍒皕lm
+     * @param param
+     * @return
+     */
+    JSONObject addStreamProxyToZlm(StreamProxyDto param);
+
+    /**
+     * 浠巣lm绉婚櫎瑙嗛浠g悊
+     * @param param
+     * @return
+     */
+    JSONObject removeStreamProxyFromZlm(StreamProxyDto param);
+
+    /**
+     * 鍒嗛〉鏌ヨ
+     * @param page
+     * @param count
+     * @return
+     */
+    PageInfo<StreamProxyDto> getAll(Integer page, Integer count);
+
+    /**
+     * 鍒犻櫎瑙嗛浠g悊
+     * @param app
+     * @param stream
+     */
+    void del(String app, String stream);
+
+    /**
+     * 鍚敤瑙嗛浠g悊
+     * @param app
+     * @param stream
+     * @return
+     */
+    boolean start(String app, String stream);
+
+    /**
+     * 鍋滅敤鐢ㄨ棰戜唬鐞�
+     * @param app
+     * @param stream
+     * @return
+     */
+    boolean stop(String app, String stream);
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/MediaServiceImpl.java
new file mode 100644
index 0000000..afd6cc9
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/MediaServiceImpl.java
@@ -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;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
index 376040a..edd8710 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
+++ b/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;
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/StreamProxyServiceImpl.java
new file mode 100644
index 0000000..0655980
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/StreamProxyServiceImpl.java
@@ -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;
+
+/**
+ * 瑙嗛浠g悊涓氬姟
+ */
+@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;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
new file mode 100644
index 0000000..09d4174
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
@@ -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.*;
+
+/**
+ * 鎷夋祦浠g悊鎺ュ彛
+ */
+@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("娣诲姞浠g悊锛� " + JSONObject.toJSONString(param));
+        streamProxyService.save(param);
+        return "success";
+    }
+
+    @RequestMapping(value = "/del")
+    @ResponseBody
+    public Object del(String app, String stream){
+        logger.info("绉婚櫎浠g悊锛� " + app + "/" + stream);
+        streamProxyService.del(app, stream);
+        return "success";
+    }
+
+    @RequestMapping(value = "/start")
+    @ResponseBody
+    public Object start(String app, String stream){
+        logger.info("鍚敤浠g悊锛� " + app + "/" + stream);
+        boolean result = streamProxyService.start(app, stream);
+        return "success";
+    }
+
+    @RequestMapping(value = "/stop")
+    @ResponseBody
+    public Object stop(String app, String stream){
+        logger.info("鍋滅敤浠g悊锛� " + app + "/" + stream);
+        boolean result = streamProxyService.stop(app, stream);
+        return "success";
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/web/ApiMediaController.java b/src/main/java/com/genersoft/iot/vmp/web/ApiMediaController.java
deleted file mode 100644
index 84910c5..0000000
--- a/src/main/java/com/genersoft/iot/vmp/web/ApiMediaController.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.genersoft.iot.vmp.web;
-
-import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.common.RealVideo;
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-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.CrossOrigin;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-import java.util.List;
-
-/**
- * 鍏煎LiveGBS鐨凙PI锛氱郴缁熸帴鍙�
- */
-@Controller
-@CrossOrigin
-@RequestMapping(value = "/api/v1/media")
-public class ApiMediaController {
-
-    private final static Logger logger = LoggerFactory.getLogger(ApiMediaController.class);
-
-    @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
-
-    @RequestMapping(value = "/list")
-    @ResponseBody
-    public JSONObject list( @RequestParam(required = false)Integer start,
-                            @RequestParam(required = false)Integer limit,
-                            @RequestParam(required = false)String q,
-                            @RequestParam(required = false)Boolean online ){
-
-        List<Object> mediaList = redisCatchStorage.getMediaList(start - 1, start - 1 + limit);
-        JSONObject jsonObject = new JSONObject();
-        jsonObject.put("code", 0);
-        jsonObject.put("data", mediaList);
-        return jsonObject;
-    }
-}
diff --git a/src/main/resources/wvp.sqlite b/src/main/resources/wvp.sqlite
index d50f18c..9147e22 100644
--- a/src/main/resources/wvp.sqlite
+++ b/src/main/resources/wvp.sqlite
Binary files differ
diff --git a/web_src/package-lock.json b/web_src/package-lock.json
index e4ed934..9e734e5 100644
--- a/web_src/package-lock.json
+++ b/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",
diff --git a/web_src/package.json b/web_src/package.json
index 4a9f766..b7a947c 100644
--- a/web_src/package.json
+++ b/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"
   },
diff --git a/web_src/src/components/ParentPlatformList.vue b/web_src/src/components/ParentPlatformList.vue
index 298504b..32c999d 100644
--- a/web_src/src/components/ParentPlatformList.vue
+++ b/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: {
diff --git a/web_src/src/components/PushVideoList.vue b/web_src/src/components/PushVideoList.vue
index 60a5c9e..4a93d7a 100644
--- a/web_src/src/components/PushVideoList.vue
+++ b/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">娣诲姞浠g悊</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="娴両D" 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)
+			}
 
 		}
 	};
diff --git a/web_src/src/components/StreamProxyList.vue b/web_src/src/components/StreamProxyList.vue
new file mode 100644
index 0000000..0a1e03d
--- /dev/null
+++ b/web_src/src/components/StreamProxyList.vue
@@ -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;">鎷夋祦浠g悊鍒楄〃</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">娣诲姞浠g悊</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="娴両D" 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="杞琀LS" 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>
diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue
index fa1c442..ae0780b 100644
--- a/web_src/src/components/UiHeader.vue
+++ b/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">鎷夋祦浠g悊</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>
diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue
index 6d893ce..b06ce25 100644
--- a/web_src/src/components/channelList.vue
+++ b/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 {
diff --git a/web_src/src/components/dialog/StreamProxyEdit.vue b/web_src/src/components/dialog/StreamProxyEdit.vue
new file mode 100644
index 0000000..a2b3c9e
--- /dev/null
+++ b/web_src/src/components/dialog/StreamProxyEdit.vue
@@ -0,0 +1,186 @@
+<template>
+  <div id="addStreamProxy" v-loading="isLoging">
+    <el-dialog
+      title="娣诲姞浠g悊"
+      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="璇烽�夋嫨浠g悊绫诲瀷"
+                >
+                  <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="娴両D" 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="杞琀LS" 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: "璇疯緭鍏ヨ浠g悊鐨勬祦", trigger: "blur" }],
+        src_url: [{ required: true, message: "璇疯緭鍏ヨ浠g悊鐨勬祦", trigger: "blur" }],
+        timeout_ms: [{ required: true, message: "璇疯緭鍏Fmpeg鎺ㄦ祦鎴愬姛瓒呮椂鏃堕棿", trigger: "blur" }],
+        ffmpeg_cmd_key: [{ required: false, message: "璇疯緭鍏Fmpeg鍛戒护鍙傛暟妯℃澘锛堝彲閫夛級", 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>
diff --git a/web_src/src/components/gb28181/chooseChannel.vue b/web_src/src/components/dialog/chooseChannel.vue
similarity index 97%
rename from web_src/src/components/gb28181/chooseChannel.vue
rename to web_src/src/components/dialog/chooseChannel.vue
index 810ebd3..219036c 100644
--- a/web_src/src/components/gb28181/chooseChannel.vue
+++ b/web_src/src/components/dialog/chooseChannel.vue
@@ -21,7 +21,7 @@
 </template>
 
 <script>
-import chooseChannelForGb from './chooseChannelForGb.vue'
+import chooseChannelForGb from '../dialog/chooseChannelForGb.vue'
 export default {
     name: 'chooseChannel',
     props: {},
diff --git a/web_src/src/components/gb28181/chooseChannelForGb.vue b/web_src/src/components/dialog/chooseChannelForGb.vue
similarity index 100%
rename from web_src/src/components/gb28181/chooseChannelForGb.vue
rename to web_src/src/components/dialog/chooseChannelForGb.vue
diff --git a/web_src/src/components/gb28181/devicePlayer.vue b/web_src/src/components/dialog/devicePlayer.vue
similarity index 98%
rename from web_src/src/components/gb28181/devicePlayer.vue
rename to web_src/src/components/dialog/devicePlayer.vue
index 2341831..48d295a 100644
--- a/web_src/src/components/gb28181/devicePlayer.vue
+++ b/web_src/src/components/dialog/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;
             }
diff --git a/web_src/src/components/platformEdit.vue b/web_src/src/components/dialog/platformEdit.vue
similarity index 100%
rename from web_src/src/components/platformEdit.vue
rename to web_src/src/components/dialog/platformEdit.vue
diff --git a/web_src/src/components/gb28181/player.vue b/web_src/src/components/dialog/player.vue
similarity index 100%
rename from web_src/src/components/gb28181/player.vue
rename to web_src/src/components/dialog/player.vue
diff --git a/web_src/src/main.js b/web_src/src/main.js
index 197fa17..ed1f36c 100644
--- a/web_src/src/main.js
+++ b/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;
 
diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js
index fc55dd5..4735e62 100644
--- a/web_src/src/router/index.js
+++ b/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,

--
Gitblit v1.8.0