Skip to content

Commit

Permalink
feat: add getDisplayMedia support for desktop #158
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Oct 10, 2023
1 parent 915ed01 commit 18bc2ed
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 3 deletions.
138 changes: 138 additions & 0 deletions client/desktop/src/main/lib/capturer-source-picker.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Capturer Picker</title>
<style>
html,
body {
margin: 0;
padding: 0;
}

.desktop-capturer-selection {
width: 100vw;
height: 100vh;
background: rgba(30, 30, 30, 0.75);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}

.desktop-capturer-selection__scroller {
width: 100%;
max-height: 100vh;
overflow-y: auto;
}

.desktop-capturer-selection__list {
max-width: calc(100% - 100px);
margin: auto;
padding: 0;
display: flex;
flex-wrap: wrap;
list-style: none;
overflow: hidden;
justify-content: center;
}

.desktop-capturer-selection__item {
display: flex;
margin: 4px;
}

.desktop-capturer-selection__btn {
display: flex;
flex-direction: column;
align-items: stretch;
margin: 0;
border: 0;
width: 160px;
height: auto;
border-radius: 3px;
padding: 4px;
background: #252626;
text-align: left;
transition: background-color 0.15s, box-shadow 0.15s;
cursor: pointer;
}

.desktop-capturer-selection__btn:hover,
.desktop-capturer-selection__btn:focus {
background: rgba(98, 100, 167, 0.8);
}

.desktop-capturer-selection__thumbnail {
width: 100%;
height: 90px;
object-fit: cover;
}

.desktop-capturer-selection__name {
margin: 6px 0 6px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
color: white;
}
</style>
</head>

<body></body>
<script>
window.electron.ipcRenderer.on('SEND_SCREEN_SHARE_SOURCES', (sources) => {
try {
const selectionElem = document.createElement('div');
selectionElem.classList = 'desktop-capturer-selection';
selectionElem.innerHTML = `
<div class="desktop-capturer-selection__scroller">
<ul class="desktop-capturer-selection__list">
${sources
.map(
({ id, name, thumbnail, display_id, appIcon }) => `
<li class="desktop-capturer-selection__item">
<button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}">
<img class="desktop-capturer-selection__thumbnail" src="${String(
thumbnail
)}" />
<span class="desktop-capturer-selection__name">${name}</span>
</button>
</li>
`
)
.join('')}
</ul>
</div>
`;
document.body.appendChild(selectionElem);

document
.querySelectorAll('.desktop-capturer-selection__btn')
.forEach((button) => {
button.addEventListener('dblclick', async () => {
try {
const id = button.getAttribute('data-id');
const source = sources.find((source) => source.id === id);
if (!source) {
throw new Error(`Source with id ${id} does not exist`);
}

window.electron.ipcRenderer.sendMessage(
'selectCapturerSource',
source
);
} catch (err) {
console.error('Error selecting desktop capture source:', err);
window.electron.ipcRenderer.sendMessage(null);
}
});
});
} catch (err) {
console.error('Error displaying desktop capture sources:', err);
window.electron.ipcRenderer.sendMessage(null);
}
});
</script>
</html>
82 changes: 80 additions & 2 deletions client/desktop/src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@
* `./src/main.js` using webpack. This gives us some performance wins.
*/
import path from 'path';
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import {
app,
BrowserWindow,
shell,
ipcMain,
desktopCapturer,
DesktopCapturerSource,
} from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
import { CONSTANT, resolveHtmlPath } from './util';
import windowStateKeeper from 'electron-window-state';
import is from 'electron-is';
import { initScreenshots } from './screenshots';
Expand Down Expand Up @@ -42,6 +49,22 @@ ipcMain.on('ipc-example', async (event, arg) => {
event.reply('ipc-example', msgTemplate('pong'));
});

ipcMain.handle(CONSTANT.DESKTOP_CAPTURER_GET_SOURCES, async (event, opts) => {
const sources = await desktopCapturer.getSources({
types: ['window', 'screen'],
});

return new Promise((resolve) => {
createCapturerSourcePicker(sources, (source) => {
if (source) {
resolve(source);
} else {
resolve(null);
}
});
});
});

