javascriptmediadevices

canvas.drawImage is giving blank screenshot in chrome but not in FF


I want to take screenshot using getDisplayMedia, but its giving me blank image.

  document
    .getElementById("takeScreenshot")
    .addEventListener("click", async () => {
      const canvas = document.createElement("canvas");
      const context = canvas.getContext("2d");
      const video = document.createElement("video");

      try {
        const captureStream = await navigator.mediaDevices.getDisplayMedia();
        video.srcObject = captureStream;
        video.addEventListener("loadedmetadata", (e) => {
          canvas.width = 600; //e.path[0]?.videoWidth; // set its size to the one of the video
          canvas.height = 600; //e.path[0]?.videoHeight; // video.videoHeight;
          context.drawImage(video, 0, 0);
          const frame = canvas.toDataURL("image/png");
          captureStream.getTracks().forEach((track) => track.stop());

          const img = document.createElement("img");
          img.src = frame;
          document.body.append(img);
        });
      } catch (err) {
        console.error("Error: " + err);
      }
    });

see issue on demo output: https://zm3kiy.csb.app/
code: https://codesandbox.io/s/practical-wright-zm3kiy?file=/index.html:324-1403


Solution

  • I think you simply need to call await video.play(); instead of relying on the "loadedmetadata".

    The code below works for me.

    document.body.onclick = async () => {
      const canvas = document.createElement("canvas");
      const video = document.createElement("video");
    
      // Capture media stream.
      const captureStream = await navigator.mediaDevices.getDisplayMedia();
      video.srcObject = captureStream;
    
      // Play it.
      await video.play();
    
      // Draw to canvas.
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      canvas.getContext("2d").drawImage(video, 0, 0);
      document.body.append(canvas);
    
      // Stop stream.
      captureStream.getTracks().forEach((track) => track.stop());
    };