63 lines
1.7 KiB
JavaScript
63 lines
1.7 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const { spawn } = require('child_process');
|
|
const dotenv = require('dotenv');
|
|
|
|
dotenv.config();
|
|
|
|
const app = express();
|
|
app.use(cors());
|
|
|
|
const port = process.env.PORT || 3000;
|
|
const rtspUrl = process.env.RTSP_URL;
|
|
|
|
app.get('/stream', (req, res) => {
|
|
if (!rtspUrl) {
|
|
return res.status(500).send('RTSP_URL not configured in .env');
|
|
}
|
|
|
|
res.writeHead(200, {
|
|
'Content-Type': 'multipart/x-mixed-replace; boundary=ffserver',
|
|
'Cache-Control': 'no-cache',
|
|
'Connection': 'close',
|
|
'Pragma': 'no-cache'
|
|
});
|
|
|
|
const ffmpegArgs = [
|
|
'-rtsp_transport', 'tcp',
|
|
'-i', rtspUrl,
|
|
'-f', 'mpjpeg',
|
|
'-r', '15', // 15 fps is enough for motion detection
|
|
'-q:v', '5', // Quality (2-31, lower is better)
|
|
'-s', '1280x720', // Resize to 720p to save bandwidth and CPU
|
|
'-' // Output to stdout
|
|
];
|
|
|
|
console.log('Starting ffmpeg with args:', ffmpegArgs.join(' '));
|
|
const ffmpeg = spawn('ffmpeg', ffmpegArgs);
|
|
|
|
ffmpeg.stdout.on('data', (data) => {
|
|
res.write(data);
|
|
});
|
|
|
|
ffmpeg.stderr.on('data', (data) => {
|
|
// Uncomment for debugging ffmpeg errors
|
|
// console.error(`ffmpeg stderr: ${data.toString()}`);
|
|
});
|
|
|
|
ffmpeg.on('close', (code) => {
|
|
console.log(`ffmpeg process exited with code ${code}`);
|
|
res.end();
|
|
});
|
|
|
|
req.on('close', () => {
|
|
console.log('Client disconnected, killing ffmpeg process');
|
|
ffmpeg.kill('SIGKILL');
|
|
});
|
|
});
|
|
|
|
app.listen(port, () => {
|
|
console.log(`Server listening on port ${port}`);
|
|
console.log(`MJPEG Stream available at http://localhost:${port}/stream`);
|
|
});
|