如何在Node.js中关闭指定摄像头ID的rtsp-relay代理流?
正确停止RTSP流媒体的解决方案
你遇到的核心问题是仅关闭WebSocket连接并未终止底层的RTSP流代理进程,导致流仍在持续推送,前端因此还能显示画面。以下是具体的解决步骤和代码示例:
1. 关联Camera ID与流实例
首先需要维护一个全局映射,将每个cameraId对应的RTSP流代理实例、WebSocket连接绑定,方便后续精准关闭目标流。
const activeStreams = new Map(); // 结构: cameraId => { streamInstance, wsConnection }
2. 终止底层RTSP流代理
根据你使用的RTSP代理工具(比如rtsp-relay或直接调用FFmpeg),主动终止流的推送进程:
- 若使用
rtsp-relay:调用流实例的stop()方法 - 若直接使用FFmpeg子进程:调用
process.kill('SIGTERM')终止FFmpeg进程
3. 完整的关闭流程
当前端发起关闭请求(或WebSocket连接断开)时,执行以下步骤:
- 通过
cameraId从映射中取出对应的流实例和WebSocket连接 - 终止RTSP流代理进程
- 关闭WebSocket连接
- 从全局映射中删除该
cameraId的条目,避免内存泄漏
Node.js 后端示例代码
const WebSocket = require('ws'); const { createProxyServer } = require('rtsp-relay'); const http = require('http'); const server = http.createServer(); const wss = new WebSocket.Server({ server }); const activeStreams = new Map(); // 初始化RTSP代理 const proxy = createProxyServer({ http: server }); wss.on('connection', (ws, req) => { const urlParams = new URLSearchParams(req.url.slice(1)); const cameraId = urlParams.get('cameraId'); if (!cameraId) return ws.close(400, 'Missing cameraId'); // 创建RTSP流并关联到cameraId const stream = proxy({ url: `rtsp://your-camera-endpoint/${cameraId}`, // 按需添加其他配置(如帧率、编码格式) }); activeStreams.set(cameraId, { stream, ws }); // 监听前端的关闭指令 ws.on('message', (data) => { const { type, targetCameraId } = JSON.parse(data); if (type === 'closeStream' && targetCameraId === cameraId) { cleanupStream(cameraId); } }); // 处理WebSocket意外断开 ws.on('close', () => cleanupStream(cameraId)); }); // 封装清理流的工具函数 function cleanupStream(cameraId) { const streamData = activeStreams.get(cameraId); if (!streamData) return; // 终止RTSP流 streamData.stream.stop(); // 关闭WebSocket连接 streamData.ws.close(); // 从映射中删除 activeStreams.delete(cameraId); } server.listen(8080);
4. 前端配合停止播放
前端收到关闭信号后,需要主动停止video元素的播放并清空源,避免显示缓存画面:
React 前端示例代码
import { useEffect, useRef } from 'react'; const CameraPlayer = ({ cameraId, onClose }) => { const videoRef = useRef(null); const wsRef = useRef(null); useEffect(() => { // 建立WebSocket连接 wsRef.current = new WebSocket(`ws://your-backend-url?cameraId=${cameraId}`); // 设置RTSP流播放地址(以rtsp-relay的HTTP流为例) videoRef.current.src = `http://your-backend-url/stream/${cameraId}`; return () => { // 组件卸载时触发关闭流程 if (wsRef.current) { wsRef.current.send(JSON.stringify({ type: 'closeStream', targetCameraId: cameraId })); wsRef.current.close(); } // 停止视频播放并清空源 if (videoRef.current) { videoRef.current.pause(); videoRef.current.src = ''; } }; }, [cameraId]); return ( <div> <video ref={videoRef} autoPlay playsInline /> <button onClick={() => { if (wsRef.current) { wsRef.current.send(JSON.stringify({ type: 'closeStream', targetCameraId: cameraId })); } onClose(); }}>关闭摄像头</button> </div> ); }; export default CameraPlayer;
额外注意事项
- 如果存在多个WebSocket连接对应同一个
cameraId,建议添加引用计数,仅当计数归0时才终止RTSP流 - 若使用FFmpeg子进程,需监听
exit事件,确保进程正常退出,避免僵尸进程 - 可添加错误处理逻辑,防止因流终止失败导致的内存泄漏
内容的提问来源于stack exchange,提问作者Arjun Chouhan
相关产品推荐
相关产品推荐

