Skip to content

Instantly share code, notes, and snippets.

@sao
Created March 29, 2025 19:17
Show Gist options
  • Save sao/3c282b2d886de79dcbdf5f67451f83f9 to your computer and use it in GitHub Desktop.
Save sao/3c282b2d886de79dcbdf5f67451f83f9 to your computer and use it in GitHub Desktop.
const puppeteer = require('puppeteer');
const { spawn } = require('child_process');
async function streamToYouTube(url, streamKey) {
const browser = await puppeteer.launch({
headless: false,
defaultViewport: null, // Allow viewport to be set by window size
args: [
'--window-size=1280,720',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--start-maximized' // Start with maximized window
]
});
const page = await browser.newPage();
// Set viewport to match window size
await page.setViewport({
width: 1280,
height: 720,
deviceScaleFactor: 1,
});
// Navigate to URL and wait for network to be idle
await page.goto(url, {
waitUntil: 'networkidle0',
timeout: 30000 // 30 second timeout
});
// Start capturing screenshots and pipe them to ffmpeg
const ffmpeg = spawn('ffmpeg', [
'-y',
'-f', 'image2pipe',
'-framerate', '30',
'-i', '-',
'-f', 'avfoundation',
'-i', ':1',
'-c:v', 'libx264',
'-preset', 'ultrafast', // Back to ultrafast for better performance
'-pix_fmt', 'yuv420p',
'-b:v', '2000k', // Further reduced bitrate
'-maxrate', '2000k',
'-bufsize', '4000k',
'-g', '30', // Reduced GOP size
'-keyint_min', '15', // Reduced keyframe interval
'-c:a', 'aac',
'-ar', '44100',
'-b:a', '128k',
'-f', 'flv',
`rtmp://a.rtmp.youtube.com/live2/${streamKey}`
]);
let lastFrameTime = Date.now();
const frameInterval = 1000 / 30; // 30fps
let frameCount = 0;
const startTime = Date.now();
// Capture screenshots and pipe to ffmpeg
while (true) {
try {
const currentTime = Date.now();
const timeSinceLastFrame = currentTime - lastFrameTime;
if (timeSinceLastFrame >= frameInterval) {
// Take screenshot with reduced quality and optimized settings
const screenshot = await page.screenshot({
type: 'jpeg',
quality: 60, // Further reduced quality
encoding: 'binary',
fullPage: false // Only capture viewport
});
// Write frame to ffmpeg
ffmpeg.stdin.write(screenshot);
// Update timing
lastFrameTime = currentTime;
frameCount++;
// Log FPS every 5 seconds
if (currentTime - startTime > 5000) {
const fps = frameCount / ((currentTime - startTime) / 1000);
console.log(`Current FPS: ${fps.toFixed(2)}`);
frameCount = 0;
}
} else {
// Sleep for the remaining time
await new Promise(resolve => setTimeout(resolve, 1));
}
} catch (error) {
console.error('Screenshot error:', error);
break;
}
}
ffmpeg.stderr.on('data', (data) => {
console.error(`FFmpeg Error: ${data}`);
});
ffmpeg.on('close', (code) => {
console.log(`FFmpeg exited with code ${code}`);
browser.close();
process.exit();
});
console.log('Streaming started...');
// Handle cleanup on exit
process.on('SIGINT', async () => {
console.log('Shutting down...');
await browser.close();
ffmpeg.stdin.end();
process.exit();
});
}
const url = 'https://silassao.com';
const streamKey = 'XXXXXX';
streamToYouTube(url, streamKey);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment