<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

    <title>webchat即时在线聊天室,视频通话</title>

    <link rel="stylesheet" href="/css/common/common.css">
    <link rel="stylesheet" href="/css/client/chat.css">
    <link href="/ref/layui-v2.6.8/layui/css/layui.css" rel="stylesheet" type="text/css" />

    <script src="/ref/jquery/jquery-3.4.1.js" type="text/javascript"></script>
    <script src="/ref/layui-v2.6.8/layui/layui.js" type="text/javascript"></script>

    <style>
        #hangUpBtn {
            position: absolute;
            bottom: 80px;
            width: 50px;
            height: 50px;
            border: none;
            border-radius: 100px;
            background-color: #fb5c5c;
            box-shadow: 0px 0px 10px #fb8d8d;
        }
        #videos {
            position: absolute;
            left: 0px;
            top: 0px;
            width: 100%;
            height: 100%;
            background-color: whitesmoke;
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
            grid-gap: 1px; /* 根据需要调整网格间距 */
        }
        #videos div {
            position: relative;
            width: 299px; /* 视频宽度自适应 */
            height: 224px; /* 保持视频宽高比 */
            background-color: black;
            border-radius: 1px;
            float: left;
            border: none;
            background-color: black;
        }
        #videos div video {
            position: relative;
            width: 299px; /* 视频宽度自适应 */
            height: 224px; /* 保持视频宽高比 */
            background-color: black;
            border-radius: 1px;
            float: left;
            border: none;
        }
        #videos div span {
            position: absolute;
            top: 15px;
            left: 15px;
            height: 20px;
            line-height: 20px;
            background-color: rgba(0, 149, 255, 0.7);
            padding: 3px 10px;
            border-radius: 100px;
            color: white;
            font-size: 13px;
        }
        #videos div span.other {
            background-color: rgba(0, 0, 0, 0.7);
        }
    </style>
</head>
<body>
<center>
    <div id = "videos"></div>
    <div class = "row text-center">
        <div class = "col-md-12">
            <button id = "hangUpBtn" class="btn-danger btn">
                <svg t="1730296252524" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4292" width="30" height="30"><path d="M115.2 115.2c19.2-32 44.8-64 76.8-83.2 32-19.2 70.4-32 115.2-32H320c70.4 0 134.4 76.8 153.6 198.4 12.8 57.6 6.4 121.6-19.2 160-12.8 32-38.4 51.2-70.4 51.2l-12.8 12.8v12.8c19.2 38.4 38.4 76.8 64 108.8 19.2 32 44.8 64 70.4 96 6.4 6.4 12.8 6.4 19.2 6.4h6.4c12.8-19.2 32-38.4 57.6-44.8 44.8-6.4 115.2 12.8 179.2 57.6 96 70.4 140.8 166.4 108.8 230.4l-6.4 6.4c-19.2 38.4-44.8 70.4-83.2 89.6-160 96-409.6-38.4-576-307.2-128-198.4-166.4-428.8-96-563.2z m288 217.6c12.8-32 19.2-76.8 12.8-128-12.8-83.2-64-147.2-96-147.2h-12.8c-32 0-64 6.4-89.6 25.6-19.2 12.8-38.4 32-51.2 57.6-64 115.2-25.6 326.4 89.6 512 147.2 230.4 371.2 364.8 499.2 288 25.6-12.8 44.8-38.4 57.6-70.4 0 0 0-6.4 6.4-6.4v-6.4c19.2-32-19.2-102.4-89.6-153.6-44.8-32-102.4-51.2-134.4-44.8-12.8 6.4-19.2 12.8-19.2 19.2 0 0-12.8 25.6-44.8 32-19.2 6.4-44.8-6.4-64-25.6l-6.4-6.4c-25.6-32-51.2-64-76.8-102.4-25.6-38.4-44.8-83.2-64-121.6V448c-6.4-19.2-6.4-38.4 0-57.6 12.8-25.6 44.8-38.4 44.8-38.4h6.4c19.2 0 25.6-12.8 32-19.2z m217.6 12.8c6.4-12.8 19.2-19.2 32-12.8 32 6.4 51.2 25.6 64 51.2 12.8 25.6 12.8 51.2 6.4 76.8-6.4 12.8-19.2 25.6-38.4 19.2-12.8-6.4-25.6-19.2-19.2-38.4 6.4-12.8 0-25.6 0-32-6.4-12.8-19.2-19.2-25.6-25.6-19.2 0-25.6-19.2-19.2-38.4zM672 256c6.4-12.8 19.2-19.2 32-12.8 96 32 147.2 134.4 115.2 230.4-6.4 12.8-19.2 25.6-38.4 19.2-12.8-6.4-25.6-19.2-19.2-38.4 19.2-64-12.8-134.4-76.8-153.6-12.8-6.4-19.2-25.6-12.8-44.8 0 6.4 0 0 0 0z m25.6-115.2c6.4-12.8 19.2-19.2 32-12.8 76.8 25.6 140.8 83.2 179.2 153.6 38.4 76.8 44.8 160 19.2 236.8-6.4 12.8-19.2 25.6-38.4 19.2-12.8-6.4-25.6-19.2-19.2-38.4 19.2-64 19.2-134.4-12.8-192-32-57.6-83.2-102.4-147.2-128-12.8 0-19.2-19.2-12.8-38.4 0 6.4 0 0 0 0z m0 0" p-id="4293" fill="#ffffff"></path></svg>
            </button>
        </div>
    </div>
