0

我需要帮助在 php 平台内设置一个简单的视频通话网络应用程序。但我有几个问题。我遵循了几个教程来实现这一点,但我仍然无法做到。

我需要实现什么?我需要为 2 个摄像头(用户)设置几个视频通话(甚至同时),每个摄像头(用户)都位于由 wordpress 的 php 页面设置的单独“房间”中。

例子:

在“www.mywebsite.com/interviews/my_specific_name_room/”中,视频通话中只有 2 个用户。在“www.mywebsite.com/visits/my_other_specific_name_room/”中,视频通话中只有 2 个用户。等等我已经做了什么?我有一个带有 apache php mysql 和 nodejs 的 VPS。视频通话在 https 协议下工作。我遵循的教程让我设置“https://www.mywebsite.com:3030/”并进行视频通话。

但是我的问题是什么?问题是有时视频通话有效,有时无效。我不知道为什么。当我从笔记本电脑和手机访问“https://www.mywebsite.com:3030/”时,视频通话几乎总是在这些设备之间进行。即使我同一个城市的朋友访问“https://www.mywebsite.com:3030/”并且我也这样做,视频通话也能正常工作。但是对于城里的其他朋友,以及我国家其他地方的每一位朋友,视频通话永远不会奏效。我们只看我们自己的视频。真的我不知道为什么。我尝试了一些解决方案,但我不太了解 nodeJS。

我的服务器代码:

const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();

const options = {
    key: fs.readFileSync('/etc/letsencrypt/live/mywebsite.com/privkey.pem'),
    cert: fs.readFileSync('/etc/letsencrypt/live/mywebsite.com/fullchain.pem'),
    requestCert: false,
    rejectUnauthorized: false
};

const server = https.createServer(options, app).listen(3030, function () {
    console.log("Servidor iniciado en el puerto 3030");
});

const { ExpressPeerServer } = require('peer');
const peerServer = ExpressPeerServer(server, {
    debug: true
});

app.use('/peerjs', peerServer);
app.get('/', (req, res) => {
    res.sendFile(__dirname + '/www/index.html');
});

const io = require('socket.io')(server, {
    serveClient: false,
    // below are engine.IO options
    origins: '*:*',
    transports: ['polling'],
    pingInterval: 10000,
    pingTimeout: 5000,
    cookie: false
});
io.on('connection', socket => {
    socket.on('the-interview', (data) => {
        socket.join(data.room);
        socket.to(data.room).broadcast.emit('user-connected', data.id)
        console.log(data.id)
    })
}) 

我的代码来自 www/index.html

