javascriptsocketswebrtcgetusermediavideo-conferencing

Video/Audio communication


I am trying to create a video conference web app using peer/getUserMedia.

Currently, when I send the unique ID to the video conference, I am able to hear/see anyone who joins my session.

However, only the first person who joins my session can communicate/see me.

I want to make it so the other users can see/hear every user thats in the video conference too.

So far this is what I have working so far. Also the code that is currently written, whenever a new user joins, my video gets added again so I see two videos of myself (if im the host), but that will be an easy fix for me

<html>
   <body>
      <h3 id="show-peer"></h3>
      <div style="display: grid; justify-content: space-around; margin:10px;">
         <div style="width: 300px;height: 200px;transform: scale(-1, 1);border: 2px solid; display:grid; margin-bottom: 5%;" id="ourVideo"></div>
         <div style="width: 300px;height: 200px;transform: scale(-1, 1);border: 2px solid; display: grid;" id="remoteVideo"></div>
      </div>
      <input id="peerID" placeholder="Peer ID">
      <button id="call-peer" onclick="callPeer()">Call Peer</button>
      <br>
      <button id="screenShare"onclick="shareScreen()">Share Screen</button>
   </body>
   <script src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
   <script>
      window.addEventListener('load',(event)=>{
          var peer= new Peer()
          var myStream;
          var currentPeer;
          var peerList=[];
          peer.on('open',function(id){
              document.getElementById("show-peer").innerHTML=id
          })
          peer.on('call',function(call){
              navigator.mediaDevices.getUserMedia({
                  video:true,
                  audio:true
              }).then((stream)=>{
                  myStream = stream
                  addOurVideo(stream)
                  call.answer(stream)
                  call.on('stream',function(remoteStream){
                      if(!peerList.includes(call.peer)){
                          addRemoteVideo(remoteStream)
                          currentPeer = call.peerConnection
                          peerList.push(call.peer)
                      }
                      
                  })
              }).catch((err)=>{
                  console.log(err+" unable to get media")
              })
          })
          function stopScreenShare(){
              let videoTrack = myStream.getVideoTracks()[0];
              var sender = currentPeer.getSenders().find(function(s){
                  return s.track.kind ==videoTrack.kind;
              })
              sender.replaceTrack(videoTrack)
          };
          document.getElementById("call-peer").addEventListener('click',(e)=>{
              let remotePeerId= document.getElementById("peerID").value;
              document.getElementById("show-peer").innerHTML= "connecting "+remotePeerId;
              console.log(remotePeerId)
              callPeer(remotePeerId);
          })
          document.getElementById("screenShare").addEventListener('click',(e)=>{
              navigator.mediaDevices.getDisplayMedia({
                  video: {
                      cursor: "always"
                  },
                  audio:{  
                      autoGainControl: false,
                      echoCancellation: false, 
                      googAutoGainControl: false,
                      noiseSuppression: false 
                  }
              }).then((stream)=>{
                      let videoTrack = stream.getVideoTracks()[0];
      
                      videoTrack.onended = function(){
                          stopScreenShare()
                      }
                      let sender = currentPeer.getSenders().find(function(s){
                          return s.track.kind == videoTrack.kind
                      })
                      sender.replaceTrack(videoTrack)
                  }).catch((err)=>{
                      console.log("unable to get display media"+err)
                  })
              })
          function callPeer(id){
              navigator.mediaDevices.getUserMedia({
                  video:true,
                  audio:true
              }).then((stream)=>{
                  myStream = stream
                  addOurVideo(stream)
                  let call = peer.call(id,stream)
                  call.on('stream',function(remoteStream){
                      if(!peerList.includes(call.peer)){
                          addRemoteVideo(remoteStream)
                          currentPeer = call.peerConnection
                          peerList.push(call.peer)
                      }
                      
                  })
              }).catch((err)=>{
                  console.log(err+" unable to get media")
              })
          }
          function addRemoteVideo(stream){
              let video = document.createElement("video");
              video.classList.add("video")
              video.srcObject = stream;
              video.play()
              document.getElementById("remoteVideo").append(video)
          }
          function addOurVideo(stream){
              let video = document.createElement("video");
              video.classList.add("video")
              video.srcObject = stream;
              video.play()
              video.muted=true;
              document.getElementById("ourVideo").append(video)
          }
      });
   </script>
</html>

Solution

  • I think you will need to create a "mesh". So with 3 peers, each user(peer) sets up two connections, one to each of the other two users. At each client's end, there are two entirely different connections.

    The user @daGrevis asked this question on the PeerJs GitHub. Ultimately this is the implementation they used for a multi-peer chat that might help:

    Chat = React.createClass
        displayName: "Foo"
    
        getInitialState: ->
            messages: []
    
        connectionsToPeerIds: (connections) ->
            _.map connections, (connection) => connection.peer
    
        componentWillMount: ->
            who = getSegments()[1]
            peer = new Peer who, key: PEER_KEY, debug: 2
            @connections = []
    
            peer.on "error", (error) =>
                alert error.type
    
            peer.on "open", (peerId) =>
                if who == "x"
                    peer.on "connection", (connection) =>
                        @connections.push connection
    
                        @listenForMessage connection
    
                        peerIds = @connectionsToPeerIds @connections
    
                        connection.on "open", =>
                            connectionsWithoutNewConnection = _.filter @connections, (c) -> c.peer != connection.peer
                            peerIdsWithoutNewConnection = @connectionsToPeerIds connectionsWithoutNewConnection
    
                            if peerIdsWithoutNewConnection.length
                                connection.send type: "newConnection", peerIds: peerIdsWithoutNewConnection
    
                if who != "x"
                    connection = peer.connect "x"
    
                    connection.on "error", (error) =>
                        alert error
    
                    connection.on "open", =>
                        @connections.push connection
    
                        @listenForMessage connection
    
                        connection.on "data", (data) =>
                            if data.type == "newConnection"
                                peerIds = data.peerIds
                                _.forEach peerIds, (peerId) =>
                                    connection = peer.connect peerId
    
                                    do (connection) =>
                                        connection.on "error", (error) =>
                                            alert error.type
    
                                        connection.on "open", =>
                                            @connections.push connection
                                            @listenForMessage connection
    
                    peer.on "connection", (connection) =>
                        connection.on "open", =>
                            @connections.push connection
    
                            @listenForMessage connection
    

    Then I found an instance wherein user @mluketin used peerjs-server, and created a conference call example https://github.com/mluketin/peerjs-helloworld-conference

    From what I know and have read, it looks like the mesh design is simplest way, but limited to the speed of the slowest peer. So if you only need 3-5 people it should be ok. Otherwise you'll probably have to follow the example that mluketin lays out.


    More links that might help:

    1. Having multiple peers PeerJS?
    2. Multiple party peer.js application