一、项目背景
最近在做一个基于 RK3588(OrangePi 5 Ultra) 的多路视频采集与录制项目。

系统需要同时接入 三路 USB UVC 相机:
- video3:第一路核心相机
- video5:第二路核心相机
- video1:第三路辅助相机
项目要求:
- 两路核心相机必须稳定 30FPS
- 同时录制 MP4 文件
- 后续还需要进行实时推流和 AI 推理
- CPU 占用尽量低
整体数据流如下:
text
USB Camera
│
▼
MJPEG 30FPS
│
▼
Hardware Decode(MPP)
│
▼
RGA 色彩转换
│
▼
MPP H264/H265 Encoder
│
▼
MP4 Recorder
最终目标不是 ffprobe 显示 30fps,而是真实采集、真实编码都稳定达到 30FPS。
二、硬件环境
开发板:
OrangePi 5 Ultra
RK3588
Ubuntu 22.04
Kernel 5.10
相机:
USB UVC Camera
960×720
MJPEG
30FPS
编码:
Rockchip MPP
mppjpegdec
mpph264enc
mpph265enc
色彩转换:
librga
三、首先确认相机本身是否支持30FPS
第一步一定不要急着写程序。
先确认相机到底支持什么格式。
查看:
bash
v4l2-ctl -d /dev/video5 --list-formats-ext
得到:
MJPG
960x720
30fps
1280x720
30fps
YUYV
960x720
10fps
这里得到两个重要结论:
- MJPEG 可以 30FPS
- YUYV 最大只有 10FPS
因此后续所有录制方案都必须基于 MJPEG。
四、第一步排查------到底是不是采集问题?
很多人第一反应就是:
GStreamer 怎么只有 28FPS?
实际上第一步应该先验证 V4L2 驱动采集能力。
测试:
bash
v4l2-ctl \
-d /dev/video5 \
--stream-mmap \
--stream-count=600 \
--stream-to=/dev/null
结果:
30.07 fps
30.08 fps
30.06 fps
说明:
- 相机本身没问题。
- USB 没问题。
- V4L2 驱动也没问题。
真正的问题发生在后面的处理链路。
五、第二步排查------GStreamer 是否已经开始掉帧?
继续验证:
bash
gst-launch-1.0 -v \
v4l2src device=/dev/video5 io-mode=2 \
! image/jpeg,width=960,height=720,framerate=30/1 \
! queue \
! fpsdisplaysink \
video-sink=fakesink \
sync=false \
text-overlay=false
观察:
current=30.08
↓
28.20
↓
26.30
↓
30.07
平均:
28.xx FPS
这里说明:
采集虽然平均还能接近30FPS,但是整个 GStreamer Pipeline 已经开始发生抖动。
虽然没有真正丢帧,但 Pipeline 已经出现 Scheduling 抖动。
六、第三步------验证最简单的录制
先不要编码。
直接保存 MJPEG。
Pipeline:
text
v4l2src
↓
jpegparse
↓
avimux
↓
AVI
命令:
bash
gst-launch-1.0 -e \
v4l2src device=/dev/video5 \
! image/jpeg,width=960,height=720,framerate=30/1 \
! jpegparse \
! avimux \
! filesink location=test.avi
优点:
- 不解码
- 不编码
- CPU 非常低
- 基本可以保持 30FPS
缺点:
- 文件巨大
三分钟接近 1GB+
显然不能用于实际项目。
七、第四步------尝试硬件 H264/H265 编码
开始尝试:
MJPEG
↓
mppjpegdec
↓
videoconvert
↓
NV12
↓
mpph264enc
测试命令:
bash
gst-launch-1.0 -e \
v4l2src device=/dev/video5 \
! image/jpeg,width=960,height=720,framerate=30/1 \
! jpegparse \
! mppjpegdec \
! videoconvert \
! video/x-raw,format=NV12 \
! mpph264enc \
! h264parse \
! mp4mux \
! filesink location=test.mp4
最终得到:
- 画面正常
- MP4正常
- 但是三路录制开始掉帧
CPU 使用率明显升高。
八、定位真正瓶颈------videoconvert
为了确认到底是谁慢,分别测试:
① 只有:
mppjpegdec
和
②
mppjpegdec
↓
videoconvert
统计结果:
不使用 videoconvert
- real ≈64s
- user ≈8s
使用 videoconvert
- real ≈70s
- user ≈207s
CPU 时间暴涨。
说明:
真正瓶颈不是 MPP Encoder ,而是 videoconvert(软件颜色转换)。
九、为什么会出现 NV16?
很多人会疑惑:
为什么 mppjpegdec 出来的不是 NV12?
查看 caps:
video/x-raw
format=NV16
原因:
MJPEG 本质来自 YUV422 ,Rockchip 的 mppjpegdec 会直接输出 NV16 。
而编码器需要 NV12。
因此以前其实一直都是:
NV16 → videoconvert → NV12
CPU 就耗在这里。
十、尝试直接送编码器
于是尝试:
NV16 → mpph265enc
结果:
FPS 非常漂亮。
但是:
- 颜色错误
- 尺寸错误
例如:
输入 960×720,输出 1280×720,颜色也偏绿。
说明:不能直接使用。
十一、最终解决方案------RGA 硬件转换
于是改成:
MJPEG
↓
mppjpegdec
↓
NV16
↓
RGA
↓
NV12
↓
mpph265enc
转换代码:
cpp
imcvtcolor(
src,
dst,
RK_FORMAT_YCbCr_422_SP,
RK_FORMAT_YCbCr_420_SP
);
整个转换完全走 RGA Hardware,CPU 基本不参与。
十二、60秒严格30FPS测试
测试程序:
bash
./rga_appsrc_h265_test \
60 \
960 \
720 \
50 \
2 \
videos \
1200000 \
/dev/video1 \
/dev/video3 \
/dev/video5
统计结果:
- video3 :1800 Frames,30.08FPS → PASS
- video5 :1800 Frames,30.08FPS → PASS
- video1:1705 Frames,28.6FPS
项目要求只有 video3 和 video5 这两路核心相机必须严格 30FPS,第三路辅助相机允许略低帧率,因此最终方案满足需求。


