Skip to content
Extraits de code Groupes Projets
Valider 7d8f9ffb rédigé par oussama aftys's avatar oussama aftys
Parcourir les fichiers

Update recorder.js

parent 8cce8ef3
Branches ant-media
Aucune requête de fusion associée trouvée
......@@ -5,11 +5,11 @@ require('dotenv').config();
const s3 = new AWS.S3({
credentials: {
accessKeyId: "AKIA3FLDZT775ARNAJHT",
secretAccessKey: "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"
accessKeyId: "AKIATAVAAX3PWTIIE46W",
secretAccessKey: "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"
},
region: process.env.AWS_REGION || "eu-north-1",
endpoint: process.env.AWS_ENDPOINT || "https://s3.eu-north-1.amazonaws.com"
region: process.env.AWS_REGION || "eu-west-3",
endpoint: process.env.AWS_ENDPOINT || "https://s3.eu-west-3.amazonaws.com"
});
......@@ -23,47 +23,51 @@ const activeRecordings = new Map();
*/
function startRecording(streamKey) {
const outputFile = `/tmp/${streamKey}.mp4`;
const ffmpegArgs = [
'-timeout', '5000000',
'-reconnect', '1',
'-reconnect_at_eof', '1',
'-reconnect_streamed', '1',
'-reconnect_delay_max', '2',
'-i', `http://127.0.0.1/hls/${streamKey}.m3u8`,
'-i', `https://ant-media-ovh.mydressin-server.com:5443/live/streams/${streamKey}_adaptive.m3u8`,
'-c', 'copy',
outputFile,
];
// Introduce a 20-second delay before starting the FFmpeg process
setTimeout(() => {
// Spawn FFmpeg process after the delay
const ffmpegProcess = spawn('ffmpeg', ffmpegArgs);
// Spawn FFmpeg process
const ffmpegProcess = spawn('ffmpeg', ffmpegArgs);
// Handle stderr output (FFmpeg logs to stderr)
ffmpegProcess.stderr.on('data', (data) => {
console.error(`[${streamKey}] FFmpeg Error: ${data}`);
});
// Handle stderr output (FFmpeg logs to stderr)
ffmpegProcess.stderr.on('data', (data) => {
console.error(`[${streamKey}] FFmpeg Error: ${data}`);
});
// Handle process errors
ffmpegProcess.on('error', (err) => {
console.error(`[${streamKey}] Process Error: ${err}`);
activeRecordings.delete(streamKey);
});
// Handle process errors
ffmpegProcess.on('error', (err) => {
console.error(`[${streamKey}] Process Error: ${err}`);
activeRecordings.delete(streamKey);
});
// Handle process exit
ffmpegProcess.on('exit', (code, signal) => {
console.log(`[${streamKey}] FFmpeg exited with code ${code} (${signal})`);
activeRecordings.delete(streamKey);
});
// Handle process exit
ffmpegProcess.on('exit', (code, signal) => {
console.log(`[${streamKey}] FFmpeg exited with code ${code} (${signal})`);
activeRecordings.delete(streamKey);
});
// Store recording information in Map
activeRecordings.set(streamKey, {
process: ffmpegProcess,
outputFile,
startTime: Date.now()
});
// Store recording information in Map
activeRecordings.set(streamKey, {
process: ffmpegProcess,
outputFile,
startTime: Date.now()
});
console.log(`[${streamKey}] FFmpeg process started after 20 seconds delay`);
}, 20000); // 20 seconds delay before starting FFmpeg
return ffmpegProcess.pid;
return 0;
}
/**
......@@ -73,56 +77,66 @@ function startRecording(streamKey) {
*/
async function stopRecording(streamKey) {
const recording = activeRecordings.get(streamKey);
if (!recording) {
throw new Error(`No active recording for ${streamKey}`);
}
const newFileName = `${streamKey}.mp4`;
const outputFile = `/tmp/${streamKey}.mp4`;
const { process: ffmpegProcess, outputFile } = recording;
const newFileName = `${streamKey}.mp4`; // Unique filename with timestamp
if (!recording) {
console.error(`[${streamKey}] No active recording found`);
// Use setImmediate to defer the upload and cleanup
setImmediate(() => uploadAndCleanup(outputFile, newFileName));
return {
status: 'error',
message: `No active recording for ${streamKey}`
};
} else {
try {
// Graceful shutdown with SIGINT
ffmpegProcess.kill('SIGINT');
// Wait for graceful exit with timeout
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(`[${streamKey}] FFmpeg shutdown timeout`));
}, 30000); // 30-second timeout
ffmpegProcess.once('exit', (code, signal) => {
clearTimeout(timeout);
if (code === 0) {
console.log(`[${streamKey}] FFmpeg exited gracefully`);
resolve();
} else {
reject(new Error(`[${streamKey}] FFmpeg exited with code ${code} (${signal})`));
}
});
});
} catch (err) {
console.error(`[${streamKey}] Error stopping recording:`, err.message);
try {
// Force kill if graceful shutdown failed
ffmpegProcess.kill('SIGKILL');
} catch (killErr) {
console.error(`[${streamKey}] Force kill failed:`, killErr.message);
const { process: ffmpegProcess } = recording;
ffmpegProcess.kill('SIGINT');
// Wait for graceful exit with timeout
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(`[${streamKey}] FFmpeg shutdown timeout`));
}, 30000); // 30-second timeout
ffmpegProcess.once('exit', (code, signal) => {
clearTimeout(timeout);
if (code === 0) {
console.log(`[${streamKey}] FFmpeg exited gracefully`);
resolve();
} else {
reject(new Error(`[${streamKey}] FFmpeg exited with code ${code} (${signal})`));
}
});
});
} catch (err) {
console.error(`[${streamKey}] Error stopping recording:`, err.message);
try {
// Force kill if graceful shutdown failed
ffmpegProcess.kill('SIGKILL');
} catch (killErr) {
console.error(`[${streamKey}] Force kill failed:`, killErr.message);
}
} finally {
// Always clean up from active recordings
activeRecordings.delete(streamKey);
}
} finally {
// Always clean up from active recordings
activeRecordings.delete(streamKey);
}
try {
await uploadAndCleanup(outputFile, newFileName);
return {
// Use setImmediate to defer the upload and cleanup
setImmediate(async () => {
try {
await uploadAndCleanup(outputFile, newFileName);
console.log(`[${streamKey}] Recording uploaded as ${newFileName}`);
} catch (err) {
console.error(`[${streamKey}] Upload failed:`, err.message);
}
});
return {
status: 'success',
message: `Recording uploaded as ${newFileName}`,
message: `Recording process initiated for ${newFileName}`,
file: newFileName
};
} catch (err) {
console.error(`[${streamKey}] Upload failed:`, err.message);
throw err;
}
}
......@@ -138,6 +152,7 @@ async function uploadAndCleanup(outputFile, newFileName) {
console.log(`[${newFileName}] Local file cleaned up`);
} catch (err) {
console.error(`[${newFileName}] Cleanup error:`, err.message);
fs.unlinkSync(outputFile);
throw err;
}
}
......@@ -152,9 +167,9 @@ async function uploadToS3(filePath, newFileName) {
return new Promise((resolve, reject) => {
// Use file stream for memory efficiency
const fileStream = fs.createReadStream(filePath);
const params = {
Bucket: 'bucket-rtmp',
Bucket: 'mydressin-live-records',
Key: newFileName,
Body: fileStream,
ContentType: 'video/mp4',
......
0% ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter