Electron DesktopCapturer API获取窗口详情局限:捕获主窗口外标签页
Electron应用内标签页屏幕录制问题解决方案
问题背景
我正在开发一款Electron应用,使用desktopCapturer API获取打开窗口(含标签页)详情以实现屏幕录制功能。目前已成功获取主窗口的详情,但无法捕获主窗口之外的标签页——desktopCapturer API仅返回主窗口信息,不包含应用内其他标签页或子窗口的详情。
用户核心代码
主进程获取窗口信息代码
ipcMain.on("get-current-webcontents-id", async (event) => { const webContents = BrowserWindow.fromId(event.sender.id).webContents; console.log("webContents: ", webContents.getTitle()); const sources = await desktopCapturer.getSources({ types: ["window", "screen"], }); console.log("sources: ", sources); const currentSource = sources.find( (source) => source.name === webContents.getTitle() ); console.log("currentSource: ", currentSource); event.returnValue = currentSource.id; });
渲染进程录制流代码
const recordingStream = async () => { const tabId = ipcRenderer.sendSync("get-current-webcontents-id"); return await navigator.mediaDevices.getUserMedia({ audio: { mandatory: { chromeMediaSource: "desktop", chromeMediaSourceId: tabId, }, }, video: { mandatory: { chromeMediaSource: "desktop", chromeMediaSourceId: tabId, }, }, }); }; const startRecording = () => { recordingStream().then((stream) => { console.log("stream: ", stream); remoteVideo.srcObject = stream; remoteVideo.play(); const mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm" }); const chunks = []; mediaRecorder.ondataavailable = (e) => { console.log("data available: ", e); if (e.data.size > 0) { chunks.push(e.data); } }; mediaRecorder.onstop = async () => { const blob = new Blob(chunks, { type: "video/webm" }); const bufferArray = await blob.arrayBuffer(); ipcRenderer.send("recording", bufferArray); }; mediaRecorder.start(); setTimeout(() => { mediaRecorder.stop(); }, 30000); }); }; startRecording();
核心原因
desktopCapturer是系统级窗口捕获API,只能识别操作系统层面的顶层独立窗口,无法感知Electron应用内部的子内容容器(如基于BrowserView/iframe实现的标签页),这些子容器属于同一个顶层窗口的WebContents实例,不会被系统窗口列表识别。
解决思路与实现方案
1. 枚举应用内所有WebContents实例
主进程可以直接获取应用内所有窗口及内部子视图的WebContents,无需依赖desktopCapturer:
// 主进程:获取应用内所有可录制的WebContents ipcMain.on("get-all-app-webcontents", (event) => { const targetContents = []; // 遍历所有主窗口 BrowserWindow.getAllWindows().forEach(mainWindow => { // 添加主窗口自身的WebContents targetContents.push({ id: mainWindow.webContents.id, title: mainWindow.webContents.getTitle(), label: `主窗口: ${mainWindow.webContents.getTitle()}` }); // 遍历窗口内的所有BrowserView(标签页常用实现) mainWindow.getBrowserViews().forEach(view => { targetContents.push({ id: view.webContents.id, title: view.webContents.getTitle(), label: `标签页: ${view.webContents.getTitle()}` }); }); }); event.returnValue = targetContents; });
2. 直接捕获指定WebContents的流(推荐)
使用Electron内置的webContents.captureStream()方法(Electron 12+支持),直接获取目标WebContents的实时音视频流,无需走系统desktopCapturer:
主进程处理录制逻辑(更稳定)
// 主进程:启动指定WebContents的录制 ipcMain.handle("start-recording-target", async (event, contentsId) => { const targetContents = electron.webContents.fromId(contentsId); if (!targetContents) return { success: false, msg: "无效的WebContents ID" }; // 获取目标内容的实时流 const stream = targetContents.captureStream({ video: true, audio: true // 需确保WebContents启用了音频捕获权限 }); const mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm" }); const chunks = []; mediaRecorder.ondataavailable = (e) => { e.data.size > 0 && chunks.push(e.data); }; mediaRecorder.onstop = async () => { const blob = new Blob(chunks, { type: "video/webm" }); const buffer = await blob.arrayBuffer(); // 这里可直接保存到本地,或通过IPC发送给渲染进程 console.log("录制完成,数据大小:", buffer.byteLength); }; mediaRecorder.start(); // 示例:30秒后自动停止 setTimeout(() => mediaRecorder.stop(), 30000); return { success: true }; });
渲染进程调用示例
// 渲染进程:获取所有可录制项并选择录制 const getAvailableTargets = () => { return ipcRenderer.sendSync("get-all-app-webcontents"); }; // 选择某一个target的id调用录制 const startTargetRecording = (targetId) => { ipcRenderer.invoke("start-recording-target", targetId) .then(res => { if (res.success) console.log("录制已启动"); else console.error(res.msg); }); };
3. 调整标签页实现方式
如果你的标签页是基于iframe实现的,由于所有iframe共享同一个WebContents,无法单独捕获。这种情况下建议改用BrowserView实现标签页,每个标签页对应独立的WebContents实例,才能支持单独录制。
关键注意事项
- 确保Electron版本≥12,
captureStream()才支持音频捕获; - 若需要捕获WebContents的音频,需在创建WebContents时配置
webPreferences: { audioCaptureEnabled: true }; - 主进程处理录制逻辑比渲染进程更稳定,避免跨进程传递流的兼容性问题。
内容的提问来源于stack exchange,提问作者bs01
相关产品推荐
相关产品推荐

