標籤:des style blog http color io os 使用 ar
Camera的可設定項極多,V4L2 支援了不少。但Sam之前對這些設定的用法和涵義都是在看videodev2.h中邊看邊理解,感覺非常生澀。直到寫這篇blog時,才發現v4l2有專門的SPEC來說明:
http://www.linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html
但也基本沒有時間仔細看了。先把自己看標頭檔看出的一些東西記錄在這裡吧。以實際設定過程為順序談談V4L2 設定。
1. 查詢V4L2 功能集:VIDIOC_QUERYCAP
struct v4l2_capability cap;
int rel = 0;
ioctl(Handle, VIDIOC_QUERYCAP, &cap);
使用ioctl VIDIOC_QUERYCAP 來查詢當前driver是否合乎規範。因為V4L2要求所有driver 和Device都支援這個Ioctl。所以,可以通過這個ioctl是否成功來判斷當前裝置和dirver 是否支援V4L2規範。當然,這樣同時還能夠得到裝置足夠的能力資訊。
struct v4l2_capability
{
__u8 driver[16]; //驅動名。
__u8 card[32]; // Device名
__u8 bus_info[32]; //在Bus系統中存放位置
__u32 version; //driver 版本
__u32 capabilities; //能力集
__u32 reserved[4];
};
能力集中包含:
V4L2_CAP_VIDEO_CAPTURE 0x00000001 The device supports the Video Capture interface.
V4L2_CAP_VIDEO_OUTPUT 0x00000002 The device supports the Video Output interface.
V4L2_CAP_VIDEO_OVERLAY 0x00000004 The device supports the Video Overlay interface.
A video overlay device typically stores captured images directly in the video memory of a graphics card,with hardware clipping and scaling.
V4L2_CAP_VBI_CAPTURE 0x00000010 The device supports the Raw VBI Capture interface, providing Teletext and Closed Caption data.
V4L2_CAP_VBI_OUTPUT 0x00000020 The device supports the Raw VBI Output interface.
V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040 The device supports the Sliced VBI Capture interface.
V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 The device supports the Sliced VBI Output interface.
V4L2_CAP_RDS_CAPTURE 0x00000100 [to be defined]
#define V4L2_CAP_TUNER 0x00010000
#define V4L2_CAP_AUDIO 0x00020000
#define V4L2_CAP_RADIO 0x00040000
#define V4L2_CAP_READWRITE 0x01000000
#define V4L2_CAP_ASYNCIO 0x02000000
#define V4L2_CAP_STREAMING 0x04000000
看起來很熟悉吧,其實就是Driver裡面的Type。
__u8 driver[16]; driver名,通常為:uvcvideo
__u8 card[32]; 裝置名稱:廠商會填寫。
__u8 bus_info[32]; bus,通常為:usb-hiusb-ehci-2.4
__u32 version;
__u32 capabilities; 通常為:V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
__u32 reserved[4];
2. 枚舉裝置所支援的image format: VIDIOC_ENUM_FMT
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(Handle, VIDIOC_ENUM_FMT, &fmtdesc);
使用ioctl VIDIOC_ENUM_FMT 依次詢問,type為:V4L2_BUF_TYPE_VIDEO_CAPTURE。 index從0開始,依次增加,直到返回. Driver會填充結構體struct v4l2_fmtdesc的其它內容,如果index超出範圍,則返回-1。
struct v4l2_fmtdesc
{
__u32 index; // 需要填充,從0開始,依次上升。
enum v4l2_buf_type type; //Camera,則填寫V4L2_BUF_TYPE_VIDEO_CAPTURE
__u32 flags; // 如果壓縮的,則Driver 填寫:V4L2_FMT_FLAG_COMPRESSED,否則為0
__u8 description[32]; // image format的描述,如:YUV 4:2:2 (YUYV)
__u32 pixelformat; //所支援的格式。如:V4L2_PIX_FMT_UYVY
__u32 reserved[4];
};
這樣,則知道當前硬體支援什麼樣的image format. 下一步,則可以設定image 了。當然,設定之前,還可以讀取當前預設設定。
3. 得到和設定Image Format: VIDIOC_G_FMT, VIDIOC_S_FMT:
3.1: 得到當前Image Format:
struct v4l2_format Format;
memset(&Format, 0, sizeof(struct v4l2_format));
Format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(Handle, VIDIOC_G_FMT, &Format);
利用ioctl VIDIOC_G_FMT. 得到當前設定。
因為Camera為CAPTURE裝置,所以需要設定type為: V4L2_BUF_TYPE_VIDEO_CAPTURE
然後Driver會填充其它內容。
struct v4l2_format
{
enum v4l2_buf_type type; // Camera,則使用者必須填寫:V4L2_BUF_TYPE_VIDEO_CAPTURE
union
{
struct v4l2_pix_format pix; // used by video capture and output devices
struct v4l2_window win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
__u8 raw_data[200];
} fmt;
};
因為是Camera, 所以採用pix. 現在分析如下:
struct v4l2_pix_format
{
__u32 width; //Image width in pixels.
__u32 height; // Image Height in pixels.
__u32 pixelformat; // Image格式,最常見的有:V4L2_PIX_FMT_YYUV
enum v4l2_field field; //是否漸進式掃描,是否隔行掃描. Sam通常採用V4L2_FIELD_NONE,逐行放置資料 (注1)
__u32 bytesperline; //每行的byte數
__u32 sizeimage; //總共的byte數,bytesperline * height
enum v4l2_colorspace colorspace; //This information supplements the pixelformat and must be set by the driver
__u32 priv;
};
3.2:設定Image Format:VIDIOC_S_FMT
之前通過VIDIOC_ENUM_FMT已經知道Device支援什麼Format。所以就不用猜測了,直接設定吧。
設定Image Format ,利用 iocto VIDIOC_S_FMT.
需要APPLICATION填寫的Struct項目有:
struct v4l2_format Format;
Format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Format.fmt.pix.width = Width;
Format.fmt.pix.height = Height;
Format.fmt.pix.pixelformat= pixelformat;//V4L2_PIX_FMT_YUYV;
Format.fmt.pix.field = field;
io_rel = ioctl(Handle, VIDIOC_S_FMT, &Format);
SamInfo:之前設定了Image Format,是指每一幀的資料格式,但Stream的行為呢,也需要設定,這就是下面所說的Stream 設定了。它就包含幀數設定和修改。
4. 得到和設定Stream資訊:VIDIOC_G_PARM, VIDIOC_S_PARM
Stream資訊,主要是設定幀數。
4.1:得到Stream資訊:
struct v4l2_streamparm Stream_Parm;
memset(&Stream_Parm, 0, sizeof(struct v4l2_streamparm));
Stream_Parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
io_rel = ioctl(Handle, VIDIOC_G_PARM, &Stream_Parm);
使用者只需要填充type為V4L2_BUF_TYPE_VIDEO_CAPTURE。 Driver就會把結構體中其它部分填充好。
struct v4l2_streamparm
{
enum v4l2_buf_type type;
union
{
struct v4l2_captureparm capture;
struct v4l2_outputparm output;
__u8 raw_data[200];
} parm;
};
因為是Camera, 所以使用capture. 它是 struct v4l2_captureparm
struct v4l2_captureparm
{
__u32 capability; //是否可以被timeperframe控制幀數。可以則:V4L2_CAP_TIMEPERFRAME
__u32 capturemode; //是否為高清模式。如果是:
則設定為:V4L2_MODE_HIGHQUALITY。 高清模式會犧牲其它資訊。通常設定為0。
struct v4l2_fract timeperframe; //幀數。
__u32 extendedmode; //定製的。如果不支援,設定為0
__u32 readbuffers;
__u32 reserved[4];
};
struct v4l2_fract timeperframe; //幀數。
struct v4l2_fract {
__u32 numerator; // 分子。例:1
__u32 denominator; //分母。 例:30
};
4.2:設定幀數:
struct v4l2_streamparm Stream_Parm;
memset(&Stream_Parm, 0, sizeof(struct v4l2_streamparm));
Stream_Parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Stream_Parm.parm.capture.timeperframe.denominator =Denominator;;
Stream_Parm.parm.capture.timeperframe.numerator = Numerator;
io_rel = ioctl(Handle, VIDIOC_S_PARM, &Stream_Parm);
請注意,哪怕ioctl返回0。也有可能沒設定成功。所以需要再次Get。
當然,哪怕Get發現設定成功。真正抓幀也可能沒那麼高。
5. 利用VIDIOC_G_CTRL得到一些設定:
一些具體的設定,如曝光模式(Exposure Type),曝光值(Exposure),增益(Gain),白平衡(WHITE_BALANCE),亮度(BRIGHTNESS),飽和度(SATURATION),對比(CONTRAST)等資訊。可以通過VIDIOC_G_CTRL得到當前值。
用法:APP 填寫結構體中的id. 通過調用VIDIOC_G_CTRL,driver 會填寫結構體中value項。
struct v4l2_control ctrl;
struct v4l2_control
{
__u32 id;
__s32 value;
};
以曝光模式,曝光,和增益為例;
曝光模式:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_EXPOSURE_AUTO;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
ctrl.value 則由Driver填寫。告知當前曝光模式。
有以下幾個選擇:
enum v4l2_exposure_auto_type {
V4L2_EXPOSURE_AUTO = 0,
V4L2_EXPOSURE_MANUAL = 1,
V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
V4L2_EXPOSURE_APERTURE_PRIORITY = 3
};
曝光:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
同樣,driver填寫ctrl.value. 內容為曝光值。
增益:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_GAIN;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
同樣,driver填寫ctrl.value. 內容為增益。
6. 利用VIDIOC_QUERYCTRL 得到設定具體資訊:
在很多情況下,我們並不知道如何設定一些資訊,例如,曝光應該設定為多少?Driver能夠接受的範圍是多少?最大,最小值是多少?步長是多少?預設值為多少?
可以通過VIDIOC_QUERYCTRL得到。
咱們還是以增益為例:
struct v4l2_queryctrl Setting;
Setting.id = V4L2_CID_GAIN;
ret = ioctl(Handle, VIDIOC_QUERYCTRL, &Setting);
Driver就會填寫結構體中所有資訊。
struct v4l2_queryctrl
{
__u32 id; //使用者佈建。指定尋找的是哪個ID。
enum v4l2_ctrl_type type;
__u8 name[32]; //ID對應的名字。
__s32 minimum;
__s32 maximum;
__s32 step; //步長
__s32 default_value;
__u32 flags;
__u32 reserved[2];
};
這樣,就知道設定什麼值是合法的了。那麼,下一步就是設定了。
7. 利用VIDIOC_S_CTRL來設定:
很簡單,設定id和value.調用ioctl就好。
還是以增益為例:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_GAIN;
ctrl.value = Gain;
ret = ioctl(Handle, VIDIOC_S_CTRL, &ctrl);
有時候,硬體設定很奇怪,可以設定某個資訊,卻無法得到如何設定的資訊。例如:HD-500可以設定增益。卻無法得到該如何設定。
8. 利用擴充Ctrl設定:
焦距(FOUCE);
注1:enum v4l2_field field; 詳解:
9.設定解析度:
struct v4l2_control control;
memset(&control, 0, sizeof(control));
control.id = V4L2_CID_AUTO_WHITE_BALANCE;
control.value = 1;
if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0)
{
printf("Couldn‘t set auto white balance!\n");
//return -1;
}
memset(&control, 0, sizeof(control));
control.id = V4L2_CID_EXPOSURE_AUTO;
control.value = 1;
if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0)
{
printf("Couldn‘t set auto exposure!\n");
//return -1;
}
memset(&control, 0, sizeof(control));
control.id = V4L2_CID_HFLIP;
control.value = 1;
if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0)
{
printf("Couldn‘t set h flip!\n");
//return -1;
}
memset(&control, 0, sizeof(control));
control.id = V4L2_CID_VFLIP;
control.value = 1;
if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0)
{
printf("Couldn‘t set v flip!\n");
//return -1;
}
Linux下實現視頻讀取(二)---camera參數設定