WebRTC Quick Start

Without signaling

Pasted image 20240105200037.png

We can use signaling or not, signaling is just a way for us to store the answer and offer. If the client can exchange this information manually, we don't really need signaling.

Example

Consider the following code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button class="createOffer">Create offer</button>

    <input type="text" placeholder="offer" class="offerInput">
    <button class="acceptOffer">accept offer</button>


    <input type="text" placeholder="answer" class="answerInput">
    <button class="acceptAnswer">accept answer</button>

    <p>Status: <span class="status"></span></p>

    <input type="text" placeholder="chat" class="chatInput">
    <button class="sendChat">send chat</button>


    <script src="app.js"></script>
</body>
</html>
const createOffer = document.querySelector(".createOffer")
const offerInput = document.querySelector(".offerInput")
const acceptOffer = document.querySelector(".acceptOffer")
const answerInput = document.querySelector(".answerInput")
const acceptAnswer = document.querySelector(".acceptAnswer")
const status = document.querySelector(".status")
const chatInput = document.querySelector(".chatInput")
const sendChat = document.querySelector(".sendChat")


let channel = null;

const connection = new RTCPeerConnection({ iceServers: [
    { urls: 'stun:stun.l.google.com:19302' }] 
})


connection.ondatachannel = (event) => {
    // the receiver will get this as it contains the information from the channel
    const receivedChannel = event.channel;
    channel = receivedChannel;

    receivedChannel.onmessage = (event) => console.log("message:", event.data)
}

connection.onconnectionstatechange = (event) => {
    status.innerText = connection.connectionState
}

createOffer.addEventListener("click", async () => {
    channel = connection.createDataChannel("channel1")
    channel.onmessage = (event) => {
        console.log("message: ", event.data)
    }

    const offer = await connection.createOffer()
    await connection.setLocalDescription(offer)

    connection.onicecandidate = (event) => {

        if (!event.candidate) {
            // when candidate is null, ICE is finished gathering.
            // We will then display in the offer
            prompt("Copy the following offer", JSON.stringify(connection.localDescription))
        }
    }
})

acceptOffer.addEventListener("click", async () => {
    const offer = JSON.parse(offerInput.value)
    await connection.setRemoteDescription(offer)

    connection.onicecandidate = (event) => {
        if (!event.candidate) {
            prompt("Copy the following answer", JSON.stringify(connection.localDescription))
        }
    }

    const answer = await connection.createAnswer()
    connection.setLocalDescription(answer)
})


acceptAnswer.addEventListener("click", async () => {
    const answer = JSON.parse(answerInput.value)
    connection.setRemoteDescription(answer)
})

sendChat.addEventListener("click", async() => {
    channel.send(chatInput.value)
})

How to use:

  1. Browser 1, click Create Offer and send it to Browser 2
  2. Browser 2, paste the code in the offerInput and press Accept Offer. Then send the answer to Browser 1
  3. Browser 1 click on Accept Answer. That's it, the connection is established

[!note]
In this example, we disable ICE Trickling