Seyed Ali Roshan
Seyed Ali Roshan

Reputation: 1574

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?

Upvotes: 2

Views: 5493

Answers (2)

alsabsab
alsabsab

Reputation: 1311

Electron desktopCapturer has a full explanation of implementing screen share.

just add this code in the main.js

app.whenReady().then(() => {
const mainWindow = new BrowserWindow()
session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
desktopCapturer.getSources({ types: ['screen'] }).then((sources) => {
  // Grant access to the first screen found.
  callback({ video: sources[0], audio: 'loopback' })
  })
})  
  mainWindow.loadFile('index.html')
})

Upvotes: 0

aabuhijleh
aabuhijleh

Reputation: 2464

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

Upvotes: 5

Related Questions