You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:04:55