RK3588 摄像头取流避坑
RK3588 摄像头取流避坑:GStreamer 死活 40fps、ffmpeg 录出来播不了
这篇是前面几篇的"下游"——摄像头驱动和时序都搞定了,结果在取流和录制这层又踩了一堆坑。
一、GStreamer 无论如何只有 40fps
摄像头本身跑 60fps,用 v4l2-ctl 测也是 60fps。但换成 GStreamer:
gst-launch-1.0 v4l2src device=/dev/video11 num-buffers=600 ! \
"video/x-raw,format=NV12,width=1280,height=720" ! fakesink sync=false
稳定 39.4fps。
排查
先做了个对照,用 v4l2-ctl 手动改缓冲数:
| 缓冲数 | 帧率 |
|---|---|
| 2 | 39.5 fps |
| 4 | 59.4 fps |
| 8 / 16 | 59.4 fps |
2 个缓冲 = 39.5fps,正好等于 GStreamer 的数字。
然后开 GStreamer 的调试日志:
GST_DEBUG=v4l2bufferpool:4,v4l2src:3 gst-launch-1.0 -q v4l2src device=/dev/video11 \
num-buffers=60 ! "video/x-raw,format=NV12,width=1280,height=720" ! fakesink sync=false 2>&1 \
| grep -E "minimum buffers|lost frames"
出来:
v4l2bufferpool: increasing minimum buffers to 2
v4l2src: lost frames detected: count = 1 ← 60 帧里报了 29 次
实收 ~30 帧 + 丢 29 帧 ≈ 59 帧,跟摄像头给的对得上。丢的那 20fps 全是因为没缓冲可写。
为什么改不了
这个版本的 v4l2src(GStreamer 1.24.2)属性表里只有这些:
blocksize / do-timestamp / io-mode / num-buffers
没有 extra-buffers(有些版本有)。试过的招都没用:
- 下游加
queue(max-size-buffers=8 / 无限)→ 没用 tee分两路 → 没用io-mode=dmabuf/mmap→ 没用io-mode=1(read 模式)→ 驱动不支持
缓冲池的 minimum 是 GStreamer 内部硬编码的 2,下游元素也没人请求更多。
我的处理
三条路,我选了第二条:
- 给 gst-plugins-good 打补丁放开缓冲池(要重编,麻烦)
- 换采集端:ffmpeg / OpenCV 默认就是 4+ 缓冲,实测 59~60fps
- 桌面上试
rkximagesink(Rockchip 自己的 sink,可能请求更多缓冲)
如果一定要用 GStreamer 显示,可以在桌面环境试 waylandsink / rkximagesink,它们的缓冲需求跟 fakesink 不一样。
二、ffmpeg 录出来的视频不能播
按直觉写的命令:
ffmpeg -f v4l2 -video_size 1280x720 -input_format nv12 -framerate 60 -i /dev/video11 out.mp4
录完 ffprobe 一看:
codec_name=mpeg4 ← 编码不对
r_frame_rate=1/60 ← 帧率元数据是 1/60
duration=240.000000 ← 5 秒的录像变成 240 秒
两个问题,而且是独立的。
问题 1:默认编码器是 mpeg4
这个 ffmpeg 编译时没带 libx264,所以默认选了 MPEG-4 Part 2。这个格式 Windows 播放器、手机、浏览器很多都不认。
看一下有哪些编码器:
ffmpeg -hide_banner -encoders | grep -iE 'h264|hevc'
h264_v4l2m2m V4L2 mem2mem H.264 encoder wrapper
h264_rkmpp Rockchip MPP H264 encoder ← 硬件编码
hevc_v4l2m2m
hevc_rkmpp
有 Rockchip 的硬件编码器,直接用它:
-c:v h264_rkmpp
问题 2:-framerate 60 把时间戳搞坏了
这个坑比较隐蔽。加详细日志看输入:
ffmpeg -f v4l2 -video_size 1280x720 -input_format nv12 -framerate 60 -i /dev/video11 -t 3 -f null -
Stream #0:0: Video: rawvideo (NV12), 1280x720, 184 kb/s, 0.02 fps, 0.02 tbr
0.02 fps = 1/60。ffmpeg 以为一帧要 60 秒,152 帧被标成 60 秒长。
去掉 -framerate 参数:
ffmpeg -f v4l2 -video_size 1280x720 -input_format nv12 -i /dev/video11 -t 3 -f null -
Stream #0:0: Video: rawvideo (NV12), 1280x720, 51 tbr
frame= 152 fps=52 ... time=00:00:02.98
这次正常了,ffmpeg 自己从驱动读到 51fps,时间戳也对。
所以正确的命令是去掉 -framerate:
ffmpeg -f v4l2 -video_size 1280x720 -input_format nv12 -i /dev/video11 \
-c:v h264_rkmpp -t 10 out.mp4
录完验证:
codec_name=h264
r_frame_rate=51/1
nb_frames=255
duration=5.000000
可以正常播放了。
三、GStreamer 录制也别用软件编码
网上常见的写法是 x264enc,那是软件编码,RK3588 上 CPU 吃得比较狠。板子上有硬件编码器,换成 mpph264enc:
gst-launch-1.0 v4l2src device=/dev/video11 num-buffers=600 ! \
"video/x-raw,format=NV12,width=1280,height=720,framerate=60/1" ! \
videoconvert ! mpph264enc ! h264parse ! mp4mux ! \
filesink location=video.mp4
(注意 GStreamer 这条要显式写 framerate=60/1 的 caps,否则元数据可能不对。)
四、还有一个小坑:颜色不对
自己解 RAW 渲染的话,别忘了 Bayer 相位。BG10 在这块板子上要用 cv2.COLOR_BayerRG2BGR,不是按名字对号入座的 BayerBG2BGR。搞错了就是红蓝互换,橙色物体渲染成蓝色。
这个我在另一篇里写过了,不重复。
五、最后列个表
| 需求 | 推荐做法 |
|---|---|
| 只要帧率、不怕软件处理 | ffmpeg(不加 -framerate)+ -c:v h264_rkmpp |
| 要低 CPU / 要显示 | GStreamer,但注意它只有 2 个缓冲 → 会丢到 40fps;试试 waylandsink/rkximagesink |
| 要录制 H.264 | ffmpeg 的 h264_rkmpp 或 gst 的 mpph264enc(都是硬件) |
| 自己解 RAW | 连续位流解包 + COLOR_BayerRG2BGR |
排查这类问题有个笨办法但很好用:拿 v4l2-ctl 当基准。它只做最原始的取流,如果它 60fps 而你的管线 40fps,那问题一定在你的管线里,跟摄像头无关。
更多推荐




所有评论(0)