From 720231d33f387c6d1bf13bdef653314c5c450809 Mon Sep 17 00:00:00 2001
From: 648540858 <18010473990@163.com>
Date: 星期六, 25 九月 2021 22:12:15 +0800
Subject: [PATCH] 添加发送媒体流, 添加媒体服务器节点管理ui,修复修改密码

---
 web_src/src/components/service/MediaServer.js                                |   55 +++
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java       |   26 +
 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java       |   29 +
 web_src/src/components/CloudRecord.vue                                       |    2 
 web_src/src/components/dialog/MediaServerEdit.vue                            |  368 +++++++++++++++++++++++
 web_src/static/images/zlm-logo.png                                           |    0 
 web_src/src/components/dialog/deviceEdit.vue                                 |    2 
 src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java                    |   13 
 sql/mysql.sql                                                                |    1 
 web_src/src/components/MediaServerManger.vue                                 |  159 +++++++++
 src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java |  166 ++++++++-
 src/main/resources/application-dev.yml                                       |    2 
 web_src/src/components/UiHeader.vue                                          |    1 
 web_src/src/components/dialog/StreamProxyEdit.vue                            |    2 
 web_src/src/components/DeviceList.vue                                        |    2 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java                 |    6 
 web_src/src/components/dialog/changePassword.vue                             |    2 
 src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java                |    3 
 web_src/static/css/iconfont.css                                              |   10 
 src/main/resources/wvp.sqlite                                                |    0 
 web_src/build/webpack.base.conf.js                                           |    1 
 src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java         |    7 
 src/main/resources/all-application.yml                                       |    2 
 src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java    |   60 +++
 web_src/src/assets/zlm-log.png                                               |    0 
 web_src/static/css/iconfont.woff2                                            |    0 
 src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java      |    3 
 web_src/src/components/control.vue                                           |    2 
 web_src/src/router/index.js                                                  |    6 
 src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java             |    7 
 30 files changed, 890 insertions(+), 47 deletions(-)

diff --git a/sql/mysql.sql b/sql/mysql.sql
index 90393e6..6f44ce8 100644
--- a/sql/mysql.sql
+++ b/sql/mysql.sql
@@ -140,6 +140,7 @@
     streamNoneReaderDelayMS int          not null,
     rtpEnable               int          not null,
     rtpPortRange            varchar(50)  not null,
+    sendRtpPortRange        varchar(50)  not null,
     recordAssistPort        int          not null,
     defaultServer           int          not null,
     createTime              varchar(50)  not null,
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
index 9831ddb..08d030a 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
@@ -18,6 +18,9 @@
 import java.io.IOException;
 import java.text.SimpleDateFormat;
 
+/**
+ * @author lin
+ */
 @WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true)
 public class ApiAccessFilter extends OncePerRequestFilter {
 
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
index 72cefe1..e608361 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -68,6 +68,10 @@
     @Value("${media.rtp.port-range}")
     private String rtpPortRange;
 
+
+    @Value("${media.rtp.send-port-range}")
+    private String sendRtpPortRange;
+
     @Value("${media.record-assist-port:0}")
     private Integer recordAssistPort = 0;
 
@@ -165,6 +169,14 @@
         }
     }
 
+    public String getSipDomain() {
+        return sipDomain;
+    }
+
+    public String getSendRtpPortRange() {
+        return sendRtpPortRange;
+    }
+
     public MediaServerItem getMediaSerItem(){
         MediaServerItem mediaServerItem = new MediaServerItem();
         mediaServerItem.setId(id);
@@ -185,6 +197,7 @@
         mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS);
         mediaServerItem.setRtpEnable(rtpEnable);
         mediaServerItem.setRtpPortRange(rtpPortRange);
+        mediaServerItem.setSendRtpPortRange(sendRtpPortRange);
         mediaServerItem.setRecordAssistPort(recordAssistPort);
 
         SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
index 907e30d..5d8acce 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
@@ -21,6 +21,9 @@
 import java.io.IOException;
 import java.net.ConnectException;
 
+/**
+ * @author lin
+ */
 @SuppressWarnings(value = {"rawtypes", "unchecked"})
 @Configuration
 public class ProxyServletConfig {
@@ -35,7 +38,7 @@
 
     @Bean
     public ServletRegistrationBean zlmServletRegistrationBean(){
-        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZLMProxySerlet(),"/zlm/*");
+        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZlmProxyServlet(),"/zlm/*");
         servletRegistrationBean.setName("zlm_Proxy");
         servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080");
         servletRegistrationBean.addUrlMappings();
@@ -45,7 +48,7 @@
         return servletRegistrationBean;
     }
 
-    class ZLMProxySerlet extends ProxyServlet{
+    class ZlmProxyServlet extends ProxyServlet{
         @Override
         protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
             String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
index bd56401..b4d1048 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -8,6 +8,7 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -44,8 +45,15 @@
 
         Map<String, Object> param = new HashMap<>();
         int result = -1;
-        int newPort = getPortFromportRange(mediaServerItem);
-        param.put("port", newPort);
+        /**
+         * 涓嶈缃帹娴佺鍙g鍒欎娇鐢ㄩ殢鏈虹鍙�
+         */
+        if (StringUtils.isEmpty(mediaServerItem.getSendRtpPortRange())){
+            param.put("port", 0);
+        }else {
+            int newPort = getPortFromportRange(mediaServerItem);
+            param.put("port", newPort);
+        }
         param.put("enable_tcp", 1);
         param.put("stream_id", streamId);
         JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
@@ -53,24 +61,24 @@
         if (openRtpServerResultJson != null) {
             switch (openRtpServerResultJson.getInteger("code")){
                 case 0:
-                    result= newPort;
+                    result= openRtpServerResultJson.getInteger("port");
                     break;
                 case -300: // id宸茬粡瀛樺湪, 鍙兘宸茬粡鍦ㄥ叾浠栫鍙f帹娴�
                     Map<String, Object> closeRtpServerParam = new HashMap<>();
                     closeRtpServerParam.put("stream_id", streamId);
                     zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);
-                    result = newPort;
+                    result = createRTPServer(mediaServerItem, streamId);;
                     break;
                 case -400: // 绔彛鍗犵敤
                     result= createRTPServer(mediaServerItem, streamId);
                     break;
                 default:
-                    logger.error("鍒涘缓RTP Server 澶辫触 {}: " + openRtpServerResultJson.getString("msg"), newPort);
+                    logger.error("鍒涘缓RTP Server 澶辫触 {}: " + openRtpServerResultJson.getString("msg"),  param.get("port"));
                     break;
             }
         }else {
             //  妫�鏌LM鐘舵��
-            logger.error("鍒涘缓RTP Server 澶辫触 {}: 璇锋鏌LM鏈嶅姟", newPort);
+            logger.error("鍒涘缓RTP Server 澶辫触 {}: 璇锋鏌LM鏈嶅姟", param.get("port"));
         }
         return result;
     }
@@ -98,7 +106,7 @@
     private int getPortFromportRange(MediaServerItem mediaServerItem) {
         int currentPort = mediaServerItem.getCurrentPort();
         if (currentPort == 0) {
-            String[] portRangeStrArray = mediaServerItem.getRtpPortRange().split(",");
+            String[] portRangeStrArray = mediaServerItem.getSendRtpPortRange().split(",");
             portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]);
             portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]);
         }
@@ -229,7 +237,9 @@
      */
     public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
-        if (mediaInfo == null) return 0;
+        if (mediaInfo == null) {
+            return 0;
+        }
         return mediaInfo.getInteger("totalReaderCount");
     }
 
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 187cbc1..7faebfa 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
@@ -108,8 +108,10 @@
     }
 
     public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) {
-        if (startGetMedia == null) return null;
-        if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) return null;
+        if (startGetMedia == null) { return null;}
+        if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) {
+            return null;
+        }
         JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
         ZLMServerConfig ZLMServerConfig = null;
         if (responseJSON != null) {
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
index d1578d1..087c10a 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
@@ -41,13 +41,19 @@
 
     private boolean rtpEnable;
 
+    private boolean status;
+
     private String rtpPortRange;
+
+    private String sendRtpPortRange;
 
     private int recordAssistPort;
 
     private String createTime;
 
     private String updateTime;
+
+    private String lastKeepaliveTime;
 
     private boolean defaultServer;
 
@@ -82,6 +88,7 @@
         secret = zlmServerConfig.getApiSecret();
         streamNoneReaderDelayMS = zlmServerConfig.getGeneralStreamNoneReaderDelayMS();
         rtpEnable = false; // 榛樿浣跨敤鍗曠鍙�;鐩村埌鐢ㄦ埛鑷繁璁剧疆寮�鍚绔彛
+        rtpPortRange = "30000,30500"; // 榛樿浣跨敤30000,30500浣滀负绾ц仈鏃跺彂閫佹祦鐨勭鍙e彿
         recordAssistPort = 0; // 榛樿鍏抽棴
 
     }
@@ -278,5 +285,27 @@
         this.currentPort = currentPort;
     }
 
+    public boolean isStatus() {
+        return status;
+    }
 
