二、摄像头采集、预览与录制——FFMedia入门系列教程

本节以 FFMedia SDK 2.6.1 为例,使用预编译的 ffmedia CLI,搭建摄像头采集 → 窗口预览 → H.265 硬件编码 → MP4 录制的完整管线。

准备环境

硬件环境

准备以下硬件:

  • Firefly RK 平台(例如 ROC-RK3588-RT Firefly Ubuntu 22.04)
  • USB 摄像头或其他可被 Firefly 平台识别为 video 设备的视频源

目标平台接入摄像头后,可先检查 video 设备路径、权限与支持的格式:

ls -l /dev/video*
v4l2-ctl -d /dev/video0 --all          # 查看设备详情
v4l2-ctl -d /dev/video0 --list-formats-ext   # 查看支持的格式

软件环境

解压 SDK 发布包并确认 CLI 可用:

tar -xzvf ffmedia_release-2.6.1.tar.gz
cd ffmedia_release-2.6.1

./bin/ffmedia --help
./bin/ffmedia run --help

确认运行时哪些模块可用:

./bin/ffmedia modules
./bin/ffmedia params cam
./bin/ffmedia params mpp-enc
./bin/ffmedia params renderer-video
./bin/ffmedia params file-writer

本节管线用到的四个模块:

模块 类别 作用
cam 输入(vi) 通过 V4L2 采集摄像头原始视频帧
renderer-video 输出(vo) 通过窗口显示摄像头画面
mpp-enc 处理(vp) 使用 Rockchip MPP 将原始帧编码为 H.265
file-writer 输出(vo) 将编码码流封装为 MP4 文件

验证摄像头可用(仅采集)

先跑最简管线——只采集、不显示,验证摄像头能正常工作:

./bin/ffmedia run \
  -m src=cam \
  -p 'src:device=/dev/video0;capture{width=1920;height=1080}'

该命令:

  • 用 -m src=cam 声明一个名为 src 的采集模块实例
  • 用 -p 设置设备路径与采集分辨率(子参数用 ; 分隔)
  • 不连接任何 Consumer,所以不会有窗口、没有输出文件

如果该命令运行后无报错,说明摄像头已被正确识别。


加入窗口预览

在采集的基础上加入显示模块,预览采集到的画面。

管线结构

完整命令

./bin/ffmedia run \
  -m src=cam \
  -p 'src:device=/dev/video0;capture{width=1920;height=1080}' \
  -m dis=renderer-video \
  -c src=dis

命令拆解

部分 含义
-m src=cam 声明采集模块实例 src
-p 'src:device=...;capture{...}' 指定设备路径与采集分辨率,多个子参数用 ; 分隔
-m dis=renderer-video 声明窗口渲染模块实例 dis
-c src=dis 连接 src → dis;省略通道号时使用默认通道,等价于 src@0=dis

配置预览窗口的位置与大小

可通过 window 参数配置预览窗口的位置与大(例如位置 (200, 0)、大小 640×480):

./bin/ffmedia run \
  -m src=cam \
  -p 'src:device=/dev/video0;capture{width=1920;height=1080}' \
  -m dis=renderer-video \
  -c src=dis \
  -p 'dis:window{x=200;y=0;width=640;height=480}'

预览窗口将按设定的位置和大小显示。按 Ctrl + C 停止预览。


同时预览与录制(分支管线)

FFMedia 的核心特性之一是一个 Producer 可以连接多个 Consumer。这一步让 cam 的输出分支为两路:

  • 支路 1:直接送 renderer-video,用于窗口预览
  • 支路 2:送 mpp-enc 编码为 H.265,再由 file-writer 封装为 MP4 文件

两条支路互不影响,共享同一份采集数据。

管线结构

完整命令

./bin/ffmedia run \
  -m src=cam \
  -p 'src:device=/dev/video0;capture{width=1920;height=1080}' \
  -m dis=renderer-video \
  -p 'dis:window{x=200;y=0;width=640;height=480}' \
  -c src=dis \
  -m enc=mpp-enc \
  -p 'enc:encode/codec=6' \
  -c src=enc \
  -m mux=file-writer \
  -p 'mux:path=/home/firefly/Videos/test.mp4' \
  -c enc=mux

新增项命令拆解

部分 含义
-m enc=mpp-enc 声明硬件编码模块实例 enc
-p 'enc:encode/codec=6' 编码格式设为 6,代表 H.265(encode/codec 是子参数写法)
-c src=enc 采集输出分支接入编码模块(同一 Producer 的第二个 Consumer)
-m mux=file-writer 声明文件输出模块实例 mux
-p 'mux:path=...' 指定 MP4 输出路径
-c enc=mux 编码码流接入封装写文件模块

整个命令中,cam 的实例名始终是 src,而它通过 -c src=dis 和 -c src=enc 同时连接了两个 Consumer。这是「一个 Producer → 多个 Consumer」的最直白体现——不需要额外写分发逻辑,框架自动完成。

编码格式枚举对照表

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。


验证录制结果

运行后可以同时观察到:

  • 显示窗口:实时预览摄像头画面
  • Videos 文件夹:test.mp4 文件在录制过程中持续增长

使用 Ctrl + C 正常停止管线后,用系统播放器打开 test.mp4,即可回放之前摄像头采集的画面。

注意:MP4 是封装格式,需要正常停止(Ctrl+C 或 kill -INT <PID>)才能完整写入文件索引。如果进程被强制杀死(例如 kill -9),文件可能大小为零、时长异常或完全无法播放。日常调试时务必使用 Ctrl+C 优雅退出。


小结

本节用一条真实管线把第一节的 CLI 三要素落地为可运行的命令:

  1. 第一步(仅采集):用 cam 验证摄像头可用,作为后续步骤的基础;
  2. 第二步(加预览):加入 renderer-video,通过 -c src=dis 完成第一路连接,实现窗口预览;
  3. 第三步(加录制):在第二步基础上,新增 mpp-enc + file-writer,通过 -c src=enc 与 -c enc=mux 接入第二路连接——一个 Producer → 多个 Consumer。