<!DOCTYPE html>
<html lang="es">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interviews Mywebsite</title>
    <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/MaterialDesign-Webfont/5.6.55/css/materialdesignicons.min.css">
    <style>
        #videogrid {
            width: 100%;
            display: grid;
        }

        #videowrap {
            max-width: 100%;
        }

        #losvideos {
            overflow: hidden;
            position: relative;
            padding-top: 56.25%;
            max-width: 900px;
        }

        video {
            width: 100%;
            height: 100%;
        }

        #conttu {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            transition: all 0.5s ease;
        }

        #contyo {
            position: absolute;
            bottom: 10px;
            right: 10px;
            width: 200px;
            cursor: pointer;
            transition: all 0.5s ease;
        }

        .watermark {
            position: absolute;
            top: 10px;
            left: 40px;
            z-index: 1;
            opacity: 0.3;
            transform: scale3d(1.3, 1.3, 1.3);
        }

        .menu {
            position: absolute;
            height: 120px;
            bottom: -120px;
            transition: all 0.7s ease;
            background-color: rgba(0, 0, 0, 0.5);
            width: 60%;
            left: 50%;
            margin-left: -30%;
            border-radius: 70px 70px 0 0;
            z-index: 1;
            display: grid;
            align-items: center;
            grid-auto-flow: column;
            justify-content: center;
            gap: 30px;
        }

        #losvideos:hover .menu {
            bottom: 0px;
            transition: all 0.7s ease;
        }

        .menu .icono {
            width: 80px;
            height: 80px;
            background-color: #5d59b5;
            border-radius: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
        }

        .menu .icono:hover {
            background-color: #383485;
        }

        .menu .icono:active {
            background-color: #24206d;
        }

        .menu .icono i {
            font-size: 50px;
            color: #fff;
        }

        .menu #nofull {
            display: none;
        }

        .tooltip {
            position: relative;
            display: inline-block;
        }

        .tooltip .tooltiptext {
            visibility: hidden;
            width: 120px;
            background-color: #fff;
            color: #900;
            text-align: center;
            border-radius: 6px;
            padding: 5px;
            position: absolute;
            z-index: 1;
            bottom: 110%;
            left: 50%;
            margin-left: -65px;
            opacity: 0;
            transition: opacity 0.3s;
        }

        .tooltip .tooltiptext::after {
            content: "";
            position: absolute;
            top: 100%;
            left: 50%;
            margin-left: -5px;
            border-width: 5px;
            border-style: solid;
            border-color: #fff transparent transparent transparent;
        }

        .tooltip:hover .tooltiptext {
            visibility: visible;
            opacity: 1;
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
    <script src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
</head>

<body>
    <h2>Interviews Mywebsite</h2>

    <div id="videogrid">
        <div id="videowrap">
            <div id="losvideos">
                <div class="watermark">
                    <img src="logo.svg" alt="">
                </div>
                <div id="conttu">
                    <video id="video_other" autoplay></video>
                </div>
                <div id="contyo">
                    <video id="my_video" autoplay muted></video>
                </div>
                <div class="menu">
                    <div id="full" class="icono tooltip">
                        <i class="mdi mdi-fullscreen"></i>
                        <span class="tooltiptext">Pantalla completa</span>
                    </div>
                    <div id="silencio" class="icono tooltip">
                        <i class="mdi mdi-microphone-off"></i>
                        <span class="tooltiptext">Silenciar micrófono</span>
                    </div>
                    <div id="stopVideo" class="icono tooltip">
                        <i class="mdi mdi-video-off"></i>
                        <span class="tooltiptext">Apagar mi cámara</span>
                    </div>
                </div>
            </div>
        </div>
    </div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
    let miStream;

    const socket = io()

    var my_video = document.querySelector("#my_video");
    if (navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({ video: true, audio: true })
            .then(function (stream) {
                console.log('Showing my own video')
                my_video.srcObject = stream;
                miStream = stream;
                peer.on('call', call => {
                    call.answer(stream)
                    const video_other = document.getElementById('video_other');
                    call.on('stream', userVideoStream => {
                        console.log('Showing other user's video 1')
                        video_other.srcObject = userVideoStream;
                    })
                })
                socket.on('user-connected', userID => {
                    connectToNewUser(userID, stream)
                });
            })
            .catch(function (error) {
                console.log("Something went wrong!");
            });
    }

    var peer = new Peer(undefined, {
        path: '/peerjs',
        host: '/',
        port: '3030'
    });     

    var room = '12345'; //room name, set by php

    // Send interviews data to server
    peer.on('open', id => {
        socket.emit('the-interview', {
            room: room,
            id: id
        });
    })

    // Function for send our ID and video to the other user
    const connectToNewUser = (userID, stream) => {
        const call = peer.call(userID, stream);
        const video_other = document.getElementById('video_other');
        call.on('stream', userVideoStream => {
            console.log('Showing other user's video 2')
            video_other.srcObject = userVideoStream;
        })
    }


    // other "work in progress" stuff for video call UI 

    // funcion para el silencio
    function silencio() {
        const enabled = miStream.getAudioTracks()[0].enabled;
        if (enabled) {
            miStream.getAudioTracks()[0].enabled = false;
            $('#silencio i').removeClass('mdi-microphone-off').addClass('mdi-microphone');
        } else {
            $('#silencio i').removeClass('mdi-microphone').addClass('mdi-microphone-off');
            miStream.getAudioTracks()[0].enabled = true;
        }
    }
    $(document).on('click', '#silencio', function () {
        silencio();
    });

    // funcion para apagar cámara
    function stopVideo() {
        const enabled = miStream.getVideoTracks()[0].enabled;
        if (enabled) {
            miStream.getVideoTracks()[0].enabled = false;
            $('#stopVideo i').removeClass('mdi-video-off').addClass('mdi-video');
        } else {
            $('#stopVideo i').removeClass('mdi-video').addClass('mdi-video-off');
            miStream.getVideoTracks()[0].enabled = true;
        }
    }

    $(document).on('click', '#stopVideo', function () {
        stopVideo();
    });

    // Pantalla completa
    $(document).on('click', '#full', function () {
        if ($(this).hasClass('activado')) {
            exitFullScreen();
        } else {
            toggleFullScreen();
        }
    })

    function toggleFullScreen() {
        var losvideos = document.getElementById("losvideos");
        if (losvideos.requestFullscreen)
            if (document.fullScreenElement) {
                document.cancelFullScreen();
            } else {
                losvideos.requestFullscreen();
            }
        else if (losvideos.msRequestFullscreen)
            if (document.msFullscreenElement) {
                document.msExitFullscreen();
            } else {
                losvideos.msRequestFullscreen();
            }
        else if (losvideos.mozRequestFullScreen)
            if (document.mozFullScreenElement) {
                document.mozCancelFullScreen();
            } else {
                losvideos.mozRequestFullScreen();
            }
        else if (losvideos.webkitRequestFullscreen)
            if (document.webkitFullscreenElement) {
                document.webkitCancelFullScreen();
            } else {
                losvideos.webkitRequestFullscreen();
            }
        else {
            alert("Fullscreen API is not supported");
        }
    }

    $('#losvideos').bind('webkitfullscreenchange mozfullscreenchange fullscreenchange', function (e) {
        var state = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen;
        var event = state ? 'FullscreenOn' : 'FullscreenOff';

        $('#full i').removeClass('mdi-fullscreen-exit').addClass('mdi-fullscreen');
        $('#full').removeClass('activado');

        if (event == "FullscreenOn") {
            $('#full i').removeClass('mdi-fullscreen').addClass('mdi-fullscreen-exit');
            $('#full').addClass('activado');
        }
    });

    function exitFullScreen() {
        if (document.exitFullscreen) {
            document.exitFullscreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.webkitExitFullscreen) {
            document.webkitExitFullscreen();
        }
    }

    $(document).on('click', '#contyo', function () {
        if ($(this).hasClass('big')) {
            $('#contyo').removeClass('big').attr('style', false)
            $('#conttu').attr('style', false)
        } else {
            $('#conttu').css({ 'width': '250px', 'z-index': '1', 'right': '10px', 'bottom': '10px', 'top': 'unset', 'left': 'unset' })
            $('#contyo').css({ 'width': '100%', 'z-index': '0', 'right': '0', 'bottom': '-4px' }).addClass('big')
        }
    })

</script>
</body>
</html>

我不知道如何在这段代码中实现 TURN 和 STUN 的东西。

拜托,我真的需要帮助。在提出问题之前,我总是到处搜索。对我来说,能够在不使用第三方服务的情况下编写程序来进行视频通话已经是一个巨大的成功。

但我现在的首要任务是让视频通话始终正常工作,无论浏览器、位置如何……什么都没有。如果有人可以提供一个建议来实现我的另一个目标,“房间”的事情,我会非常感激。

4

1 回答 1

0

我不知道你的项目有多大。然而,对于我的小项目,这个解决方案效果很好。您只需在https://xirsys.com/注册一个帐户并注册免费的 TURN 服务器服务。之后,在客户端代码中,在 Peer initiation 中,将 peer 构造函数的配置添加到 iceserver。

var peer = new Peer(undefined, {
    path: '/peerjs',
    host: '/',
    port: '3030',
    config: {'iceServers':.......}
});     

希望我的回答对你有所帮助。

于 2021-08-20T09:41:08.320 回答