实践教程:基于RV1126与ZeroTier的RTSP摄像头内网穿透与远程访问

一、 项目背景与目标 🎯

我手上有一块RV1126的开发板,外接了一个SunplusIT SPCA2688 USB摄像头。我的目标是搭建一个可以从任何地方远程访问的实时监控系统。

初始本地推流实现(详细教程请参考RV1126平台(Buildroot Linux)+ SunplusIT SPCA2688 USB摄像头 RTSP推流全流程复盘与问题解决记录-优快云博客

首先,我在开发板上成功实现了局域网内的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 来观看实时监控画面。

这一切在家里用起来很方便,但我萌生了一个新想法:我希望能随时随地,无论是在公司、在路上还是在朋友家,都能看到这个摄像头的画面。

我的核心目标是:

  1. 实现公网远程访问。

  2. 方案必须是永久免费的。

  3. 要足够稳定可靠。

二、 方案选择:为什么是ZeroTier

经过一番了解,我发现有几种主流方案,比如“公网IP+DDNS+端口转发”、“FRP内网穿透”等。但这些方案要么依赖运营商提供公网IP,要么需要自己有一台云服务器,都有些门槛。

最终,我选择了 ZeroTier。它的理念深深吸引了我:创建一个“虚拟的局域网”,把我所有的设备(开发板、手机、电脑)都连接到这个虚拟网络里。设备之间就像真的在同一个路由器下一样,可以通过虚拟IP直接通信。最关键的是,它的免费套餐对于个人使用来说完全足够,而且安全性很高,不需要把任何端口暴露在公网上。

三、 实施过程:一步步实现远程访问

我的计划很清晰:给开发板和我的手机都装上ZeroTier客户端,让它们“手拉手”,然后通过虚拟IP访问视频流。

  1. 注册和创建网络:先去ZeroTier官网 my.zerotier.com 注册了一个账号,过程很简单。登录后,我点击“Create A Network”,系统立刻为我生成了一个16位的网络ID(Network ID),这个ID就是我虚拟局域网的唯一门牌号。

  2. 手机入网ZeroTier | Download我在手机上下载了ZeroTier One的App,输入网络ID,轻松加入了网络。并且在my.zerotier.com官网后台的“Members”列表里,给我的手机勾选了“Auth?”,批准了它的加入。手机很快就获得了一个10.144.x.x开头的虚拟IP。电脑入网也是同理,在这里ZeroTier | Download下载电脑版的,安装之后打开,然后选择join New Network,把上面第一步生成的网络ID输入进去

  3. 在开发板上安装客户端:这是挑战的开始。我的RV1126是Buildroot制作的精简Linux系统,没有apt-get,连curl命令都没有。官方提供的一键安装脚本完全用不了。

  4. 攻克安装难关:我最终采用了最原始也最可靠的方法:

    • 在电脑上Index of /dist/debian/bullseye/下载了ZeroTier的.deb安装包(armhf架构,兼容rv1126的armv7l架构开发板)。

    • 在我的Ubuntu虚拟机里,用dpkg-deb -x命令解压这个.deb包,解压后,可以看到解压目录下的usr/sbin目录下的zerotier-one和zerotier-cli这两个最核心的可执行文件

    • zerotier-one和zerotier-cli文件从Ubuntu虚拟机传输到开发板。我使用的是 adb push 命令,这个方式对于连接了USB的开发板来说非常方便。adb push zerotier-cli /root

  5. 启动服务与最终连接:为了避免每次都手动敲大量命令,我编写了一个Shell脚本,命名为push_stream_to_internet.sh,将所有在开发板上需要执行的命令都整合了进去。这个脚本帮我完成了移动文件、设置权限、创建开机自启服务、启动服务、并最终加入网络的所有操作。下面是这个脚本的完整内容:

    #!/bin/sh
    
    # --- 欢迎信息 ---
    echo "============================================="
    echo "===   ZeroTier 智能安装与启动脚本 v2.0   ==="
    echo "============================================="
    echo ""
    
    # ----------------------------------------------------------------------
    # 关键改动:增加端口健康检查与自动清理功能
    # ----------------------------------------------------------------------
    echo "--- 步骤 1/6: 健康检查:清理端口 9993 ---"
    PID_TO_KILL=$(netstat -lnp | grep ':9993' | awk '{print $7}' | sed 's/\/.*//')
    
    if [ -n "$PID_TO_KILL" ]; then
        echo "警告:端口 9993 已被进程 PID: $PID_TO_KILL 占用。正在强制终止..."
        kill -9 "$PID_TO_KILL"
        sleep 2
        echo "✅ 旧进程已清理。"
    else
        echo "✅ 端口 9993 状态正常。"
    fi
    # ----------------------------------------------------------------------
    
    # --- 文件检查与准备 ---
    echo "--- 步骤 2/6: 检查 ZeroTier 执行文件 ---"
    if [ ! -f "/usr/bin/zerotier-one" ]; then
        echo "在 /usr/bin/ 未找到zerotier-one,正在从源位置拷贝..."
        if [ -f "/mediamtx_v1.13.0_linux_armv7/zerotier-one" ]; then
            cp "/mediamtx_v1.13.0_linux_armv7/zerotier-one" "/usr/bin/zerotier-one"
        else
            echo "错误:在 /mediamtx_v1.13.0_linux_armv7 目录下也未找到 'zerotier-one' 文件!"
            exit 1
        fi
    fi
    echo "✅ 执行文件准备就绪。"
    
    
    # --- 获取 Network ID ---
    echo "--- 步骤 3/6: 配置 Network ID ---"
    echo "请输入您的16位 ZeroTier Network ID,然后按回车:"
    read NETWORK_ID
    if [ -z "$NETWORK_ID" ]; then
        echo "错误:Network ID 不能为空!"
        exit 1
    fi
    
    # --- 安装与配置 ---
    echo "--- 步骤 4/6: 设置权限与目录 ---"
    chmod +x /usr/bin/zerotier-one
    mkdir -p /var/lib/zerotier-one
    echo "✅ 完成"
    
    echo "--- 步骤 5/6: 创建开机自启服务 ---"
    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" in
        start) 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 "--- 步骤 6/6: 启动服务并加入网络 ---"
    /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-getcurl一键安装ZeroTier,结果提示command not found

  • 原因分析: 我的RV1126是Buildroot构建的,为了追求极致的性能和最小的体积,系统里没有包含这些包管理器和下载工具。

  • 解决方案: 手动部署。最终采用的方法是:在电脑上下载适配ARM架构的.deb包,在Ubuntu虚拟机中用dpkg-deb -x命令解压,从中提取出zerotier-onezerotier-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生成新固件并烧录。在烧录重启进入到系统后,执行zerotier-one -d命令启动虚拟网卡,然后使用ifconfig命令,如果能看到一个名为ztxxx开头的网卡,这就代表成功了。(注意:需要提前将zerotier-one 移植到开发板内)

