ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Moonfire NVR 安装实战:Rust 工具链与 FFmpeg 版本对齐指南

Moonfire NVR 安装实战:Rust 工具链与 FFmpeg 版本对齐指南

简介:Moonfire NVR 是一款开源的安全摄像机网络视频录像机,面向需要自建 IP 摄像机录像方案、又希望控制硬件成本的开发者与运维人员。它把来自摄像机的 H.264-over-RTSP 流直接落盘,视频帧以旋转目录形式存放,其余元数据写入 SQLite3 数据库,并可动态拼装任意时间段的 .mp4 文件;由于不解码、不分析、不重编码,CPU 占用极低,可在普通机器上同时处理多路 1080p/30fps 流。资源包共 116 个文件,约 5.22MB,以 Rust 源码(48 个 .rs)为核心,辅以 24 个 JavaScript 前端脚本、11 个 Markdown 文档、5 个 SQL 建表脚本,以及 TOML 配置、Dockerfile 与容器编排文件,便于直接构建与部署。目前已有 1420 人学习下载。借助完整源码与数据库结构,读者可理解低开销录像管线的设计思路,并在此基础上做二次开发或排错参考。

1. 装 Moonfire NVR 之前,先把 Rust 工具链和 FFmpeg 版本对齐

第一次在一台旧工控机上装 Moonfire NVR,我图省事用了系统自带的 Rust 1.65,cargo build --release跑到一半直接报edition2021相关错误,回头查才发现项目已经要求更新的工具链。这类 NVR 项目不像普通 Node 服务,装错了不会给你友好提示,而是编译阶段就翻车。Moonfire NVR 是一套用 Rust 写的网络视频录像机,负责从 RTSP 摄像头拉流、切片存盘、再通过内置 HTTP 服务回放,适合想把几路 IP 摄像头统一管起来、又不想依赖厂商云平台的开发者。它把录像落成标准格式的片段文件,元数据进 SQLite,装好之后浏览器就能看回放。这篇笔记按「环境清单 → 编译安装 → 配置摄像头 → 排错 → 验证」走一遍,新手能照抄,熟手能看清版本取舍。

2. 环境准备:Rust 版本、FFmpeg 依赖和磁盘布局

2.1 先定版本,别用发行版自带的 Rust

Moonfire NVR 的编译对 Rust 版本有硬要求,常见做法是用 rustup 装稳定版而不是发行版仓库里的旧包。我一般固定一个较新的 stable,比如 1.75 以上,避免 edition 和依赖解析的兼容问题。装之前先确认系统架构和 glibc 版本,老 CentOS 7 这类 glibc 2.17 的机器在链接阶段容易出问题,能换 Debian 12 或 Ubuntu 22.04 就换。

# 安装 rustup 并固定 stable 工具链 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y source "$HOME/.cargo/env" rustup default stable rustc --version # 确认输出 1.75 以上 cargo --version

这段先装 rustup 再切 stable,-y表示非交互安装,适合脚本化。source那行是把 cargo 加进当前 shell 的 PATH,不执行的话后面cargo会提示找不到命令。rustc --version是自检,版本低于项目要求就别急着往下走,先rustup update stable。

2.2 FFmpeg 和系统库:编译期和运行期都要

Moonfire NVR 依赖 FFmpeg 做转码和封装,编译时需要开发头文件,运行时需要动态库。Debian/Ubuntu 系一条命令能装齐,注意装的是-dev包而不是只装运行时。

# Debian/Ubuntu:编译依赖 + 运行时库 sudo apt update sudo apt install -y build-essential pkg-config libssl-dev \ libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev \ libavdevice-dev libswscale-dev libswresample-dev ffmpeg sqlite3

build-essential提供 gcc 和 make,pkg-config让 cargo 能找到 FFmpeg 的.pc文件,libssl-dev是 HTTPS 相关依赖。后面一串libav*-dev是 FFmpeg 各模块的开发包,少装一个编译时就会报Package xxx was not found。ffmpeg和sqlite3是运行时和排查用的命令行工具,建议一起装上。

2.3 磁盘和目录:录像盘单独挂,别和系统盘混

NVR 最怕的是录像把系统盘写满导致服务挂掉。我一般单独挂一块盘,挂到/var/lib/moonfire-nvr,文件系统用 ext4 或 xfs 都行,挂载参数里加noatime减少无谓写入。目录规划上,数据库和录像片段放同一个根目录下,Moonfire NVR 会自己建子目录。

# 假设新盘是 /dev/sdb,格式化和挂载 sudo mkfs.ext4 /dev/sdb sudo mkdir -p /var/lib/moonfire-nvr sudo mount -o noatime /dev/sdb /var/lib/moonfire-nvr # 写入 fstab 保证重启后自动挂载 echo '/dev/sdb /var/lib/moonfire-nvr ext4 defaults,noatime 0 2' | sudo tee -a /etc/fstab

noatime的作用是读文件时不更新访问时间,对大量小片段文件的录像场景能明显减少元数据写入。写 fstab 时用设备名还是 UUID 看习惯,生产环境我更倾向 UUID,避免盘符漂移。挂载完df -h确认容量,再往下走。

3. 编译与初始化:从源码到能启动的二进制

3.1 拉源码并编译 release 版本

Moonfire NVR 目前以源码编译为主,没有现成的通用二进制包。克隆后进目录编译,第一次编译会拉不少 crate,时间取决于网络和机器性能,十几分钟到半小时都正常。

