使用 peerjs socket.io nodeJS 进行视频通话有时才有效

Posted

技术标签:

【中文标题】使用 peerjs socket.io nodeJS 进行视频通话有时才有效【英文标题】:Video call with peerjs socket.io nodeJS only works sometimes 【发布时间】:2021-10-19 19:05:32 【问题描述】:

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

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

例子:

在“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" >
                </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 的东西。

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

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

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

以上是关于使用 peerjs socket.io nodeJS 进行视频通话有时才有效的主要内容,如果未能解决你的问题,请参考以下文章

NodeJs Socket.io Rooms

使用 Socket.IO 和 NodeJS 实现音频聊天

NodeJs Socket.io 房间

使用 Nodejs 和 Socket io

验证 socket.io/nodejs 的用户

我是不是破坏了在我的 AJAX 脚本中使用 NodeJS / socket.io 的好处?