rtc-examples 0.9.0

Examples of WebRTC.rs stack with SansIO RTC API
Documentation
<html>
<!--
      SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
      SPDX-License-Identifier: MIT
  -->
<head>
  <title>trickle-ice</title>
  <style>
    #iceConnectionStates, #inboundDataChannelMessages {
      border: 1px solid #ccc;
      padding: 10px;
      height: 200px;
      overflow-y: auto;
      font-family: monospace;
      background-color: #f9f9f9;
    }

    .ice-checking { color: orange; }
    .ice-connected { color: green; }
    .ice-disconnected { color: red; }
    .ice-closed { color: gray; }


    .data-msg { margin: 2px 0; }
  </style>
</head>

<body>
<h3>Controls</h3>
<button id="startBtn">Start</button>
<button id="stopBtn">Stop</button>

<h3> ICE Connection States </h3>
<div id="iceConnectionStates"></div> <br />

<h3> Inbound DataChannel Messages </h3>
<div id="inboundDataChannelMessages"></div>
</body>

<script>
  const socket = new WebSocket(`ws://${window.location.hostname}:8081`)
  let pc = null
  let dc = null
  let offerCreated = false

  function createPeerConnection() {
    pc = new RTCPeerConnection({
      iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
    })


  // Handle outgoing ICE candidates
  pc.onicecandidate = e => {
    if (e.candidate && e.candidate.candidate !== "") {
      if (socket.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify(e.candidate))
      } else {
        console.warn("WebSocket not open, candidate not sent")
      }
    }
  }
  // ICE connection state change handler
  pc.oniceconnectionstatechange = () => {
    let el = document.createElement('p')
    el.appendChild(document.createTextNode(pc.iceConnectionState))
    el.className = 'ice-' + pc.iceConnectionState.toLowerCase()
    document.getElementById('iceConnectionStates').appendChild(el);
  }

    // Incoming DataChannel from remote
    pc.ondatachannel = event => {
      dc = event.channel
      setupDataChannel(dc)
    }
  }

    // Setup a DataChannel
    function setupDataChannel(channel) {
      channel.onopen = () => console.log("DataChannel open")
      channel.onmessage = event => {
        let el = document.createElement('p')
        el.textContent = `${new Date().toLocaleTimeString()} - ${event.data}`
        el.className = 'data-msg'
        document.getElementById('inboundDataChannelMessages').appendChild(el)
      }
      channel.onclose = () => console.log("DataChannel closed")
    }

  // Handle incoming WebSocket messages
  socket.onmessage = e => {
    try {
      let msg = JSON.parse(e.data)
      if (!msg) return console.log('failed to parse msg')
      if (msg.candidate) {
        pc.addIceCandidate(msg)
      } else {
        pc.setRemoteDescription(msg)
      }
    } catch (err) {
      console.warn("Failed to parse message:", e.data)
    }
  }

  // Start button handler
  document.getElementById('startBtn').onclick = () => {
    if (offerCreated) return

    if (!pc) createPeerConnection()

    dc = pc.createDataChannel('data')
    setupDataChannel(dc)

    pc.createOffer().then(offer => {
      pc.setLocalDescription(offer)
      socket.send(JSON.stringify(offer))
      offerCreated = true
    })
  }

  // Stop button handler
  document.getElementById('stopBtn').onclick = () => {
    if (dc) {
      dc.close()
      console.log("DataChannel closed")
      dc = null
      offerCreated = false
    }
    if (pc) {
      // Show disconnected before closing
      let el = document.createElement('p')
      el.textContent = `disconnected`
      el.className = 'ice-disconnected'
      document.getElementById('iceConnectionStates').appendChild(el)

      setTimeout(() => {
        pc.close()
        console.log("PeerConnection closed")
        el = document.createElement('p')
        el.textContent = `closed`
        el.className = 'ice-closed'
        document.getElementById('iceConnectionStates').appendChild(el)
        pc = null
      }, 50) // 50ms delay
    }

  }

</script>
</html>