git clone https://github.com/scottlamb/moonfire-nvr.git cd moonfire-nvr # 编译 release 版本,产物在 target/release/ cargo build --release

--release会开优化,编译慢但运行快,NVR 这种长期跑的服务必须用 release。编译过程中如果卡在某个 crate 下载,多半是网络问题,可以配 cargo 镜像源。编译成功后二进制在target/release/moonfire-nvr,可以先./target/release/moonfire-nvr --version确认能执行。

3.2 初始化数据库和目录结构

Moonfire NVR 第一次运行前要初始化,它会建 SQLite 数据库和录像目录。用init子命令指定根目录,这个目录就是前面挂载的录像盘路径。

# 初始化,--dir 指向录像根目录 ./target/release/moonfire-nvr init --dir /var/lib/moonfire-nvr

init会创建db和sample等子目录,数据库文件在db下。如果提示目录已存在且非空,说明之前初始化过,别重复执行,否则可能覆盖配置。初始化完成后可以用sqlite3 /var/lib/moonfire-nvr/db/db.sqlite3 '.tables'看看表结构,确认数据库正常生成。

3.3 配置文件的写法:摄像头、时区和端口

配置是 Moonfire NVR 落地最关键的一步,主配置文件通常叫moonfire-nvr.toml,放在录像根目录下。里面要写监听地址、时区、摄像头列表。摄像头用 RTSP URL 描述,凭据直接嵌在 URL 里,所以文件权限要收紧。

# /var/lib/moonfire-nvr/moonfire-nvr.toml [server] # 监听所有网卡的 8080 端口,前面一般再挂反向代理 bind_addrs = ["0.0.0.0:8080"] [[cameras]] # 摄像头短名,回放 URL 里会用到 short_name = "front-door" # RTSP 地址,用户名密码按实际填 url = "rtsp://user:pass@192.168.1.50:554/stream1" # 录像保留天数 retain_bytes = 200000000000

bind_addrs决定服务监听在哪,只给内网用就写内网 IP。short_name是摄像头标识,不能重复。url里的 RTSP 路径各家摄像头不一样,常见的是/stream1或/h264,填错会一直拉流失败。retain_bytes是保留容量上限,按盘大小和路数算,别设得比盘还大。配置文件权限建议chmod 600,因为里面有摄像头密码。

4. 避坑与排查:编译、拉流、权限三类高频问题

4.1 编译报 FFmpeg 找不到

现象是cargo build中途报Package libavcodec was not found in the pkg-config search path。原因是只装了运行时库没装-dev包,或者PKG_CONFIG_PATH没指对。解决是先补装对应libav*-dev,再用pkg-config --modversion libavcodec确认能查到版本。如果装在非标准路径,导出PKG_CONFIG_PATH=/usr/local/lib/pkgconfig再编译。

4.2 服务起来了但摄像头一直离线

现象是 Web 界面里摄像头状态一直是未连接,日志里反复出现 RTSP 超时。原因通常是 URL 路径不对、凭据错误,或者摄像头只允许特定网段访问。解决是先用ffplay或ffmpeg -i在命令行直接拉这个 RTSP URL,能出画面说明 URL 没问题,问题在 Moonfire NVR 的配置或网络;拉不出来就先修摄像头侧。常见做法是把摄像头和 NVR 放同一网段,减少中间设备干扰。

4.3 录像目录权限不足导致写盘失败

现象是服务能启动、能看实时画面,但回放里没有新片段,日志报Permission denied。原因是运行服务的用户对录像根目录没有写权限。解决是chown -R把目录给运行用户,或者用 systemd 的User=指定用户并确保该用户对目录有读写权限。别图省事用 root 跑,出问题不好排查。

4.4 磁盘写满后服务异常

现象是跑一段时间后服务响应变慢甚至退出。原因是保留策略没生效或retain_bytes设得过大,录像把盘写满。解决是调小retain_bytes,并确认服务有权限删除旧片段。我一般还会加一个磁盘监控,使用率超过 85% 就告警,给自己留处理时间。

5. 装完怎么验证:从拉流到回放走一遍闭环

装完不能只看进程在不在,要验证「拉流 → 切片 → 回放」整条链路。第一步看服务状态和日志,确认没有启动错误。

# 前台启动,观察日志输出 ./target/release/moonfire-nvr run --dir /var/lib/moonfire-nvr

run子命令前台运行,日志直接打到终端,方便看拉流和切片是否正常。看到类似stream和recording相关的日志说明在正常工作。确认没问题后再交给 systemd 托管,写一个 unit 文件,ExecStart指向二进制和run --dir,Restart=on-failure保证异常退出能拉起。

第二步用浏览器访问http://服务器IP:8080,进摄像头列表看状态是否在线,点进回放看有没有新片段生成。第三步直接查磁盘,确认片段文件在增长。

# 看录像目录下片段文件数量和总大小 find /var/lib/moonfire-nvr -name '*.mp4' | wc -l du -sh /var/lib/moonfire-nvr

find统计片段数量,du看总占用,两个数都在涨说明切片和落盘正常。如果数量不涨,回到日志看拉流是否中断。最后一步是模拟回放:在 Web 界面选一个时间段播放,能正常拖动进度条、画面不花,这套环境就算跑通了。

我自己的习惯是每次装完都强制走一遍「命令行拉流 → 服务日志 → 浏览器回放 → 磁盘增长」这四步,少一步都可能留下隐患。有次偷懒跳过磁盘检查,结果保留策略没生效,一周后盘满服务挂了,从那以后这四步一步不敢省。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进