npm init -y
npm install express socket.io fluent-ffmpeg
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const ffmpeg = require('fluent-ffmpeg');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
const inputStreamUrl = 'rtmp://27.74.246.165:4334/livestream/0902577267_input'; // Địa chỉ RTMP đầu vào
const outputStreamUrl = 'rtmp://27.74.246.165:4334/livestream/0902577267_output'; // Địa chỉ RTMP đầu ra
let lastStats = {};
let serverStatus = 'off'; // Biến lưu trạng thái hiện tại của server (on/off)
let restartDelay = 5000; // Độ trễ trước khi thử lại nếu stream bị ngắt
// Thiết lập trang HTML client
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
// Hàm để xác định loại video dựa trên độ phân giải
function determineVideoQuality(width, height) {
if (width >= 3840 && height >= 2160) {
return '4K';
} else if (width >= 1920 && height >= 1080) {
return 'Full HD';
} else if (width >= 1280 && height >= 720) {
return 'HD';
} else {
return 'SD';
}
}
// Hàm để bắt đầu theo dõi và phát lại stream RTMP
function monitorStream() {
ffmpeg(inputStreamUrl)
.addOption('-fflags', '+genpts') // Tạo lại giá trị PTS nếu cần
.addOption('-analyzeduration', '20000000') // Tăng thời gian phân tích luồng để thu thập thêm thông tin
.addOption('-probesize', '20000000') // Tăng kích thước bộ đệm để phân tích luồng
.on('start', () => {
console.log('Stream started');
serverStatus = 'on'; // Cập nhật trạng thái server
io.emit('streamStatus', { status: 'on' }); // Phát sự kiện khi stream bắt đầu
lastStats = { frameDropped: 0 }; // Reset thống kê
})
.on('codecData', (data) => {
console.log('Codec Data: ', data);
// Xác định loại video dựa trên độ phân giải từ codecData nếu có
if (data.width && data.height) {
lastStats.resolution = determineVideoQuality(data.width, data.height);
} else {
lastStats.resolution = 'N/A'; // Đảm bảo cập nhật N/A nếu không có dữ liệu
}
lastStats.frameRate = data.fps || 'Unknown';
lastStats.bitrate = data.bitrate || 'N/A';
})
.on('stderr', (stderrLine) => {
console.log('FFmpeg stderr output: ', stderrLine);
// Kiểm tra nếu stderr có thông tin bitrate và frame rate
const bitrateMatch = stderrLine.match(/bitrate=\s*([\d.]+kbits\/s)/);
const frameMatch = stderrLine.match(/frame=\s*(\d+)/);
const fpsMatch = stderrLine.match(/fps=\s*([\d.]+)/);
const resolutionMatch = stderrLine.match(/(\d{3,4})x(\d{3,4})/); // Tìm độ phân giải trong stderr
if (bitrateMatch) {
lastStats.bitrate = bitrateMatch[1];
}
if (frameMatch) {
lastStats.frames = parseInt(frameMatch[1], 10);
}
if (fpsMatch) {
lastStats.frameRate = fpsMatch[1];
}
if (resolutionMatch) {
const width = parseInt(resolutionMatch[1], 10);
const height = parseInt(resolutionMatch[2], 10);
lastStats.resolution = determineVideoQuality(width, height); // Xác định loại video từ stderr
}
})
.on('error', (err) => {
console.log('Stream error: ' + err.message);
serverStatus = 'off'; // Cập nhật trạng thái server
io.emit('streamStatus', { status: 'off' }); // Phát sự kiện khi stream gặp lỗi
setTimeout(() => {
console.log('Attempting to restart stream...');
monitorStream(); // Khởi động lại stream sau 5 giây
}, restartDelay);
})
.on('end', () => {
console.log('Stream ended');
serverStatus = 'off'; // Cập nhật trạng thái server
io.emit('streamStatus', { status: 'off' }); // Phát sự kiện khi stream kết thúc
setTimeout(() => {
console.log('Attempting to restart stream...');
monitorStream(); // Khởi động lại stream sau 5 giây
}, restartDelay);
})
.videoCodec('copy') // Sao chép video codec mà không mã hóa lại
.audioCodec('copy') // Sao chép audio codec mà không mã hóa lại
.format('flv') // Định dạng FLV cho RTMP
.output(outputStreamUrl) // Phát luồng RTMP tới địa chỉ đầu ra
.run();
}
// Gửi thông báo liên tục tới client mỗi 15 giây
setInterval(() => {
const currentTime = new Date();
// Lấy ngày, tháng, năm và định dạng lại
const formattedDate = `${currentTime.getDate()}/${currentTime.getMonth() + 1}/${currentTime.getFullYear()}`;
const formattedTime = currentTime.toLocaleTimeString(); // Lấy giờ hiện tại
io.emit('streamUpdate', {
time: `${formattedDate}, ${formattedTime}`, // Định dạng: Ngày/Tháng/Năm, Giờ:Phút:Giây
bitrate: lastStats.bitrate || 'N/A',
resolution: lastStats.resolution || 'N/A',
frameRate: lastStats.frameRate || 'Unknown',
frameDropped: lastStats.frameDropped || 0
});
}, 15000);
io.on('connection', (socket) => {
console.log('Client connected');
// Gửi ngay trạng thái hiện tại của server khi client kết nối
socket.emit('streamStatus', { status: serverStatus });
socket.on('disconnect', () => {
console.log('Client disconnected');
});
});
server.listen(3993, () => {
console.log('Server is running on http://localhost:3993');
monitorStream(); // Bắt đầu theo dõi và phát lại luồng
});
<!DOCTYPE html>
<html lang="en">
<center>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TRẠNG THÁI SERVER RTMP</title>
</head>
<body>
<h1>TRẠNG THÁI SERVER RTMP</h1>
<div>
<p><strong>Trạng thái server:</strong> <span id="server-status">Đang kiểm tra...</span></p>
<p><strong>Hôm nay:📅</strong> <span id="time">Vui lòng chờ trong giây lát...</span></p>
<p><strong>Bitrate hiện tại:🚴</strong> <span id="bitrate">N/A</span></p>
<p><strong>Frame Rate:♒</strong> <span id="frameRate">N/A</span></p>
<p><strong>Frames bị rớt:♒</strong> <span id="frameDropped">N/A</span></p>
</div>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io.connect('http://localhost:3000'); // Kết nối tới server Socket.IO
// Nhận thông tin cập nhật từ server qua sự kiện 'streamUpdate'
socket.on('streamUpdate', (data) => {
// Cập nhật thông tin nhận được vào HTML
document.getElementById('time').textContent = data.time;
document.getElementById('bitrate').textContent = data.bitrate;
document.getElementById('frameRate').textContent = data.frameRate;
document.getElementById('frameDropped').textContent = data.frameDropped;
});
// Lắng nghe sự kiện từ server về trạng thái stream RTMP
socket.on('streamStatus', (data) => {
if (data.status === 'on') {
document.getElementById('server-status').textContent = "🌍Đang hoạt động";
} else {
document.getElementById('server-status').textContent = "🔴Ngừng hoạt động";
}
});
// Log lỗi khi gặp lỗi kết nối
socket.on('error', (error) => {
document.getElementById('server-status').textContent = "Lỗi kết nối server";
console.error('Socket.IO error:', error);
});
</script>
</body>
</center>
</html>