本节以 FFMedia SDK 2.6.1 为例,使用预编译的 ffmedia CLI,搭建上游流源 → 网络拉流 → 编码码流直通(或转码)→ RTSP/RTMP 服务的完整管线。
本节重点介绍编码码流的直接转发:不解码、不重新编码,延迟和 CPU 占用都较低。只有当上游编码格式与目标输出协议不兼容时,才需要增加 mpp-dec + mpp-enc 转码链路。
准备环境
硬件环境
准备以下硬件:
- Firefly RK 平台(例如 ROC-RK3588-RT Firefly Ubuntu 22.04)
- 一台 PC,与开发板处于同一局域网,用于提供上游流源并验证转发结果
- (可选)IPC 网络摄像头,用于提供真实码流的上游源
上下游之间必须能互相访问对应端口。跨网段或防火墙环境下,需放行 8554/tcp、8554/udp 与 1935/tcp。
软件环境
目标板上解压 SDK 发布包,并确认 CLI 可用:
tar -xzvf ffmedia_release-2.6.1.tar.gz
cd ffmedia_release-2.6.1
./bin/ffmedia modules
确认网络模块的参数定义,参数名与枚举名会随 SDK 版本变化:
./bin/ffmedia params rtsp-client
./bin/ffmedia params rtsp-server
./bin/ffmedia params rtmp-client
./bin/ffmedia params rtmp-server
./bin/ffmedia params mpp-enc
本节管线用到的模块:
| 模块 | 类别 | 作用 |
|---|---|---|
rtsp-client |
输入(vi) | 拉取 RTSP 流,输出压缩视频与可用的音频通道 |
rtmp-client |
输入(vi) | 通过 publish=true 拉取 RTMP 流 |
ffmpeg-demux |
输入(vi) | 通过 FFmpeg 的协议与 demuxer 读取媒体(HLS、WHEP、SRT 等) |
mpp-dec |
处理(vp) | 使用 Rockchip MPP 将码流解码为原始帧(仅转码时需要) |
mpp-enc |
处理(vp) | 使用 Rockchip MPP 将原始帧重新编码(仅转码时需要) |
rtsp-server |
输出(vo) | 接收编码媒体包,作为 RTSP 服务端向客户端分发 |
rtmp-server |
输出(vo) | 接收编码媒体包,作为 RTMP 服务端向客户端分发 |
上游流源准备
演示环境里的上游源:
| 方案 | 说明 |
|---|---|
| MediaMTX + ffmpeg 合成流 | PC 上零配置起步,同时提供 RTSP 与 RTMP 两种上游,覆盖本节全部管线 |
| IPC 网络摄像头 | 真实码流,但通常只提供 RTSP,RTMP 拉流管线仍需 MediaMTX |
在 PC 上启动 MediaMTX(单文件免安装,默认监听 RTSP 8554 / RTMP 1935):
mediamtx.exe
默认配置就监听 8554 (RTSP) 和 1935 (RTMP),当然也提供监听 8888 (HLS) 、1935 (WebRTC) 等:
再推入两路测试流。第一路为 H.264 + AAC,用于直通管线:
ffmpeg -re -f lavfi -i testsrc2=size=1280x720:rate=30 \
-f lavfi -i sine=frequency=1000 \
-c:v libx264 -profile:v high -g 60 -pix_fmt yuv420p \
-c:a aac -b:a 128k \
-f flv rtmp://127.0.0.1:1935/live
拓扑如下:
+-> rtsp-server :8554/live
ffmpeg ---> MediaMTX ------------+-> rtmp-server :1935/live
rtmp://<PC_IP>:1935/live +-> hls-server :8888/live/index.m3u8
+-> ...
第二路为 H.265,用于后面的转码管线:
ffmpeg -re -f lavfi -i testsrc2=size=1280x720:rate=30 \
-c:v libx265 -preset fast -g 60 -pix_fmt yuv420p -tag:v hvc1 \
-f rtsp rtsp://127.0.0.1:8554/h265
推流成功后,PC 对外提供以下上游地址(<PC_IP> 为 PC 的局域网地址):
| 上游地址 | 编码 | 用于 |
|---|---|---|
rtsp://<PC_IP>:8554/live |
H.264 + AAC | RTSP 拉流、分支管线 |
rtmp://<PC_IP>:1935/live |
H.264 + AAC | RTMP 拉流 |
rtsp://<PC_IP>:8554/h265 |
H.265 | 转码管线 |
Windows 可以用 ipconfig 查看 <PC_IP>,例如这里为 192.168.31.183:
若改用 IPC 摄像头,先在 Web 后台把视频编码设为 H.264,再按厂商格式拼接 RTSP 地址。
地址与端口
| 名称 | 示例 | 说明 |
|---|---|---|
| 上游 RTSP 地址 | rtsp://192.168.1.100:8554/live |
被目标板拉取的源地址 |
| 上游 RTMP 地址 | rtmp://192.168.1.100:1935/live |
被目标板拉取的源地址 |
| 转发设备地址 | 172.16.18.63 |
目标板在局域网中的地址 |
| RTSP 服务端口 | 8554 |
避免使用需要高权限的 554 |
| RTMP 服务端口 | 1935 |
RTMP 默认端口 |
| 服务路径 | /live |
Server 的默认路径 |
原理
网络模块收发的是编码后的码流,不涉及像素数据。因此"拉流 → 转发"的最短链路里没有解码与编码环节:
直通(默认) 转码(编码不兼容时才用)
────────────────────── ──────────────────────────────────
client → server client → mpp-dec → mpp-enc → server
不解码、不重编码 解码到帧,再按目标编码重编
延迟低、CPU 占用低 延迟增加,占用 MPP 硬件单元
输入编码被完整保留 可把 H.265 转成 H.264 以适配 RTMP
选择依据只有一条——看上游视频编码:
| 上游编码 | 输出 RTSP | 输出 RTMP |
|---|---|---|
| H.264 | 直通即可 | 直通即可 |
| H.265 | 直通即可(RTSP 兼容性更宽) | 必须转码,RTMP 客户端通常不支持 H.265 |
验证上游可用
先跑最简验证——不涉及开发板,确认上游源本身可用。这一步能把"源的问题"和"板子的问题"提前分开:
ffprobe rtsp://<PC_IP>:8554/live
ffplay rtsp://<PC_IP>:8554/live
ffprobe 输出中重点看流的编码标识:
Stream #0:0: Video: h264 ...
Stream #0:1: Audio: aac ...
例如:
上游流在 PC 上播放:
如果看到
hevc/h265,后续转发为 RTMP 时要使用本文的转码管线。
拉流并转发为 RTSP 服务
先跑最简转发管线——拉流并原样提供 RTSP 服务。
管线结构
直通管线:
rtsp-client → rtsp-server
完整命令
./bin/ffmedia run \
-m src=rtsp-client \
-m relay=rtsp-server \
-p 'src:source{url=rtsp://<PC_IP>:8554/live}' \
-p 'relay:endpoint/port=8554' \
-c src=relay
命令拆解
| 部分 | 含义 |
|---|---|
-m src=rtsp-client |
声明拉流模块实例 src |
-p 'src:source{url=...}' |
指定上游 RTSP 地址,子参数用 ; 分隔 |
-m relay=rtsp-server |
声明 RTSP 服务端模块实例 relay |
-p 'relay:endpoint/port=8554' |
服务端口设为 8554(endpoint/port 是子参数写法) |
-c src=relay |
连接 src → relay;省略通道号时自动匹配兼容的视频/音频通道 |
验证
其他机器通过以下地址拉取目标板转发的流:
ffplay rtsp://<BOARD_IP>:8554/live
或者使用 VLC 客户端拉取开发板提供的 RTSP 流:
FFMedia 打印的信息:
该链路不含解码模块,属于压缩码流直通。如果只需要视频,可以显式连接视频通道:
-c src@0=relay。
上游走 TCP 传输
RTSP 客户端默认使用 UDP。跨网段或防火墙环境下如果 UDP 不通,先查看 params rtsp-client 中 stream-type 支持的枚举值,再配置为 TCP:
-p 'src:stream-type=tcp;source{url=rtsp://<PC_IP>:8554/live}'
转发为 RTMP 服务
在上一步的基础上,只把服务端模块换成 rtmp-server。
管线结构
直通管线:
rtsp-client → rtmp-server
完整命令
./bin/ffmedia run \
-m src=rtsp-client \
-m relay=rtmp-server \
-p 'src:source{url=rtsp://<PC_IP>:8554/live}' \
-p 'relay:endpoint/port=1935' \
-c src=relay
新增项命令
| 部分 | 含义 |
|---|---|
-m relay=rtmp-server |
声明 RTMP 服务端模块实例 relay |
-p 'relay:endpoint/port=1935' |
服务端口设为 1935(RTMP 默认端口) |
拉流侧的 src 实例与连接关系完全不变。
验证
ffplay rtmp://<BOARD_IP>:1935/live
或者使用 VLC 客户端拉取开发板提供的 RTMP 流:
FFMedia 打印的信息:
注意:直通转发要求上游视频是 H.264。如果上游是 H.265,或 RTMP 客户端提示不支持视频 codec,请使用下一小节的转码管线。
上游为 H.265 时转码后转发
当上游编码与目标协议不兼容时,需要在管线中插入解码与编码模块。本例把 H.265 转成 H.264 后再提供 RTMP 服务。
管线结构
转码管线:
rtsp-client → mpp-dec → mpp-enc → rtmp-server
完整命令
./bin/ffmedia run \
-m src=rtsp-client \
-m dec=mpp-dec \
-m enc=mpp-enc \
-m relay=rtmp-server \
-p 'src:source{url=rtsp://<PC_IP>:8554/h265}' \
-p 'enc:encode{codec=5;fps=30;gop=60};output-timeout-ms=-1' \
-p 'relay:endpoint/port=1935' \
-c src=dec \
-c dec=enc \
-c enc=relay
新增项命令
| 部分 | 含义 |
|---|---|
-m dec=mpp-dec |
声明硬件解码模块实例 dec |
-m enc=mpp-enc |
声明硬件编码模块实例 enc |
-p 'enc:encode{codec=5;fps=30;gop=60}' |
编码格式设为 5(H.264),帧率 30,GOP 60 |
-p '...;output-timeout-ms=-1' |
关闭输出超时,避免转码链路在无输出时提前退出 |
-c src=dec |
拉流输出接入解码模块 |
-c dec=enc |
解码帧接入编码模块(连接的是帧,不再是码流) |
-c enc=relay |
编码后的 H.264 码流接入 RTMP 服务端 |
编码格式枚举对照表
encode/codec 参数的取值对应不同的视频编码格式,常见值如下:
| 枚举名称 | 整数值 | 说明 |
|---|---|---|
MEDIA_CODEC_UNKNOWN |
0 | 未知编码格式 |
MEDIA_CODEC_VIDEO_VCM |
1 | VCM 视频编码 |
MEDIA_CODEC_VIDEO_MPEG4 |
2 | MPEG-4 视频编码 |
MEDIA_CODEC_VIDEO_MPEG1 |
3 | MPEG-1 视频编码 |
MEDIA_CODEC_VIDEO_MPEG2 |
4 | MPEG-2 视频编码 |
MEDIA_CODEC_VIDEO_H264 |
5 | H.264 (AVC) 视频编码 ← 本节示例使用 |
MEDIA_CODEC_VIDEO_H265 |
6 | H.265 (HEVC) 视频编码 |
MEDIA_CODEC_VIDEO_VP8 |
7 | VP8 视频编码 |
MEDIA_CODEC_VIDEO_VP9 |
8 | VP9 视频编码 |
MEDIA_CODEC_VIDEO_AV1 |
9 | AV1 视频编码 |
MEDIA_CODEC_VIDEO_MJPEG |
10 | Motion JPEG 视频编码 |
MEDIA_CODEC_VIDEO_H266 |
11 | H.266 (VVC) 视频编码 |
MEDIA_CODEC_VIDEO_RAW |
12 | 原始视频数据 |
具体可以参考 SDK 的 include/ffmedia/base/ff_type.hpp。
注意:该管线只处理视频。如果还需要保留上游音频,需单独规划音频通道的编码与连接,不能假设音频会自动穿过视频转码链路。
从 RTMP 上游拉流
rtmp-client 同时支持拉流和推流,方向由 publish 参数决定:
| 取值 | 方向 | 在管线中的位置 |
|---|---|---|
publish=true |
拉流 | 作为根源模块(Producer) |
publish=false |
推流 | 作为下游消费者(Consumer) |
完整命令
拉流并提供 RTSP 服务:
./bin/ffmedia run \
-m src=rtmp-client \
-m relay=rtsp-server \
-p 'src:source{url=rtmp://<PC_IP>:1935/live;publish=true};timeout/seconds=1' \
-p 'relay:endpoint/port=8554' \
-c src=relay
新增项命令
| 部分 | 含义 |
|---|---|
-m src=rtmp-client |
声明 RTMP 客户端模块实例 src |
-p 'src:source{url=rtmp://...;publish=true};timeout/seconds=1' |
指定上游 RTMP 地址,并显式声明拉流方向以及超时时间 |
只把 relay 换成 rtmp-server(端口 1935),即可得到"RTMP 拉流 → RTMP 服务"的组合,连接关系不变。
注意:拉流模式下必须显式设置
publish=true。漏掉该参数时模块会按推流模式工作,表现为拉不到流。
同一上游同时提供 RTSP 与 RTMP
管线结构
分支管线(同一个拉流源可以分支连接到两个服务端):
+-> rtsp-server :8554/live
rtsp-client --------+
+-> rtmp-server :1935/live
完整命令
./bin/ffmedia run \
-m src=rtsp-client \
-m rtsp=rtsp-server \
-m rtmp=rtmp-server \
-p 'src:source{url=rtsp://<PC_IP>:8554/live}' \
-p 'rtsp:endpoint/port=8554' \
-p 'rtmp:endpoint/port=1935' \
-c src=rtsp \
-c src=rtmp
新增项命令
| 部分 | 含义 |
|---|---|
-m rtsp=rtsp-server |
声明 RTSP 服务端实例 rtsp |
-m rtmp=rtmp-server |
声明 RTMP 服务端实例 rtmp |
-c src=rtsp |
第一路分支连接(同一 Producer 的第一个 Consumer) |
-c src=rtmp |
第二路分支连接(同一 Producer 的第二个 Consumer) |
src 的实例名始终不变,它通过两条 -c src=... 同时连接了两个服务端——不需要额外写分发逻辑,框架自动完成。两条分支互不影响。
验证
ffplay rtsp://<BOARD_IP>:8554/live
ffplay rtmp://<BOARD_IP>:1935/live
该方式仍然是码流直通。若上游编码可以被 RTSP 客户端播放但不适合 RTMP,只需把 RTMP 分支改为
mpp-dec → mpp-enc(H.264) → rtmp-server,RTSP 分支保持直通。
拉取 FFmpeg 支持的其他输入
rtsp-client 和 rtmp-client 是专用网络模块;需要 HLS、HTTP、SRT、WHEP 等 FFmpeg 支持的输入格式时,改用 ffmpeg-demux:
| 模块 | 适用协议 |
|---|---|
rtsp-client / rtmp-client |
RTSP、RTMP,专用实现 |
ffmpeg-demux |
HLS、HTTP、SRT、WHEP,以及 FFmpeg 支持的其他输入 |
查看 ffmpeg-demux 支持的参数:
./bin/ffmedia params ffmpeg-demux
基本参数格式为:
-p 'src:input-format=<FORMAT>;source{uri=<INPUT_URI>;loop=0}'
| 部分 | 含义 |
|---|---|
input-format |
FFmpeg 的输入格式短名称;不确定时可省略,让 FFmpeg 自动探测 |
source/uri |
输入地址;使用 uri,不要写成专用客户端的 url |
source/loop=0 |
网络流不循环,一直运行到断流或收到停止信号 |
以 HLS 输入为例(把地址换成一个真实可用的 .m3u8):
./bin/ffmedia run \
-m src=ffmpeg-demux \
-m relay=rtsp-server \
-p 'src:input-format=hls;source{uri=http://<PC_IP>:8888/live/index.m3u8;loop=0}' \
-p 'relay:endpoint/port=8554' \
-c src@0=relay
这里使用 src@0 只转发视频通道,避免上游的 Opus 等音频编码与 RTSP Server 的输入能力不匹配。
RTSP 的 TCP 选项则通过 ffmpeg/options 传递:
-p 'src:input-format=rtsp;source{uri=rtsp://<PC_IP>:8554/live;loop=0};ffmpeg{options=rtsp_transport=tcp}'
IPC 网络摄像头
IPC 网络摄像头通常是一个“采集 + 编码 + RTSP 服务”一体的小流媒体设备,它直接替代了“PC + MediaMTX + ffmpeg 推流”这一整段。
主要原理如下:
- 采集端:sensor 出图 → ISP 处理(3A、降噪)→ VENC 硬件编码成 H.264/H.265
- 服务端:固件里跑着一个内置 RTSP Server,监听 554 等某个端口
- 它对外是一个标准的 RTSP 源——FFMedia 的 rtsp-client 拉它,和拉 MediaMTX 的流没有任何区别
拓扑如下:
IPC 摄像头(对外就是一个 RTSP 源)
图像传感器 + ISP --> 硬件编码 H.264 / H.265 --> 内置 RTSP Server :554
|
| rtsp://admin:<Password>@<IPC_IP>:554/Streaming/Channels/101
v
RK3588 板
rtsp-client(拉流,不解码)--> rtsp-server :8554 / rtmp-server :1935
|
| rtsp://<BOARD_IP>:8554/live 或 rtmp://<BOARD_IP>:1935/live
v
PC 客户端
ffplay / VLC / ...
例如对网络摄像头拉流后转发为 rtmp-server ,编码格式设为 5(H.264),帧率 30,GOP 60:
./bin/ffmedia run \
-m src=rtsp-client \
-m dec=mpp-dec \
-m enc=mpp-enc \
-m relay=rtmp-server \
-p 'src:source{url=rtsp://admin:<Password>@<IPC_IP>:554/Streaming/Channels/101}' \
-p 'enc:encode{codec=5;fps=30;gop=60};output-timeout-ms=-1' \
-p 'relay:endpoint/port=1935' \
-c src=dec \
-c dec=enc \
-c enc=relay
其中这里的 rtsp://admin:<Password>@<IPC_IP>:554/Streaming/Channels/101} 要替换成实际网络摄像头的 RTSP URL。
小结
本节把 CLI 三要素用到网络管线上,每一步只增加一个模块或一条连接:
- 第一步(上游验证):用 MediaMTX + ffmpeg 合成流充当上游源,先在 PC 上用
ffprobe/ffplay确认可用,把问题边界划在开发板之外; - 第二步(拉流 + RTSP 服务):用
rtsp-client拉流,rtsp-server分发,完成最简直通管线; - 第三步(换 RTMP 服务端):只替换
relay的模块与端口(1935),拉流侧完全不动; - 第四步(插入转码):上游为 H.265 时,在
src与relay之间加入mpp-dec与mpp-enc(codec=5转成 H.264); - 第五步(换拉流端):用
rtmp-client+publish=true替换rtsp-client,显式声明拉流方向; - 第六步(分支管线):一个
rtsp-client通过两条-c src=...同时连接rtsp-server与rtmp-server; - 第七步(换通用拉流模块):需要 HLS、SRT 等 FFmpeg 支持的输入时,用
ffmpeg-demux替换专用客户端; - 第八步(换真实上游源):把上游地址换成 IPC 摄像头的 RTSP URL。


















