Linux服务器用Puppeteer实现带声浏览器录屏如何解决多进程音频串扰
Linux多进程网页音视频录制音频串扰解决方案
核心原理
所有音频串扰的根源是多进程的Chrome默认共用系统PulseAudio的同一个输出 sink,FFmpeg采集时会拾取所有输出到该sink的音频。只要为每个录制进程分配独立的专属虚拟音频sink,让对应Chrome的音频仅输出到该sink,FFmpeg仅采集该sink的音频流,即可完全实现音频隔离。
前置依赖
确保服务器已安装PulseAudio相关工具:
apt install pulseaudio pulseaudio-utils
代码修改点
- 动态生成唯一ID,用于标识当前录制进程的专属资源
- 动态分配xvfb显示号,避免多进程显示冲突
- 启动录制前为当前进程创建专属PulseAudio虚拟sink
- 启动Chrome时传入环境变量指定音频输出到专属sink
- FFmpeg音频输入指定为专属sink的monitor端口
- 进程结束后自动清理虚拟sink资源,避免残留
修改后完整代码
var Xvfb = require('xvfb'); var puppeteer = require('puppeteer'); const { spawn, spawnSync } = require('child_process'); async function record() { // 生成当前进程唯一标识 const uniqueId = Date.now() + '_' + Math.floor(Math.random() * 10000); // 动态分配xvfb显示号,避免冲突 const displayNum = 100 + Math.floor(Math.random() * 100); // 1. 创建专属PulseAudio虚拟sink const loadSinkRes = spawnSync('pactl', [ 'load-module', 'module-null-sink', `sink_name=rec_sink_${uniqueId}`, `sink_properties=device.description="rec_sink_${uniqueId}"` ]); const sinkModuleId = loadSinkRes.stdout.toString().trim(); if (!sinkModuleId) { throw new Error('创建虚拟音频设备失败'); } var xvfb = new Xvfb({ displayNum: displayNum, reuse: false, xvfb_args: [ "-screen", "0", "1920x1080x24", "-ac", "-nolisten", "tcp", "-dpi", "96", "+extension", "RANDR" ] }); xvfb.startSync(); // 2. 启动Chrome时传入环境变量,指定音频输出到专属sink var browser = await puppeteer.launch({ headless: false, defaultViewport: null, ignoreDefaultArgs: [ "--mute-audio", "--enable-automation" ], args: [ "--use-fake-ui-for-media-stream", "--window-size=1920,1080", "--start-fullscreen" ], env: { ...process.env, PULSE_SINK: `rec_sink_${uniqueId}` } }); const page = await browser.newPage(); var url = "http://www.noiseaddicts.com/free-samples-mp3/?id=2544"; await page.goto(url); await page.click('span.map_play'); var time = new Date().getTime(); // 3. FFmpeg音频输入指定为专属sink的monitor var options = [ "-video_size", "1920x1080", "-framerate", "30", "-f", "x11grab", "-draw_mouse", "0", "-i", `:${displayNum}`, "-f", "pulse", "-ac", "2", "-i", `rec_sink_${uniqueId}.monitor`, "./output" + time + ".mkv" ]; var cmd = 'ffmpeg'; var proc = spawn(cmd, options); proc.stdout.on('data', function (data) { console.log(data); }); proc.stderr.setEncoding("utf8") proc.stderr.on('data', function (data) { console.log(data); }); proc.on('close', async function () { console.log('finished'); // 4. 清理资源:先关浏览器、卸载虚拟sink,再关xvfb await browser.close(); spawnSync('pactl', ['unload-module', sinkModuleId]); xvfb.stopSync(); }); } record();
注意事项
- 若PulseAudio以系统服务运行,需确保运行Node.js进程的用户有pactl操作权限
- 高并发场景下可预先分配一批可用的display号和sink标识,避免动态生成时出现冲突
- 可添加异常捕获逻辑,确保进程意外退出时也能卸载sink模块,避免残留无效音频设备占用资源
内容的提问来源于stack exchange,提问作者NA GUY
相关产品推荐
相关产品推荐

