How to change the video source for Picture in Picture mode JavaScript?

311 Views Asked by At

What I'm building is a Video Conference app and I'm developing the app so we can use Picture in Picture mode (PiP). I want the PiP window to always show the video of the person that is currently speaking or is sharing screen. However, I'm not sure how I can switches the video source of this PiP window. The flow that I can think about is to first exitPictureInPicture(), then from the new video tag which is "pinned/active", I call requestPictureInPicture() again. But I felt like this is a bad approach, I think it would like we are closing the PiP mode and turning it back on. Definitely not what we want. Is there alternative way I can switch the PiP mode video source without having to exit the PiP mode first?

1

There are 1 best solutions below

0
Kaiido On

In Chrome and Safari, you can simply change the srcObject of your <video> to point to the new MediaStream, it should work on the fly without noticeable interruption.

const vid = document.querySelector("video");
const [btn1, btn2, btn3] = document.querySelectorAll("button");

// StackSnippets and other online REPL don't really like gUM
// So we use canvas streams here for the demo but it works just the same with gUM
const stream1 = getCanvasStream("red");
const stream2 = getCanvasStream("green");

btn1.onclick = async evt => vid.srcObject = stream1;
btn2.onclick = async evt => vid.srcObject = stream2;
btn3.onclick = (evt) => vid.requestPictureInPicture();
if (!vid.requestPictureInPicture) { btn3.remove(); }

function getCanvasStream(color) {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  ctx.fillStyle = color;
  let x = 0;
  const anim = t => {
    x = (x + 2) % 300;
    ctx.clearRect(0, 0, 300, 150);
    ctx.fillRect(x, 0, 10, 10);
    requestAnimationFrame(anim);
  }
  requestAnimationFrame(anim);
  return canvas.captureStream(30);
}
<button>use stream 1</button>
<button>use stream 2</button>
<button>request PiP</button><br>
<video autoplay muted></video>

In Firefox's own picture in picture mode however, they will close the popup a few seconds after the video got interrupted, this is true for when you switch the srcObject, or when you change the MediaStreamTrack of you MediaStream. So to support this browser (which still doesn't support the official requestPictureInPicture API, btw), you'd have to resort to an ugly hack where you'd draw you video content over a <canvas> and display this canvas's MediaStream, got through its .captureStream() method. This means that you need to draw every frame on that canvas, and you may also face issues with task throttling when the original tab isn't visible anymore... Least to say it's not great. And there is no easy way to feature detect this, apart from failing the first time, or using the heuristics that Firefox is the only main browser exposing this behavior, and also the only one not supporting requestPictureInPicture.