article setCodecPreferences negotiationneeded

My starting point.

by jib1

HTML

<button id="button">Start!</button>
pc1 codec: <select id="pc1Codec">
  <option value="">Default</option>
  <option value="video/VP8">VP8</option>
  <option value="video/VP9">VP9</option>
  <option value="video/H264">H264</option>
</select>
<br><br>
<video id="video1" width="160" height="120" autoplay muted></video>
<video id="video2" width="160" height="120" autoplay></video><br>
<div id="div" style="white-space: pre;"></div>
<div id="div1" style="white-space: pre;"></div>
<div id="div2" style="white-space: pre;"></div>

JavaScript

const pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();

pc1Codec.onchange = async () => {
  try {
    console.log(`Switching codec to ${pc1Codec.value}`);
    const [transceiver] = pc1.getTransceivers();
    const codecs = RTCRtpReceiver.getCapabilities("video").codecs;
    sortByMimeTypes(codecs, [pc1Codec.value]);
    transceiver.setCodecPreferences(codecs);
    pc1.onnegotiationneeded(); // <--- hack
  } catch (e) {
    console.log(e);
  }
};

button.onclick = async () => {
  const stream = await navigator.mediaDevices.getUserMedia({video: true});
  const [track] = stream.getVideoTracks();
  pc1.addTrack(track, stream); // send camera track from pc1 → pc2
  pc2.addTrack(track, stream); // send camera track from pc2 → pc1

  video2.srcObject = (await new Promise(r => pc2.ontrack = r)).streams[0];
  const unmuted2 = new Promise(r => video2.srcObject.getVideoTracks()[0].onunmute = r);

  const [transceiver2] = pc2.getTransceivers();

  video1.srcObject = (await new Promise(r => pc1.ontrack = r)).streams[0];
  const unmuted1 = new Promise(r => video1.srcObject.getVideoTracks()[0].onunmute = r);

  await Promise.all([unmuted1, unmuted2]); // wait on unmute for stats
  while (true) {
    update1(`pc1 is sending ${(await getOutboundCodecStat(pc1)).mimeType}`);
    update2(`pc2 is sending ${(await getOutboundCodecStat(pc2)).mimeType}`);
    await wait(500);
  }
};

function sortByMimeTypes(codecs, preferredOrder) {
  codecs.sort((a, b) => {
    const indexA = preferredOrder.indexOf(a.mimeType);
    const indexB = preferredOrder.indexOf(b.mimeType);
    const orderA = indexA >= 0 ? indexA : Number.MAX_VALUE;
    const orderB = indexB >= 0 ? indexB : Number.MAX_VALUE;
    return orderA - orderB;
  });
}

pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
pc1.oniceconnectionstatechange = () => console.log(pc1.iceConnectionState);

pc1.onnegotiationneeded = async () => {
  await...