</center>
</body>
<script src="/js/client/video.chat.js" type="text/javascript"></script>
<script type="text/javascript">
    var wsHost = document.domain;
    var wsPort = 8101;
    function getUserParamByName(key) {
        var url = window.location.search;
        var reg = new RegExp("(^|&)" + key + "=([^&]*)(&|$)");
        var result = url.substr(1).match(reg);
        return result ? decodeURIComponent(result[2]) : "";
    }
    var userId = getUserParamByName("userId");
    var userName = getUserParamByName("userName");
    var targetUserId = getUserParamByName("targetUserId");
    var connectedUserIds = new Array();
    var onlineUserMap;
    var videos = document.querySelector("#videos");
    // RTCPeerConnection管理
    const peerConnections = {};
    var configuration = {
        "iceServers": [
            {
                "urls": "stun:stun.l.google.com:19302"
            }
        ]
    };
    var PeerConnection = (window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCPeerConnection || undefined);
    var RTCSessionDescription = (window.webkitRTCSessionDescription || window.mozRTCSessionDescription || window.RTCSessionDescription || undefined);
    navigator.getUserMedia = (navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia);

    // 初始化WebSocket连接对象，用于信令服务器通信
    let localStream;
    var conn;
    function initSignalServerConnection() {
        conn = new WebSocket("ws://" + wsHost + ":" + wsPort + "/ws/group-video-chat/" + targetUserId + "/" + userId);
        conn.onopen = function () {
            console.log("Connected to the signaling server");
            send({
                type: "call",
                userId: userId,
                targetUserId: targetUserId,
                groupId: targetUserId
            })
        };
        conn.onmessage = function (msg) {
            var data = JSON.parse(msg.data);
            console.log("Got message =====> ", data);
            switch (data.type) {
                case "online":
                    handleOnline(data);
                    break;
                case "offline":
                    handleOffline(data);
                    break;
                case "offer":
                    handleOffer(data.offer, data.userId);
                    break;
                case "answer":
                    handleAnswer(data.answer, data.userId);
                    break;
                case "candidate":
                    handleCandidate(data.candidate, data.userId);
                    break;
                case "leave":
                    handleLeave(data.userId);
                    break;
                default:
                    break;
            }
        };
        conn.onerror = function (err) {
            console.log("Got error", err);
            initSignalServerConnection();
        };
        conn.onclose = function (err) {
            initSignalServerConnection();
        }
    }

    startMedia();
    function startMedia() {
        // 初始化本地视频流，并绑定到页面当前客户端视频标签上
        navigator.getUserMedia({ video: true, audio: true }, function (stream) {
            localStream = stream;
            // 创建视频元素并绑定到页面上
            var videoContainer = document.createElement('div');
            var span = document.createElement('span');
            var video = document.createElement('video');
            video.autoplay = true;
            video.muted = true;
            // 将本地流绑定到本地视频元素
            video.srcObject = localStream;
            span.innerText = userName;
            videoContainer.id = "video-" + userId;
            videoContainer.appendChild(video);
            videoContainer.appendChild(span);
            videos.appendChild(videoContainer);
            initSignalServerConnection();
        }, function (error) {
            console.log(error);
        });
    }

    function send(message) {
        conn.send(JSON.stringify(message));
    }

    function getPeerId(uid) {
        let peerKeyArr = [userId, uid];
        return peerKeyArr.sort().join('-');
    }

    function getPeerConnection(uid) {
        let peerKey = getPeerId(uid);
        if (peerConnections[peerKey]) {
            return peerConnections[peerKey];
        }
        var peerConnection = new PeerConnection(configuration);
        peerConnections[peerKey] = peerConnection;
        return peerConnection;
    }

    function handleOnline(message) {
        onlineUserMap = new Map(Object.entries(message.onlineUserMap));
        for (let i = 0; i < message.userIds.length; i++) {
            var uid = message.userIds[i];
            if (connectedUserIds.includes(uid)) {
                continue;
            }
            connectedUserIds.push(uid);
            if (uid == userId) {
                continue;
            }
            var peerConnection = getPeerConnection(uid);
            var videoContainer = document.createElement('div');
            var span = document.createElement('span');
            var video = document.createElement('video');
            video.autoplay = true;
            video.muted = true;
            video.controls = false;
            span.innerText = onlineUserMap.get(uid).userName;
            span.className = "other";
            videoContainer.id = "video-" + uid;
            // 将视频元素添加到页面上
            videoContainer.appendChild(video);
            videoContainer.appendChild(span);
            videos.appendChild(videoContainer);
            peerConnection.addStream(localStream);
            peerConnection.onaddstream = function(event){
                video.srcObject = event.stream;
            };
            peerConnection.onicecandidate = function (event) {
                if (event.candidate) {
                    send({
                        type: "candidate",
                        candidate: event.candidate,
                        userId: userId,
                        targetUserId: uid,
                        groupId: targetUserId
                    });
                }
            };
            // 创建一个offer描述
            peerConnection.createOffer(function (offer) {
                send({
                    type: "offer",
                    offer: offer,
                    userId: userId,
                    targetUserId: uid,
                    groupId: targetUserId
                });
                peerConnection.setLocalDescription(offer);
            }, function (error) {
                alert("Error when creating an offer");
            });
        }
    }

    function handleOffline(message) {
        // 获取下线用户
        var offlineUserId = message.offlineUserId;
        var offlineUserName = onlineUserMap.get(offlineUserId).userName;
        var offlineVideo = document.getElementById("video-"+offlineUserId);
        if (offlineVideo) {
            offlineVideo.remove();
            layer.msg(offlineUserName + "退出会议！");
        }
        getPeerConnection(offlineUserId).close();
        peerConnections[getPeerId(offlineUserId)] = null;
        connectedUserIds = connectedUserIds.filter(function (uid) { return uid !== offlineUserId; });
    }

    function handleOffer(offer, from) {
        var peerConnection = getPeerConnection(from);
        peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
        peerConnection.createAnswer(function (answer) {
            peerConnection.setLocalDescription(answer);
            send({
                type: "answer",
                answer: answer,
                userId: userId,
                targetUserId: from,
                groupId: targetUserId
            });
        }, function (error) {
            alert("Error when creating an answer");
        });
    }

    function handleAnswer(answer, from) {
        var peerConnection = getPeerConnection(from);
        peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
    }

    // 处理收到的ICE候选
    function handleCandidate(candidate, from) {
        var peerConnection = getPeerConnection(from);
        peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
    }

    function handleLeave(from) {
        var offlineUserName = onlineUserMap.get(from).userName;
        layer.msg(offlineUserName + "退出会议！");
        document.getElementById("video-" + from).remove();
        getPeerConnection(from).close();
        peerConnections[getPeerId(from)] = null;
        connectedUserIds = connectedUserIds.filter(function (uid) { return uid !== from; });
    }

    hangUpBtn.addEventListener("click", function () {
        connectedUserIds.forEach(function (user) {
            send({
                type: "leave",
                userId: userId,
                targetUserId: targetUserId,
                groupId: targetUserId
            });
            handleLeave(user);
            // 在iframe页面中执行以下代码
            var index = parent.layer.getFrameIndex(window.name); // 获取当前iframe层的索引
            parent.layer.close(index); // 关闭父级弹窗
        });
    });
</script>
</html>