vue3+TS 前端调用海康摄像头视频流,后端用 Node.js 做 RTSP 转 WebSocket-FLV 转发,并且前后端优化延迟方案

Vue3+TS 前端调用海康摄像头视频流实践(RTSP → WS-FLV)

在物联网/安防监控项目中,前端经常需要接入摄像头实时视频流。以海康威视摄像头为例,它默认输出的是 RTSP 协议,但前端浏览器并不直接支持 RTSP。常见的做法是:

  • 后端转码/转协议:将 RTSP 转成浏览器支持的格式(如 FLV/HLS/WebRTC)。

  • 前端播放:通过 Video.js、flv.js 等播放器加载流媒体。

本文记录了一个基于 Vue3 + TypeScript 前端 + Node.js 后端 的实践过程。


一、整体架构

  1. 摄像头输出 :RTSP 流(如 rtsp://username:password@ip:554/Streaming/Channels/101)。

  2. 后端转码 :使用 ffmpeg 将 RTSP 转换为 FLV 格式,并通过 WebSocket 推流。

  3. 前端播放 :使用 flv.js 在 Vue3 中播放 WebSocket-FLV 视频流。

架构图如下:

海康摄像头(RTSP) → Node.js(FFmpeg转码+WebSocket) → 前端Vue3(FLV.js播放器)

二、后端实现(Node.js)

1. 安装依赖

javascript 复制代码
npm init -y
npm install express ws
  1. Node.js 转发服务
javascript 复制代码
// server.ts
import express from "express";
import { WebSocketServer } from "ws";
import { spawn } from "child_process";

const app = express();
const port = 3000;

// WebSocket 服务
const wss = new WebSocketServer({ port: 9999 });

wss.on("connection", (ws) => {
  console.log("客户端已连接");

  const rtspUrl =
    "rtsp://username:password@192.168.1.100:554/Streaming/Channels/101";

  // ffmpeg 拉流并输出 FLV 格式到 stdout
  const ffmpeg = spawn("ffmpeg", [
    "-rtsp_transport",
    "tcp",
    "-i",
    rtspUrl,
    "-f",
    "flv",
    "-an", // 不要音频
    "pipe:1", // 输出到 stdout
  ]);

  ffmpeg.stdout.on("data", (chunk) => {
    ws.send(chunk);
  });

  ffmpeg.stderr.on("data", (err) => {
    console.error("FFmpeg 错误:", err.toString());
  });

  ws.on("close", () => {
    console.log("客户端断开连接");
    ffmpeg.kill("SIGINT");
  });
});

app.listen(port, () => {
  console.log(`HTTP 服务已启动: http://localhost:${port}`);
});

运行:

javascript 复制代码
ts-node server.ts

三、前端实现(Vue3 + TS)

1. 安装依赖

javascript 复制代码
npm install flv.js
  1. 创建播放器组件
javascript 复制代码
<!-- CameraPlayer.vue -->
<template>
  <div class="video-box">
    <video
      ref="videoRef"
      class="video"
      controls
      autoplay
      muted
      style="width: 100%; height: auto;"
    ></video>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, ref, onBeforeUnmount } from "vue";
import flvjs from "flv.js";

const videoRef = ref<HTMLVideoElement | null>(null);
let flvPlayer: flvjs.Player | null = null;

onMounted(() => {
  if (flvjs.isSupported() && videoRef.value) {
    flvPlayer = flvjs.createPlayer({
      type: "flv",
      url: "ws://localhost:9999", // WebSocket 地址
      isLive: true,
    });
    flvPlayer.attachMediaElement(videoRef.value);
    flvPlayer.load();
    flvPlayer.play();
  }
});

onBeforeUnmount(() => {
  if (flvPlayer) {
    flvPlayer.destroy();
    flvPlayer = null;
  }
});
</script>

