You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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连接断开)时,执行以下步骤:

  1. 通过cameraId从映射中取出对应的流实例和WebSocket连接
  2. 终止RTSP流代理进程
  3. 关闭WebSocket连接
  4. 从全局映射中删除该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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 04:42:56