问题6:加入网络后,无法获取虚拟IP

然后再次会到开发板终端执行zerotier-cli listnetworks,应该就能看到开发板成功获取到了虚拟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官网后台批准它进门,需要登陆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

    • 下图为示例,具体的端口号和ip根据你们自己实际情况来

    • # 解决后
      [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,或者降低视频流的码率和分辨率。

五、如何在远端查看开发板上接的摄像头的视频流

1. 开发板已经启动并在后台运行了mediamtx流媒体服务器

2.ffmpeg一直在往外推流,将视频画面推向8554这个端口。示例: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

3.执行push_stream_to_internet.sh脚本,保证开发板已经加入到Network ID,前面步骤已经介绍过

4.手机端打开zeroTier One软件,保持NETwork ID为加入状态,下面的status一定为OK,如果不是OK的话,检查一下是不是NETwork ID错了,或者去网站看一下是不是没有给此设备授权。如果加入成功,应该如下图所示

此时相当于你的手机和开发板已经加入了同一个虚拟局域网内(可以把NETwork ID理解成网关)

3.用手机端查看视频方法:打开vlc软件,打开串流,输入地址 rtsp://为开发板分配的虚拟地址:推流端口号/live/main_stream(请注意,这个端口号要和推流的端口地址保持一致,也就是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这里面面8554,也要和路由器后台管理中设置的端口映射的那个端口号保持一致)

然后就可以看到开发板连接摄像头采集到的视频画面:

4.用电脑查看视频方法:打开vlc软件,打开网络串流,输入地址 rtsp://为开发板分配的虚拟地址:推流端口号/live/main_stream,即可正常观看,效果图如下:

六、流程梳理与原理总结

数据流转路径

摄像头 → [V4L2] → FFmpeg → **** → MediaMTX → [Linux网络栈] → ZeroTier虚拟网卡 → 互联网加密隧道 → 手机的ZeroTier虚拟网卡 → **** → VLC播放器

各环节角色解析
  • 摄像头 (Camera): 作为整个流程的数据源头,它是一个硬件设备,负责捕捉现实世界的动态画面,并通过MIPI-CSI等高速接口,将原始的数字视频帧源源不断地送入开发板的SoC处理器中。

  • V4L2(Video for Linux 2)是 Linux 系统中用于处理视频设备的内核级 API 框架,全称为 “第二代 Linux 视频接口”。它为摄像头、视频采集卡、电视卡等视频设备提供了统一的软件接口,支持:视频捕获(从摄像头获取图像),视频输出(向设备发送视频),视频压缩 / 解压缩。应用程序通过 V4L2 接口与硬件交互,无需关心底层设备细节,实现了跨硬件的兼容性。常见的视频工具(如 Cheese、ffmpeg)和开发库(如 OpenCV)都依赖 V4L2 与摄像头通信。控制设备参数(如亮度、帧率、分辨率)

  • FFmpeg: 这是系统的核心处理引擎。它扮演着“采集、编码与推送”的多重角色。首先,它通过V4L2接口与内核驱动交互,获取摄像头的原始视频帧;接着,它利用CPU(或硬件编码器)将这些原始帧实时压缩成高效的H.264网络视频格式;最后,它作为一个RTSP客户端,主动将编码好的视频流推送到本地的MediaMTX服务器。

  • MediaMTX: 这是一个轻量级的流媒体分发中心。它作为“信号塔”,接收来自FFmpeg的本地推流,并按照RTSP/RTP标准协议进行管理。它时刻准备着,随时响应来自公网的播放请求,并将视频流分发给任何合法的客户端。

  • ZeroTier虚拟网卡与服务 (开发板端): 这是数据的**“隧道入口”与“加密官”**。当MediaMTX向外发送视频流时,Linux网络协议栈会将数据包路由到ZeroTier创建的虚拟网卡。此时,在后台运行的ZeroTier服务会拦截这些数据包,对其进行端到端加密和封装,然后通过物理网卡发送到互联网。

  • 互联网加密隧道 (Encrypted Tunnel): 这是ZeroTier建立的**“私人点对点高速公路”**。借助UDP打洞等NAT穿透技术,视频数据包在这条加密通道中,安全、高效地从您的开发板直接穿梭到您手机所在的网络,尽可能避免了服务器中转带来的延迟 。  

  • 手机的ZeroTier虚拟网卡与服务 (手机端): 这是数据的**“隧道出口”与“解密官”**。手机的ZeroTier服务接收从隧道传来的加密数据包,将其解密并还原成标准的IP数据包,然后注入到手机的ZeroTier虚拟网卡中。对于手机操作系统和VLC来说,这些数据包看起来就像是来自同一个局域网。

  • VLC播放器 (VLC Player): 作为最终的**“显示终端”**,它向开发板的ZeroTier虚拟IP地址发起RTSP播放请求。在收到由ZeroTier解密后的RTP视频流数据后,VLC负责对其进行解码,并将生动的画面呈现在您的手机屏幕上。

核心原理解析

这套方案的精髓在于分层解耦与网络虚拟化。其原理可以概括为:首先,在开发板这个“孤岛”上,利用 FFmpegMediaMTX 搭建一个功能完备但仅限本地访问的RTSP“电视台”。然后,通过 ZeroTier 这项网络虚拟化技术,建立一个跨越广域网的加密虚拟局域网,相当于为这个“孤岛”和您的手机之间修建了一座“私密跨海大桥”。最后,您的手机VLC播放器,通过这座桥梁分配到的虚拟IP地址,直接、安全地“收看”这个本地电视台的节目,从而以一种优雅且高效的方式实现了安全的公网远程监控。

七:心得体会

经历了上述所有挑战后,我终于实现了最初的目标。现在,无论我身在何处,只要手机有网络,就能随时打开VLC,输入 rtsp://为开发板分配的虚拟地址:推流端口号/live/main_stream,看到RV1126上的摄像头采集到的实时画面。

这套推流方案有以下优点:

  • 高度安全 :无需在路由器上暴露任何公网端口,所有视频流都经过端到端加密,只有您授权的设备才能访问,从根本上杜绝了被攻击的风险。

  • 零成本运行 : 核心软件(ZeroTier, FFmpeg等)完全免费,没有商业摄像头常见的云存储或月度订阅费用,实现了永久的免费远程访问。

  • 数据主权与灵活性 : 完全控制自己的视频数据,它不会上传到任何第三方服务器。同时,整套系统不受任何厂商限制,您可以自由更换硬件、软件和定制功能。

  • 强大的网络适应性 : 无需公网IP,无论您家里的网络环境多么复杂,也无论您的手机在任何地方(4G、5G、Wi-Fi),都可以通过一个固定的虚拟IP地址稳定连接。

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值