十三、常用测试命令汇总
1)查看相机支持格式
bash
v4l2-ctl -d /dev/video5 --list-formats-ext
2)查看当前参数
bash
v4l2-ctl -d /dev/video5 --all
3)测试底层采集 FPS
bash
v4l2-ctl \
-d /dev/video5 \
--set-parm=30 \
--stream-mmap \
--stream-count=600 \
--stream-to=/dev/null
4)测试 GStreamer Pipeline FPS
bash
gst-launch-1.0 -v \
v4l2src device=/dev/video5 io-mode=2 \
! image/jpeg,width=960,height=720,framerate=30/1 \
! queue max-size-buffers=60 \
! fpsdisplaysink \
video-sink=fakesink \
sync=false \
text-overlay=false
5)MJPEG 原始 AVI 录制
bash
gst-launch-1.0 -e \
v4l2src device=/dev/video5 \
! image/jpeg,width=960,height=720,framerate=30/1 \
! jpegparse \
! avimux \
! filesink location=test.avi
6)MPP H264 编码录制
bash
gst-launch-1.0 -e \
v4l2src device=/dev/video5 \
! image/jpeg,width=960,height=720,framerate=30/1 \
! jpegparse \
! mppjpegdec \
! videoconvert \
! video/x-raw,format=NV12 \
! mpph264enc \
! h264parse \
! mp4mux \
! filesink location=test.mp4
7)RGA + MPP H265 测试程序
bash
./rga_appsrc_h265_test \
60 \
960 \
720 \
50 \
2 \
videos \
1200000 \
/dev/video1 \
/dev/video3 \
/dev/video5
8)检查录制结果
bash
ffprobe \
-hide_banner \
-select_streams v:0 \
-show_entries \
stream=codec_name,width,height,avg_frame_rate,r_frame_rate,nb_frames,duration,bit_rate \
video3_rga_nv12_h265_strict.mp4
期望输出:
width=960
height=720
avg_frame_rate=30/1
nb_frames=1800
十四、最终架构
最终采用如下录制架构:
text
USB Camera
│
▼
MJPEG (30FPS)
│
▼
mppjpegdec
│
▼
NV16
│
▼
RGA(NV16 → NV12)
│
▼
mpph264enc / mpph265enc
│
▼
MP4 Recorder
│
▼
Frame Monitor(统计帧率、掉帧、同步状态)
其中,Frame Monitor 持续统计以下指标:
- 实际采集帧数(Capture Frames)
- 编码输出帧数(Encoded Frames)
- 平均 FPS(Average FPS)
- 最大帧间隔(Max Frame Interval)
- 超过 40 ms / 50 ms 的帧数
- 核心两路相机是否达到
expected_frames = seconds × 30
这些统计信息能够快速判断问题究竟发生在采集、颜色转换还是编码阶段,对于后续定位性能瓶颈非常有帮助。
十五、总结
经过多轮测试与逐步排查,最终得到以下结论:
- USB UVC 相机本身可以稳定输出 30FPS,V4L2 驱动不是瓶颈。
- MPP(Rockchip 硬件编解码)性能充足,三路实时编码并非主要限制因素。
- 软件
videoconvert是三路实时录制最大的性能瓶颈,CPU 开销明显。 mppjpegdec原生输出为 NV16,直接送入编码器虽然帧率高,但会导致颜色和分辨率异常。- 使用 RGA 完成 NV16→NV12 的硬件色彩转换后,可以兼顾正确画面和低 CPU 占用。
- 在当前方案下,两路核心相机(video3、video5)已经连续 60 秒实现严格 1800 帧(30FPS),满足项目需求。
下一步计划:
- 验证 RGA 输出的视频颜色和分辨率在长时间运行下保持正确。
- 完成 5 分钟、10 分钟压力测试,观察是否存在累计掉帧。
- 在实际业务曝光、补光条件下再次验证帧率稳定性。
- 将这套 MPP + RGA 的录制链路完整迁移到正式项目,实现三路采集、实时录制、推流与 AI 推理协同运行。
希望这篇实践记录能为使用 RK3588、OrangePi 5 Ultra、GStreamer、MPP、RGA 实现多路 USB UVC 相机稳定录制的开发者提供一些参考。欢迎交流更多关于多路采集、硬件编解码和嵌入式视觉系统优化的经验。