从零构建Web端多路安防监控系统:Node.js+FFmpeg+Vue2全栈实战

监控系统在现代企业管理中扮演着重要角色,但商业解决方案往往价格昂贵且定制化程度低。本文将手把手带您实现一套基于Web的轻量级多路视频监控系统,核心技术栈采用FFmpeg进行视频流转码、Node.js建立实时通信桥梁、Vue2完成前端展示。这套方案特别适合中小型门店、物业管理系统等需要低成本部署多路监控的场景。

1. 环境准备与工具链配置

1.1 FFmpeg安装与验证

FFmpeg作为多媒体处理的核心工具,需要优先配置。推荐从官方推荐源获取预编译版本:

# 对于Windows系统
wget https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip
unzip ffmpeg-release-essentials.zip -d /opt/ffmpeg

配置环境变量后,通过以下命令验证安装:

ffmpeg -version

提示:Linux/macOS用户可通过包管理器直接安装,如 brew install ffmpeg apt install ffmpeg

1.2 Node.js环境搭建

确保安装Node.js 14+版本,推荐使用nvm进行版本管理:

nvm install 16
nvm use 16

关键依赖包清单:

  • node-rtsp-stream :RTSP转WebSocket的核心库
  • ws :WebSocket协议实现
  • express (可选):用于构建管理界面

2. 服务端视频流转发架构

2.1 多路流媒体服务设计

创建 stream-manager.js 作为核心服务文件:

const Stream = require('node-rtsp-stream');
const cameraConfigs = [
  {
    name: 'entrance',
    url: 'rtsp://camera1_ip:554/stream',
    width: 1280,
    height: 720
  },
  {
    name: 'cashier',
    url: 'rtsp://camera2_ip:554/stream', 
    fps: 25
  }
];

const streams = cameraConfigs.map((config, index) => {
  return new Stream({
    name: config.name,
    streamUrl: config.url,
    wsPort: 9000 + index,
    ffmpegOptions: {
      '-stats': '',
      '-r': config.fps || 30,
      '-s': `${config.width || 1920}x${config.height || 1080}`,
      '-b:v': '1500k',
      '-bufsize': '2000k'
    }
  });
});

console.log(`启动 ${streams.length} 路视频流服务`);

2.2 性能优化关键参数

参数 推荐值 说明
-r 25-30 帧率,越高越流畅但带宽消耗大
-s 1280x720 分辨率,平衡清晰度和性能
-b:v 1500k 视频比特率
-preset ultrafast 编码速度优先

注意:实际参数需根据网络条件和硬件性能调整,可通过 ffmpeg -h full 查看所有选项

3. 前端多画面监控实现

3.1 Vue2集成jsmpeg播放器

在public/index.html中添加:

<script src="https://cdn.jsdelivr.net/npm/jsmpeg@1.0.0/jsmpeg.min.js"></script>

创建VideoPlayer组件:

<template>
  <div class="monitor-grid">
    <div 
      v-for="(camera, index) in cameras"
      :key="index"
      class="camera-view"
    >
      <canvas :id="`canvas-${index}`"></canvas>
      <div class="camera-label">{{ camera.name }}</div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      cameras: [
        { name: '入口', port: 9000 },
        { name: '收银台', port: 9001 }
      ],
      players: []
    }
  },
  mounted() {
    this.initPlayers();
  },
  methods: {
    initPlayers() {
      this.cameras.forEach((camera, index) => {
        const canvas = document.getElementById(`canvas-${index}`);
        const player = new JSMpeg.Player(
          `ws://${window.location.hostname}:${camera.port}`,
          { canvas, autoplay: true }
        );
        this.players.push(player);
      });
    }
  },
  beforeDestroy() {
    this.players.forEach(player => player.destroy());
  }
}
</script>

<style>
.monitor-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  gap: 10px;
}
.camera-view {
  position: relative;
  background: #000;
}
.camera-label {
  position: absolute;
  bottom: 5px;
  left: 5px;
  color: white;
  background: rgba(0,0,0,0.5);
  padding: 2px 5px;
}
</style>

3.2 自适应布局方案

针对不同屏幕尺寸的响应式设计:

/* 小屏幕:单列布局 */
@media (max-width: 768px) {
  .monitor-grid {
    grid-template-columns: 1fr;
  }
}

