From 8db95778fffe41dc9f562a02d1e42dbe2582a18a Mon Sep 17 00:00:00 2001
From: ‘liusuyi’ <1951119284@qq.com>
Date: 星期四, 14 九月 2023 11:44:19 +0800
Subject: [PATCH] 优化流媒体测试页

---
 ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java    |    7 
 ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java            |    6 
 ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java |   64 ++++++
 ard-work/src/main/resources/templates/mediaMTX.html                       |  335 +++------------------------------
 ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java         |   12 +
 lib/mediamtx/mediamtx.yml                                                 |    2 
 ard-work/src/main/resources/templates/preview.html                        |  169 ++++++++--------
 7 files changed, 201 insertions(+), 394 deletions(-)

diff --git a/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java b/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
index a4e65f3..48490b4 100644
--- a/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
+++ b/ard-work/src/main/java/com/ruoyi/media/controller/MediaController.java
@@ -22,7 +22,7 @@
 import java.util.stream.Collectors;
 
 /**
- * @Description:
+ * @Description: 娴佸獟浣撲笟鍔�
  * @ClassName: controller
  * @Author: 鍒樿嫃涔�
  * @Date: 2023骞�07鏈�13鏃�9:26
@@ -75,9 +75,7 @@
     @PreAuthorize("@ss.hasPermi('media:stream:edit')")
     @PutMapping
     public AjaxResult edit(@RequestBody StreamInfo streamInfo) {
-        mediaService.removePath(new String[]{streamInfo.getName()});
-        vtduService.deleteVtduByName(streamInfo.getName());
-        Map<String, String> map = mediaService.addPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(), streamInfo.getIsCode());
+        Map<String, String> map = mediaService.editPath(streamInfo.getName(), streamInfo.getRtspSource(), streamInfo.getMode(), streamInfo.getIsCode());
         Vtdu vtdu = new Vtdu();
         vtdu.setName(streamInfo.getName());
         vtdu.setSourceUrl(streamInfo.getRtspSource());
@@ -86,6 +84,7 @@
         vtdu.setRtspUrl(map.get("rtspUrl"));
         vtdu.setRtmpUrl(map.get("rtmpUrl"));
         vtdu.setWebrtcUrl(map.get("webrtcUrl"));
+        vtduService.updateVtdu(vtdu);
         return AjaxResult.success(map);
     }
 
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java b/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
index 195ab1a..436fe72 100644
--- a/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
+++ b/ard-work/src/main/java/com/ruoyi/media/service/IMediaService.java
@@ -16,7 +16,17 @@
      * 鍒樿嫃涔�
      * 2023/8/12 13:56:52
      */
-    Map<String,String> addPath(String name, String rtspPath, String mode, String isCode);
+    Map<String,String> addPath(String name, String sourceUrl, String mode, String isCode);
+    /**
+     * 淇敼璺緞
+     * name 鍚嶇О
+     * rtspPath rtsp鍦板潃
+     * mode 妯″紡锛氬疄鏃�/鎸夐渶
+     * isCode 鏄惁杞爜
+     * 鍒樿嫃涔�
+     * 2023/8/12 13:56:52
+     */
+    Map<String,String> editPath(String name, String sourceUrl, String mode, String isCode);
 
     StreamInfo getPathInfo(String name);
 
