【问题标题】:webrtc video chat doesn't work when 3rd person joins the chat当第三个人加入聊天时,webrtc 视频聊天不起作用
【发布时间】:2021-09-28 03:09:26
【问题描述】:

我第一次尝试 webrtc 的视频聊天应用程序,我希望每次聊天最多 3 人...我的代码适用于 2 人聊天

但是一旦第三个人加入聊天,一切都会出错......我在页面中获得了多个视频标签,但没有一个来自第三个梨...... 2人聊天

这是工作网址

https://chate-test-3000.herokuapp.com/

这是我的代码

const PEARS = [];
var video_counter = 0 ;
const STREAMES = [] ;

var myVideoArea = document.querySelector('#myvideo');

var configuration = {
    'iceServers': [{
        'url': 'stun:stun.l.google.com:19302'
    }]
};
var rtcPeerConn;

const ROOM = 'caht1';
const SIGNAL_ROOM = 'newsingal1234567898765';

io = io.connect("" ,  {transports:['websocket']});
io.emit('ready' , { chat_room : ROOM , signaling_room : SIGNAL_ROOM});


io.emit('signal' , { text :'ready for video ? ' , room : SIGNAL_ROOM , type : 'user_here'});
io.on('signlaing_message' , function(data){

   console.log('signal recived');
   console.log(data);

  if(!PEARS.includes(data.pear_id))
  {
    console.log('adding new pear --- ' , data.pear_id);
    PEARS.push(data.pear_id);
    startSignaling(data.pear_id);
  }

  if (data.type != "user_here")
  {
        var message = JSON.parse(data.message);
        if (message.sdp) {
            rtcPeerConn.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
                // if we received an offer, we need to answer
                if (rtcPeerConn.remoteDescription.type == 'offer') {
                    rtcPeerConn.createAnswer(sendLocalDesc, logError);
                }
            }, logError);
        }
        else {
            rtcPeerConn.addIceCandidate(new RTCIceCandidate(message.candidate));
        }
  }

})


function startSignaling(pear_id) {


    if(!rtcPeerConn)
    rtcPeerConn = new RTCPeerConnection(configuration);

    // send any ice candidates to the other peer
    rtcPeerConn.onicecandidate = function (evt) {
        if (evt.candidate)
            io.emit('signal',{"type":"ice candidate", "message": JSON.stringify({ 'candidate': evt.candidate }), "room":SIGNAL_ROOM});
        displaySignalMessage("completed that ice candidate...");
    };

    // let the 'negotiationneeded' event trigger offer generation
    rtcPeerConn.onnegotiationneeded = function () {
        displaySignalMessage("on negotiation called");
        rtcPeerConn.createOffer(sendLocalDesc, logError);
    }

    // once remote stream arrives, show it in the remote video element
    rtcPeerConn.ontrack = function (evt) {
        displaySignalMessage("going to add their stream...");

        video_counter++ ;
        let vid = 'video-box-'+video_counter  ;
        console.log('adding new STREAM  !!')
        console.log('###### streams  ' , evt.streams);

        if(!STREAMES.includes(evt.streams[0].id))
        {
            STREAMES.push(evt.streams[0].id);
            $('#video-wrapper').append(`<video data-id="${evt.streams[0].id}" id="${vid}" autoplay loop autobuffer muted playsinline controls></video>`);
            console.log(' video length ..... ' , $('#video-wrapper').find('#'+vid).length );
            var theirVideoArea = $('#video-wrapper').find('#'+vid)[0];
            console.log(theirVideoArea);
            theirVideoArea.srcObject = evt.streams[0] ;
            theirVideoArea.play();
        }
        
    };

    // get a local stream, show it in our video tag and add it to be sent
        navigator.getUserMedia = navigator.getUserMedia  || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        navigator.getUserMedia({
            'audio': true,
            'video': true
        }, function (stream) {
            displaySignalMessage("going to display my stream...");

            myVideoArea.srcObject = stream
            myVideoArea.play();

            for (const track of stream.getTracks()) {
            rtcPeerConn.addTrack(track, stream);
            }

        }, logError);

}


function sendLocalDesc(desc) {
    rtcPeerConn.setLocalDescription(desc, function () {
        displaySignalMessage("sending local description");
        io.emit('signal',{"type":"SDP", "message": JSON.stringify({ 'sdp': rtcPeerConn.localDescription }), "room":SIGNAL_ROOM});
    }, logError);
}

function logError(error) {
    $('#error-area').append(`<div> ${error.name} : ${error.message}</div>`);
}

function displaySignalMessage(text  ){
    $('#signal-area').append(`<div>${text}</div>`);
}

我还使用一个简单的 nodejs 服务器来发送信号,并使用 socket.io 连接到服务器

---------------------- 编辑 - PEER.JS -----------------

这是我切换到 peerjs 后的代码

const SIGNAL_ROOM = 'zxsingalroom';
var MY_PEER_ID = '' ;
const CurrentPeers = [] ;
io = io.connect("" ,  {transports:['websocket']});
io.emit('ready' , { chat_room : ROOM , signaling_room : SIGNAL_ROOM});



var peer = new Peer({
    config: {'iceServers': [
            { url: 'stun:stun.l.google.com:19302' },
        ]} /* Sample servers, please use appropriate ones */
});

peer.on('open', function(id) {
    console.log('My peer ID is: ' + id);
    MY_PEER_ID = id ;
    io.emit('peer_id_offer' , {  chat_room  : ROOM , id : id});
});

