Search code examples
electronscreen-capture

capture screen with electron when rendering a web site


I have an electron application that loads a web page on the internet. one of the sites main features is the ability to capture screen, it uses the

navigator.mediaDevices.getDisplayMedia({video: true});

but obviously, the electron will through the Permission denied because there will be no 'selecting window to capture' popped up to grant any permission to it.

I already check out some articles and saw desktopCapture

the problem is, this is happening and running through the web page javascript not my application's code so I don't know how to affect it.

so what should I do to make capturing the screen works in this situation?


Solution

  • You can override navigator.mediaDevices.getDisplayMedia to call Electron's desktopCapturer API like shown below. This implementation assumes you have contextIsolation enabled which is the default behaviour in Electron >= 12

    // preload.js
    
    const { desktopCapturer, contextBridge } = require("electron");
    const { readFileSync } = require("fs");
    const { join } = require("path");
    
    // inject renderer.js into the web page
    window.addEventListener("DOMContentLoaded", () => {
      const rendererScript = document.createElement("script");
      rendererScript.text = readFileSync(join(__dirname, "renderer.js"), "utf8");
      document.body.appendChild(rendererScript);
    });
    
    contextBridge.exposeInMainWorld("myCustomGetDisplayMedia", async () => {
      const sources = await desktopCapturer.getSources({
        types: ["window", "screen"],
      });
    
      // you should create some kind of UI to prompt the user
      // to select the correct source like Google Chrome does
      const selectedSource = sources[0]; // this is just for testing purposes
    
      return selectedSource;
    });
    
    // renderer.js
    
    navigator.mediaDevices.getDisplayMedia = async () => {
      const selectedSource = await globalThis.myCustomGetDisplayMedia();
    
      // create MediaStream
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: false,
        video: {
          mandatory: {
            chromeMediaSource: "desktop",
            chromeMediaSourceId: selectedSource.id,
            minWidth: 1280,
            maxWidth: 1280,
            minHeight: 720,
            maxHeight: 720,
          },
        },
      });
    
      return stream;
    };
    

    Now when this API is called, a stream will be returned to the caller as expected

    navigator.mediaDevices.getDisplayMedia({video: true});
    

    I have created a GitHub repo that has a working implementation of this solution