javascripthtmlp2p

P2P Connection with ICE-SERVER


I would like to try to create 2 HTML pages that exchange data between them, one transmits the audio, while the other receives the audio and plays it in the second HTML page, without using an actual server.

I tried to look online, but I didn't understand much 🫥


Solution

  • I believe you want to create two HTML pages where one is the sender and the other is the receiver.Do you want to add the audio files as a media embedding or you want to initiate your device's microphone for audio recording.

    As you mentioned, you may not need an actual server as you can just set up a webRTC API (web Real Time Communication) that allows you establish a peer-to-peer communication between the two HTML pages. The most commonly used protocol for this is the ICE protocol (Interactive Connectivity Establishment).

    Let me share an example of how you can set up the sender and receiver HTML pages. Create a sender.html file as shown below:

    <body>
        <audio id="local" autoplay muted></audio>
        <button onclick="start(this)">Start Audio</button>
        <button id="stream" onclick="stream(this)" disabled>Broadcast Audio</button>
      
        <script>
          // get audio elements
          const local = document.querySelector("audio#local");
          let peerConnection;
      
          const channel = new BroadcastChannel("stream-audio");
          channel.onmessage = e => {
            if (e.data.type === "icecandidate") {
              peerConnection?.addIceCandidate(e.data.candidate);
            } else if (e.data.type === "answer") {
              peerConnection?.setRemoteDescription(e.data);
            }
          }
      
          
          // functon to start the audio and ask for permission to access microphone
          const start = async (e) => {
            e.disabled = true;
            document.getElementById("stream").disabled = false;  // enable the stream button
            await navigator.mediaDevices.getUserMedia({ audio: true })
              .then((stream) => local.srcObject = stream);
          }
      
          const stream = async (e) => {
            e.disabled = true;
      
            const config = {};
            peerConnection = new RTCPeerConnection(config);  // local peer connection
      
            // add an ice candidate event listener, 
            peerConnection.addEventListener("icecandidate", e => {
              let candidate = null;
              
              // prepare a candidate object
              if (e.candidate !== null) {
                candidate = {
                  candidate: e.candidate.candidate,
                  sdpMid: e.candidate.sdpMid,
                  sdpMLineIndex: e.candidate.sdpMLineIndex,
                };
              }
              channel.postMessage({ type: "icecandidate", candidate });
            });
    
      
            // add media tracks to the peer connection
            await local.srcObject.getTracks()
              .forEach(track => peerConnection.addTrack(track, local.srcObject));
              
      
            // Create an offer and send through the browser channel, this is necessary to establish a connection (handshake)
            peerConnection.createOffer({ offerToReceiveAudio: true })
              .then(async offer => {
                await peerConnection.setLocalDescription(offer);
                channel.postMessage({ type: "offer", sdp: offer.sdp });
              });
          }
        </script>
      </body>

    Create a receiver.html file as shown below:

    <body>
        <audio id="remote" controls></audio>    
        <script>
          // get audio elements
          const remote = document.querySelector("audio#remote");
          let peerConnection;
      
          const channel = new BroadcastChannel("stream-audio");
          channel.onmessage = e => {
            if (e.data.type === "icecandidate") {
              peerConnection?.addIceCandidate(e.data.candidate)
            } else if (e.data.type === "offer") {
              handleOffer(e.data)
            }
          }
      
          const handleOffer = async (offer)  =>{
            const config = {};
            peerConnection = new RTCPeerConnection(config);
            peerConnection.addEventListener("track", e => remote.srcObject = e.streams[0]);
            peerConnection.addEventListener("icecandidate", e => {
              let candidate = null;
              if (e.candidate !== null) {
                candidate = {
                  candidate: e.candidate.candidate,
                  sdpMid: e.candidate.sdpMid,
                  sdpMLineIndex: e.candidate.sdpMLineIndex,
                }
              }
              channel.postMessage({ type: "icecandidate", candidate })
            });
            await peerConnection.setRemoteDescription(offer)
              .then(() => peerConnection.createAnswer())
              .then(async answer => {
                await peerConnection.setLocalDescription(answer);
                channel.postMessage({
                  type: "answer",
                  sdp: answer.sdp,
                });
              });
          }
        </script>
      </body>

    Create both of these html file and open to the sender.html page. Start the audio and broadcast the audio from that html page. Open the receiver.html file on another window or page on the same browser.

    I hope I have been able to help!