Async in-content picker remember

by jib1

HTML

<button id="go">Go!</button><br><br>
Camera: <select id="camera"></select><br>
Microphone: <select id="microphone"></select><br>
<video id="video" height="100" autoplay controls></video><br>
<div id="div"></div><br>

JavaScript

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

async function getUserMedia({video, audio}) {
  if (video === true) {
    video = {deviceId: localStorage.video};
  }
  if (audio === true) {
    audio = {deviceId: localStorage.audio};
  }
  const stream = await navigator.mediaDevices.getUserMedia({video, audio});
  for (const track of stream.getTracks()) {
    localStorage[track.kind] = track.getSettings().deviceId;
  }
  return stream;
}

go.onclick = async () => {
  try {
    video.srcObject = await getUserMedia({audio: true, video: true});
    updateSelectors(video.srcObject.getTracks().map(t => t.getSettings().deviceId));
  } catch (e) {
    console.log(e);
  }
};

async function replaceUserMedia(constraints, ...oldTracks) {
  try {
     return await getUserMedia(constraints);
  } catch (e) {
    if (e.name != "NotReadableError") throw e;
    for (const track of oldTracks) {
      track.stop();
    }
    return await getUserMedia(constraints);
  }
}

microphone.onchange = async () => {
  try {
    const constraints = {audio: {deviceId: {exact: microphone.value}}};
    let [track] = video.srcObject.getAudioTracks();
    track = (await replaceUserMedia(constraints, track)).getAudioTracks()[0];
    video.srcObject = new MediaStream([track, ...video.srcObject.getVideoTracks()]);
  } catch (e) {
    updateSelectors([video.srcObject.getAudioTracks()[0].getSettings().deviceId]);
    console.log(e);
  }
};

camera.onchange = async () => {
  try {
    const constraints = {video: {deviceId: {exact: camera.value}}};
    let [track] = video.srcObject.getVideoTracks();
    track = (await replaceUserMedia(constraints, track)).getVideoTracks()[0];
    video.srcObject = new MediaStream([track, ...video.srcObject.getAudioTracks()]);
  } catch (e) {
    updateSelectors([video.srcObject.getVideoTracks()[0].getSettings().deviceId]);
    console.log(e);
  }
};

navigator.mediaDevices.ondevicechange...