diff --git a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java b/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
index 32fc297..2471f51 100644
--- a/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
+++ b/ard-work/src/main/java/com/ruoyi/media/service/impl/MediaServiceImpl.java
@@ -131,18 +131,17 @@
         //GPU纭В鐮佺紪鐮� -hwaccel cuvid -c:v h264_cuvid  浣跨敤cuda瑙g爜   -c:v h264_nvenc 浣跨敤cuda缂栫爜
         //String cmd = rootPath  + "/lib/mediamtx/" + "ffmpeg -hwaccel cuvid -c:v h264_cuvid  -rtsp_transport udp  -i " + rtspPath + " -c:v h264_nvenc  -r 25 -threads 4  -b:v 2048k -bf 0 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
         if (isCode.equals("1")) {
-            String cmd = rootPath + "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 25 -sc_threshold 0 -threads 6  -b:v 4096k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
+            String cmd =  "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 25 -sc_threshold 0 -threads 6  -b:v 2048k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
             if (!softwareDecoding) {
-                cmd = rootPath + "ffmpeg -hwaccel cuvid -c:v h264_cuvid  -rtsp_transport tcp  -i " + sourceUrl + " -c:v h264_nvenc  -r 25 -g 60 -threads 6  -b:v 4096k -bf 0 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
+                cmd =  "ffmpeg -hwaccel cuvid -c:v h264_cuvid  -rtsp_transport tcp  -i " + sourceUrl + " -c:v h264_nvenc  -r 25 -g 60 -threads 6  -b:v 2048k -bf 0 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
             }
             if (mode.equals("0")) {
                 mediaInfo.setRunondemand(cmd);
                 mediaInfo.setRunondemandrestart(true);
+                mediaInfo.setRunondemandcloseafter("5s");
             } else {
                 mediaInfo.setRunoninit(cmd);
                 mediaInfo.setRunoninitrestart(true);
-                //mediaInfo.setRunonready(cmd);
-                //mediaInfo.setRunonreadyrestart(true);
             }
         } else {
             mediaInfo.setSource(sourceUrl);
@@ -158,6 +157,63 @@
     }
 
     @Override
