Files
Retribusi/proxy/rtsp-websocket-proxy.js

152 lines
4.5 KiB
JavaScript
Raw Permalink Normal View History

/**
* RTSP to WebSocket Proxy Server
* Convert RTSP stream ke WebSocket untuk jsmpeg player
*
* Usage:
* node rtsp-websocket-proxy.js <rtsp_url> <ws_port>
*
* Example:
* node rtsp-websocket-proxy.js rtsp://10.60.0.10:8554/cam1 8082
*/
const WebSocket = require('ws');
const { spawn } = require('child_process');
// Parse arguments
const rtspUrl = process.argv[2] || 'rtsp://10.60.0.10:8554/cam1';
const wsPort = parseInt(process.argv[3]) || 8082;
console.log('🚀 Starting RTSP to WebSocket Proxy');
console.log('📹 RTSP URL:', rtspUrl);
console.log('🔌 WebSocket Port:', wsPort);
console.log('');
// Create WebSocket server
const wss = new WebSocket.Server({
port: wsPort,
perMessageDeflate: false
});
let ffmpegProcess = null;
let clients = new Set();
wss.on('connection', (ws) => {
console.log('✅ New WebSocket client connected');
clients.add(ws);
// Start FFmpeg process jika belum ada
if (!ffmpegProcess) {
startFFmpeg();
}
ws.on('close', () => {
console.log('❌ WebSocket client disconnected');
clients.delete(ws);
// Stop FFmpeg jika tidak ada client lagi
if (clients.size === 0 && ffmpegProcess) {
console.log('⏹️ No clients, stopping FFmpeg');
stopFFmpeg();
}
});
ws.on('error', (error) => {
console.error('❌ WebSocket error:', error.message);
});
});
function startFFmpeg() {
console.log('🎬 Starting FFmpeg process...');
// FFmpeg command untuk convert RTSP ke MPEG1 video stream
// Format: MPEG1 video (untuk jsmpeg) dengan resolusi 800x600, bitrate 1000k
const ffmpegArgs = [
'-i', rtspUrl, // Input RTSP URL
'-f', 'mpegts', // Output format: MPEG Transport Stream
'-codec:v', 'mpeg1video', // Video codec: MPEG1 (untuk jsmpeg)
'-s', '800x600', // Resolution
'-b:v', '1000k', // Video bitrate
'-bf', '0', // No B-frames
'-codec:a', 'mp2', // Audio codec: MP2
'-b:a', '128k', // Audio bitrate
'-r', '25', // Frame rate: 25 fps
'pipe:1' // Output to stdout
];
ffmpegProcess = spawn('ffmpeg', ffmpegArgs, {
stdio: ['ignore', 'pipe', 'pipe']
});
ffmpegProcess.stdout.on('data', (data) => {
// Broadcast ke semua connected clients
clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
try {
client.send(data);
} catch (error) {
console.error('Error sending data to client:', error.message);
}
}
});
});
ffmpegProcess.stderr.on('data', (data) => {
// FFmpeg logs ke stderr, bisa di-ignore atau di-log untuk debugging
const message = data.toString();
if (message.includes('error') || message.includes('Error')) {
console.error('FFmpeg error:', message);
}
});
ffmpegProcess.on('exit', (code, signal) => {
console.log(`⚠️ FFmpeg process exited with code ${code}, signal ${signal}`);
ffmpegProcess = null;
// Restart jika masih ada clients
if (clients.size > 0) {
console.log('🔄 Restarting FFmpeg...');
setTimeout(() => startFFmpeg(), 2000);
}
});
ffmpegProcess.on('error', (error) => {
console.error('❌ FFmpeg spawn error:', error.message);
console.error('💡 Make sure FFmpeg is installed and available in PATH');
ffmpegProcess = null;
});
console.log('✅ FFmpeg process started');
}
function stopFFmpeg() {
if (ffmpegProcess) {
console.log('⏹️ Stopping FFmpeg process...');
ffmpegProcess.kill('SIGTERM');
ffmpegProcess = null;
}
}
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\n🛑 Shutting down...');
stopFFmpeg();
wss.close(() => {
console.log('✅ Server closed');
process.exit(0);
});
});
process.on('SIGTERM', () => {
console.log('\n🛑 Shutting down...');
stopFFmpeg();
wss.close(() => {
console.log('✅ Server closed');
process.exit(0);
});
});
console.log(`✅ WebSocket server listening on ws://0.0.0.0:${wsPort}`);
console.log('💡 Connect from browser: ws://localhost:' + wsPort);
console.log('💡 Press Ctrl+C to stop\n');