+    public void setStatus(boolean status) {
+        this.status = status;
+    }
+
+    public String getLastKeepaliveTime() {
+        return lastKeepaliveTime;
+    }
+
+    public void setLastKeepaliveTime(String lastKeepaliveTime) {
+        this.lastKeepaliveTime = lastKeepaliveTime;
+    }
+
+    public String getSendRtpPortRange() {
+        return sendRtpPortRange;
+    }
+
+    public void setSendRtpPortRange(String sendRtpPortRange) {
+        this.sendRtpPortRange = sendRtpPortRange;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
index f65a155..81cf8f1 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
@@ -4,6 +4,7 @@
 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 
 import java.util.List;
 
@@ -49,7 +50,11 @@
 
     void clearMediaServerForOnline();
 
-    void add(MediaServerItem mediaSerItem);
+    WVPResult<String> add(MediaServerItem mediaSerItem);
 
     void resetOnlineServerItem(MediaServerItem serverItem);
+
+    WVPResult<MediaServerItem> checkMediaServer(String ip, int port, String secret);
+
+    boolean checkMediaRecordServer(String ip, int port);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
index 33c0051..18c1b93 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.service.impl;
 
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
@@ -14,10 +15,11 @@
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
 import com.genersoft.iot.vmp.utils.redis.JedisUtil;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import okhttp3.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -57,9 +59,6 @@
     private MediaServerMapper mediaServerMapper;
 
     @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
-    @Autowired
     private VideoStreamSessionManager streamSession;
 
     @Autowired
@@ -97,7 +96,9 @@
 
     @Override
     public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId) {
-        if (mediaServerItem == null || mediaServerItem.getId() == null) return null;
+        if (mediaServerItem == null || mediaServerItem.getId() == null) {
+            return null;
+        }
         // 鑾峰彇mediaServer鍙敤鐨剆src
         String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId();
 
@@ -107,7 +108,9 @@
             return null;
         }else {
             String ssrc = ssrcConfig.getPlaySsrc();
-            if (streamId == null) streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
+            if (streamId == null) {
+                streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
+            }
             int rtpServerPort = mediaServerItem.getRtpProxyPort();
             if (mediaServerItem.isRtpEnable()) {
                 rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
@@ -131,7 +134,9 @@
 
     @Override
     public void releaseSsrc(MediaServerItem mediaServerItem, String ssrc) {
-        if (mediaServerItem == null || ssrc == null) return;
+        if (mediaServerItem == null || ssrc == null) {
+            return;
+        }
         SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
         ssrcConfig.releaseSsrc(ssrc);
         mediaServerItem.setSsrcConfig(ssrcConfig);
@@ -141,7 +146,6 @@
 
     /**
      * zlm 閲嶅惎鍚庨噸缃粬鐨勬帹娴佷俊鎭紝 TODO 缁欐鍦ㄤ娇鐢ㄧ殑璁惧鍙戦�佸仠姝㈠懡浠�
-     * @param mediaServerItem
      */
     @Override
     public void clearRTPServer(MediaServerItem mediaServerItem) {
@@ -174,9 +178,15 @@
     public List<MediaServerItem> getAll() {
         List<MediaServerItem> result = new ArrayList<>();
         List<Object> mediaServerKeys = redisUtil.scan(String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX));
-        for (int i = 0; i < mediaServerKeys.size(); i++) {
-            String key = (String) mediaServerKeys.get(i);
-            result.add((MediaServerItem)redisUtil.get(key));
+        String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
+        for (Object mediaServerKey : mediaServerKeys) {
+            String key = (String) mediaServerKey;
+            MediaServerItem mediaServerItem = (MediaServerItem) redisUtil.get(key);
+            // 妫�鏌ョ姸鎬�
+            if (redisUtil.zScore(onlineKey, mediaServerItem.getId()) != null) {
+                mediaServerItem.setStatus(true);
+            }
+            result.add(mediaServerItem);
         }
         return result;
     }
@@ -208,7 +218,9 @@
      */
     @Override
     public MediaServerItem getOne(String mediaServerId) {
-        if (mediaServerId == null) return null;
+        if (mediaServerId == null) {
+            return null;
+        }
         String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerId;
         return (MediaServerItem)redisUtil.get(key);
     }
@@ -225,8 +237,34 @@
     }
 
     @Override
-    public void add(MediaServerItem mediaSerItem) {
-        mediaServerMapper.add(mediaSerItem);
+    public WVPResult<String> add(MediaServerItem mediaServerItem) {
+        WVPResult<String> result = new WVPResult<>();
+        mediaServerItem.setCreateTime(this.format.format(System.currentTimeMillis()));
+        mediaServerItem.setUpdateTime(this.format.format(System.currentTimeMillis()));
+        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
+        if (responseJSON != null) {
+            JSONArray data = responseJSON.getJSONArray("data");
+            if (data != null && data.size() > 0) {
+                ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+                if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) {
+                    result.setCode(-1);
+                    result.setMsg("淇濆瓨澶辫触锛屽獟浣撴湇鍔D [ " + zlmServerConfig.getGeneralMediaServerId() + " ] 宸插瓨鍦紝璇蜂慨鏀瑰獟浣撴湇鍔″櫒閰嶇疆");
+                    return result;
+                }
+                zlmServerConfig.setIp(mediaServerItem.getIp());
+                handLeZLMServerConfig(zlmServerConfig);
+                result.setCode(0);
+                result.setMsg("success");
+            }else {
+                result.setCode(-1);
+                result.setMsg("杩炴帴澶辫触");
+            }
+
+        }else {
+            result.setCode(-1);
+            result.setMsg("杩炴帴澶辫触");
+        }
+       return result;
     }
 
     /**
@@ -249,13 +287,27 @@
             // docker閮ㄧ讲涓嶄細浣跨敤zlm閰嶇疆鐨勭鍙e彿涓嶆槸榛樿鐨勫垯涓嶅仛鏇存柊锛� 閰嶇疆淇敼闇�瑕佽嚜琛屼慨鏀箂erver閰嶇疆;
             MediaServerItem serverItemFromConfig = mediaConfig.getMediaSerItem();
             serverItemFromConfig.setId(zlmServerConfig.getGeneralMediaServerId());
-            if (mediaConfig.getHttpPort() == 0) serverItemFromConfig.setHttpPort(zlmServerConfig.getHttpPort());
-            if (mediaConfig.getHttpSSlPort() == 0) serverItemFromConfig.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
-            if (mediaConfig.getRtmpPort() == 0) serverItemFromConfig.setRtmpPort(zlmServerConfig.getRtmpPort());
-            if (mediaConfig.getRtmpSSlPort() == 0) serverItemFromConfig.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
-            if (mediaConfig.getRtspPort() == 0) serverItemFromConfig.setRtspPort(zlmServerConfig.getRtspPort());
-            if (mediaConfig.getRtspSSLPort() == 0) serverItemFromConfig.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
-            if (mediaConfig.getRtpProxyPort() == 0) serverItemFromConfig.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
+            if (mediaConfig.getHttpPort() == 0) {
+                serverItemFromConfig.setHttpPort(zlmServerConfig.getHttpPort());
+            }
+            if (mediaConfig.getHttpSSlPort() == 0) {
+                serverItemFromConfig.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
+            }
+            if (mediaConfig.getRtmpPort() == 0) {
+                serverItemFromConfig.setRtmpPort(zlmServerConfig.getRtmpPort());
+            }
+            if (mediaConfig.getRtmpSSlPort() == 0) {
+                serverItemFromConfig.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
+            }
+            if (mediaConfig.getRtspPort() == 0) {
+                serverItemFromConfig.setRtspPort(zlmServerConfig.getRtspPort());
+            }
+            if (mediaConfig.getRtspSSLPort() == 0) {
+                serverItemFromConfig.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
+            }
+            if (mediaConfig.getRtpProxyPort() == 0) {
+                serverItemFromConfig.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
+            }
             if (serverItem != null){
                 mediaServerMapper.delDefault();
                 mediaServerMapper.add(serverItemFromConfig);
@@ -319,9 +371,10 @@
 
     @Override
     public void addCount(String mediaServerId) {
-        if (mediaServerId == null) return;
+        if (mediaServerId == null) {
+            return;
+        }
         String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
-        Double aDouble = redisUtil.zScore(key, mediaServerId);
         redisUtil.zIncrScore(key, mediaServerId, 1);
 
     }
@@ -399,4 +452,71 @@
     }
 
 
+    @Override
+    public WVPResult<MediaServerItem> checkMediaServer(String ip, int port, String secret) {
+        WVPResult<MediaServerItem> result = new WVPResult<>();
+        if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) {
+            result.setCode(-1);
+            result.setMsg("姝よ繛鎺ュ凡瀛樺湪");
+            return result;
+        }
+        MediaServerItem mediaServerItem = new MediaServerItem();
+        mediaServerItem.setIp(ip);
+        mediaServerItem.setHttpPort(port);
+        mediaServerItem.setSecret(secret);
+        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
+        if (responseJSON == null) {
+            result.setCode(-1);
+            result.setMsg("杩炴帴澶辫触");
+            return result;
+        }
+        JSONArray data = responseJSON.getJSONArray("data");
+        ZLMServerConfig zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+        if (zlmServerConfig == null) {
+            result.setCode(-1);
+            result.setMsg("璇诲彇閰嶇疆澶辫触");
+            return result;
+        }
+        if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) {
+            result.setCode(-1);
+            result.setMsg("濯掍綋鏈嶅姟ID [" + zlmServerConfig.getGeneralMediaServerId() + " ] 宸插瓨鍦紝璇蜂慨鏀瑰獟浣撴湇鍔″櫒閰嶇疆");
+            return result;
+        }
+        mediaServerItem.setHttpSSlPort(zlmServerConfig.getHttpPort());
+        mediaServerItem.setRtmpPort(zlmServerConfig.getRtmpPort());
+        mediaServerItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
+        mediaServerItem.setRtspPort(zlmServerConfig.getRtspPort());
+        mediaServerItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
+        mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
+        mediaServerItem.setStreamIp(ip);
+        mediaServerItem.setHookIp(sipConfig.getIp());
+        mediaServerItem.setSdpIp(ip);
+        mediaServerItem.setStreamNoneReaderDelayMS(zlmServerConfig.getGeneralStreamNoneReaderDelayMS());
+        result.setCode(0);
+        result.setMsg("鎴愬姛");
+        result.setData(mediaServerItem);
+        return result;
+    }
+
+    @Override
+    public boolean checkMediaRecordServer(String ip, int port) {
+        boolean result = false;
+        OkHttpClient client = new OkHttpClient();
+        String url = String.format("http://%s:%s/index/api/record",  ip, port);
+
+        FormBody.Builder builder = new FormBody.Builder();
+
+        Request request = new Request.Builder()
+                .get()
+                .url(url)
+                .build();
+        try {
+            Response response = client.newCall(request).execute();
+            if (response != null) {
+                result = true;
+            }
+        } catch (Exception e) {}
+
+        return result;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
index 2167c55..8d45b05 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
@@ -32,6 +32,7 @@
             "streamNoneReaderDelayMS, " +
             "rtpEnable, " +
             "rtpPortRange, " +
+            "sendRtpPortRange, " +
             "recordAssistPort, " +
             "defaultServer, " +
             "createTime, " +
@@ -55,6 +56,7 @@
             "${streamNoneReaderDelayMS}, " +
             "${rtpEnable}, " +
             "'${rtpPortRange}', " +
+            "'${sendRtpPortRange}', " +
             "${recordAssistPort}, " +
             "${defaultServer}, " +
             "'${createTime}', " +
@@ -79,6 +81,7 @@
             "<if test=\"streamNoneReaderDelayMS != null\">, streamNoneReaderDelayMS=${streamNoneReaderDelayMS}</if>" +
             "<if test=\"rtpEnable != null\">, rtpEnable=${rtpEnable}</if>" +
             "<if test=\"rtpPortRange != null\">, rtpPortRange='${rtpPortRange}'</if>" +
+            "<if test=\"sendRtpPortRange != null\">, sendRtpPortRange='${sendRtpPortRange}'</if>" +
             "<if test=\"secret != null\">, secret='${secret}'</if>" +
             "<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
             "WHERE id='${id}'"+
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
index 3bfa595..881a20a 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
@@ -57,7 +57,9 @@
     @ApiOperation("娴佸獟浣撴湇鍔″垪琛�")
     @GetMapping(value = "/media_server/list")
     @ResponseBody
-    public WVPResult<List<MediaServerItem>> getMediaServerList(){
+    public WVPResult<List<MediaServerItem>> getMediaServerList(boolean detail){
+        List<MediaServerItem> all = mediaServerService.getAll();
+
         WVPResult<List<MediaServerItem>> result = new WVPResult<>();
         result.setCode(0);
         result.setMsg("success");
@@ -86,6 +88,60 @@
         result.setData(mediaServerService.getOne(id));
         return result;
     }
+
+    @ApiOperation("娴嬭瘯娴佸獟浣撴湇鍔�")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name="ip", value = "娴佸獟浣撴湇鍔P", dataTypeClass = String.class),
+            @ApiImplicitParam(name="port", value = "娴佸獟浣撴湇鍔TT绔彛", dataTypeClass = Integer.class),
+            @ApiImplicitParam(name="secret", value = "娴佸獟浣撴湇鍔ecret", dataTypeClass = String.class),
+    })
+    @GetMapping(value = "/media_server/check")
+    @ResponseBody
+    public WVPResult<MediaServerItem> checkMediaServer(@RequestParam String ip, @RequestParam int port, @RequestParam String secret){
+        return mediaServerService.checkMediaServer(ip, port, secret);
+    }
+
+    @ApiOperation("娴嬭瘯娴佸獟浣撳綍鍍忕鐞嗘湇鍔�")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name="ip", value = "娴佸獟浣撴湇鍔P", dataTypeClass = String.class),
+            @ApiImplicitParam(name="port", value = "娴佸獟浣撴湇鍔TT绔彛", dataTypeClass = Integer.class),
+            @ApiImplicitParam(name="secret", value = "娴佸獟浣撴湇鍔ecret", dataTypeClass = String.class),
+    })
+    @GetMapping(value = "/media_server/record/check")
+    @ResponseBody
+    public WVPResult<String> checkMediaRecordServer(@RequestParam String ip, @RequestParam int port){
+        boolean checkResult = mediaServerService.checkMediaRecordServer(ip, port);
+        WVPResult<String> result = new WVPResult<>();
+        if (checkResult) {
+            result.setCode(0);
+            result.setMsg("success");
+
+        }else {
+            result.setCode(-1);
+            result.setMsg("杩炴帴澶辫触");
+        }
+        return result;
+    }
+
+    @ApiOperation("淇濆瓨娴佸獟浣撴湇鍔�")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name="mediaServerItem", value = "娴佸獟浣撲俊鎭�", dataTypeClass = MediaServerItem.class)
+    })
+    @PostMapping(value = "/media_server/save")
+    @ResponseBody
+    public WVPResult<String> checkMediaServer(@RequestBody  MediaServerItem mediaServerItem){
+        if (mediaServerService.getOne(mediaServerItem.getId()) != null) {
+           mediaServerService.update(mediaServerItem);
+        }else {
+            return mediaServerService.add(mediaServerItem);
+        }
+        WVPResult<String> result = new WVPResult<>();
+        result.setCode(0);
+        result.setMsg("success");
+        return result;
+    }
+
+
 
     @ApiOperation("閲嶅惎鏈嶅姟")
     @GetMapping(value = "/restart")
@@ -155,6 +211,8 @@
                 case "base":
                     jsonObject.put("base", userSetup);
                     break;
+                default:
+                    break;
             }
         }
         result.setData(jsonObject);
diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml
index 856d6c5..f2d85ec 100644
--- a/src/main/resources/all-application.yml
+++ b/src/main/resources/all-application.yml
@@ -119,6 +119,8 @@
         enable: true
         # [鍙�塢 鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�,
         port-range: 30000,30500 # 绔彛鑼冨洿
+        # [鍙�塢 鍥芥爣绾ц仈鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鍙戦�佸獟浣撴祦,
+        send-port-range: 30000,30500 # 绔彛鑼冨洿
     # 褰曞儚杈呭姪鏈嶅姟锛� 閮ㄧ讲姝ゆ湇鍔″彲浠ュ疄鐜皕lm褰曞儚鐨勭鐞嗕笌涓嬭浇锛� 0 琛ㄧず涓嶄娇鐢�
     record-assist-port: 0
 
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 062f70e..44f70e6 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -63,6 +63,8 @@
         enable: true
         # [鍙�塢 鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�,
         port-range: 30000,30500 # 绔彛鑼冨洿
+        # [鍙�塢 鍥芥爣绾ц仈鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鍙戦�佸獟浣撴祦,
+        send-port-range: 30000,30500 # 绔彛鑼冨洿
     # 褰曞儚杈呭姪鏈嶅姟锛� 閮ㄧ讲姝ゆ湇鍔″彲浠ュ疄鐜皕lm褰曞儚鐨勭鐞嗕笌涓嬭浇锛� 0 琛ㄧず涓嶄娇鐢�
     record-assist-port: 0
 
diff --git a/src/main/resources/wvp.sqlite b/src/main/resources/wvp.sqlite
index b95368d..40bdad8 100644
--- a/src/main/resources/wvp.sqlite
+++ b/src/main/resources/wvp.sqlite
Binary files differ
diff --git a/web_src/build/webpack.base.conf.js b/web_src/build/webpack.base.conf.js
index a07e683..72539e3 100644
--- a/web_src/build/webpack.base.conf.js
+++ b/web_src/build/webpack.base.conf.js
@@ -27,6 +27,7 @@
     alias: {
       'vue$': 'vue/dist/vue.esm.js',
       '@': resolve('src'),
+      '@static': resolve('static'),
     }
   },
   module: {
diff --git a/web_src/src/assets/zlm-log.png b/web_src/src/assets/zlm-log.png
new file mode 100644
index 0000000..5f492dc
--- /dev/null
+++ b/web_src/src/assets/zlm-log.png
Binary files differ
diff --git a/web_src/src/components/CloudRecord.vue b/web_src/src/components/CloudRecord.vue
index e39cd5b..f5d052a 100644
--- a/web_src/src/components/CloudRecord.vue
+++ b/web_src/src/components/CloudRecord.vue
@@ -111,7 +111,7 @@
       },
       getMediaServerList: function (){
         let that = this;
-        that.mediaServerObj.getMediaServerList((data)=>{
+        that.mediaServerObj.getOnlineMediaServerList((data)=>{
           that.mediaServerList = data.data;
           if (that.mediaServerList.length > 0) {
             that.mediaServerId = that.mediaServerList[0].id
diff --git a/web_src/src/components/DeviceList.vue b/web_src/src/components/DeviceList.vue
index 7644a96..8d97833 100644
--- a/web_src/src/components/DeviceList.vue
+++ b/web_src/src/components/DeviceList.vue
@@ -60,7 +60,7 @@
 							<el-button-group>
                 <el-button size="mini" icon="el-icon-video-camera-solid" v-bind:disabled="scope.row.online==0"  type="primary" @click="showChannelList(scope.row)">閫氶亾</el-button>
                 <el-button size="mini" icon="el-icon-location" v-bind:disabled="scope.row.online==0"  type="primary" @click="showDevicePosition(scope.row)">瀹氫綅</el-button>
-                <el-button size="mini" icon="el-icon-delete" type="primary" @click="edit(scope.row)">缂栬緫</el-button>
+                <el-button size="mini" icon="el-icon-edit" type="primary" @click="edit(scope.row)">缂栬緫</el-button>
                 <el-button size="mini" icon="el-icon-delete" type="danger" v-if="scope.row.online==0"  @click="deleteDevice(scope.row)">鍒犻櫎</el-button>
 							</el-button-group>
 							</template>
diff --git a/web_src/src/components/MediaServerManger.vue b/web_src/src/components/MediaServerManger.vue
new file mode 100644
index 0000000..19674d1
--- /dev/null
+++ b/web_src/src/components/MediaServerManger.vue
@@ -0,0 +1,159 @@
+<template>
+	<div id="mediaServerManger">
+		<el-container>
+			<el-header>
+				<uiHeader></uiHeader>
+			</el-header>
+			<el-main id="msMain">
+        <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="add">娣诲姞鑺傜偣</el-button>
+        </div>
+
+        <el-row :gutter="12">
+          <el-col :span="num" v-for="item in mediaServerList" :key="item.id">
+            <el-card shadow="hover" :body-style="{ padding: '0px'}" class="server-card">
+              <div class="card-img-zlm"></div>
+              <div style="padding: 14px;text-align: left">
+                <span style="font-size: 16px">{{item.id}}</span>
+                <div style="margin-top: 13px; line-height: 12px; ">
+                  <span style="font-size: 14px; color: #999; margin-top: 5px">鍒涘缓鏃堕棿锛�  {{item.createTime}}</span>
+                  <el-button icon="el-icon-edit" style="padding: 0;float: right;" type="text" @click="edit(item)">缂栬緫</el-button>
+                </div>
+              </div>
+              <i v-if="item.status" class="iconfont icon-online server-card-status-online" title="鍦ㄧ嚎"></i>
+              <i v-if="!item.status" class="iconfont icon-online server-card-status-offline" title="绂荤嚎"></i>
+            </el-card>
+          </el-col>
+        </el-row>
+        <mediaServerEdit ref="mediaServerEdit" ></mediaServerEdit>
+			</el-main>
+		</el-container>
+	</div>
+</template>
+
+<script>
+	import uiHeader from './UiHeader.vue'
+  import MediaServer from './service/MediaServer'
+  import mediaServerEdit from './dialog/MediaServerEdit'
+	export default {
+		name: 'mediaServerManger',
+		components: {
+			uiHeader,mediaServerEdit
+		},
+		data() {
+			return {
+        mediaServerObj : new MediaServer(),
+        mediaServerList: [], //璁惧鍒楄〃
+        winHeight: window.innerHeight - 200,
+        updateLooper: false,
+        currentPage:1,
+        count:15,
+        num: this.getNumberByWidth(),
+        total:0,
+			};
+		},
+		computed: {
+
+		},
+		mounted() {
+			this.initData();
+			this.updateLooper = setInterval(this.initData, 2000);
+		},
+		destroyed() {
+			clearTimeout(this.updateLooper);
+		},
+		methods: {
+			initData: function() {
+        this.getServerList()
+			},
+      currentChange: function(val){
+        this.currentPage = val;
+        this.getServerList();
+      },
+      handleSizeChange: function(val){
+        this.count = val;
+        this.getServerList();
+      },
+      getServerList: function(){
+        this.mediaServerObj.getMediaServerList((data)=>{
+          this.mediaServerList = data.data;
+        })
+      },
+      add: function (){
+        this.$refs.mediaServerEdit.openDialog(null, this.initData)
+      },
+      edit: function (row){
+        this.$refs.mediaServerEdit.openDialog(row, this.initData)
+      },
+      getNumberByWidth(){
+        let candidateNums = [1, 2, 3, 4, 6, 8, 12, 24]
+        let clientWidth = window.innerWidth - 30;
+        let interval = 20;
+        let itemWidth = 360;
+        let num = (clientWidth + interval)/(itemWidth + interval)
+        let result = Math.ceil(24/num);
+        let resultVal = 24;
+        for (let i = 0; i < candidateNums.length; i++) {
+          let value = candidateNums[i]
+          if (i + 1 >= candidateNums.length) {
+            return  24;
+          }
+          if (value <= result && candidateNums[i + 1] > result ) {
+            return  value;
+          }
+        }
+
+        console.log("aadada:    "+ resultVal)
+        return resultVal;
+      },
+			dateFormat: function(/** timestamp=0 **/) {
+				var ts = arguments[0] || 0;
+				var t,y,m,d,h,i,s;
+				t = ts ? new Date(ts*1000) : new Date();
+				y = t.getFullYear();
+				m = t.getMonth()+1;
+				d = t.getDate();
+				h = t.getHours();
+				i = t.getMinutes();
+				s = t.getSeconds();
+				// 鍙牴鎹渶瑕佸湪杩欓噷瀹氫箟鏃堕棿鏍煎紡
+				return y+'-'+(m<10?'0'+m:m)+'-'+(d<10?'0'+d:d)+' '+(h<10?'0'+h:h)+':'+(i<10?'0'+i:i)+':'+(s<10?'0'+s:s);
+			}
+
+		}
+	};
+</script>
+
+<style>
+  .server-card{
+    position: relative;
+    margin-bottom: 20px;
+  }
+  .card-img-zlm{
+    width: 200px; height: 200px;
+    background: url('~@static/images/zlm-logo.png') no-repeat center;
+    background-position: center;
+    background-size: contain;
+    margin: 0 auto;
+  }
+  .server-card-status-online{
+    position: absolute;
+    right: 20px;
+    top: 20px;
+    color: #3caf36;
+    font-size: 18px;
+  }
+  .server-card-status-offline{
+    position: absolute;
+    right: 20px;
+    top: 20px;
+    color: #808080;
+    font-size: 18px;
+  }
+	.server-card:hover {
+    border: 1px solid #adadad;
+  }
+</style>
diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue
index 4ab4642..b2e9bbb 100644
--- a/web_src/src/components/UiHeader.vue
+++ b/web_src/src/components/UiHeader.vue
@@ -6,6 +6,7 @@
             <el-menu-item index="/pushVideoList">鎺ㄦ祦鍒楄〃</el-menu-item>
             <el-menu-item index="/streamProxyList">鎷夋祦浠g悊</el-menu-item>
             <el-menu-item index="/cloudRecord">浜戠褰曞儚</el-menu-item>