peer.on('call' , function (call) {


    navigator.mediaDevices.getUserMedia({ video : true , audio : true })
        .then((stream) => {

            call.answer(stream);
            call.on('stream' , function(remoteStream){
                if(!CurrentPeers.includes(call.peer))
                {
                    CurrentPeers.push(call.peer);
                    addRemoteVideo(remoteStream);
                }

            })


        })
        .catch( (e)=>{
            console.log('error2' , e );
        });

})


io.on('peer_id_recived' , function(data){

    console.log(`peer id recived : `);
    console.log(data);


    for (let [key, value] of Object.entries(data.peer_ids)) {
        if(value.peer_id != MY_PEER_ID)
        {
            callPeer(value.peer_id);
        }
    }

});


function callPeer( id )
{
        console.log('calling peers 1 .... ');
        navigator.mediaDevices.getUserMedia({ video : true , audio : true })
        .then(  (stream) => {
            console.log('calling peers  2 .... ' + id);
            addOurVideo(stream);

            let  call = peer.call(id , stream);
            console.log( typeof call);
            call.on('stream' , function(remoteStream){
                console.log('calling peers  3 .... ');
                if(!CurrentPeers.includes(call.peer))
                {
                    CurrentPeers.push(call.peer);
                    addRemoteVideo(remoteStream);
                }

            })
        })
        .catch( (e)=>{
            console.log('error1' , e );
        });
}

function addRemoteVideo(stream){

    console.log(' adding remote stream!!!');
    let total_perrs = CurrentPeers.length ;
    let vid = `video-box-${total_perrs}`;

   $('#video-wrapper').append(`<video  id="${vid}" autoplay loop autobuffer muted playsinline controls></video>`);
    var theirVideoArea = $('#video-wrapper').find('#'+vid)[0];
   theirVideoArea.srcObject = stream ;
   theirVideoArea.play();

}
function addOurVideo(stream){

    console.log(' adding our stream');
    var ourVideArea = $('#video-wrapper').find('#our-video')[0];
    ourVideArea.srcObject = stream ;
    ourVideArea.play();

}

【问题讨论】:

  • 你在使用 peerJS 吗?如果是这样,当新用户进入聊天室时,您如何管理呼叫和响应?您是否在用户连接上发送 videoStream 对象?当您在连接后从其他对等方收到 videoStream 对象时,您是将其附加到视频网格还是只是再次附加自己的视频?
  • 您是否遇到任何错误?尝试console.log(CurrentPeers) 看看是否建立了连接,如果是这种情况,那么您知道它的前端问题可能与您将视频附加到视频网格的方式有关
  • @ahmedcheikhsidahmed thanx 切换到对等点后它似乎正在工作,但是当连接断开时,我经历了一些视频冻结,我得到了这个WebRTC: ICE failed, add a TURN server and see about:webrtc for more details ...它开始显示后再等几秒钟....但是我认为为了拥有一个好的产品我应该解决一些这样的问题,因为您似乎有使用 webrtc 的经验,您有什么建议或技巧可以与我分享吗?是否需要使用商业服务器才能拥有一个好的应用程序?
  • 酷!很高兴它已修复,我将发布一个答案,回顾我在 cmets 中的建议,并为可能遇到相同问题的其他人提出一些关于您最后一个错误的建议

标签: javascript node.js webrtc video-streaming


【解决方案1】:

您应该使用某种 P2P 或媒体服务器来处理来自不同客户端的多个同时连接 PeerJS 是一个不错的选择。 对于WebRTC: ICE failed, add a TURN server and see about:webrtc for more details 错误,它正是它所说的 STUN 服务器用于创建连接,但如果无法建立 P2P 连接,则回退是所有通信都通过 TURN 服务器,因此它们需要大量资源和带宽。 TURN 服务器通常不是免费的,但一个可能解决您的问题的开源选项是使用 COTURN 服务器https://github.com/coturn/coturn 您应该将以下示例配置放在您的 PeerJS 选项中

"iceServers": [
         {
          "urls": "stun:vc.example.com:3478"
         },
         {
          "urls": "turn:vc.example.com:3478",
          "username": "coturnUser",
          "credential": "coturnUserPassword"
         }
      ],

您可以在urls 之前指定"iceTransportPolicy": "relay" 以仅使用中继服务器(无P2P)

【讨论】:

  • thanx 实际上我更喜欢 p2p ,所以如果我添加一个回合服务器......它可以作为后备选项吗?这意味着它将首先尝试 p2p,如果失败,它将使用 TURN 服务器?我应该自己托管 COTURN 服务器,对吗? ... p2p 连接一般可靠吗?
  • 是的,连接将首先使用 STUN 服务器建立,然后使用 P2P 进行通信,如果 P2P 连接出现问题,COTURN 服务器将用作后备。是的,您确实需要自己配置和托管 COTURN 服务器
  • 您可以使用商业 TURN 中继服务器,例如有很多 xirsys.com。由于原始问题已回答,您能否将答案标记为正确
  • 我的意思是如果我决定使用商业服务器,我只需要更改iceServers 对吗?
  • 是的,您只需添加您的提供商提供的地址和凭据,然后设置 "iceTransportPolicy": "relay" 为 TURN
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-23
  • 1970-01-01
  • 2016-03-19
相关资源
最近更新 更多