I am trying a record a video by running xvfb inside a docker image. No matter what I do it gives me black screen.
Screen size same in xvfb and ffmpeg and puppeteer.
It will would really great if someone can help.
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
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"]
this is code code that launches puppeteer and from a nodejs application and create spawns a process for ffmpeg
export class UnixBrowserRecorder implements Recorder {
url = 'https://stackoverflow.com/questions/3143698/uncaught-syntaxerror-unexpected-token'; // Replace with your URL
outputFilePath = `/app/output_video.mp4`; // Output file path within the container
durationInSeconds = 6; // Duration of the video in seconds
resolution = '1280x720';
public async capture(): Promise<string> {
const browser = await puppeteer.launch({
args: [
'--no-sandbox', // Required in Docker
'--disable-setuid-sandbox', // Required in Docker
'--disable-dev-shm-usage', // Required in Docker
'--headless', // Run browser in headless mode
'--disable-gpu', // Disable GPU acceleration
`--window-size=${this.resolution}` // Set window size
],
executablePath: '/usr/bin/chromium' // Specify the path to Chromium executable
});
const page = await browser.newPage();
await page.goto(this.url);
await page.screenshot({
"type": "png", // can also be "jpeg" or "webp" (recommended)
"path": `/app/screenshot.png`, // where to save it
"fullPage": true, // will scroll down to capture everything if true
});
//ffmpeg -video_size `DISPLAY=:5 xdpyinfo | grep 'dimensions:'|awk '{print $2}'` -framerate 30 -f x11grab -i :5.0+0,0 output.mpg
const ffmpegProcess = spawn('ffmpeg', [
'-video_size', this.resolution,
'-framerate', '30',
'-f', 'x11grab',
'-i', ':99', // Use display :99 (assuming Xvfb is running on this display)
'-t', this.durationInSeconds.toString(),
'-c:v', 'libx264',
'-loglevel', 'debug',
'-pix_fmt', 'yuv420p',
this.outputFilePath
]);
// Log ffmpeg output
ffmpegProcess.stdout.on('data', data => {
console.log(`ffmpegProcess stdout: ${data}`);
});
ffmpegProcess.stderr.on('data', data => {
console.error(`ffmpegProcess stderr: ${data}`);
});
// Handle ffmpegProcess process exit
ffmpegProcess.on('close', code => {
console.log(`ffmpeg process exited with code ${code}`);
});
// Wait for the duration to complete
await new Promise(resolve => setTimeout(resolve, this.durationInSeconds * 1000));
// Close the FFmpeg stream and process
ffmpegProcess.stdin.end();
// Close Puppeteer
await page.close();
await browser.close();
return "Video generated successfully";
}
}
I was finally able to capture the video by removing below argument while launching puppeteer
'--headless', // Run browser in headless mode