/* 中等屏幕:两列布局 */ 
@media (min-width: 769px) and (max-width: 1200px) {
  .monitor-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 大屏幕:四列布局 */
@media (min-width: 1201px) {
  .monitor-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

4. 系统部署与调优

4.1 局域网部署要点

  1. 网络配置检查

    • 确保摄像头与服务器在同一局域网
    • 开放必要的端口(默认554用于RTSP)
    • 禁用防火墙或添加例外规则
  2. 服务自启动配置

    pm2 start stream-manager.js --name "video-stream"
    pm2 save
    pm2 startup
    

4.2 常见问题排查指南

现象 可能原因 解决方案
画面卡顿 网络带宽不足 降低分辨率或帧率
连接超时 端口未开放 检查防火墙设置
花屏现象 编码参数不匹配 调整 -b:v 比特率
高延迟 编码复杂度高 使用 -preset ultrafast

4.3 安全增强措施

  • 使用HTTPS加密WebSocket通信
  • 实现基础认证中间件
  • 定期更新FFmpeg到最新版本
  • 限制访问IP范围
// 示例:基础认证中间件
app.use((req, res, next) => {
  const auth = req.headers.authorization;
  if (!auth || auth !== `Bearer ${process.env.API_KEY}`) {
    return res.sendStatus(403);
  }
  next();
});

5. 进阶功能扩展

5.1 视频录制与回放

扩展服务端代码实现录制功能:

const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');

function startRecording(streamUrl, outputDir) {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const outputPath = path.join(outputDir, `${timestamp}.mp4`);
  
  const ffmpeg = spawn('ffmpeg', [
    '-i', streamUrl,
    '-c:v', 'copy',
    '-c:a', 'copy',
    '-f', 'segment',
    '-segment_time', '3600',
    '-strftime', '1',
    path.join(outputDir, '%Y-%m-%d_%H-%M-%S.mp4')
  ]);

  ffmpeg.on('error', (err) => {
    console.error('录制错误:', err);
  });

  return ffmpeg;
}

5.2 动态画面布局控制

实现前端布局切换功能:

<template>
  <div>
    <div class="layout-controls">
      <button @click="changeLayout(1)">单画面</button>
      <button @click="changeLayout(4)">四画面</button>
      <button @click="changeLayout(9)">九画面</button>
    </div>
    <!-- ... -->
  </div>
</template>

<script>
export default {
  methods: {
    changeLayout(count) {
      this.currentLayout = count;
      this.$el.style.setProperty('--columns', Math.ceil(Math.sqrt(count)));
    }
  }
}
</script>

<style>
:root {
  --columns: 2;
}
.monitor-grid {
  grid-template-columns: repeat(var(--columns), 1fr);
}
</style>

5.3 状态监控面板

添加系统状态展示组件:

<template>
  <div class="status-panel">
    <div v-for="(camera, index) in cameraStatus" :key="index">
      <h3>{{ camera.name }}</h3>
      <p>帧率: {{ camera.fps }}fps</p>
      <p>延迟: {{ camera.latency }}ms</p>
      <div class="status-indicator" :class="{ active: camera.connected }"></div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      cameraStatus: []
    }
  },
  created() {
    this.setupStatusMonitor();
  },
  methods: {
    setupStatusMonitor() {
      // 通过WebSocket获取状态信息
      const ws = new WebSocket('ws://localhost:9000/status');
      ws.onmessage = (event) => {
        this.cameraStatus = JSON.parse(event.data);
      };
    }
  }
}
</script>

<style>
.status-indicator {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: red;
}
.status-indicator.active {
  background: green;
}
</style>

6. 性能监控与优化实战

实现一个简单的性能监控中间件:

const monitorMiddleware = (req, res, next) => {
  const start = process.hrtime();
  
  res.on('finish', () => {
    const diff = process.hrtime(start);
    const duration = diff[0] * 1e3 + diff[1] * 1e-6;
    console.log(`${req.method} ${req.url} - ${duration.toFixed(2)}ms`);
    
    // 记录到性能日志
    fs.appendFileSync(
      'performance.log',
      `${new Date().toISOString()},${req.method},${req.url},${duration}\n`
    );
  });
  
  next();
};

结合上述方案,我们构建了一套完整的Web端监控系统解决方案。在实际项目中,建议先从单路视频开始测试,逐步增加摄像头数量,同时密切监控系统资源占用情况。

更多推荐