模拟 GStreamer 获取帧率能力:CIF 与 ISP 的差异
在使用 GStreamer (gst-launch-1.0) 搭建视频采集管道时,我们通常会在 caps 中指定分辨率、像素格式和帧率,例如:
bash
gst-launch-1.0 v4l2src device=/dev/video0 ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=60/1 ! \
mpph264enc ! h264parse ! matroskamux ! \
filesink location=/home/firefly/capture.mkv
这里的 framerate=60/1 并不是随意写的,而是来自 V4L2 驱动的能力枚举。GStreamer 在启动时会调用 V4L2 的 ioctl 来查询设备支持的格式、分辨率和帧率。
V4L2 查询帧率的命令
我们可以用 v4l2-ctl 来模拟 GStreamer 的查询过程:
bash
v4l2-ctl -d /dev/video0 --list-frameintervals width=3864,height=2192,pixelformat=NV12
这条命令会触发驱动的 VIDIOC_ENUM_FRAMEINTERVALS 回调,返回该分辨率和像素格式下支持的帧率。
ISP 与 CIF 的差异
在 Rockchip 平台上,通常:
/dev/video11→ ISP 路径/dev/video0→ CIF 路径
两者在 enum_frameintervals 的实现上有明显不同。
1. ISP 路径 (rkisp_enum_frameintervals)
源码片段:
c
fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
fival->stepwise.min = fi.interval; // 来自 sensor g_frame_interval
- ISP 返回 连续型 (CONTINUOUS) 帧率能力。
- 如果 sensor 没实现
g_frame_interval,就 fallback 到默认 30fps。 - 上层(如 GStreamer)认为"从这个最小值开始都合法",所以 caps negotiation 很宽松,看起来你随便写
framerate=25/1或framerate=60/1都能通过。
2. CIF 路径 (rkcif_enum_frameintervals)
源码片段:
c
if (dev->hw_dev->adapt_to_usbcamerahal) {
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
fival->discrete = fi.interval;
} else {
fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
fival->stepwise.min = fi.interval;
}
-
CIF 返回 离散型 (DISCRETE) 帧率能力。
-
如果 sensor 没实现
g_frame_interval,理论上应该 fallback 到 30fps。 -
但如果返回值错误(例如分母=0),上层 GStreamer 就会报错:
gst_value_set_fraction: assertion 'denominator != 0' failed导致 pipeline negotiation 失败。
实际现象
-
ISP 节点 (video11)
→ 返回 continuous,caps negotiation 宽松,随便写 fps 不报错。
→ 实际运行还是按照 sensor 的真实 fps。
-
CIF 节点 (video0)
→ 返回 discrete,caps negotiation 严格,必须匹配正确的 fps。
→ 如果驱动返回非法值(如分母=0),GStreamer 直接报错,pipeline 无法启动。
总结
- GStreamer 获取帧率能力时,底层调用的是
VIDIOC_ENUM_FRAMEINTERVALS。 - ISP 路径返回 连续型能力 → 宽松,不报错。
- CIF 路径返回 离散型能力 → 严格,必须正确,否则 negotiation 失败。
- 如果 sensor 没实现
g_frame_interval,ISP 会 fallback 到 30fps,而 CIF 如果处理不当,就可能返回非法值,导致上层报错。