v4l2架構專題模組crop及selection分析 --- 從一個camera測試說起
Linux v4l2架構學習總連結
前天機芯部門給了我一個mipi的攝像頭,讓我接到rv1126上看看能不能抓到圖。這個感覺簡單啊,而且我們的攝像頭都不需要配置暫存器。
這個攝像頭引數:raw8,2lanes,512*192,25fps
複製一份imx291的程式碼直接修改,改完之後測試
測試方法如下:
v4l2-ctl -d /dev/video0 --set-fmt-video=width=512,height=192,pixelformat=BG12 --stream-mmap=3 --stream-to=/tmp/bg12.bin --stream-count=1 --stream-poll
執行發現,沒有抓到資料也沒有timeout,直接退出了。
dmesg發現出現瞭如下一條資訊
rkcif_mipi_lvds: crop size is bigger than input
這部分程式碼如下:
rkcif_start_streaming() -> rkcif_sanity_check_fmt(stream, NULL)
static int rkcif_sanity_check_fmt(struct rkcif_stream *stream, const struct v4l2_rect *s_crop) { struct rkcif_device *dev = stream->cifdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct v4l2_rect input, *crop; stream->cif_fmt_in = get_input_fmt(dev->active_sensor->sd, &input, stream->id + 1); if (!stream->cif_fmt_in) { v4l2_err(v4l2_dev, "Input fmt is invalid\n"); return -EINVAL; } if (s_crop) crop = (struct v4l2_rect *)s_crop; else crop = &stream->crop[CROP_SRC_ACT]; if (crop->width + crop->left > input.width || crop->height + crop->top > input.height) { v4l2_err(v4l2_dev, "crop size is bigger than input\n"); return -EINVAL; } ... }
看程式碼可以知道input.width及heigth小於crop的width或者heigth
於是去找crop的賦值程式碼
rkcif_stream_init()
#define RKCIF_DEFAULT_WIDTH 640 #define RKCIF_DEFAULT_HEIGHT 480 void rkcif_stream_init(struct rkcif_device *dev, u32 id) { ... for (i = 0; i < CROP_SRC_MAX; i++) { stream->crop[i].left = 0; stream->crop[i].top = 0; stream->crop[i].width = RKCIF_DEFAULT_WIDTH; stream->crop[i].height = RKCIF_DEFAULT_HEIGHT; } ... }
於是一頓修改
#define RKCIF_DEFAULT_WIDTH 480
#define RKCIF_DEFAULT_HEIGHT 120
v4l2-ctl可以正常跑了,於是這件事也就扔到腦後了....
時間來到今天,最近一直在學習v4l2,於是建了一個群討論v4l2相關問題。
問題的起源是:
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080 ...
v4l2-ctl -d /dev/video0 --set-fmt-video=width=720,height=480 ...
這2條命令設定的解析度不同,但是抓到的圖的大小相同,都是1080p的大小,於是開始分析
首先開啟log,抓取v4l2-ctl過程中的ioctl的呼叫
echo 3 > /sys/class/video4linux/video0/dev_debug
可以看到如下log
720*480
1920*1080
可以看到sizeimage都是4147200,這就導致了抓到的資料大小都一樣。
看到這裡就知道下一步去看對應程式碼
rkcif_set_fmt() -> rkcif_sync_crop_info()
static void rkcif_sync_crop_info(struct rkcif_stream *stream)
{
struct rkcif_device *dev = stream->cifdev;
struct v4l2_subdev_selection input_sel;
int ret;
if (dev->terminal_sensor.sd) {
input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
ret = v4l2_subdev_call(dev->terminal_sensor.sd,
pad, get_selection, NULL,
&input_sel);
if (!ret) {
stream->crop[CROP_SRC_SENSOR] = input_sel.r;
stream->crop_enable = true;
stream->crop_mask |= CROP_SRC_SENSOR_MASK;
dev->terminal_sensor.selection = input_sel;
} else {
dev->terminal_sensor.selection.r = dev->terminal_sensor.raw_rect;
}
}
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_SENSOR];
...
}
討論的問題驅動程式碼有這個get_selection,裡面將input_sel.r.width = 1920,input_sel.r.height = 1080
這裡導致了v4l2-ctl修改解析度,但是抓圖資料大小不變。
其實到這裡就可以停下了,但是由於之前閱讀程式碼的時候跳過了crop這些程式碼,所以這裡打算再追一下,
於是發現
.vidioc_s_crop = rkcif_s_crop,
.vidioc_g_crop = rkcif_g_crop,
.vidioc_s_selection = rkcif_s_selection,
.vidioc_g_selection = rkcif_g_selection,
這幾個ioctl中都有對crop的操作,於是就想到v4l2-ctl是不是支援直接設定?
v4l2-ctl -h
可以看到一條這樣的資訊
--help-selection crop/selection options
於是
4l2-ctl --help-selection
列印如下
Selection/Cropping options:
--get-cropcap query the crop capabilities [VIDIOC_CROPCAP]
--get-crop query the video capture crop window [VIDIOC_G_CROP]
--set-crop top=<x>,left=<y>,width=<w>,height=<h>
set the video capture crop window [VIDIOC_S_CROP]
--get-cropcap-output
query crop capabilities for video output [VIDIOC_CROPCAP]
--get-crop-output query the video output crop window [VIDIOC_G_CROP]
--set-crop-output top=<x>,left=<y>,width=<w>,height=<h>
set the video output crop window [VIDIOC_S_CROP]
--get-cropcap-overlay
query crop capabilities for video overlay [VIDIOC_CROPCAP]
--get-crop-overlay query the video overlay crop window [VIDIOC_G_CROP]
--set-crop-overlay top=<x>,left=<y>,width=<w>,height=<h>
set the video overlay crop window [VIDIOC_S_CROP]
--get-cropcap-output-overlay
query the crop capabilities for video output overlays
[VIDIOC_CROPCAP]
--get-crop-output-overlay
query the video output overlay crop window [VIDIOC_G_CROP]
--set-crop-output-overlay top=<x>,left=<y>,width=<w>,height=<h>
set the video output overlay crop window [VIDIOC_S_CROP]
--get-selection target=<target>
query the video capture selection rectangle [VIDIOC_G_SELECTION]
See --set-selection command for the valid <target> values.
--set-selection target=<target>,flags=<flags>,top=<x>,left=<y>,width=<w>,height=<h>
set the video capture selection rectangle [VIDIOC_S_SELECTION]
target=crop|crop_bounds|crop_default|compose|compose_bounds|
compose_default|compose_padded|native_size
flags=le|ge|keep-config
--get-selection-output target=<target>
query the video output selection rectangle [VIDIOC_G_SELECTION]
See --set-selection command for the valid <target> values.
--set-selection-output target=<target>,flags=<flags>,top=<x>,left=<y>,width=<w>,height=<h>
set the video output selection rectangle [VIDIOC_S_SELECTION]
See --set-selection command for the arguments.
ok,現在可以這樣操作
[[email protected]_RV1109:/]#v4l2-ctl -d /dev/video0 --get-crop
Crop: Left 0, Top 0, Width 1920, Height 1080
[[email protected]_RV1109:/]#v4l2-ctl -d /dev/video0 --set-crop top=0,left=0,width=720,heigth=480
[[email protected]_RV1109:/]#v4l2-ctl -d /dev/video0 --get-crop
Crop: Left 0, Top 0, Width 720, Height 480
用v4l2-ctl測試
v4l2-ctl -d /dev/video0 --set-fmt-video=width=720,height=480 ...
發現抓到的資料大小沒有問題了。
故事結束了,後面就開始分析具體的程式碼,看看是如何實現的