if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
Expand Down Expand Up @@ -76,6 +99,7 @@ const installExtensions = async () => {
const webPreferences: Electron.WebPreferences = {
nodeIntegration: false,
contextIsolation: true,
devTools: true,
webSecurity: false, // skip same-origin
allowRunningInsecureContent: true, // allow visit http page in https
preload: app.isPackaged
Expand All @@ -85,6 +109,7 @@ const webPreferences: Electron.WebPreferences = {

let welcomeWindow: BrowserWindow | null = null;
let mainWindow: BrowserWindow | null = null;
let capturerSourcePickerWindow: BrowserWindow | null = null;

const createWelcomeWindow = async () => {
// 创建一个新的浏览器窗口
Expand Down Expand Up @@ -259,6 +284,59 @@ const createMainWindow = async (url: string) => {
}
};

const createCapturerSourcePicker = async (
sources: DesktopCapturerSource[],
onSelected: (source: DesktopCapturerSource | null) => void
) => {
// 创建一个新的浏览器窗口
capturerSourcePickerWindow = new BrowserWindow({
width: 800,
height: 600,
alwaysOnTop: true,
parent: mainWindow ?? undefined,
modal: true,
autoHideMenuBar: true,
minimizable: false,
maximizable: false,
webPreferences,
});

// 加载欢迎窗口的HTML文件
capturerSourcePickerWindow.webContents.loadFile(
require.resolve('./lib/capturer-source-picker.html')
);

capturerSourcePickerWindow.webContents.on('did-finish-load', () => {
if (capturerSourcePickerWindow) {
capturerSourcePickerWindow.webContents.send(
'SEND_SCREEN_SHARE_SOURCES',
sources.map((s) => ({
...s,
thumbnail: s.thumbnail.toDataURL(),
}))
);
}
});

// 监听从渲染进程发送的选择捕获源事件
capturerSourcePickerWindow.webContents.on(
'ipc-message',
(e, channel, data) => {
if (channel === 'selectCapturerSource') {
onSelected(data);
if (capturerSourcePickerWindow) {
capturerSourcePickerWindow.close();
}
}
}
);

capturerSourcePickerWindow.on('closed', () => {
onSelected(null);
capturerSourcePickerWindow = null;
});
};

/**
* Add event listeners...
*/
Expand Down
12 changes: 11 additions & 1 deletion client/desktop/src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import {
IpcRendererEvent,
webFrame,
} from 'electron';
import { CONSTANT } from './util';

export type Channels =
| 'ipc-example'
| 'webview-message'
| 'close'
| 'selectServer';
| 'selectServer'
| 'selectCapturerSource';

contextBridge.exposeInMainWorld('electron', {
ipcRenderer: {
Expand All @@ -26,6 +28,14 @@ contextBridge.exposeInMainWorld('electron', {
once(channel: Channels, func: (...args: unknown[]) => void) {
ipcRenderer.once(channel, (_event, ...args) => func(...args));
},
getDesktopCapturerSource:
async (): Promise<Electron.DesktopCapturerSource> => {
const source = await ipcRenderer.invoke(
CONSTANT.DESKTOP_CAPTURER_GET_SOURCES
);

return source;
},
},
});

Expand Down
4 changes: 4 additions & 0 deletions client/desktop/src/main/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ export function resolveHtmlPath(htmlFileName: string) {
export function getDefaultLoggerPath(): string {
return log.transports.file.getFile().path;
}

export const CONSTANT = {
DESKTOP_CAPTURER_GET_SOURCES: 'DESKTOP_CAPTURER_GET_SOURCES',
};
3 changes: 3 additions & 0 deletions client/desktop/src/renderer/preload.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ declare global {
func: (...args: unknown[]) => void
): (() => void) | undefined;
once(channel: string, func: (...args: unknown[]) => void): void;
getDesktopCapturerSource: () => Promise<
Electron.DesktopCapturerSource[]
>;
};
};
}
Expand Down
20 changes: 20 additions & 0 deletions client/web/plugins/com.msgbyte.env.electron/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,23 @@ forwardSharedEvent('receiveUnmutedMessage');
setTimeout(() => {
checkUpdate();
}, 1000);

navigator.mediaDevices.getDisplayMedia = async (
options: DisplayMediaStreamOptions
) => {
const source = await (
window as any
).electron.ipcRenderer.getDesktopCapturerSource();

const stream = await window.navigator.mediaDevices.getUserMedia({
// audio: options.audio,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: source.id,
},
} as any,
});

return stream;
};

0 comments on commit 18bc2ed

Please sign in to comment.