個(gè)人主頁(yè):https://chengang.plus/
UVC協(xié)議文檔網(wǎng)址:https://www.usb.org/documents?search=&type%5B0%5D=55&items_per_page=50
主要下載USB Video Class 1_5歧匈,關(guān)注下載zip包中的UVC 1.5 Class specification.pdf文件洒琢,里面有接口相關(guān)的解釋匈庭。
Android盒子控制攝像頭項(xiàng)目已經(jīng)差不多4年了,很多知識(shí)點(diǎn)已經(jīng)忘記照雁,現(xiàn)在重新溫固一遍,下面兩篇文章可以幫助回顧答恶。
https://my.oschina.net/u/2007478/blog/968470
https://blog.csdn.net/go_str/article/details/80844175
下邊兩個(gè)網(wǎng)址中可以找到代碼中各種結(jié)構(gòu)體的解釋:
https://www.kernel.org/doc/html/v4.13/media/v4l-drivers/uvcvideo.html
https://www.linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html
1饺蚊、Linux kernel下的UVC
我們先從Android官網(wǎng)git clone一下kernel的源碼:
clone到本地之后就可以通過Source Insight查看源碼了。查看源碼之前先project -> rebuild project悬嗓,這樣代碼中各對(duì)象之間可以點(diǎn)擊跳轉(zhuǎn)污呼。
1.1 初始化
goldfish\drivers\media\usb\uvc\uvc_driver.c
static int __init uvc_init(void)
{
int ret;
ret = usb_register(&uvc_driver.driver);
return 0;
}
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
在入口函數(shù)uvc_init
中,核心的一行是usb_register
包竹,也就是注冊(cè)USB設(shè)備燕酷,在注冊(cè)完成之后會(huì)調(diào)用uvc_probe
函數(shù)籍凝。
goldfish\include\linux\usb.h
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
看看這個(gè)uvc_probe
函數(shù):
goldfish\drivers\media\usb\uvc\uvc_driver.c
static int uvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct uvc_device *dev;
int ret;
if (id->idVendor && id->idProduct)
uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
"(%04x:%04x)\n", udev->devpath, id->idVendor,
id->idProduct);
else
uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
udev->devpath);
uvc_parse_control(dev);//1
v4l2_device_register(&intf->dev, &dev->vdev);//2
uvc_ctrl_init_device(dev);//3
uvc_scan_device(dev);
uvc_register_chains(dev);
usb_set_intfdata(intf, dev);
ret = uvc_status_init(dev);
usb_enable_autosuspend(udev);
}
- 每一個(gè)攝像頭設(shè)備在底層初始化完成之后,都會(huì)有一個(gè)vendorId和productId苗缩。
-
uvc_parse_control
會(huì)根據(jù)設(shè)備的vendorId和productId去對(duì)特定廠商的攝像頭做一些適配饵蒂。 -
v4l2_device_register
,該方法將設(shè)備注冊(cè)到v4l2挤渐,v4l2是Video for linux2的簡(jiǎn)稱苹享,為linux中關(guān)于視頻設(shè)備的內(nèi)核驅(qū)動(dòng)。該方法在goldfish\drivers\media\v4l2-core\v4l2-device.c
中浴麻。 -
uvc_ctrl_init_device
得问,初始化設(shè)備控制。
1.2 初始化設(shè)備控制
這里是我們需要重點(diǎn)關(guān)注的软免」常可以先跟蹤一下這個(gè)調(diào)用棧。
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
int uvc_ctrl_init_device(struct uvc_device *dev)
{
list_for_each_entry(entity, &dev->entities, list) {
struct uvc_control *ctrl;
unsigned int bControlSize = 0, ncontrols;
__u8 *bmControls = NULL;
//第一部分
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
bmControls = entity->extension.bmControls;
bControlSize = entity->extension.bControlSize;
} else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
bmControls = entity->processing.bmControls;
bControlSize = entity->processing.bControlSize;
} else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
bmControls = entity->camera.bmControls;
bControlSize = entity->camera.bControlSize;
}
//第二部分
/* Initialize all supported controls */
ctrl = entity->controls;
for (i = 0; i < bControlSize * 8; ++i) {
if (uvc_test_bit(bmControls, i) == 0)
continue;
ctrl->entity = entity;
ctrl->index = i;
uvc_ctrl_init_ctrl(dev, ctrl);
ctrl++;
}
}
}
1.2.1 ENTITY類型過濾
用于區(qū)分終端類型膏萧,重點(diǎn)關(guān)注UVC_ITT_CAMERA
類型漓骚,看看這個(gè)類型在UVC協(xié)議文檔里面的定義:
攝像頭傳感器,僅用于描述攝像頭終端榛泛。那么代碼里面的描述是:
goldfish\include\uapi\linux\usb\video.h
/* B.2. Input Terminal Types */
#define UVC_ITT_VENDOR_SPECIFIC 0x0200
#define UVC_ITT_CAMERA 0x0201
#define UVC_ITT_MEDIA_TRANSPORT_INPUT 0x0202
1.2.2 初始化uvc_control
從第一部分中取出camera.bmControls
和camera.bControlSize
蝌蹂,這兩個(gè)變量是干嘛的呢,還是看協(xié)議文檔:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-fiCTUia0-1593603543308)(https://ftp.bmp.ovh/imgs/2020/06/8968c359b0b5fc9b.png)]
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-zcNwSHhb-1593603543310)(https://ftp.bmp.ovh/imgs/2020/06/fdf441b15a72d2f9.png)]
bControlSize
是對(duì)應(yīng)著bmControls
的位大胁芟恰孤个;
bmControls
對(duì)應(yīng)著攝像頭支持的控制參數(shù),如果控制參數(shù)位置為1沛简,表示支持該控制齐鲤。
1.2.2.1 方法uvc_ctrl_init_ctrl
接下來調(diào)用uvc_ctrl_init_ctrl
方法:
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
{
const struct uvc_control_info *info = uvc_ctrls;
const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
const struct uvc_control_mapping *mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings);
for (; info < iend; ++info) {
if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
ctrl->index == info->index) {
uvc_ctrl_add_info(dev, ctrl, info);
break;
}
}
if (!ctrl->initialized)
return;
for (; mapping < mend; ++mapping) {
if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
ctrl->info.selector == mapping->selector)
__uvc_ctrl_add_mapping(dev, ctrl, mapping);
}
}
1.2.2.2 結(jié)構(gòu)體uvc_control_info
-
uvc_ctrls,這個(gè)結(jié)構(gòu)體的類型是
uvc_control_info
椒楣,是一個(gè)靜態(tài)數(shù)組给郊。可以理解為一個(gè)實(shí)體類別下對(duì)應(yīng)著多個(gè)控制功能捧灰,每個(gè)功能有對(duì)應(yīng)著不同的操作方式淆九,以當(dāng)前項(xiàng)目需要用到的功能舉例:
static struct uvc_control_info uvc_ctrls[] = {
{
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.index = 11,
.size = 8,
.flags = UVC_CTRL_FLAG_SET_CUR
| UVC_CTRL_FLAG_GET_RANGE
| UVC_CTRL_FLAG_RESTORE
| UVC_CTRL_FLAG_AUTO_UPDATE,
},
{
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
.index = 12,
.size = 4,
.flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
| UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
| UVC_CTRL_FLAG_GET_DEF
| UVC_CTRL_FLAG_AUTO_UPDATE,
},
}
entity 實(shí)體是一種類型,
UVC_GUID_UVC_CAMERA
只是其中一種凤壁,還有UVC_GUID_UVC_PROCESSING
等吩屹。selector 對(duì)應(yīng)的是實(shí)體下的一種功能,比如相對(duì)絕對(duì)轉(zhuǎn)動(dòng)拧抖。
index 對(duì)應(yīng)著在
uvc_ctrls
中的序號(hào)煤搜。
/* Bit index in bmControls */
-
size 對(duì)應(yīng)著具體操作位的長(zhǎng)度,比如上邊列舉出來的
UVC_CT_PANTILT_ABSOLUTE_CONTROL
和UVC_CT_PANTILT_RELATIVE_CONTROL
控制唧席,看看在協(xié)議文檔中的定義:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-q0hHvuSC-1593603543313)(https://ftp.bmp.ovh/imgs/2020/06/7b49813393d6df2a.png)]
對(duì)于PanTilt Absolute來說擦盾,高四位代表著左右的角度嘲驾,低四位代表著上下的角度,都是有符號(hào)整數(shù)迹卢,總共八位辽故,所以size為8。
對(duì)于PanTilt Relative來說腐碱,總共四位誊垢,每一位代表不同的控制屬性,第一位表示左右相對(duì)症见;第二位表示左右控制的速度喂走;第三位表示上下相對(duì);第四位表示上下的速度谋作。所以size為4芋肠。
-
flags 表示對(duì)于這些
selector
支持的功能操作:
詳細(xì)的解釋如下:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-avwX0hkZ-1593603543317)(https://ftp.bmp.ovh/imgs/2020/06/bf6ed7fc695057a8.png)]
1.2.2.3 結(jié)構(gòu)體uvc_control_mapping
uvc_ctrl_mappings是uvc_control_mapping
類型的結(jié)構(gòu)體變量,也是一個(gè)靜態(tài)的結(jié)構(gòu)體遵蚜,簡(jiǎn)略看下里面定義了啥:
static struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_PAN_ABSOLUTE,
.name = "Pan (Absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.size = 32,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
},
{
.id = V4L2_CID_TILT_ABSOLUTE,
.name = "Tilt (Absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.size = 32,
.offset = 32,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
},
}
可以看到這里的id
開頭都是V4L2
帖池,而entity
和selector
都對(duì)應(yīng)著uvc_control_info uvc_ctrls
中定義的entity
和selector
。
另外v4l2_type
對(duì)應(yīng)著設(shè)置的數(shù)據(jù)類型吭净,data_type
則定義了數(shù)據(jù)為有符號(hào)還是無符號(hào)睡汹。
這個(gè)結(jié)構(gòu)體從我的理解來看,就是將UVC定義的控制寂殉,映射到v4l2帮孔,并建立兩者之間的關(guān)系。
1.2.2.4 方法uvc_ctrl_add_info
這個(gè)方法核心就一行代碼:
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
const struct uvc_control_info *info)
{
ctrl->info = *info;
}
將上一步uvc_control_info
變量給到uvc_control
的info不撑。uvc_control
的定義如下:
goldfish\drivers\media\usb\uvc\uvcvideo.h
struct uvc_control {
struct uvc_entity *entity;
struct uvc_control_info info;
__u8 index; /* Used to match the uvc_control entry with a
uvc_control_info. */
__u8 dirty:1,
loaded:1,
modified:1,
cached:1,
initialized:1;
__u8 *uvc_data;
};
1.2.2.5 方法__uvc_ctrl_add_mapping
同樣的,將uvc_control_mapping
數(shù)據(jù)賦值到uvc_control
對(duì)象中:
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
{
struct uvc_control_mapping *map;
map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
list_add_tail(&map->list, &ctrl->info.mappings);
}
uvc_control_info
的mappings
作為鏈表頭晤斩,將map->list
添加到后面焕檬。
1.3 總結(jié)
在初始化的過程中可以將UVC協(xié)議的文檔跟代碼建立聯(lián)系,以幫助理解代碼的邏輯澳泵。待理解了各種數(shù)據(jù)類型定義的原理及流程之后实愚,發(fā)現(xiàn)其實(shí)現(xiàn)了UVC與V4L2的連接,這樣下一步的工作就比較好開展了兔辅。
UVC初始化的部分到這里告一段落腊敲,接下來要根據(jù)具體需求做一些定制的工作。