四、常见问题

  1. 延迟高?

    • RTSP → FLV 转码存在几百毫秒到几秒延迟,可以调节 ffmpeg 参数(如 -tune zerolatency)。

    • 如果对实时性要求极高,可以考虑 WebRTC 方案。

  2. 卡顿/黑屏?

    • 检查摄像头 RTSP 地址是否正确。

    • 确保 Node.js 服务和前端在同一个网络下。

    • 增加 -rtsp_transport tcp 确保走 TCP。

  3. 浏览器兼容性?

    • flv.js 基于 MSE(Media Source Extensions),主流 Chrome/Edge 都支持。

    • Safari 不支持 FLV,苹果设备建议使用 HLS(m3u8)。


五、总结

本文介绍了如何用 Node.js + FFmpeg 将海康摄像头的 RTSP 流 转换为 WebSocket-FLV 流 ,并在 Vue3 + TypeScript 前端中通过 flv.js 播放。

这种方式实现成本较低,适合局域网监控、安防平台、物联网视频监控等场景。若对低延迟和移动端兼容性要求高,可以进一步升级到 WebRTC 方案。

FFmpeg 低延迟 RTSP → FLV 配置参数优化

在默认配置下,ffmpeg 从 RTSP 拉流再转码推送到前端会有 1~3 秒延迟。通过合理的参数调整,可以将延迟控制在 500ms 左右,甚至更低。

  1. 基础命令
javascript 复制代码
ffmpeg -rtsp_transport tcp -i rtsp://user:pass@192.168.1.100:554/Streaming/Channels/101 \
  -preset ultrafast \
  -tune zerolatency \
  -fflags nobuffer \
  -flags low_delay \
  -strict experimental \
  -an \
  -c:v libx264 \
  -f flv pipe:1

2. 参数说明(关键优化点)

  • -rtsp_transport tcp

    强制走 TCP,避免 UDP 丢包造成花屏。

  • -fflags nobuffer

    禁用缓冲,降低延迟。

  • -flags low_delay

    设置低延迟模式。

  • -preset ultrafast

    x264 编码速度最快的预设,降低延迟(代价是压缩率差一些,文件体积大)。

  • -tune zerolatency

    针对实时应用优化,禁用 B 帧,减少缓存。

  • -c:v libx264

    使用 H.264 编码(浏览器兼容性最好)。

    如果摄像头本身就是 H.264 编码,可以用 -c copy 直接拷贝,进一步减少延迟和 CPU 消耗。

  • -an

    去掉音频,减少带宽和处理压力。

  • -f flv

    输出为 FLV 格式,方便前端 flv.js 播放。

  • pipe:1

    把数据输出到标准输出(stdout),由 Node.js WebSocket 转发给前端。


3. Node.js 集成示例(优化版)

javascript 复制代码
const ffmpeg = spawn("ffmpeg", [
  "-rtsp_transport", "tcp",
  "-i", rtspUrl,
  "-preset", "ultrafast",
  "-tune", "zerolatency",
  "-fflags", "nobuffer",
  "-flags", "low_delay",
  "-an",
  "-c:v", "libx264",   // 或 "-c", "copy" 直接拷贝
  "-f", "flv",
  "pipe:1"
]);

4. 进一步优化方向

  1. CPU 占用太高?

    • 如果摄像头 RTSP 已经是 H.264 编码,建议用 -c copy(不转码),直接推送:
javascript 复制代码
ffmpeg -rtsp_transport tcp -i rtsp://... -c copy -an -f flv pipe:1
  1. 延迟还是大?

    • 确保网络延迟低,避免跨公网。

    • 在前端 flv.js 设置 isLive: true,并控制 stashInitialSize(缓冲大小)。

  2. 兼容 Safari / iOS?

    • Safari 不支持 FLV,建议额外推一份 HLS (.m3u8) 流。

👉 这样配置后,一般延迟可以控制在 300ms~800ms,适合安防监控、实时预览等场景。

既然后端 ffmpeg 已经优化到低延迟模式,那前端 flv.js 也要做对应的参数调整,否则缓冲区过大就会拖慢播放。下面是一个 前端 flv.js 播放器低延迟模式配置示例。


前端 flv.js 播放器参数优化(低延迟模式)

1. 基础配置

在创建播放器时,flv.js.createPlayer 支持传入 config 对象,可以调整缓冲策略:

javascript 复制代码
flvPlayer = flvjs.createPlayer(
  {
    type: "flv",
    url: "ws://localhost:9999",
    isLive: true, // 必须开启直播模式
  },
  {
    enableWorker: true,            // 启用分离线程解码,降低卡顿
    enableStashBuffer: true,       // 是否启用缓冲区
    stashInitialSize: 128,         // 初始缓冲区大小,默认 384KB,调小可降低延迟
    fixAudioTimestampGap: false,   // 去掉音频时间戳修复,避免延迟
    autoCleanupSourceBuffer: true, // 自动清理 SourceBuffer,防止内存泄漏
  }
);

2. 参数解释

  • isLive: true

    表示是直播流,不允许拖拽和缓存太多。

  • enableWorker: true

    使用 Web Worker 解析流,避免主线程阻塞,提升性能。

  • enableStashBuffer: true

    开启缓冲池,防止因网络波动而花屏。

  • stashInitialSize: 128

    初始缓冲大小(KB),默认是 384KB,这会导致 1~3 秒延迟

    调小到 128KB(甚至 64KB)可以明显降低延迟,但网络不稳定时更容易卡顿。

  • fixAudioTimestampGap: false

    不修复音频时间戳(直播通常只关心实时性,禁用该特性可减少延迟)。

  • autoCleanupSourceBuffer: true

    当缓存过大时自动清理,避免内存占用过多。

javascript 复制代码
<template>
  <div class="video-box">
    <video
      ref="videoRef"
      class="video"
      controls
      autoplay
      muted
      style="width: 100%; height: auto;"
    ></video>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, ref, onBeforeUnmount } from "vue";
import flvjs from "flv.js";

const videoRef = ref<HTMLVideoElement | null>(null);
let flvPlayer: flvjs.Player | null = null;

onMounted(() => {
  if (flvjs.isSupported() && videoRef.value) {
    flvPlayer = flvjs.createPlayer(
      {
        type: "flv",
        url: "ws://localhost:9999", // WebSocket 地址
        isLive: true,
      },
      {
        enableWorker: true,
        enableStashBuffer: true,
        stashInitialSize: 128, // KB,调小减少延迟
        fixAudioTimestampGap: false,
        autoCleanupSourceBuffer: true,
      }
    );
    flvPlayer.attachMediaElement(videoRef.value);
    flvPlayer.load();
    flvPlayer.play();
  }
});

onBeforeUnmount(() => {
  if (flvPlayer) {
    flvPlayer.destroy();
    flvPlayer = null;
  }
});
</script>

4. 调优建议

  1. 超低延迟

    • stashInitialSize 设置为 64 或更小,延迟能控制在 300~500ms。

    • 但要注意:网络抖动大时可能卡顿。

  2. 平衡模式

    • stashInitialSize 维持 128~256,延迟在 500ms~1s,稳定性更好。
  3. 移动端兼容

    • iOS/Safari 不支持 flv.js,需要额外提供 HLS(m3u8)。

这样前后端都优化后,一般能做到 500ms 内延迟,满足大部分监控、实时视频预览场景。

相关推荐
Dontla3 小时前
Yarn命令与npm命令的区别与联系(npm:Node.js的官方包管理工具;Yarn:Facebook开发的JavaScript包管理工具)
javascript·npm·node.js
APItesterCris3 小时前
Node.js/Python 调用 1688 API 实时拉取商品信息的实现方案
node.js
EndingCoder3 小时前
Node.js 模块系统详解
javascript·node.js
前端Hardy3 小时前
HTML&CSS:动态歌词高亮展示效果
前端·javascript·css
PineappleCoder3 小时前
手把手教你做:高安全 Canvas 水印的实现与防篡改技巧
前端·javascript·css
RoyLin3 小时前
图数据库基础
前端·后端·typescript
可乐爱宅着3 小时前
如何在next.js中处理表单提交
前端·next.js
卤代烃3 小时前
[性能优化] 如何高效的获取 base64Image 的 meta 信息
前端·性能优化·agent
IT_陈寒3 小时前
Vite 5大性能优化技巧:构建速度提升300%的实战分享!
前端·人工智能·后端