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 后端 的实践过程。
一、整体架构
摄像头输出:RTSP 流(如
rtsp://username:password@ip:554/Streaming/Channels/101
)。后端转码:使用
ffmpeg
将 RTSP 转换为 FLV 格式,并通过 WebSocket 推流。前端播放:使用
flv.js
在 Vue3 中播放 WebSocket-FLV 视频流。
架构图如下:
海康摄像头(RTSP) → Node.js(FFmpeg转码+WebSocket) → 前端Vue3(FLV.js播放器)
二、后端实现(Node.js)
1. 安装依赖
npm init -y
npm install express ws
2. Node.js 转发服务
// 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 格式到 stdoutconst 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}`);
});
运行:
ts-node server.ts
三、前端实现(Vue3 + TS)
1. 安装依赖
npm install flv.js
2. 创建播放器组件
<!-- CameraPlayer.vue -->
<template><div class="video-box"><videoref="videoRef"class="video"controlsautoplaymutedstyle="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>
四、常见问题
延迟高?
RTSP → FLV 转码存在几百毫秒到几秒延迟,可以调节 ffmpeg 参数(如
-tune zerolatency
)。如果对实时性要求极高,可以考虑 WebRTC 方案。
卡顿/黑屏?
检查摄像头 RTSP 地址是否正确。
确保 Node.js 服务和前端在同一个网络下。
增加
-rtsp_transport tcp
确保走 TCP。
浏览器兼容性?
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. 基础命令
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 集成示例(优化版)
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. 进一步优化方向
CPU 占用太高?
如果摄像头 RTSP 已经是 H.264 编码,建议用
-c copy
(不转码),直接推送:
ffmpeg -rtsp_transport tcp -i rtsp://... -c copy -an -f flv pipe:1
延迟还是大?
确保网络延迟低,避免跨公网。
在前端
flv.js
设置isLive: true
,并控制stashInitialSize
(缓冲大小)。
兼容 Safari / iOS?
Safari 不支持 FLV,建议额外推一份 HLS (
.m3u8
) 流。
👉 这样配置后,一般延迟可以控制在 300ms~800ms,适合安防监控、实时预览等场景。
既然后端 ffmpeg
已经优化到低延迟模式,那前端 flv.js
也要做对应的参数调整,否则缓冲区过大就会拖慢播放。下面是一个 前端 flv.js 播放器低延迟模式配置示例。
前端 flv.js 播放器参数优化(低延迟模式)
1. 基础配置
在创建播放器时,flv.js.createPlayer
支持传入 config
对象,可以调整缓冲策略:
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
当缓存过大时自动清理,避免内存占用过多。
<template><div class="video-box"><videoref="videoRef"class="video"controlsautoplaymutedstyle="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. 调优建议
超低延迟:
stashInitialSize
设置为64
或更小,延迟能控制在 300~500ms。但要注意:网络抖动大时可能卡顿。
平衡模式:
stashInitialSize
维持128~256
,延迟在 500ms~1s,稳定性更好。
移动端兼容:
iOS/Safari 不支持
flv.js
,需要额外提供 HLS(m3u8)。
这样前后端都优化后,一般能做到 500ms 内延迟,满足大部分监控、实时视频预览场景。