实践教程:基于RV1126与ZeroTier的RTSP摄像头内网穿透与远程访问
一、 项目背景与目标 🎯
我手上有一块RV1126的开发板,外接了一个SunplusIT SPCA2688 USB摄像头。我的目标是搭建一个可以从任何地方远程访问的实时监控系统。
初始本地推流实现(详细教程请参考RV1126平台(Buildroot Linux)+ SunplusIT SPCA2688 USB摄像头 RTSP推流全流程复盘与问题解决记录-CSDN博客
首先,我在开发板上成功实现了局域网内的RTSP推流。我使用的是 MediaMTX
作为RTSP服务器,并用 FFmpeg
来捕获摄像头数据、进行编码,然后推送到MediaMTX
。
我将所有命令都写在了一个脚本 rtsp_usb_camera_test.sh
中,方便一键启动。脚本内容如下
#!/bin/sh# 启动 MediaMTX RTSP 服务器,并在后台运行
/mediamtx_v1.13.0_linux_armv7/mediamtx &# 等待3秒,确保服务器已完全启动
sleep 3# 使用 FFmpeg 从v4l2设备捕获视频,用libx264软编码,并推送到本机的RTSP服务器
ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -framerate 15 -i /dev/video45 \
-c:v libx264 -preset ultrafast -tune zerolatency \
-f rtsp -rtsp_transport tcp rtsp://127.0.0.1:8554/live/main_stream
在开发板上运行这个脚本后,我可以在同一个WiFi下的电脑或手机上,用VLC播放器打开地址 rtsp://192.168.1.16:8554/live/main_stream
来观看实时监控画面。
这一切在家里用起来很方便,但我萌生了一个新想法:我希望能随时随地,无论是在公司、在路上还是在朋友家,都能看到这个摄像头的画面。
我的核心目标是:
-
实现公网远程访问。
-
方案必须是永久免费的。
-
要足够稳定可靠。
二、 方案选择:为什么是ZeroTier
经过一番了解,我发现有几种主流方案,比如“公网IP+DDNS+端口转发”、“FRP内网穿透”等。但这些方案要么依赖运营商提供公网IP,要么需要自己有一台云服务器,都有些门槛。
最终,我选择了 ZeroTier。它的理念深深吸引了我:创建一个“虚拟的局域网”,把我所有的设备(开发板、手机、电脑)都连接到这个虚拟网络里。设备之间就像真的在同一个路由器下一样,可以通过虚拟IP直接通信。最关键的是,它的免费套餐对于个人使用来说完全足够,而且安全性很高,不需要把任何端口暴露在公网上。
三、 实施过程:一步步实现远程访问
我的计划很清晰:给开发板和我的手机都装上ZeroTier客户端,让它们“手拉手”,然后通过虚拟IP访问视频流。
-
注册和创建网络:我先去ZeroTier官网
my.zerotier.com
注册了一个账号,过程很简单。登录后,我点击“Create A Network”,系统立刻为我生成了一个16位的网络ID(Network ID),这个ID就是我虚拟局域网的唯一门牌号。 -
手机入网:我在手机上下载了ZeroTier One的App,输入网络ID,轻松加入了网络。并且在官网后台的“Members”列表里,给我的手机勾选了“Auth?”,批准了它的加入。手机很快就获得了一个
10.144.x.x
开头的虚拟IP。 -
在开发板上安装客户端:这是挑战的开始。我的RV1126是Buildroot制作的精简Linux系统,没有
apt-get
,连curl
命令都没有。官方提供的一键安装脚本完全用不了。 -
攻克安装难关:我最终采用了最原始也最可靠的方法:
-
在电脑上下载了ZeroTier的
.deb
安装包(armhf
架构,兼容我的armv7l
开发板)。 -
在我的Ubuntu虚拟机里,用
dpkg-deb -x
命令解压这个.deb
包,从中提取出了zerotier-one
这个最核心的可执行文件。 -
将
zerotier-one
文件从Ubuntu虚拟机传输到开发板。我使用的是adb push
命令,这个方式对于连接了USB的开发板来说非常方便。
-
-
启动服务与最终连接:为了避免每次都手动敲大量命令,我编写了一个Shell脚本,将所有在开发板上需要执行的命令都整合了进去。这个脚本帮我完成了移动文件、设置权限、创建开机自启服务、启动服务、并最终加入网络的所有操作。下面是这个脚本的完整内容:
#!/bin/sh# 任何命令失败则立即退出 set -e# 清理旧的、可能存在的服务进程,防止端口占用 killall zerotier-one >/dev/null 2>&1 || true# --- 欢迎与检查 --- echo "=============================================" echo "=== ZeroTier 最终版安装脚本 (遵从指示) ===" echo "=============================================" echo "脚本将从 /root 目录读取文件,并安装到 /usr/bin 目录。" echo ""if [ ! -f "/root/zerotier-one" ]; thenecho "错误:在 /root 目录下未找到 'zerotier-one' 文件!"echo "请先将 zerotier-one 文件传输到 /root 目录再运行。"exit 1 fi# --- 获取 Network ID --- echo "请输入您的16位 ZeroTier Network ID,然后按回车:" read NETWORK_ID if [ -z "$NETWORK_ID" ]; thenecho "错误:Network ID 不能为空!"exit 1 fi# --- 开始安装 --- echo "" echo "--- 步骤 1/5: 移动文件到 /usr/bin 并创建链接 ---" mv /root/zerotier-one /usr/bin/ ln -sf /usr/bin/zerotier-one /usr/bin/zerotier-cli echo "✅ 完成"echo "--- 步骤 2/5: 设置文件执行权限 ---" chmod +x /usr/bin/zerotier-one echo "✅ 完成"echo "--- 步骤 3/5: 创建数据目录 ---" mkdir -p /var/lib/zerotier-one echo "✅ 完成"echo "--- 步骤 4/5: 创建开机自启服务 ---" # 注意:这里的路径已更正为 /usr/bin/zerotier-one cat > /etc/init.d/S99zerotier << EOF #!/bin/sh start() {echo "Starting ZeroTier One..."/usr/bin/zerotier-one -d } stop() {echo "Stopping ZeroTier One..."PID=\$(ps | grep '[z]erotier-one' | awk '{print \$1}')if [ ! -z "\$PID" ]; then kill \$PID; fi } case "\$1" instart) start ;;stop) stop ;;restart) stop; start ;;*) echo "Usage: \$0 {start|stop|restart}"; exit 1 ;; esac exit 0 EOF chmod +x /etc/init.d/S99zerotier echo "✅ 完成"echo "--- 步骤 5/5: 启动服务并加入网络 ---" /etc/init.d/S99zerotier start sleep 3 zerotier-cli join "$NETWORK_ID" echo "✅ '加入网络' 请求已发送!" echo ""# --- 最终提示 --- echo "======================================================" echo "🎉 恭喜!脚本执行完毕!" echo "🚨【 最后一步,非常重要!】🚨" echo "请立即登录 ZeroTier 官网后台 (my.zerotier.com)," echo "找到这台新设备,并把它前面的复选框打勾授权!" echo "======================================================"
6.最终测试:在经历了一系列问题的排查后,我在开发板上运行
zerotier-cli listnetworks
,终于看到了梦寐以求的虚拟IP!然后,我在手机VLC里,输入了rtsp://<开发板的虚拟IP>:8554/live/main_stream
,看到了清晰的画面。成功!
四、 遇到的问题与解决方案(踩坑全记录)🚧
这段旅程并非一帆风顺,我遇到了大大小小9个问题。正是解决了这些问题,才让我对嵌入式Linux和网络知识有了更深刻的理解。
问题1:开发板系统太精简,无法自动安装
-
现象: 在开发板上想用
apt-get
或curl
一键安装ZeroTier,结果提示command not found
。 -
原因分析: 我的RV1126是Buildroot构建的,为了追求极致的性能和最小的体积,系统里没有包含这些包管理器和下载工具。
-
解决方案: 手动部署。最终采用的方法是:在电脑上下载适配ARM架构的
.deb
包,在Ubuntu虚拟机中用dpkg-deb -x
命令解压,从中提取出zerotier-one
和zerotier-cli
这两个最核心的可执行文件,再通过adb push
命令传输到开发板上。
问题2:在官网和GitHub上找不到预编译包
-
现象: 按照一些教程去ZeroTier官网或GitHub Releases页面下载,发现
.tar.gz
格式的通用二进制文件都消失了,只有源代码。 -
原因分析: ZeroTier官方调整了发布策略,不再提供显式的通用二进制包下载,这给非主流发行版的用户带来了困难。
-
解决方案: 曲线救国。既然找不到通用包,我就去找特定发行版(Debian)的包。我从下面的地址下载了
armhf
架构的.deb
包,这成为了后续提取文件的基础。下载地址:
https://download.zerotier.com/dist/debian/bullseye/zerotier-one_1.12.2_armhf.deb
问题3:解压.deb
包时,提示符号链接创建失败
-
现象: 在Ubuntu虚拟机里解压
.deb
包时,报错tar: ... Cannot create symlink: Operation not supported
。 -
原因分析: 我当时偷懒,直接在Windows和Ubuntu的共享文件夹(VMware的hgfs)里进行解压。Windows的NTFS文件系统原生不支持Linux的符号链接(symlink),导致解压失败。
-
解决方案: 更换工作目录。将
.deb
文件从共享文件夹复制到Ubuntu自己的主目录(~/
)下,这里的ext4文件系统完美支持所有Linux特性,再执行解压命令,问题解决。
问题4:启动服务时,报错端口9993被占用
-
现象: 运行启动命令后,报
fatal error: cannot bind to local control interface port 9993
。但用ps
命令又可能看不到zerotier-one
进程。 -
原因分析: 这是个“假死”现象。很可能是之前的进程异常崩溃,但它占用的网络端口没有被内核及时释放,成了一个“僵尸端口”。
-
解决方案: 精准定位并强行终止。使用
netstat -anp | grep 9993
命令,它能无视进程状态,直接查出是哪个进程ID(PID)占用了该端口。然后使用kill -9 <PID>
命令,将这个“幽灵”进程彻底杀死,从而释放端口。
问题5:启动服务时,报错找不到TUN/TAP设备
-
现象: 解决了端口问题后,又出现新错误
could not open TUN/TAP device: No such file or directory
。 -
原因分析: 这是个更底层的系统问题。意味着我的Linux内核在编译时,就没有包含“虚拟网卡”(TUN)这个功能。ZeroTier依赖此功能才能工作。
-
解决方案: 重新编译内核。我回到了Buildroot开发环境,执行
make linux-menuconfig
,进入内核配置菜单。按照路径Device Drivers -> Network device support
,找到了Universal TUN/TAP device driver support
选项,并将其设置为y
(<*>
,直接编译进内核),然后重新make
生成新固件并烧录。
问题6:加入网络后,无法获取虚拟IP
-
现象: 在开发板上执行
zerotier-cli join <nwid>
后返回200 join OK
,但listnetworks
命令的结果一直只有表头,没有任何网络信息。# 解决前 [root@ATK-DLRV1126:~]# zerotier-cli listnetworks 200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
-
原因分析: 设备发出“敲门”请求后,我作为“房主”,没有在ZeroTier官网后台批准它进门。
问题7:远程视频画面花屏、卡顿严重
-
现象: 手机在4G网络下终于能看到画面了,但画质极差,满屏的马赛克和色块,根本无法正常观看。用
zerotier-cli peers
诊断,看到了RELAY
状态# 解决前 [root@ATK-DLRV1126:/]# zerotier-cli peers 200 peers <ztaddr> <ver> <role> <lat> <link> <lastTX> <lastRX> <path> 0678ebc45f - LEAF -1 RELAY ...
-
原因分析: 我的手机和开发板之间的连接模式是
RELAY
(中继),而不是DIRECT
(直连)。数据通过服务器中转,延迟和带宽都极差,无法承载视频流。 -
解决方案: 优化网络,打通直连路径。我登录到开发板连接的路由器(天翼网关)后台,在“高级设置” -> “端口映射”功能里,为开发板的局域网IP(
192.168.1.16
)添加了一条规则,将UDP协议的9993端口转发出去。-
虚拟服务名称:
ZeroTier
-
局域网IP:
192.168.1.16
-
服务协议:
UDP
-
内部端口:
9993
-
外部端口:
9993
# 解决后 [root@ATK-DLRV1126:/]# zerotier-cli peers 200 peers <ztaddr> <ver> <role> <lat> <link> <lastTX> <lastRX> <path> 632ea29085 1.14.2 LEAF 265 DIRECT 4459 13116 35.209.122.2/28655 ...
-
问题8:手机使用移动数据时无法连接ZeroTier
-
现象: 手机关掉WiFi用流量,ZeroTier App的连接开关打不开,并提示
Not on Wi-Fi...
。 -
原因分析: ZeroTier App为了防止误耗用户流量,默认设置只在Wi-Fi下工作。
-
解决方案: 修改App设置。在ZeroTier App的设置(右上角三个点)里,找到
Use Mobile Data
(允许使用移动数据)选项,并将其开启。
问题9:远程播放时,局域网无法同时观看
-
现象: 当手机通过ZeroTier观看视频时,局域网内的另一台电脑用VLC就无法打开视频流了。
-
原因分析: 性能瓶颈。我的
ffmpeg
命令使用的是CPU进行软件编码(libx264
),这本身就消耗了大量CPU资源。当MediaMTX
服务器需要同时服务两条视频流(一条给ZeroTier,一条给局域网)时,开发板的CPU和网络I/O不堪重负,导致无法响应新的连接请求。 -
解决方案: 承认性能极限。对于RV1126这样的嵌入式设备,同时进行实时编码和多路网络分发是一项沉重的任务。这个问题暂时作为性能极限来接受。后续优化的方向可以是利用硬件编码来解放CPU,或者降低视频流的码率和分辨率。
五、最终成果与感悟
经历了上述所有挑战后,我终于实现了最初的目标。现在,无论我身在何处,只要手机有网络,就能随时打开VLC,输入 rtsp://10.144.66.102:8554/live/main_stream
,看到家里的实时画面。
这个过程虽然曲折,但每解决一个问题,都让我对Linux系统、网络知识和解决问题的思路有了新的认识。从一个简单的需求出发,最终完成了一个可靠、免费、安全的个人项目,这种成就感无与伦比。希望我的这份记录,能为后来的朋友们照亮前路,少走一些弯路。
最终实现效果图: