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

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'

验证步骤

  1. 检查/app/screenshot.png是否正常生成,确认Puppeteer能正确渲染页面。
  2. 查看ffmpeg日志,若出现frame=xxx的输出,说明正在正常抓取画面。
  3. 若问题仍存在,将ffmpeg的-loglevel设为debug,检查是否有X11抓取相关的错误提示。

内容的提问来源于stack exchange,提问作者Vivek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:24:57