+            <el-menu-item index="/mediaServerManger">鑺傜偣绠$悊</el-menu-item>
             <el-menu-item index="/parentPlatformList/15/1">鍥芥爣绾ц仈</el-menu-item>
             <el-menu-item @click="openDoc">鍦ㄧ嚎鏂囨。</el-menu-item>
 <!--            <el-submenu index="/setting">-->
diff --git a/web_src/src/components/control.vue b/web_src/src/components/control.vue
index 6326712..a8ee8d4 100644
--- a/web_src/src/components/control.vue
+++ b/web_src/src/components/control.vue
@@ -139,7 +139,7 @@
         this.initTable();
         this.updateData();
         this.chartInterval = setInterval(this.updateData, 3000);
-        this.mediaServer.getMediaServerList((data)=>{
+        this.mediaServer.getOnlineMediaServerList((data)=>{
           this.mediaServerList = data.data;
           if (this.mediaServerList && this.mediaServerList.length > 0) {
             this.mediaServerChoose = this.mediaServerList[0].id
diff --git a/web_src/src/components/dialog/MediaServerEdit.vue b/web_src/src/components/dialog/MediaServerEdit.vue
new file mode 100644
index 0000000..b67daf8
--- /dev/null
+++ b/web_src/src/components/dialog/MediaServerEdit.vue
@@ -0,0 +1,368 @@
+<template>
+  <div id="mediaServerEdit" v-loading="isLoging">
+    <el-dialog
+      title="濯掍綋鑺傜偣"
+      :width="dialogWidth"
+      top="2rem"
+      :close-on-click-modal="false"
+      :visible.sync="showDialog"
+      :destroy-on-close="true"
+      @close="close()"
+    >
+      <div id="formStep" style="margin-top: 1rem; margin-right: 20px;">
+        <el-form v-if="currentStep == 1" ref="mediaServerForm" :rules="rules" :model="mediaServerForm" label-width="140px" >
+          <el-form-item label="IP" prop="ip">
+            <el-input v-model="mediaServerForm.ip"  placeholder="濯掍綋鏈嶅姟IP" clearable></el-input>
+          </el-form-item>
+          <el-form-item label="HTTP绔彛" prop="port">
+            <el-input v-model="mediaServerForm.httpPort" placeholder="濯掍綋鏈嶅姟HTTP绔彛"  clearable></el-input>
+          </el-form-item>
+          <el-form-item label="SECRET" prop="secret">
+            <el-input v-model="mediaServerForm.secret" placeholder="濯掍綋鏈嶅姟SECRET"  clearable></el-input>
+          </el-form-item>
+          <el-form-item>
+            <div style="float: right;">
+              <el-button type="primary" v-if="currentStep === 1 && serverCheck === 1" @click="next" >涓嬩竴姝�</el-button>
+              <el-button @click="close">鍙栨秷</el-button>
+              <el-button type="primary" @click="checkServer" >娴嬭瘯</el-button>
+              <i v-if="serverCheck === 1" class="el-icon-success" style="color: #3caf36"></i>
+              <i v-if="serverCheck === -1" class="el-icon-error" style="color: #c80000"></i>
+            </div>
+          </el-form-item>
+        </el-form>
+        <el-row :gutter="24">
+          <el-col :span="12">
+            <el-form v-if="currentStep === 2 || currentStep === 3" ref="mediaServerForm1" :rules="rules" :model="mediaServerForm" label-width="140px" >
+              <el-form-item label="IP" prop="ip">
+                <el-input  v-if="currentStep === 2" v-model="mediaServerForm.ip" disabled></el-input>
+                <el-input  v-if="currentStep === 3"  v-model="mediaServerForm.ip"></el-input>
+              </el-form-item>
+              <el-form-item label="HTTP绔彛" prop="port">
+                <el-input  v-if="currentStep === 2"  v-model="mediaServerForm.httpPort" disabled></el-input>
+                <el-input  v-if="currentStep === 3"  v-model="mediaServerForm.httpPort"></el-input>
+              </el-form-item>
+              <el-form-item label="SECRET" prop="secret">
+                <el-input v-if="currentStep === 2"  v-model="mediaServerForm.secret" disabled></el-input>
+                <el-input v-if="currentStep === 3"  v-model="mediaServerForm.secret"></el-input>
+              </el-form-item>
+              <el-form-item label="HOOK IP" prop="ip">
+                <el-input v-model="mediaServerForm.hookIp" placeholder="濯掍綋鏈嶅姟HOOK_IP" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="SDP IP" prop="ip">
+                <el-input v-model="mediaServerForm.sdpIp" placeholder="濯掍綋鏈嶅姟SDP_IP" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="娴両P" prop="ip">
+                <el-input v-model="mediaServerForm.streamIp" placeholder="濯掍綋鏈嶅姟娴両P" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="HTTPS PORT" prop="port">
+                <el-input v-model="mediaServerForm.httpSSlPort" placeholder="濯掍綋鏈嶅姟HTTPS_PORT" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="RTSP PORT" prop="port">
+                <el-input v-model="mediaServerForm.rtspPort" placeholder="濯掍綋鏈嶅姟RTSP_PORT" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="RTSPS PORT" prop="port">
+                <el-input v-model="mediaServerForm.rtspSSLPort" placeholder="濯掍綋鏈嶅姟RTSPS_PORT" clearable></el-input>
+              </el-form-item>
+
+            </el-form>
+          </el-col>
+          <el-col :span="12">
+            <el-form v-if="currentStep === 2 || currentStep === 3"  ref="mediaServerForm2" :rules="rules" :model="mediaServerForm" label-width="180px" >
+              <el-form-item label="RTMP PORT" prop="port">
+                <el-input v-model="mediaServerForm.rtmpPort" placeholder="濯掍綋鏈嶅姟RTMP_PORT" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="RTMPS PORT" prop="port">
+                <el-input v-model="mediaServerForm.rtmpSSlPort" placeholder="濯掍綋鏈嶅姟RTMPS_PORT" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="鑷姩閰嶇疆濯掍綋鏈嶅姟" >
+                <el-switch v-model="mediaServerForm.autoConfig"></el-switch>
+              </el-form-item>
+              <el-form-item label="鏀舵祦绔彛妯″紡" >
+                <el-switch  active-text="澶氱鍙�" inactive-text="鍗曠鍙�" v-model="mediaServerForm.rtpEnable"></el-switch>
+              </el-form-item>
+
+              <el-form-item v-if="!mediaServerForm.rtpEnable" label="鏀舵祦绔彛" prop="port">
+                <el-input v-model.number="mediaServerForm.rtpProxyPort" clearable></el-input>
+              </el-form-item>
+              <el-form-item v-if="mediaServerForm.rtpEnable" label="鏀舵祦绔彛" prop="port">
+                <el-input v-model="mediaServerForm.rtpPortRange1" placeholder="璧峰" clearable style="width: 100px" prop="port"></el-input>
+                -
+                <el-input v-model="mediaServerForm.rtpPortRange2" placeholder="缁堟"  clearable style="width: 100px" prop="port"></el-input>
+              </el-form-item>
+              <el-form-item label="鎺ㄦ祦绔彛" prop="port">
+                <el-input v-model="mediaServerForm.sendRtpPortRange1" placeholder="璧峰" clearable style="width: 100px" prop="port"></el-input>
+                -
+                <el-input v-model="mediaServerForm.sendRtpPortRange2" placeholder="缁堟"  clearable style="width: 100px" prop="port"></el-input>
+              </el-form-item>
+              <el-form-item label="鏃犱汉瑙傜湅澶氫箙鍚庡仠姝㈡媺娴�" >
+                <el-input v-model.number="mediaServerForm.streamNoneReaderDelayMS" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="褰曞儚绠$悊鏈嶅姟绔彛" prop="port">
+                <el-input v-model.number="mediaServerForm.recordAssistPort">
+<!--                  <el-button v-if="mediaServerForm.recordAssistPort > 0" slot="append" type="primary" @click="checkRecordServer">娴嬭瘯</el-button>-->
+                  <el-button v-if="mediaServerForm.recordAssistPort > 0" class="el-icon-check" slot="append" type="primary" @click="checkRecordServer"></el-button>
+                </el-input>
+                <i v-if="recordServerCheck == 1" class="el-icon-success" style="color: #3caf36; position: absolute;top: 14px;"></i>
+                <i v-if="recordServerCheck == 2" class="el-icon-loading" style="color: #3caf36; position: absolute;top: 14px;"></i>
+                <i v-if="recordServerCheck === -1" class="el-icon-error" style="color: #c80000; position: absolute;top: 14px;"></i>
+              </el-form-item>
+              <el-form-item>
+                <div style="float: right;">
+                  <el-button type="primary"  @click="onSubmit" >鎻愪氦</el-button>
+                  <el-button @click="close">鍙栨秷</el-button>
+                </div>
+              </el-form-item>
+            </el-form>
+          </el-col>
+        </el-row>
+
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import MediaServer from './../service/MediaServer'
+
+export default {
+  name: "streamProxyEdit",
+  props: {},
+  computed: {},
+  created() {
+    this.setDialogWidth()
+  },
+  data() {
+    const isValidIp = (rule, value, callback) => { // 鏍¢獙IP鏄惁绗﹀悎瑙勫垯
+      var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
+      console.log(this.mediaServerForm.ip)
+      if (!reg.test(this.mediaServerForm.ip)) {
+        return callback(new Error('璇疯緭鍏ユ湁鏁堢殑IP鍦板潃'))
+      } else {
+        callback()
+      }
+      return true
+    }
+    const isValidPort = (rule, value, callback) => { // 鏍¢獙IP鏄惁绗﹀悎瑙勫垯
+      var reg = /^(([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5]))$/
+      if (!reg.test(this.mediaServerForm.httpPort)) {
+        return callback(new Error('璇疯緭鍏ユ湁鏁堢殑绔彛鍙�'))
+      } else {
+        callback()
+      }
+      return true
+    }
+    return {
+      dialogWidth: 0,
+      defaultWidth: 1000,
+      listChangeCallback: null,
+      showDialog: false,
+      isLoging: false,
+      dialogLoading: false,
+
+      currentStep: 1,
+      platformList: [],
+      mediaServer: new MediaServer(),
+      serverCheck: 0,
+      recordServerCheck: 0,
+      mediaServerForm: {
+        id: "",
+        ip: "",
+        autoConfig: true,
+        hookIp: "",
+        sdpIp: "",
+        streamIp: "",
+        streamNoneReaderDelayMS: "",
+        secret: "035c73f7-bb6b-4889-a715-d9eb2d1925cc",
+        httpPort: "",
+        httpSSlPort: "",
+        recordAssistPort: "",
+        rtmpPort: "",
+        rtmpSSlPort: "",
+        rtpEnable: false,
+        rtpPortRange: "",
+        sendRtpPortRange: "",
+        rtpPortRange1: "",
+        rtpPortRange2: "",
+        sendRtpPortRange1: "",
+        sendRtpPortRange2: "",
+        rtpProxyPort: "",
+        rtspPort: "",
+        rtspSSLPort: "",
+      },
+
+      rules: {
+        ip:  [{ required: true, validator: isValidIp, message: '璇疯緭鍏ユ湁鏁堢殑IP鍦板潃', trigger: 'blur' }],
+        port:  [{ required: true, validator: isValidPort, message: '璇疯緭鍏ユ湁鏁堢殑绔彛鍙�', trigger: 'blur' }],
+        secret: [{ required: true, message: "璇疯緭鍏ecret", trigger: "blur" }],
+        timeout_ms: [{ required: true, message: "璇疯緭鍏Fmpeg鎺ㄦ祦鎴愬姛瓒呮椂鏃堕棿", trigger: "blur" }],
+        ffmpeg_cmd_key: [{ required: false, message: "璇疯緭鍏Fmpeg鍛戒护鍙傛暟妯℃澘锛堝彲閫夛級", trigger: "blur" }],
+      },
+    };
+  },
+  methods: {
+    setDialogWidth() {
+      let val = document.body.clientWidth
+      if (val < this.defaultWidth) {
+        this.dialogWidth = '100%'
+      } else {
+        this.dialogWidth = this.defaultWidth + 'px'
+      }
+    },
+    openDialog: function (param, callback) {
+      this.showDialog = true;
+      this.listChangeCallback = callback;
+      if (param != null) {
+        this.mediaServerForm = param;
+        this.currentStep = 3;
+        if (param.rtpPortRange) {
+          let rtpPortRange = this.mediaServerForm.rtpPortRange.split(",");
+          if (rtpPortRange.length > 0) {
+            this.mediaServerForm["rtpPortRange1"] = rtpPortRange[0]
+            this.mediaServerForm["rtpPortRange2"] = rtpPortRange[1]
+          }
+        }
+        let sendRtpPortRange = this.mediaServerForm.sendRtpPortRange.split(",");
+        this.mediaServerForm["sendRtpPortRange1"] = sendRtpPortRange[0]
+        this.mediaServerForm["sendRtpPortRange2"] = sendRtpPortRange[1]
+      }
+    },
+    checkServer: function() {
+      let that = this;
+      that.serverCheck = 0;
+      that.mediaServer.checkServer(that.mediaServerForm, data =>{
+        if (data.code === 0) {
+          if (parseInt(that.mediaServerForm.httpPort) !== parseInt(data.data.httpPort)) {
+            that.$message({
+              showClose: true,
+              message: '濡傛灉浣犳鍦ㄤ娇鐢╠ocker閮ㄧ讲浣犵殑濯掍綋鏈嶅姟锛岃娉ㄦ剰鐨勭鍙f槧灏勩��',
+              type: 'warning',
+              duration: 0
+            });
+          }
+          let httpPort = that.mediaServerForm.httpPort;
+          that.mediaServerForm = data.data;
+          that.mediaServerForm.httpPort = httpPort;
+          that.mediaServerForm.autoConfig = true;
+          that.mediaServerForm.sendRtpPortRange1 = 30000
+          that.mediaServerForm.sendRtpPortRange2 = 30500
+          that.mediaServerForm.rtpPortRange1 = 30000
+          that.mediaServerForm.rtpPortRange2 = 30500
+          that.serverCheck = 1;
+        }else {
+          that.serverCheck = -1;
+          that.$message({
+            showClose: true,
+            message: data.msg,
+            type: "error",
+          });
+        }
+
+      })
+    },
+    next: function (){
+      this.currentStep = 2;
+      this.defaultWidth = 900;
+      this.setDialogWidth();
+    },
+    checkRecordServer: function (){
+      let that = this;
+      that.recordServerCheck = 2;
+      if (that.mediaServerForm.recordAssistPort <= 0 || that.mediaServerForm.recordAssistPort > 65535 ) {
+        that.recordServerCheck = -1;
+        that.$message({
+          showClose: true,
+          message: "绔彛鍙峰簲璇ュ湪-65535涔嬮棿",
+          type: "error",
+        });
+        return;
+      }
+      that.mediaServer.checkRecordServer(that.mediaServerForm, data =>{
+        if (data.code === 0) {
+          that.recordServerCheck = 1;
+        }else {
+          that.recordServerCheck = -1;
+          that.$message({
+            showClose: true,
+            message: data.msg,
+            type: "error",
+          });
+        }
+      })
+    },
+    onSubmit: function () {
+      this.dialogLoading = true;
+      let that = this;
+      if (this.mediaServerForm.rtpEnable) {
+        this.mediaServerForm.rtpPortRange = this.mediaServerForm.rtpPortRange1 + "," + this.mediaServerForm.rtpPortRange2;
+      }
+      this.mediaServerForm.sendRtpPortRange = this.mediaServerForm.sendRtpPortRange1 + "," + this.mediaServerForm.sendRtpPortRange2;
+      that.mediaServer.addServer(this.mediaServerForm, data => {
+        if (data.code === 0) {
+          that.$message({
+            showClose: true,
+            message: "淇濆瓨鎴愬姛",
+            type: "success",
+          });
+          if (this.listChangeCallback) this.listChangeCallback();
+          that.close()
+        }else {
+          that.$message({
+            showClose: true,
+            message: data.msg,
+            type: "error",
+          });
+        }
+      })
+    },
+    close: function () {
+      this.showDialog = false;
+      this.dialogLoading = false;
+      this.mediaServerForm = {
+        id: "",
+        ip: "",
+        autoConfig: true,
+        hookIp: "",
+        sdpIp: "",
+        streamIp: "",
+        streamNoneReaderDelayMS: "",
+        secret: "035c73f7-bb6b-4889-a715-d9eb2d1925cc",
+        httpPort: "",
+        httpSSlPort: "",
+        recordAssistPort: "",
+        rtmpPort: "",
+        rtmpSSlPort: "",
+        rtpEnable: false,
+        rtpPortRange: "",
+        sendRtpPortRange: "",
+        rtpPortRange1: "",
+        rtpPortRange2: "",
+        sendRtpPortRange1: "",
+        sendRtpPortRange2: "",
+        rtpProxyPort: "",
+        rtspPort: "",
+        rtspSSLPort: "",
+      };
+      this.listChangeCallback = null
+      this.currentStep = 1;
+    },
+    deviceGBIdExit: async function (deviceGbId) {
+      var result = false;
+      var that = this;
+      await that.$axios({
+        method: 'post',
+        url:`/api/platform/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/dialog/StreamProxyEdit.vue b/web_src/src/components/dialog/StreamProxyEdit.vue
index 3310d9c..ea3a64f 100644
--- a/web_src/src/components/dialog/StreamProxyEdit.vue
+++ b/web_src/src/components/dialog/StreamProxyEdit.vue
@@ -203,7 +203,7 @@
       }).catch(function (error) {
         console.log(error);
       });
-      this.mediaServer.getMediaServerList((data)=>{
+      this.mediaServer.getOnlineMediaServerList((data)=>{
         this.mediaServerList = data;
       })
     },
diff --git a/web_src/src/components/dialog/changePassword.vue b/web_src/src/components/dialog/changePassword.vue
index 39aba8d..eade6a8 100644
--- a/web_src/src/components/dialog/changePassword.vue
+++ b/web_src/src/components/dialog/changePassword.vue
@@ -89,7 +89,7 @@
         method: 'post',
         url:"/api/user/changePassword",
         params: {
-          oldpassword: crypto.createHash('md5').update(this.oldPassword, "utf8").digest('hex'),
+          oldPassword: crypto.createHash('md5').update(this.oldPassword, "utf8").digest('hex'),
           password: this.newPassword
         }
       }).then((res)=> {
diff --git a/web_src/src/components/dialog/deviceEdit.vue b/web_src/src/components/dialog/deviceEdit.vue
index 464de02..5d2ffa5 100644
--- a/web_src/src/components/dialog/deviceEdit.vue
+++ b/web_src/src/components/dialog/deviceEdit.vue
@@ -82,7 +82,7 @@
     },
     getMediaServerList: function (){
       let that = this;
-      that.mediaServerObj.getMediaServerList((data)=>{
+      that.mediaServerObj.getOnlineMediaServerList((data)=>{
         that.mediaServerList = data.data;
       })
     },
diff --git a/web_src/src/components/service/MediaServer.js b/web_src/src/components/service/MediaServer.js
index b905352..e9de603 100644
--- a/web_src/src/components/service/MediaServer.js
+++ b/web_src/src/components/service/MediaServer.js
@@ -6,10 +6,20 @@
     this.$axios = axios;
   }
 
-  getMediaServerList(callback){
+  getOnlineMediaServerList(callback){
     this.$axios({
       method: 'get',
       url:`/api/server/media_server/online/list`,
+    }).then(function (res) {
+      if (typeof (callback) == "function") callback(res.data)
+    }).catch(function (error) {
+      console.log(error);
+    });
+  }
+  getMediaServerList(callback){
+    this.$axios({
+      method: 'get',
+      url:`/api/server/media_server/list`,
     }).then(function (res) {
       if (typeof (callback) == "function") callback(res.data)
     }).catch(function (error) {
@@ -27,6 +37,49 @@
       console.log(error);
     });
   }
+
+  checkServer(param, callback){
+    this.$axios({
+      method: 'get',
+      url:`/api/server/media_server/check`,
+      params: {
+        ip: param.ip,
+        port: param.httpPort,
+        secret: param.secret
+      }
+    }).then(function (res) {
+      if (typeof (callback) == "function") callback(res.data)
+    }).catch(function (error) {
+      console.log(error);
+    });
+  }
+
+  checkRecordServer(param, callback){
+    this.$axios({
+      method: 'get',
+      url:`/api/server/media_server/record/check`,
+      params: {
+        ip: param.ip,
+        port: param.recordAssistPort
+      }
+    }).then(function (res) {
+      if (typeof (callback) == "function") callback(res.data)
+    }).catch(function (error) {
+      console.log(error);
+    });
+  }
+
+  addServer(param, callback){
+    this.$axios({
+      method: 'post',
+      url:`/api/server/media_server/save`,
+      data: param
+    }).then(function (res) {
+      if (typeof (callback) == "function") callback(res.data)
+    }).catch(function (error) {
+      console.log(error);
+    });
+  }
 }
 
 export default MediaServer;
diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js
index 4b0810d..59bbb23 100644
--- a/web_src/src/router/index.js
+++ b/web_src/src/router/index.js
@@ -10,6 +10,7 @@
 import login from '../components/Login.vue'
 import parentPlatformList from '../components/ParentPlatformList.vue'
 import cloudRecord from '../components/CloudRecord.vue'
+import mediaServerManger from '../components/MediaServerManger.vue'
 import test from '../components/test.vue'
 import web from '../components/setting/Web.vue'
 import sip from '../components/setting/Sip.vue'
@@ -71,6 +72,11 @@
       component: cloudRecord,
     },
     {
+      path: '/mediaServerManger',
+      name: 'mediaServerManger',
+      component: mediaServerManger,
+    },
+    {
       path: '/setting/web',
       name: 'web',
       component: web,
diff --git a/web_src/static/css/iconfont.css b/web_src/static/css/iconfont.css
index 972cf6b..0f9b608 100644
--- a/web_src/static/css/iconfont.css
+++ b/web_src/static/css/iconfont.css
@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 1291092 */
-  src: url('iconfont.woff2?t=1626163621710') format('woff2'),
-       url('iconfont.woff?t=1626163621710') format('woff'),
-       url('iconfont.ttf?t=1626163621710') format('truetype');
+  src: url('iconfont.woff2?t=1631767887536') format('woff2'),
+       url('iconfont.woff?t=1631767887536') format('woff'),
+       url('iconfont.ttf?t=1631767887536') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-online:before {
+  content: "\e600";
+}
+
 .icon-xiangqing2:before {
   content: "\e798";
 }
diff --git a/web_src/static/css/iconfont.woff2 b/web_src/static/css/iconfont.woff2
index c8d7e3d..42e2eca 100644
--- a/web_src/static/css/iconfont.woff2
+++ b/web_src/static/css/iconfont.woff2
Binary files differ
diff --git a/web_src/static/images/zlm-logo.png b/web_src/static/images/zlm-logo.png
new file mode 100644
index 0000000..5f492dc
--- /dev/null
+++ b/web_src/static/images/zlm-logo.png
Binary files differ

--
Gitblit v1.8.0