Docker环境下Xvfb录屏出现黑屏问题求助
Docker下Xvfb录屏黑屏问题解决指南
问题背景
在Docker镜像中使用Xvfb配合ffmpeg和Puppeteer录屏时,录制的视频始终显示黑屏。已确认Xvfb、ffmpeg与Puppeteer的屏幕分辨率保持一致,相关配置与代码如下:
start-xvfb.sh
# Start Xvfb Xvfb :99 -screen 0 1280x720x24 & # Set the display environment variable export DISPLAY=:99 # Run the application (assuming it starts with npm start) npm run dev
Dockerfile
FROM node:lts-alpine3.19 # Install dependencies using apk RUN apk update && \ apk add --no-cache \ gnupg \ ffmpeg \ libx11 \ libxcomposite \ libxdamage \ libxi \ libxtst \ nss \ cups-libs \ libxrandr \ alsa-lib \ pango \ gtk+3.0 \ xvfb \ bash \ curl \ udev \ ttf-freefont \ chromium \ chromium-chromedriver # Set working directory WORKDIR /app # Copy package.json and install dependencies COPY package.json . RUN npm install --force # Copy remaining source code COPY . . # Add a script to start Xvfb COPY start-xvfb.sh /app/start-xvfb.sh RUN chmod +x /app/start-xvfb.sh # Expose the port EXPOSE 4200 EXPOSE 3000 # Command to start Xvfb and run the application CMD ["./start-xvfb.sh"]
Node.js录屏代码
export class UnixBrowserRecorder implements Recorder { url = 'https://stackoverflow.com/questions/3143698/uncaught-syntaxerror-unexpected-token'; outputFilePath = `/app/output_video.mp4`; durationInSeconds = 6; resolution = '1280x720'; public async capture(): Promise<string> { const browser = await puppeteer.launch({ args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--headless', '--disable-gpu', `--window-size=${this.resolution}` ], executablePath: '/usr/bin/chromium' }); const page = await browser.newPage(); await page.goto(this.url); await page.screenshot({ "type": "png", "path": `/app/screenshot.png`, "fullPage": true, }); const ffmpegProcess = spawn('ffmpeg', [ '-video_size', this.resolution, '-framerate', '30', '-f', 'x11grab', '-i', ':99', '-t', this.durationInSeconds.toString(), '-c:v', 'libx264', '-loglevel', 'debug', '-pix_fmt', 'yuv420p', this.outputFilePath ]); ffmpegProcess.stdout.on('data', data => { console.log(`ffmpegProcess stdout: ${data}`); }); ffmpegProcess.stderr.on('data', data => { console.error(`ffmpegProcess stderr: ${data}`); }); ffmpegProcess.on('close', code => { console.log(`ffmpeg process exited with code ${code}`); }); await new Promise(resolve => setTimeout(resolve, this.durationInSeconds * 1000)); ffmpegProcess.stdin.end(); await page.close(); await browser.close(); return "Video generated successfully"; } }

解决方案
1. 确保Puppeteer绑定到Xvfb的显示
Puppeteer启动时未明确指定DISPLAY环境变量和显示参数,可能未连接到Xvfb的:99显示。修改启动配置:
const browser = await puppeteer.launch({ args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--headless=new', // 改用新版无头模式,旧版可能不支持X11渲染 '--disable-gpu', `--window-size=${this.resolution}`, `--display=:99` // 强制指定目标显示 ], executablePath: '/usr/bin/chromium', env: { ...process.env, DISPLAY: ':99' } // 传递DISPLAY环境变量给Chromium });
2. 修正ffmpeg的X11抓取参数
ffmpeg输入参数需明确指定屏幕编号(Xvfb的screen 0对应:99.0),否则可能抓取到空显示:
const ffmpegProcess = spawn('ffmpeg', [ '-video_size', this.resolution, '-framerate', '30', '-f', 'x11grab', '-i', ':99.0', // 明确指定screen 0 '-t', this.durationInSeconds.toString(), '-c:v', 'libx264', '-loglevel', 'info', // 降低日志级别,便于排查关键信息 '-pix_fmt', 'yuv420p', this.outputFilePath ]);
3. 等待Xvfb完全初始化再启动应用
原start-xvfb.sh中Xvfb启动后立即运行应用,可能Xvfb还未完成初始化。添加等待逻辑:
# Start Xvfb Xvfb :99 -screen 0 1280x720x24 & XVFB_PID=$! # 等待Xvfb初始化完成 until xdpyinfo -display :99 > /dev/null 2>&1; do sleep 0.5 done # 设置显示环境变量 export DISPLAY=:99 # 启动应用 npm run dev # 退出时清理Xvfb进程 trap "kill $XVFB_PID" EXIT
4. 验证Xvfb显示可用性
在容器内手动执行以下命令,确认Xvfb运行正常:
# 检查显示分辨率 xdpyinfo -display :99 | grep dimensions # 测试X11渲染(需先安装xeyes) apk add xeyes xeyes -display :99
5. 排查无头模式兼容性
旧版--headless模式可能不会将画面渲染到X11显示,可尝试:
- 改用
--headless=new(Chromium 112+支持) - 临时关闭无头模式测试:
'--headless=false'+'--start-maximized'
验证步骤
- 检查
/app/screenshot.png是否正常生成,确认Puppeteer能正确渲染页面。 - 查看ffmpeg日志,若出现
frame=xxx的输出,说明正在正常抓取画面。 - 若问题仍存在,将ffmpeg的
-loglevel设为debug,检查是否有X11抓取相关的错误提示。
内容的提问来源于stack exchange,提问作者Vivek
相关产品推荐
相关产品推荐

