Skip to content

Electron - 音频与视频捕获

捕获音频和视频对于视频会议、屏幕录制工具或需要访问网络摄像头(例如,捕捉个人资料图片)的功能至关重要。Electron 使用 Web 标准 API,如 navigator.mediaDevices.getUserMedia,以及其自己的 desktopCapturer 模块来捕获屏幕/窗口,从而简化了此过程。

首先,让我们在 main.js 中设置主进程。我们将启用 contextIsolation 并使用预加载脚本。可以使用 app.getPath('userData') API 来获取用于保存用户特定数据(例如录音)的标准目录。

const { app, BrowserWindow } = require('electron');
const path = require('path');
let mainWindow;
// Example: Define a path for saved recordings within the app's user data directory
// const recordingsPath = path.join(app.getPath('userData'), 'saved_recordings');
// You would then use Node's fs module (typically in the main process) to manage this directory.
// console.log('Recordings will be saved in:', recordingsPath);
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
});
mainWindow.loadFile(path.join(__dirname, 'index.html'));
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

创建 preload.js 文件。这将是安全地将 desktopCapturer API 暴露给渲染进程的关键:

const { contextBridge, desktopCapturer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
getScreenSources: () => desktopCapturer.getSources({ types: ['window', 'screen'] })
});

现在,创建 index.html 来处理网络摄像头/麦克风捕获和桌面捕获:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Audio and Video Capture</title>
<style>
body { font-family: Arial, sans-serif; padding: 10px; }
video { border: 1px solid black; margin-top: 10px; max-width: 100%; background-color: #333; }
.controls button { margin: 5px; padding: 8px 12px; }
.section { margin-bottom: 20px; padding: 10px; border: 1px solid #ccc; }
</style>
</head>
<body>
<div class="section">
<h2>Webcam & Microphone Capture</h2>
<button id="startWebcam">Start Webcam/Mic</button>
<video id="webcamVideo" autoplay playsinline></video>
</div>
<div class="section">
<h2>Screen/Window Capture</h2>
<p>Select a source to capture:</p>
<select id="sourceSelect"></select>
<button id="startScreenCapture">Start Screen Capture</button>
<video id="screenVideo" autoplay playsinline></video>
</div>
<script src="./renderer.js"></script>
</body>
</html>

最后,创建 renderer.js 来实现捕获逻辑:

// renderer.js
document.addEventListener('DOMContentLoaded', () => {
const webcamVideo = document.getElementById('webcamVideo');
const screenVideo = document.getElementById('screenVideo');
const startWebcamBtn = document.getElementById('startWebcam');
const startScreenCaptureBtn = document.getElementById('startScreenCapture');
const sourceSelect = document.getElementById('sourceSelect');
// Webcam and Microphone Capture
startWebcamBtn.onclick = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
webcamVideo.srcObject = stream;
webcamVideo.onloadedmetadata = () => webcamVideo.play();
} catch (e) {
console.error('Error accessing media devices.', e);
alert(`Error accessing media devices: ${e.name} - ${e.message}`);
}
};
// Populate screen/window sources
async function populateSources() {
if (window.electronAPI && window.electronAPI.getScreenSources) {
try {
const sources = await window.electronAPI.getScreenSources();
sourceSelect.innerHTML = ''; // Clear existing options
sources.forEach(source => {
const option = document.createElement('option');
option.value = source.id;
option.text = source.name;
sourceSelect.appendChild(option);
});
} catch (e) {
console.error('Error getting screen sources:', e);
alert('Error getting screen sources. Check console.');
}
} else {
console.warn('electronAPI.getScreenSources not available. Is preload script working?');
alert('Screen capture API not available.');
}
}
populateSources(); // Call on load
// Screen/Window Capture
startScreenCaptureBtn.onclick = async () => {
const sourceId = sourceSelect.value;
if (!sourceId) {
alert('Please select a source to capture.');
return;
}
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false, // or { mandatory: { chromeMediaSource: 'desktop' } } for audio capture from desktop
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceId,
minWidth: 1280,
maxWidth: 1920,
minHeight: 720,
maxHeight: 1080
}
}
});
screenVideo.srcObject = stream;
screenVideo.onloadedmetadata = () => screenVideo.play();
} catch (e) {
console.error('Error starting screen capture.', e);
alert(`Error starting screen capture: ${e.name} - ${e.message}`);
}
};
});

使用 npx electron . 运行应用。您将看到两个部分:

  1. Webcam & Microphone Capture: 点击“Start Webcam/Mic”将提示请求权限。如果获得许可,您的网络摄像头画面将出现在第一个视频元素中。

  2. Screen/Window Capture: 下拉列表中将填充可用的屏幕和打开的窗口。选择其中一个,然后点击“Start Screen Capture”。所选屏幕/窗口的内容将流式传输到第二个视频元素。

然后可以进一步处理这些捕获的流(MediaStream 对象),例如,使用 MediaRecorder API 进行录制,或通过网络传输进行视频会议。为了保存捕获的媒体,您通常会在渲染进程中使用 MediaRecorder,然后通过 IPC 将生成的 Blob 数据发送到主进程,以便使用 Node.js 的 fs 模块保存到文件中。这使文件系统操作远离渲染进程,符合安全最佳实践。

有关使用 MediaRecorder API 保存流的详细信息,请参阅 MDN Web Docs:https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder