You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 22:13:53 +01:00
Added reconnect logic for FFmpeg on Windows
This commit is contained in:
committed by
GitHub
parent
2ade42d1fb
commit
c3e18de6e4
@@ -141,39 +141,93 @@ checkFFmpeg().then((ffmpegPath) => {
|
|||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
// Windows (FFmpeg DirectShow Capture)
|
// Windows (FFmpeg DirectShow Capture)
|
||||||
const commandDef = buildCommand(ffmpegPath);
|
let ffmpeg;
|
||||||
|
let restartTimer = null;
|
||||||
|
let lastTimestamp = null;
|
||||||
|
let lastCheckTime = Date.now();
|
||||||
|
let audioErrorLogged = false;
|
||||||
|
let staleCount = 0;
|
||||||
|
|
||||||
let ffmpegArgs = commandDef.args;
|
function launchFFmpeg() {
|
||||||
|
const commandDef = buildCommand(ffmpegPath);
|
||||||
|
let ffmpegArgs = commandDef.args;
|
||||||
|
|
||||||
// Apply audio boost if enabled
|
// Apply audio boost if enabled
|
||||||
if (serverConfig.audio.audioBoost) {
|
if (serverConfig.audio.audioBoost) {
|
||||||
ffmpegArgs.splice(ffmpegArgs.indexOf('pipe:1'), 0, '-af', 'volume=3.5');
|
ffmpegArgs.splice(ffmpegArgs.indexOf('pipe:1'), 0, '-af', 'volume=3.5');
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug(`${consoleLogTitle} Launching FFmpeg with args: ${ffmpegArgs.join(' ')}`);
|
||||||
|
ffmpeg = spawn(ffmpegPath, ffmpegArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
|
||||||
|
|
||||||
|
audioServer.waitUntilReady.then(() => {
|
||||||
|
audioServer.Server.StdIn = ffmpeg.stdout;
|
||||||
|
audioServer.Server.Run();
|
||||||
|
connectMessage(`${consoleLogTitle} Connected FFmpeg (capture) → FFmpeg (process) → Server.StdIn${serverConfig.audio.audioBoost ? ' (audio boost)' : ''}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
ffmpeg.stderr.on('data', (data) => {
|
||||||
|
const msg = data.toString();
|
||||||
|
logFfmpeg(`[FFmpeg stderr]: ${msg}`);
|
||||||
|
|
||||||
|
if (msg.includes('I/O error') && !audioErrorLogged) {
|
||||||
|
audioErrorLogged = true;
|
||||||
|
logError(`${consoleLogTitle} Audio device "${serverConfig.audio.audioDevice}" failed to start.`);
|
||||||
|
logError('Please start the server with: node . --ffmpegdebug for more info.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect frozen timestamp
|
||||||
|
const match = msg.match(/time=(\d\d):(\d\d):(\d\d\.\d+)/);
|
||||||
|
if (match) {
|
||||||
|
const [_, hh, mm, ss] = match;
|
||||||
|
const totalSec = parseInt(hh) * 3600 + parseInt(mm) * 60 + parseFloat(ss);
|
||||||
|
|
||||||
|
if (lastTimestamp !== null && totalSec === lastTimestamp) {
|
||||||
|
const now = Date.now();
|
||||||
|
staleCount++;
|
||||||
|
if (staleCount >= 10 && now - lastCheckTime > 10000 && !restartTimer) {
|
||||||
|
restartTimer = setTimeout(() => {
|
||||||
|
restartTimer = null;
|
||||||
|
staleCount = 0;
|
||||||
|
try {
|
||||||
|
ffmpeg.kill('SIGKILL');
|
||||||
|
} catch (e) {
|
||||||
|
logWarn(`${consoleLogTitle} Failed to kill FFmpeg process: ${e.message}`);
|
||||||
|
}
|
||||||
|
launchFFmpeg(); // Restart FFmpeg
|
||||||
|
}, 0);
|
||||||
|
setTimeout(() => logWarn(`${consoleLogTitle} FFmpeg appears frozen. Restarting...`), 100);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lastTimestamp = totalSec;
|
||||||
|
lastCheckTime = Date.now();
|
||||||
|
staleCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ffmpeg.on('exit', (code, signal) => {
|
||||||
|
if (signal) {
|
||||||
|
logFfmpeg(`[FFmpeg exited] with signal ${signal}`);
|
||||||
|
logWarn(`${consoleLogTitle} FFmpeg was killed with signal ${signal}`);
|
||||||
|
} else {
|
||||||
|
logFfmpeg(`[FFmpeg exited] with code ${code}`);
|
||||||
|
if (code !== 0) {
|
||||||
|
logWarn(`${consoleLogTitle} FFmpeg exited unexpectedly with code ${code}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry on device fail
|
||||||
|
if (audioErrorLogged) {
|
||||||
|
logWarn(`${consoleLogTitle} Retrying in 10 seconds...`);
|
||||||
|
setTimeout(() => {
|
||||||
|
audioErrorLogged = false;
|
||||||
|
launchFFmpeg();
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
launchFFmpeg(); // Initial launch
|
||||||
logDebug(`${consoleLogTitle} Launching FFmpeg with args: ${ffmpegArgs.join(' ')}`);
|
|
||||||
const ffmpeg = spawn(ffmpegPath, ffmpegArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
||||||
|
|
||||||
audioServer.waitUntilReady.then(() => {
|
|
||||||
audioServer.Server.StdIn = ffmpeg.stdout;
|
|
||||||
audioServer.Server.Run();
|
|
||||||
connectMessage(`${consoleLogTitle} Connected FFmpeg (capture) → FFmpeg (process) → Server.StdIn${serverConfig.audio.audioBoost ? ' (audio boost)' : ''}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
ffmpeg.stderr.on('data', (data) => {
|
|
||||||
logFfmpeg(`[FFmpeg stderr]: ${data}`);
|
|
||||||
|
|
||||||
if (data.includes('I/O error') && !audioErrorLogged) {
|
|
||||||
audioErrorLogged = true;
|
|
||||||
logError(`${consoleLogTitle} Audio device "${serverConfig.audio.audioDevice}" failed to start.`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ffmpeg.on('exit', (code) => {
|
|
||||||
logFfmpeg(`[FFmpeg exited] with code ${code}`);
|
|
||||||
if (code !== 0) {
|
|
||||||
logWarn(`${consoleLogTitle} FFmpeg exited unexpectedly with code ${code}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (process.platform === 'darwin') {
|
} else if (process.platform === 'darwin') {
|
||||||
// macOS (rec --> 3las.server.js --> FFmpeg)
|
// macOS (rec --> 3las.server.js --> FFmpeg)
|
||||||
const commandDef = buildCommand(ffmpegPath);
|
const commandDef = buildCommand(ffmpegPath);
|
||||||
|
|||||||
Reference in New Issue
Block a user