gUM/gDM h264 downscale maxbitrate

by jib1

HTML

<script src="https://webrtchacks.github.io/adapter/adapter-latest.js"></script>
<script src="https://hg.mozilla.org/mozilla-central/raw-file/tip/dom/media/webrtc/tests/mochitests/sdpUtils.js"></script>
Height <input id="heightNumber" type="number" min="2" max="1920" value="1080" size="5"><input id="heightRange" type="range" min="2" max="1920" value="1080">
Framerate <input id="fpsNumber" type="number" min="1" max="120" value="30" size="3"><input id="fpsRange" type="range" min="1" max="120" value="30"><br>
Downscale <input id="downscaleNumber" type="number" min="0.01" max="1" value="1" step="0.01" size="4"><input id="downscaleRange" type="range" min="0.01" max="1" value="1" step="0.01">
Decimate <input id="decimateNumber" type="number" min="1" max="120" value="120" size="4"><input id="decimateRange" type="range" min="1" max="120" value="120">
Max bitrate <input id="rateNumber" type="number" min="2" max="9000000" value="2000000" size="9"><input id="rateRange" type="range" min="2" max="9000000" value="2000000"><br><br>
<button id="gdm">gDM!</button>
<button id="gum">gUM!</button>
<label><input type="checkbox" id="h264" checked>h264</label>
<button id="button2">Renegotiate direction</button>
<button id="button3">ReplaceTrack(null)</button>
<div id="div"></div>
<table>
  <tr>
    <td>Receiver</td>
    <td>Sender</td>
  </tr>
  <tr>
    <td id="v2info">0x0</td>
    <td id="v1info">0x0</td>
  </tr>
  <tr>
    <td id="v2brinfo">0 bps</td>
    <td id="v1brinfo">0 bps</td>
  </tr>
  <tr>
    <td><video id="v2" autoplay></video></td>
    <td><video id="v1" autoplay muted></video></td>
  </tr>
</table>

CSS

body { white-space: nowrap; font-family: monospace; }

JavaScript

const console = { log: msg => div.innerHTML += `${msg}<br>` };
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
const update = (div, msg) => div.innerHTML = msg;

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

heightRange.oninput = () => applyIt(heightNumber.value = heightRange.value);
heightNumber.oninput = () => applyIt(heightRange.value = heightNumber.value);
downscaleRange.oninput = () => applyIt(downscaleNumber.value = downscaleRange.value);
downscaleNumber.oninput = () => applyIt(downscaleRange.value = downscaleNumber.value);
fpsRange.oninput = () => applyIt(fpsNumber.value = fpsRange.value);
fpsNumber.oninput = () => applyIt(fpsRange.value = fpsNumber.value);
decimateRange.oninput = () => applyIt(decimateNumber.value = decimateRange.value);
decimateNumber.oninput = () => applyIt(decimateRange.value = decimateNumber.value);
rateRange.oninput = () => applyIt(rateNumber.value = rateRange.value);
rateNumber.oninput = () => applyIt(rateRange.value = rateNumber.value);

const applyIt = () => apply(heightNumber.value, downscaleNumber.value, fpsNumber.value, decimateNumber.value, rateNumber.value);

let track, applying = false, height = heightNumber.value, scale, fps, decimate, rate, nulltrack = false;

async function apply(h, s, f, d, r) {
  height = h;
  scale = s;
  fps = f;
  decimate = d;
  rate = r;
  if (applying || !track) return;
  try {
    applying = true;
    do {
      h = height;
      s = scale;
      f = fps;
      d = decimate;
      r = rate;
      const [sender] = pc1.getSenders();
      const params = sender.getParameters();
      params.encodings[0].scaleResolutionDownBy = 1 / s;
      params.encodings[0].maxFramerate = d;
      params.encodings[0].maxBitrate = r;
      await Promise.all([
        track.applyConstraints({height: h, frameRate: f}),
        sender.setParameters(params),
      ]);
    } while (h != height || s != scale || f != fps || d !=...