文章目录
-
-
- [1. 公网IP](#1. 公网IP)
- 2.云服务器拥有公网IP
- [3. wget下载](#3. wget下载)
- [4.配置云-frps 服务端](#4.配置云-frps 服务端)
- [5. 配置本地-frpc 客户端](#5. 配置本地-frpc 客户端)
- 6.视频流
- [7. 整体数据流向](#7. 整体数据流向)
- 8.开发板上电启动
-
1. 公网IP



- 从公司(外网)访问家里的监控设备,核心难题是:家里的设备通常处于内网,没有独立公网IP
- 方案一:Tailscale / ZeroTier 组网方案 ⭐ 开发者首选
本质:基于WireGuard的虚拟组网(Overlay Network),让公司设备"假装"在家里的局域网中。 - 方案二:FRP 内网穿透 ⭐ 开发者备选方案
本质:自建反向代理服务器,通过云服务器中转流量。 - 方案三:P2P云直连(厂商SDK集成) ⭐ 快速出活
本质:使用摄像头厂商的P2P云服务,通过SDK接入。 - 方案四:端口映射 + DDNS(公网IP方案)
本质:直接在路由器上开放摄像头端口,用DDNS解决IP变动。




- 手机4G网络:是内网还是外网?
4G网络是运营商级内网(CGNAT),没有独立的公网IP。 - 4G网络 vs 家宽网络
手机(10.x.x.x) ─→ 运营商NAT ─→ 公网IP ─→ 互联网
家宽(192.168.x) ─→ 路由器NAT ─→ 运营商NAT ─→ 公网IP
2.云服务器拥有公网IP
阿里云
- 配置防火墙-添加规则
开放一个TCP端口(例如 7000),这是FRP服务端将要监听的端口。授权对象可设置为 0.0.0.0/0
7000:FRP服务端与客户端通信的端口。
8554:你希望外部访问RTSP视频流的端口
FRP
FRP方案成熟可靠吗?
是的,非常成熟可靠。
久经考验:FRP是一个发展多年、广泛使用的开源项目。其设计目标就是为了应对生产环境,性能稳定,适合长期运行。
行业认可:它被评价为"国内最稳的内网穿透开源方案"和"高性能自托管解决方案",很多开发者用它来远程访问NAS或进行远程办公。
性能强劲:FRP采用Go语言开发,支持高并发。在P2P模式下,同运营商网络的延迟可以低至50ms以内。带宽取决于你的云服务器,没有额外限制。
安全可控:FRP支持TLS加密传输、Token认证和IP白名单等多种安全机制。由于所有流量都经过你自己的云服务器,数据安全完全由你掌控。
🤔 为什么需要在本地和云端都安装?
这是由FRP的内网穿透原理决定的,它需要在两个地方各司其职。
云服务器上的 frps (服务端):它就像一个 "接线总机" ,拥有固定的公网"号码"(IP地址)。它的任务是等待来自外网(你的手机)和内网(你家开发板)的连接,并进行"通话"转接。
开发板上的 frpc (客户端):它像一个 "分机" ,主动拨打"总机"frps的号码,并建立一条保持畅通的"热线"(即隧道)。这条"热线"让frps随时能把外网的访问请求转接过来
- 下载frp

- 在阿里云服务器上下载(服务端)
c
# 下载 amd64 版本
wget https://github.com/fatedier/frp/releases/download/v0.71.0/frp_0.71.0_linux_amd64.tar.gz
# 解压
tar -xzf frp_0.71.0_linux_amd64.tar.gz
# 进入目录
cd frp_0.71.0_linux_amd64
# 查看文件,应该有 frps(服务端)和 frpc(客户端)
ls -l
- 在开发板上(客户端)
请根据你的开发板架构下载对应版本。若不确认,可在开发板终端执行 uname -m,若显示 aarch64 则下载 arm64 版本
c
# 下载 arm64 版本(示例)
wget https://github.com/fatedier/frp/releases/download/v0.71.0/frp_0.71.0_linux_arm64.tar.gz
# 解压
tar -xzf frp_0.71.0_linux_arm64.tar.gz
# 进入目录
cd frp_0.71.0_linux_arm64
3. wget下载
- 所有GitHub Release的下载链接都有一个固定的标准格式:
以你的截图为例:
用户名:fatedier
仓库名:frp
版本号:v0.71.0
文件名:frp_0.71.0_linux_amd64.tar.gz
把它们拼起来,就是完整的下载链接:
https://github.com/fatedier/frp/releases/download/v0.71.0/frp_0.71.0_linux_amd64.tar.gz
所以,只要你在页面上看到了文件名,用这个公式就能构造出任何文件的下载地址。
4.配置云-frps 服务端
配置云服务器(frps 服务端)
-
进入 FRP 目录
cd ~/frp_0.71.0_linux_amd64
-
创建配置文件 frps.toml
vim frps.toml
-
内容
c
bindPort = 7000
auth.method = "token"
auth.token = "your_secure_token_here" # 请修改为一个强密码,例如用命令 openssl rand -hex 16 生成
注意:auth.token 是服务端和客户端互相认证的凭证,务必保持一致。
- 启动 frps 服务(前台测试)
./frps -c frps.toml
如果看到类似 start frps success 的日志,说明服务端启动成功。按 Ctrl+C 停止,然后我们设置为后台服务 - 设置开机自启(systemd)
c
# ─── 复制二进制文件到系统路径 ───
sudo cp frps /usr/local/bin/ # 将 frps(FRP 服务端程序)复制到 /usr/local/bin/,此目录在系统 PATH 中,可直接执行
# ─── 创建配置文件目录 ───
sudo mkdir -p /etc/frp/ # 创建 FRP 配置文件夹 /etc/frp/,-p 表示如果父目录不存在则自动创建
# ─── 复制配置文件到指定目录 ───
sudo cp frps.toml /etc/frp/ # 将 frps.toml(FRP 服务端配置文件)复制到 /etc/frp/ 目录下
# ─── 创建 systemd 服务文件(让 frps 作为系统服务开机自启) ───
sudo vim /etc/systemd/system/frps.service # 用 vim 编辑器创建 service 文件,路径是 systemd 服务定义的标准位置
# ─── service 文件内容如下 ───
[Unit] # Unit 段:描述服务信息和依赖关系
Description=frp server # 服务描述:FRP 服务端
After=network-online.target # 在"网络完全就绪"之后才启动此服务
Wants=network-online.target # 弱依赖:建议网络就绪后再启动,但不是强制要求
[Service] # Service 段:定义如何运行此服务
Type=simple # 启动类型为 simple(执行 ExecStart 后即认为服务已启动)
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml # 启动命令:执行 frps,加载 /etc/frp/ 下的配置文件
Restart=on-failure # 重启策略:非正常退出时自动重启(进程崩溃或退出码非0)
RestartSec=5s # 重启间隔:等待 5 秒后再重启,防止频繁重启打满 CPU
[Install] # Install 段:定义服务安装/启用的行为
WantedBy=multi-user.target # 设置此服务在"多用户模式下"自动启动(即系统正常启动后运行)
# ─── 保存退出后执行以下命令 ───
sudo systemctl daemon-reload # 重新加载 systemd 配置,让新创建的 frps.service 生效
sudo systemctl enable --now frps # enable = 设置开机自启;--now = 立即启动服务,相当于 enable + start 二合一
sudo systemctl status frps # 查看服务运行状态,输出中应显示 active (running),表示运行正常
虽然这台云服务器只做监控,但作为中转服务器,它需要7×24小时持续监听,等待你家开发板和手机随时连接。如果只是前台挂着,SSH断开连接进程就会终止,整个远程监控就失效了。
所以,虽然只做监控,但服务需要长期稳定运行,必须后台化。
5. 配置本地-frpc 客户端
-
进入 FRP 客户端目录
cd ~/frp_0.71.0_linux_arm64 # 或你解压的目录
-
创建配置文件 frpc.toml
vim frpc.toml
-
内容
c
serverAddr = "你的云服务器公网IP" # 例如 123.123.123.123
serverPort = 7000
auth.token = "your_secure_token_here" # 与服务器端一致
[[proxies]]
name = "rtsp_stream"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8554 # 开发板上 MediaMTX 的 RTSP 端口
remotePort = 8554 # 云服务器上对外暴露的端口
- 启动 frpc 客户端(前台测试)
./frpc -c frpc.toml
如果看到 login to server success 和 start proxy success 的日志,说明连接成功。按 Ctrl+C 停止,然后设为后台服务。
6.视频流
数据流向是这样的:
摄像头 → FFmpeg → MediaMTX (RTSP服务器) → frpc (客户端) → 云服务器 → 手机VLC

-
运行 MediaMTX
./mediamtx
-
H.264 软编码推流命令
c
ffmpeg -re -f v4l2 -input_format mjpeg -video_size 1280x720 -framerate 30 -i /dev/video0 \
-c:v libx264 -preset ultrafast -tune zerolatency -crf 25 -b:v 2M -g 60 \
-f rtsp -rtsp_transport tcp rtsp://127.0.0.1:8554/live

- 访问公网IP
手机 VLC 访问 rtsp://88.88.888.88:8554/live
- 效果:延迟画面2-3秒,但是很流畅
7. 整体数据流向
整体数据流向(理解架构)
摄像头硬件 → FFmpeg (采集+H.264编码) → MediaMTX (本机RTSP服务器) → frpc (FRP客户端) → 公网云服务器 (frps) → 手机VLC (远程拉流)

8.开发板上电启动
所有文件放在 /home/baiwen/video1/
/home/baiwen/video1/
├── mediamtx # MediaMTX 可执行文件
├── mediamtx.yml # MediaMTX 配置文件
├── frp_0.71.0_linux_arm64/ # FRP 客户端目录
│ └── frpc
│ └── frpc.toml
└── start_camera.sh # 开机自启脚本
- 编写开机自启脚本 创建 /home/baiwen/video1/start_camera.sh
c
#!/bin/bash
# ============================================================
# 开发板摄像头开机自启脚本(通俗易懂版)
# 功能:开机后检测网络 -> 启动 MediaMTX -> 启动 frpc -> 启动 ffmpeg
# 所有日志都会写入 /home/baiwen/video1/camera.log
# ============================================================
# 定义日志文件的路径,所有输出都写到这里
LOG_FILE="/home/baiwen/video1/camera.log"
# 把脚本本身的标准输出和错误输出,都重定向追加到日志文件
# 这样后面所有 echo 打印的内容,都会自动进入 camera.log
exec >> "$LOG_FILE" 2>&1
# 定义一个函数 log,用来打印带时间戳的日志
# 用法:log "要打印的内容"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
# 定义一个函数 cleanup,用于在服务被停止时,清理所有子进程
# 这个函数会在脚本收到 SIGTERM 或 SIGINT 信号时被调用
cleanup() {
log "收到终止信号,正在停止所有服务..."
# kill 命令:向三个进程发送终止信号
# 2>/dev/null 表示如果进程不存在,错误信息不打印
kill $MEDIAMTX_PID $FRPC_PID $FFMPEG_PID 2>/dev/null
# wait 命令:等待这三个进程完全退出
wait $MEDIAMTX_PID $FRPC_PID $FFMPEG_PID 2>/dev/null
log "所有服务已停止。"
exit 0
}
# trap 命令:告诉脚本,当收到 SIGTERM(systemctl stop 时发送)或 SIGINT(Ctrl+C)时,
# 自动执行 cleanup 函数
trap cleanup SIGTERM SIGINT
# 打印一条启动分隔线,方便在日志中区分每次启动
echo "=========================================="
log "系统启动,开始执行摄像头推流脚本"
# 定义要检测的目标IP(你的阿里云公网IP)
TARGET_IP="47.108.128.94"
log "开始检测网络(目标:$TARGET_IP)..."
# 循环最多 30 次,每次间隔 2 秒,总共等待最多 60 秒
for i in {1..30}
do
# ping 一次目标IP,-c 1 表示只发一个包,-W 1 表示超时1秒
# > /dev/null 2>&1 表示丢弃 ping 的所有输出(不打印)
ping -c 1 -W 1 "$TARGET_IP" > /dev/null 2>&1
# $? 表示上一条命令的执行结果,0 代表成功
if [ $? -eq 0 ]; then
log "网络已连通(尝试 $i 次)"
break # 网络通了就跳出循环,不再继续等待
fi
# 如果已经试了 30 次还没通,就放弃启动
if [ $i -eq 30 ]; then
log "网络超时,退出脚本"
exit 1
fi
# 每次循环末尾等待 2 秒
sleep 2
done
# ---------- 启动 MediaMTX(RTSP 服务器) ----------
# 进入 video1 目录,如果进不去就记日志并退出
cd /home/baiwen/video1
if [ $? -ne 0 ]; then
log "无法进入 /home/baiwen/video1 目录"
exit 1
fi
log "启动 MediaMTX ..."
# 用 & 把 mediamtx 放到后台运行
# 不加 nohup,让它可以作为脚本的子进程被 systemd 管理
./mediamtx &
# $! 表示刚刚放到后台的进程的 PID
MEDIAMTX_PID=$!
log "MediaMTX PID: $MEDIAMTX_PID"
# 等待 2 秒,让 MediaMTX 完成端口绑定
sleep 2
# ---------- 启动 frpc(FRP 客户端) ----------
# 进入 frp 目录
cd /home/baiwen/video1/frp_0.71.0_linux_arm64
if [ $? -ne 0 ]; then
log "找不到 /home/baiwen/video1/frp_0.71.0_linux_arm64 目录"
exit 1
fi
log "启动 FRP 客户端 ..."
# 后台启动 frpc,并把它的输出也写到同一个日志文件
./frpc -c frpc.toml &
FRPC_PID=$!
log "FRP 客户端 PID: $FRPC_PID"
# 等待 3 秒,让 frpc 和云服务器建立连接
sleep 3
# ---------- 启动 ffmpeg(视频推流) ----------
# 回到 video1 目录
cd /home/baiwen/video1
log "启动 FFmpeg 视频推流 ..."
# 后台启动 ffmpeg,参数说明:
# -re : 按实时速度读取摄像头
# -f v4l2 : 使用 V4L2 驱动
# -input_format mjpeg : 输入格式为 MJPEG
# -video_size 1280x720 : 分辨率 720P
# -framerate 30 : 帧率 30 fps
# -i /dev/video0 : 摄像头设备
# -c:v libx264 : 使用 H.264 软件编码
# -preset ultrafast : 编码速度优先(降低 CPU 占用)
# -tune zerolatency : 零延迟调优
# -crf 25 : 画质因子,数字越大画质越低
# -b:v 2M : 目标码率 2 Mbps
# -g 60 : 每 60 帧一个关键帧(2秒)
# -f rtsp : 输出为 RTSP
# -rtsp_transport tcp : 强制使用 TCP 传输
# 最后是推流地址
ffmpeg -re -f v4l2 -input_format mjpeg -video_size 1280x720 -framerate 30 \
-i /dev/video0 \
-c:v libx264 -preset ultrafast -tune zerolatency -crf 25 -b:v 2M -g 60 \
-f rtsp -rtsp_transport tcp rtsp://127.0.0.1:8554/live &
FFMPEG_PID=$!
log "FFmpeg PID: $FFMPEG_PID"
log "所有服务已启动,进入守护等待状态..."
echo "=========================================="
# ---------- 关键:让脚本保持运行 ----------
# wait 命令不带参数,会一直等待所有后台子进程
# 这样脚本不会退出,systemd 就会认为服务还在运行,
# 不会把 MediaMTX、frpc、ffmpeg 这些子进程干掉
wait
- 赋予执行权限
chmod +x /home/baiwen/video1/start_camera.sh
- 转换 Windows 换行符(如果从 Windows 复制)
sed -i 's/\r$//' /home/baiwen/video1/start_camera.sh
- 设置 systemd 服务
创建 /etc/systemd/system/camera.service
- sudo vim /etc/systemd/system/camera.service
c
[Unit]
Description=Camera Auto Streaming Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=baiwen
ExecStart=/home/baiwen/video1/start_camera.sh
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
- 启用开机自启
sudo systemctl daemon-reload
sudo systemctl enable camera.service
sudo systemctl start camera.service # 立即启动测试
- 查看日志
所有日志集中在 /home/baiwen/video1/camera.log
- 在手机 VLC 中打开网络串流实时查看