Created
March 29, 2025 19:17
-
-
Save sao/3c282b2d886de79dcbdf5f67451f83f9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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