Demo HTML page

Here is a simplified example of a web page that connects to a server and displays a video.
You can access the sample at /demo, for example 'http://localhost:4000/demo'

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Live STANAG4609 to webRTC streaming</title>

  <style>
    .videoContainer {
      display: inline-block;
      vertical-align: top;
    }

    .klvContainer {
      display: inline-block;
      margin-left: 10px;
      vertical-align: top;
      text-align: left;
      line-height: 0.8;
    }

    .msgContainer {
      margin-left: 100px;
      vertical-align: top;    
      color:red;
    }

    .klvs {
      font-size: 0.6em;
    }
  </style>
</head>

<body>
  <div class="msgContainer">
    <div id="Message"> </div>
  </div>
  <div class="videoContainer">
    <video id="videoWindow" />
  </div>
  <div class="klvContainer">
    <div id="Info"> </div>
    <hr>
    <div id="Time"> </span>PacketTime: <span id="PacketTime"></span></div>
    <div id="KLVS"> </span>KLV: <span id="Klvs" class="klvs"></span></div>
  </div>


  <script>

    // Get a channel info from the httpserver
    fetch(window.location.href + 'info')
      .then(res => res.json())
      .then(data => {
        document.getElementById("Info").innerText = data.channelName + ', ' + data.url;
      });

    const configuration = {
      iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
    };

    let pc = new RTCPeerConnection(configuration);
    let log = (msg) => {
      console.log(msg);
    };

    pc.ontrack = function (event) {
      let el = document.getElementById("videoWindow");
      el.srcObject = event.streams[0];
      el.muted = true;
      el.autoplay = true;
      el.controls = true;
    };

    pc.onconnectionstatechange = e => log(pc.iceConnectionState);
    pc.oniceconnectionstatechange = e => log(pc.iceConnectionState);
    pc.onicegatheringstatechange = e => log(pc.iceGatheringState);
    pc.addTransceiver("video", { direction: "sendrecv" });

    let dataChannel = pc.createDataChannel("klv");
    dataChannel.binaryType = "arraybuffer";
    dataChannel.onclose = () => console.log("dataChannel has closed");
    dataChannel.onopen = () => console.log("dataChannel has opened");
    dataChannel.onmessage = e => {
      let dataView = new DataView(e.data);
      let decoder = new TextDecoder("utf8");

      let msg = JSON.parse(
        decoder.decode(dataView)
      );

      switch (msg.type) {

        case 'klv':
          document.getElementById("Time").innerText = new Date(msg.klvs["2"] / 1000).toISOString();
          document.getElementById("Klvs").innerText = JSON.stringify(msg.klvs, null, 2);
          break;
        case 'msg':
          document.getElementById("Message").innerText = msg.msg;
          break;
        case 'state':
          document.getElementById("Message").innerText = msg.state;
          break;
        default:
          break;
      }  
    };

    // ice
    let wsProto = "ws";
    if (window.location.protocol != "http:") {
      wsProto += "s";
    }
    const socket = new WebSocket(
      wsProto + `://${window.location.host}/ws`
    );
    socket.onmessage = e => {
      let msg = JSON.parse(e.data);
      if (!msg) {
        return console.log("failed to parse msg");
      }

      if (msg.error) {
        document.getElementById("Message").innerText = msg.error
      }
      else
        if (msg.candidate) {
          pc.addIceCandidate(msg);
        } else {
          pc.setRemoteDescription(msg);
        }
    };

    pc.onicecandidate = e => {
      if (e.candidate && e.candidate.candidate !== "") {
        socket.send(JSON.stringify(e.candidate));
      }
    };
    socket.onopen = () => {
      pc.createOffer().then((offer) => {
        pc.setLocalDescription(offer);
        socket.send(JSON.stringify(offer));
      });
    };

  </script>
</body>

</html>