+    public Map<String, String> editPath(String name, String sourceUrl, String mode, String isCode) {
+        String rtspUrl = "rtsp://" + mediamtxHost + ":7554/" + name;
+        String rtmpUrl = "rtmp://" + mediamtxHost + ":1935/" + name;
+        String webrtcUrl = "http://" + mediamtxHost + ":8889/" + name;
+
+        Conf mediaInfo = new Conf();
+        String rootPath = System.getProperty("user.dir").replaceAll("\\\\", "/") + "/lib/mediamtx/";
+        //-vcodec libx264 //鎸囧畾瑙嗛缂栫爜鍣ㄤ负 libx264锛屼娇鐢� H.264 缂栫爜鏍煎紡杩涜瑙嗛鍘嬬缉
+        //-preset ultrafast  //--preset鐨勫弬鏁颁富瑕佽皟鑺傜紪鐮侀�熷害鍜岃川閲忕殑骞宠 锛屾湁ultrafast锛堣浆鐮侀�熷害鏈�蹇紝瑙嗛寰�寰�涔熸渶妯$硦锛夈�乻uperfast銆乿eryfast銆乫aster銆乫ast銆乵edium銆乻low銆乻lower銆乿eryslow銆乸lacebo杩�10涓�夐」锛屼粠蹇埌鎱�
+        //-r 25 //璁剧疆杈撳嚭瑙嗛鐨勫抚鐜囦负 25 甯�/绉�
+        //-g 20 //鍏抽敭甯ч棿闅�20
+        //-sc_threshold 0 //灏嗗叾璁剧疆涓�0锛�-sc_threshold 0锛夌鐢ㄥ満鏅彉鍖栨娴�
+        //-rtsp_transport tcp //杩欎釜閫夐」鍛婅瘔 FFmpeg 浣跨敤 TCP 浣滀负 RTSP 鐨勪紶杈撳崗璁�
+        //-threads 4: 鎸囧畾瑕佷娇鐢ㄧ殑绾跨▼鏁颁负 4銆�//杩欏厑璁� FFmpeg 鍦ㄥ鏍稿鐞嗗櫒涓婁娇鐢ㄥ涓嚎绋嬫潵杩涜瑙嗛缂栫爜锛屼互鍔犲揩閫熷害銆�
+        // -i //鐢ㄤ簬鎸囧畾杈撳叆濯掍綋鏂囦欢鎴栬緭鍏ユ祦鐨勫湴鍧�
+        // -bf 0 绂佺敤B甯э紝鍥犱负webrtc鍦ㄧ綉椤佃皟鐢ㄦ椂鎺у埗鍙颁竴鐩磋緭鍑� WebRTC doesn鈥檛 support H264 streams with B-frames
+        //-f rtsp //杩欎釜閫夐」鍛婅瘔 FFmpeg 杈撳嚭涓� RTSP 鏍煎紡銆�
+        //CPU杞В鐮佺紪鐮�
+        //String cmd = rootPath + "/lib/mediamtx/" +"ffmpeg -rtsp_transport tcp -i " + rtspPath + " -vcodec libx264 -preset:v ultrafast -r 25 -threads 4  -b:v 2048k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
+        //GPU纭В鐮佺紪鐮� -hwaccel cuvid -c:v h264_cuvid  浣跨敤cuda瑙g爜   -c:v h264_nvenc 浣跨敤cuda缂栫爜
+        //String cmd = rootPath  + "/lib/mediamtx/" + "ffmpeg -hwaccel cuvid -c:v h264_cuvid  -rtsp_transport udp  -i " + rtspPath + " -c:v h264_nvenc  -r 25 -threads 4  -b:v 2048k -bf 0 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
+        if (isCode.equals("1")) {
+            mediaInfo.setSource("publisher");
+            String cmd =  "ffmpeg -rtsp_transport tcp -i " + sourceUrl + " -vcodec libx264 -preset:v ultrafast -r 25 -keyint_min 25 -g 25 -sc_threshold 0 -threads 6  -b:v 2048k -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
+            if (!softwareDecoding) {
+                cmd =  "ffmpeg -hwaccel cuvid -c:v h264_cuvid  -rtsp_transport tcp  -i " + sourceUrl + " -c:v h264_nvenc  -r 25 -g 60 -threads 6  -b:v 2048k -bf 0 -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH";
+            }
+            if (mode.equals("0")) {
+                mediaInfo.setRunondemand(cmd);
+                mediaInfo.setRunondemandcloseafter("5s");
+                mediaInfo.setRunondemandrestart(true);
+                mediaInfo.setRunoninit("");
+                mediaInfo.setRunoninitrestart(false);
+            } else {
+                mediaInfo.setRunoninit(cmd);
+                mediaInfo.setRunoninitrestart(true);
+                mediaInfo.setRunondemand("");
+                mediaInfo.setRunondemandrestart(false);
+            }
+        } else {
+            mediaInfo.setSource(sourceUrl);
+            mediaInfo.setRunondemand("");
+            mediaInfo.setRunondemandrestart(false);
+            mediaInfo.setRunoninit("");
+            mediaInfo.setRunoninitrestart(false);
+        }
+        mediaInfo.setMaxReaders(100);
+        mediaInfo.setSourceprotocol("tcp");
+        mediaClient.editPath(name, mediaInfo);
+        Map<String,String> map=new HashMap<>();
+        map.put("rtspUrl",rtspUrl);
+        map.put("rtmpUrl",rtmpUrl);
+        map.put("webrtcUrl",webrtcUrl);
+        return map;
+    }
+
+    @Override
     public StreamInfo getPathInfo(String name) {
         Items item = mediaClient.getPathInfo(name);
         StreamInfo info = new StreamInfo();
diff --git a/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java b/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
index 7103be5..0e39f82 100644
--- a/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
+++ b/ard-work/src/main/java/com/ruoyi/utils/forest/MediaClient.java
@@ -18,7 +18,11 @@
      */
     @Post("/config/paths/add/{name}")
     String addPath( @Var("name") String name, @JSONBody Conf body);
-
+    /**
+     * 淇敼璺緞
+     */
+    @Post("/config/paths/edit/{name}")
+    String editPath( @Var("name") String name, @JSONBody Conf body);
     /**
      * 绉婚櫎璺緞
      */
diff --git a/ard-work/src/main/resources/templates/mediaMTX.html b/ard-work/src/main/resources/templates/mediaMTX.html
index 90c51b4..7b1680a 100644
--- a/ard-work/src/main/resources/templates/mediaMTX.html
+++ b/ard-work/src/main/resources/templates/mediaMTX.html
@@ -1,307 +1,40 @@
 <!DOCTYPE html>
-<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<html>
 <head>
     <meta charset="UTF-8">
-    <title>娴嬭瘯椤�</title>
-    <script th:src="@{/js/jquery-3.6.4.min.js}"></script>
-    <link rel="stylesheet" th:href="@{/css/bootstrap.css}"/>
-    <script th:src="@{/js/bootstrap.js}"></script>
-    <style>
-        .video-container {
-            display: inline-block;
-            vertical-align: top;
-            width: 33%; /* 3涓棰戝钩鍧囧垎閰嶄竴琛岀殑瀹藉害 */
-            /*padding: 2px; !* 鍙互鏍规嵁闇�瑕佽皟鏁村唴杈硅窛 *!*/
-            box-sizing: border-box;
-        }
-
-        .video-container video {
-            width: 100%;
-            height: 100%;
-            /*object-fit: cover; !* 瑙嗛濉厖瀹瑰櫒锛屼繚鎸佸楂樻瘮 *!*/
-        }
-    </style>
+    <title>video.js鎾斁rtmp娴�</title>
+    <!--寮曞叆鎾斁鍣ㄦ牱寮�-->
+    <link href="http://vjs.zencdn.net/5.19/video-js.min.css" rel="stylesheet">
+    <!--寮曞叆鎾斁鍣╦s-->
+    <script src="http://vjs.zencdn.net/5.19/video.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/videojs-flash@2/dist/videojs-flash.min.js"></script>
+</head>
 <body>
-<div>
-    <div class="row">
-        <div class="video-container">
-            <video id="video1" muted autoplay loop controls></video>
-        </div>
-        <div class="video-container">
-            <video id="video2" muted autoplay loop controls></video>
-        </div>
-        <div class="video-container">
-            <video id="video3" muted autoplay loop controls></video>
-        </div>
-        <div class="video-container">
-            <video id="video4" muted autoplay loop controls></video>
-        </div>
-        <div class="video-container">
-            <video id="video5" muted autoplay loop controls></video>
-        </div>
-    </div>
-</div>
-<script th:inline="javascript">
-    var chanMap = new Map();
-    window.onload = function () {
-
-        chanMap.set("video1", "http://127.0.0.1:8889/245/");
-        chanMap.set("video2", "http://127.0.0.1:8889/164/");
-        chanMap.set("video3", "http://127.0.0.1:8889/164/");
-        chanMap.set("video4", "http://127.0.0.1:8889/165/");
-        chanMap.set("video5", "http://127.0.0.1:8889/165/");
-        console.log(chanMap);
-    }
-    const linkToIceServers = (links) => (
-        (links !== null) ? links.split(', ').map((link) => {
-            const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
-            const ret = {
-                urls: [m[1]],
-            };
-
-            if (m[3] !== undefined) {
-                ret.username = unquoteCredential(m[3]);
-                ret.credential = unquoteCredential(m[4]);
-                ret.credentialType = "password";
-            }
-
-            return ret;
-        }) : []
-    );
-    const parseOffer = (offer) => {
-        const ret = {
-            iceUfrag: '',
-            icePwd: '',
-            medias: [],
-        };
-
-        for (const line of offer.split('\r\n')) {
-            if (line.startsWith('m=')) {
-                ret.medias.push(line.slice('m='.length));
-            } else if (ret.iceUfrag === '' && line.startsWith('a=ice-ufrag:')) {
-                ret.iceUfrag = line.slice('a=ice-ufrag:'.length);
-            } else if (ret.icePwd === '' && line.startsWith('a=ice-pwd:')) {
-                ret.icePwd = line.slice('a=ice-pwd:'.length);
-            }
-        }
-
-        return ret;
-    };
-    const generateSdpFragment = (offerData, candidates) => {
-        const candidatesByMedia = {};
-        for (const candidate of candidates) {
-            const mid = candidate.sdpMLineIndex;
-            if (candidatesByMedia[mid] === undefined) {
-                candidatesByMedia[mid] = [];
-            }
-            candidatesByMedia[mid].push(candidate);
-        }
-
-        let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n'
-            + 'a=ice-pwd:' + offerData.icePwd + '\r\n';
-
-        let mid = 0;
-
-        for (const media of offerData.medias) {
-            if (candidatesByMedia[mid] !== undefined) {
-                frag += 'm=' + media + '\r\n'
-                    + 'a=mid:' + mid + '\r\n';
-
-                for (const candidate of candidatesByMedia[mid]) {
-                    frag += 'a=' + candidate.candidate + '\r\n';
-                }
-            }
-            mid++;
-        }
-
-        return frag;
-    }
-
-    class WHEPClient {
-        constructor(wurl, videoId) {
-            this.video = videoId;
-            this.url = new URL('whep', wurl);
-            this.pc = null;
-            this.restartTimeout = null;
-            this.eTag = '';
-            this.queuedCandidates = [];
-            this.start();
-        }
-
-        start() {
-            console.log("requesting ICE servers");
-            fetch(this.url, {
-                method: 'OPTIONS',
-            })
-                .then((res) => this.onIceServers(res))
-                .catch((err) => {
-                    console.log('error: ' + err);
-                    this.scheduleRestart();
-                });
-        }
-
-        onIceServers(res) {
-            this.pc = new RTCPeerConnection({
-                iceServers: linkToIceServers(res.headers.get('Link')),
-            });
-
-            const direction = "sendrecv";
-            this.pc.addTransceiver("video", {direction});
-            this.pc.addTransceiver("audio", {direction});
-
-            this.pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
-            this.pc.oniceconnectionstatechange = () => this.onConnectionState();
-
-            this.pc.ontrack = (evt) => {
-                console.log("new track:", evt.track.kind);
-                document.getElementById(this.video).srcObject = evt.streams[0];
-            };
-
-            this.pc.createOffer()
-                .then((offer) => this.onLocalOffer(offer));
-        }
-
-        onLocalOffer(offer) {
-            this.offerData = parseOffer(offer.sdp);
-            this.pc.setLocalDescription(offer);
-
-            console.log("sending offer");
-
-            fetch(this.url, {
-                method: 'POST',
-                headers: {
-                    'Content-Type': 'application/sdp',
-                },
-                body: offer.sdp,
-            })
-                .then((res) => {
-                    if (res.status !== 201) {
-                        throw new Error('bad status code');
-                    }
-                    this.eTag = res.headers.get('E-Tag');
-                    return res.text();
-                })
-                .then((sdp) => this.onRemoteAnswer(new RTCSessionDescription({
-                    type: 'answer',
-                    sdp,
-                })))
-                .catch((err) => {
-                    console.log('error: ' + err);
-                    this.scheduleRestart();
-                });
-        }
-
-        onConnectionState() {
-            if (this.restartTimeout !== null) {
-                return;
-            }
-
-            console.log("peer connection state:", this.pc.iceConnectionState);
-
-            switch (this.pc.iceConnectionState) {
-                case "disconnected":
-                    this.scheduleRestart();
-            }
-        }
-
-        onRemoteAnswer(answer) {
-            if (this.restartTimeout !== null) {
-                return;
-            }
-
-            this.pc.setRemoteDescription(new RTCSessionDescription(answer));
-
-            if (this.queuedCandidates.length !== 0) {
-                this.sendLocalCandidates(this.queuedCandidates);
-                this.queuedCandidates = [];
-            }
-        }
-
-        onLocalCandidate(evt) {
-            if (this.restartTimeout !== null) {
-                return;
-            }
-
-            if (evt.candidate !== null) {
-                if (this.eTag === '') {
-                    this.queuedCandidates.push(evt.candidate);
-                } else {
-                    this.sendLocalCandidates([evt.candidate])
-                }
-            }
-        }
-
-        sendLocalCandidates(candidates) {
-            fetch(this.url, {
-                method: 'PATCH',
-                headers: {
-                    'Content-Type': 'application/trickle-ice-sdpfrag',
-                    'If-Match': this.eTag,
-                },
-                body: generateSdpFragment(this.offerData, candidates),
-            })
-                .then((res) => {
-                    if (res.status !== 204) {
-                        throw new Error('bad status code');
-                    }
-                })
-                .catch((err) => {
-                    console.log('error: ' + err);
-                    this.scheduleRestart();
-                });
-        }
-
-        scheduleRestart() {
-            if (this.restartTimeout !== null) {
-                return;
-            }
-
-            if (this.pc !== null) {
-                this.pc.close();
-                this.pc = null;
-            }
-
-            this.restartTimeout = window.setTimeout(() => {
-                this.restartTimeout = null;
-                this.start();
-            }, restartPause);
-
-            this.eTag = '';
-            this.queuedCandidates = [];
-        }
-
-        stop() {
-            if (this.pc) {
-                try {
-                    this.pc.close();
-                } catch (e) {
-                    console.log("Failure close peer connection:" + e);
-                }
-                this.pc = null;
-            }
-        }
-    }
-
-    let videoMap = new Map();
-    $('video').click(function (e) {
-        let ID = e.target.id;//鑾峰彇褰撳墠鐐瑰嚮浜嬩欢鐨勫厓绱�
-        console.log(ID);
-        console.log(videoMap);
-        if (videoMap.get(ID) != null) {
-            closeVideo(ID);
-        } else {
-            let stream = chanMap.get(ID);
-            let client = new WHEPClient(stream, ID);
-            videoMap.set(ID, client);
-        }
-    });
-
-    function closeVideo(id) {
-        console.log("鍏抽棴" + id)
-        let client = videoMap.get(id);
-        client.stop(id);
-        videoMap.delete(id);
-    }
+<!--vjs-big-play-centered 鎾斁鎸夐挳灞呬腑-->
+<!--poster榛樿鐨勬樉绀虹晫闈紝灏辨槸杩樻病鐐规挱鏀撅紝缁欎綘鏄剧ず鐨勭晫闈�-->
+<!--controls 瑙勫畾娴忚鍣ㄥ簲璇ヤ负瑙嗛鎻愪緵鎾斁鎺т欢-->
+<!--preload="auto" 鏄惁鎻愬墠鍔犺浇-->
+<!--autoplay 鑷姩鎾斁-->
+<!--loop=true 鑷姩寰幆-->
+<!--data-setup='{"example_option":true}' 鍙互鎶婁竴浜涘睘鎬у啓鍒拌繖涓噷闈㈡潵锛屽data-setup={"autoplay":true}-->
+<video id="my-player"
+       class="video-js vjs-default-skin vjs-big-play-centered" controls
+       preload="auto" autoplay="autoplay"
+       poster="images/logo.png" width="500" height="400"
+       data-setup='{}'>
+    <!--src: 瑙勫畾濯掍綋鏂囦欢鐨� URL  type:瑙勫畾濯掍綋璧勬簮鐨勭被鍨�-->
+    <source src='rtmp://192.168.1.194:1935/164' type='rtmp/flv' />
+</video>
+<script type="text/javascript">
+    // 璁剧疆flash璺緞,鐢ㄤ簬鍦╲ideojs鍙戠幇娴忚鍣ㄤ笉鏀寔HTML5鎾斁鍣ㄧ殑鏃跺�欒嚜鍔ㄥ敜璧穎lash鎾斁鍣�
+    videojs.options.flash.swf = 'https://cdn.bootcss.com/videojs-swf/5.4.1/video-js.swf';
+    //my-player涓洪〉闈ideo鍏冪礌鐨刬d
+    var player = videojs('my-player');
+    //鎾斁
+    player.play();
+    //    1. 鎾斁   player.play()
+    //    2. 鍋滄   player.pause()
+    //    3. 鏆傚仠   player.pause()
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/ard-work/src/main/resources/templates/preview.html b/ard-work/src/main/resources/templates/preview.html
index 63871f9..fd773b0 100644
--- a/ard-work/src/main/resources/templates/preview.html
+++ b/ard-work/src/main/resources/templates/preview.html
@@ -20,7 +20,7 @@
 
         .container {
             background-color: #151414; /* 灏嗙綉鏍奸」鐩殑棰滆壊璁剧疆涓虹孩鑹茶儗鏅� */
-            flex: 9;
+            flex: 30;
             border: 10px solid;
             box-sizing: border-box;
             display: grid;
@@ -68,6 +68,7 @@
             height: 100%;
             object-fit: fill;
         }
+
         #loadingMessage {
             position: absolute;
             top: 50%;
@@ -90,87 +91,23 @@
     <div id="loadingMessage">姝e湪鍙栨祦</div>
     <div class="container2">
         <div class="button-container">
+            <button class="toggle-button" onclick="closeAllVideo()">鍏抽棴</button>
             <button class="toggle-button" onclick="changeGrid(1, 1)">1x1</button>
             <button class="toggle-button" onclick="changeGrid(2, 2)">2x2</button>
             <button class="toggle-button" onclick="changeGrid(3, 3)">3x3</button>
             <button class="toggle-button" onclick="changeGrid(4, 4)">4x4</button>
+            <button class="toggle-button" onclick="changeGrid(5, 5)">5x5</button>
+            <button class="toggle-button" onclick="changeGrid(6, 6)">6x6</button>
+            <button class="toggle-button" onclick="changeGrid(7, 7)">7x7</button>
+            <button class="toggle-button" onclick="changeGrid(8, 8)">8x8</button>
+            <button class="toggle-button" onclick="changeGrid(9, 9)">9x9</button>
+            <input id="videoUrl" type="text" value="http://192.168.1.11:8889/164/" style="width: 250px"/>
         </div>
     </div>
 </div>
 
 <script>
-    function calculateAspectRatio(videoWidth, videoHeight) {
-        return videoWidth / videoHeight;
-    }
-
-    function adjustGridItemSize(gridItem, videoWidth, videoHeight) {
-        const aspectRatio = calculateAspectRatio(videoWidth, videoHeight);
-        gridItem.style.aspectRatio = aspectRatio; /* 璁剧疆瀹介珮姣� */
-    }
-
-    function changeGrid(rows, cols) {
-        closeAllVideo();
-        const gridContainer = document.getElementById('gridContainer');
-        gridContainer.innerHTML = '';
-
-        for (let i = 1; i <= rows * cols; i++) {
-            const gridItem = document.createElement('div');
-            gridItem.className = 'grid-item';
-            const videoContainer = document.createElement('div');
-            videoContainer.className = 'video-container';
-            const video = document.createElement('video');
-            video.id = "video" + i;
-            video.controls = true;
-            video.autoplay = true;
-            video.muted = true;
-            video.loop= true;
-
-            videoContainer.appendChild(video);
-            gridItem.appendChild(videoContainer);
-            gridContainer.appendChild(gridItem);
-
-            video.addEventListener('loadedmetadata', function () {
-                adjustGridItemSize(gridItem, gridItem.videoWidth, gridItem.videoHeight);
-            });
-            video.addEventListener("click", function () {
-                loadingMessage.style.display = "block";
-                video.play().then(function () {
-                    loadingMessage.style.display = "none";
-                }).catch(function (error) {
-                    console.error("Error playing the video:", error);
-                    loadingMessage.style.display = "none";
-                });
-            });
-            console.log(video.id)
-        }
-
-        gridContainer.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
-    }
-
-
-    let mediamtxHost = "192.168.1.227"
-    var chanMap = new Map();
-    window.onload = function () {
-        changeGrid(2, 2);
-        chanMap.set("video1", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video2", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video3", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video4", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video5", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video6", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video7", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video8", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video9", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video10", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video11", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video12", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video13", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video14", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video15", "http://" + mediamtxHost + ":8889/164/");
-        chanMap.set("video16", "http://" + mediamtxHost + ":8889/164/");
-
-        console.log(chanMap);
-    }
+    //whep鎿嶄綔鏂规硶
     const linkToIceServers = (links) => (
         (links !== null) ? links.split(', ').map((link) => {
             const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
@@ -401,36 +338,104 @@
         }
     }
 
-    let videoMap = new Map();
+    //鍒濆鍖栧姞杞�
+    var chanMap = new Map();
+    window.onload = function () {
+        let videoUrl = $("#videoUrl").val();
+        for (let i = 1; i < 82; i++) {
+            chanMap.set("video" + i, videoUrl);
+        }
+        console.log(chanMap);
+        changeGrid(2, 2);
+    }
+    //缁戝畾鐐瑰嚮浜嬩欢
+    let playMap = new Map();
     $(document).on('click', 'video', function () {
         let ID = this.id;//鑾峰彇褰撳墠鐐瑰嚮浜嬩欢鐨勫厓绱�
         console.log(ID);
-        console.log(videoMap);
-        if (videoMap.get(ID) != null) {
+        console.log(playMap);
+        if (playMap.get(ID) != null) {
             closeVideo(ID);
         } else {
             let stream = chanMap.get(ID);
             let client = new WHEPClient(stream, ID);
-            videoMap.set(ID, client);
+            playMap.set(ID, client);
         }
     });
 
+    //鍏抽棴涓�涓獀ideo
     function closeVideo(id) {
         console.log("鍏抽棴" + id)
-        let client = videoMap.get(id);
+        let client = playMap.get(id);
         client.stop(id);
-        videoMap.delete(id);
+        playMap.delete(id);
     }
 
+    //鍏抽棴鎵�鏈塿ideo
     function closeAllVideo() {
-        videoMap.forEach((val, key) => {
-            console.log(val, key);
+        playMap.forEach((val, key) => {
             val.stop(key);
-            videoMap.delete(key);
+            closeVideo(key);
+            playMap.delete(key);
         })
     }
 
+    //鍔ㄦ�佹敼鍙榞rid
+    function calculateAspectRatio(videoWidth, videoHeight) {
+        return videoWidth / videoHeight;
+    }
 
+    function adjustGridItemSize(gridItem, videoWidth, videoHeight) {
+        const aspectRatio = calculateAspectRatio(videoWidth, videoHeight);
+        gridItem.style.aspectRatio = aspectRatio; /* 璁剧疆瀹介珮姣� */
+    }
+
+    function changeGrid(rows, cols) {
+        closeAllVideo();
+        let num = rows * cols;
+        let videoUrl = $("#videoUrl").val();
+        for (let i = 1; i < num + 1; i++) {
+            chanMap.set("video" + i, videoUrl);
+        }
+        const gridContainer = document.getElementById('gridContainer');
+        gridContainer.innerHTML = '';
+
+        for (let i = 1; i <= rows * cols; i++) {
+            const gridItem = document.createElement('div');
+            gridItem.className = 'grid-item';
+            const videoContainer = document.createElement('div');
+            videoContainer.className = 'video-container';
+            const video = document.createElement('video');
+            video.id = "video" + i;
+            video.controls = true;
+            video.autoplay = true;
+            video.muted = true;
+            video.loop = true;
+
+            videoContainer.appendChild(video);
+            gridItem.appendChild(videoContainer);
+            gridContainer.appendChild(gridItem);
+
+            video.addEventListener('loadedmetadata', function () {
+                adjustGridItemSize(gridItem, gridItem.videoWidth, gridItem.videoHeight);
+            });
+            video.addEventListener("click", function () {
+                loadingMessage.style.display = "block";
+                video.play().then(function () {
+                    loadingMessage.style.display = "none";
+                }).catch(function (error) {
+                    console.error("Error playing the video:", error);
+                    loadingMessage.style.display = "none";
+                });
+            });
+            console.log(video.id)
+            let stream = chanMap.get(video.id);
+            let client = new WHEPClient(stream, video.id);
+            playMap.set(video.id, client);
+        }
+
+        gridContainer.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
+    }
 </script>
 </body>
 </html>
diff --git a/lib/mediamtx/mediamtx.yml b/lib/mediamtx/mediamtx.yml
index df30797..9dfc5b5 100644
--- a/lib/mediamtx/mediamtx.yml
+++ b/lib/mediamtx/mediamtx.yml
@@ -15,7 +15,7 @@
 writeTimeout: 10s
 # Size of the queue of outgoing packets.
 # A higher value allows to increase throughput, a lower value allows to save RAM.
-writeQueueSize: 8192
+writeQueueSize: 512
 # Maximum size of outgoing UDP packets.
 # This can be decreased to avoid fragmentation on networks with a low UDP MTU.
 udpMaxPayloadSize: 1472

--
Gitblit v1.9.3