UVC系列2-探索Android UVC协议
文章選取android下linux-3.10作為分析對象,具體的UVC初始化過程可以參考csdn大神寫的博客,地址是:http://blog.csdn.net/orz415678659。
uvc加載攝像頭的過程無非是初始化設(shè)備,加載設(shè)備,獲取設(shè)備相關(guān)參數(shù)并加載相關(guān)參數(shù)到buffer,此時就已經(jīng)將視頻和控制參數(shù)加載到buffer了,這篇文章主要關(guān)注的是控制相關(guān)的參數(shù)。
需要關(guān)注的兩個核心文件是:
- drivers\media\usb\uvc\uvc_ctrl.c
- drivers\media\usb\uvc\uvc_v4l2.c
首先看看uvc_ctrl.c文件中的struct uvc_control_info uvc_ctrls[]結(jié)構(gòu)體,這個結(jié)構(gòu)體中定義了攝像頭的控制參數(shù)詳情,主要包含了各種類型的控制,比如白平衡,曝光度等。其中需要重點(diǎn)關(guān)注的參數(shù)是上下左右和焦距的控制參數(shù),如下:
{.entity = UVC_GUID_UVC_CAMERA,.selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,.index = 9,.size = 2,.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_ZOOM_RELATIVE_CONTROL,.index = 10,.size = 3,.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 = 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, }在這里可以看到這里定義了相對控制和絕對控制的參數(shù),有焦距和上下左右,其中zoom是焦距調(diào)節(jié),pan是左右,tilt是上下,縮寫是ptz,所以云臺攝像頭也叫ptz攝像頭。
另外需要重點(diǎn)關(guān)注的變量是struct uvc_control_mapping uvc_ctrl_mappings[],這個結(jié)構(gòu)體枚舉出來了所有的控制類型,比如id = V4L2_CID_PAN_ABSOLUTE和id = V4L2_CID_TILT_ABSOLUTE的selector等于uvc_ctrls[]中的selector 是UVC_CT_PANTILT_RELATIVE_CONTROL參數(shù),這兩個id分別表示左右和上下的絕對控制,絕對控制的意思是給一個角度,攝像頭轉(zhuǎn)到指定的角度之后就停了。焦距的參數(shù)與pan和tilt的參數(shù)類似。稍微有點(diǎn)不一樣的地方是zoom在3.10版本中已經(jīng)添加了相對和絕對控制,參數(shù)id分別為V4L2_CID_ZOOM_ABSOLUTE,V4L2_CID_ZOOM_CONTINUOUS,其中V4L2_CID_ZOOM_CONTINUOUS是相對控制,從名稱也可以看出continuous是持續(xù)的意思,相對控制就是發(fā)給攝像頭一個參數(shù),攝像頭會一直不停的轉(zhuǎn)動,直到最大角度才停止,中途如果給發(fā)送了停止位,才會停止。
在這么多控制參數(shù)中,重點(diǎn)看一下焦距的相對控制,如下:
看看這個參數(shù)中定義了get和set,其實(shí)這個是映射到的具體get和set方法,具體方法是:
static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,__u8 query, const __u8 *data) {__s8 zoom = (__s8)data[0];switch (query) {case UVC_GET_CUR:return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]);case UVC_GET_MIN:case UVC_GET_MAX:case UVC_GET_RES:case UVC_GET_DEF:default:return data[2];} } static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,__s32 value, __u8 *data) {data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;data[2] = min((int)abs(value), 0xff); }第一個函數(shù)是獲取當(dāng)前焦距所處的位置,在UVC_GET_CUR處,當(dāng)前位置為0是,返回0,否則的話大于0,返回當(dāng)前值,否則返回相反數(shù)。
第二個函數(shù)是設(shè)置zoom的值,data[0]填寫的值有三種情況,0,1,0xff,其中0表示停止,1表示拉遠(yuǎn),0xff表示拉近。
在這兩個函數(shù)中可以看到UVC_GET_MIN和UVC_GET_MAX,意思是獲取最大值和最小值,其實(shí)在設(shè)備初始化的時候,會將設(shè)備的這些各個參數(shù)的最大值和最小值讀取并保存。
具體取值的地方在__uvc_query_v4l2_ctrl這個函數(shù)中,函數(shù)定義如下:
static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,struct uvc_control *ctrl,struct uvc_control_mapping *mapping,struct v4l2_queryctrl *v4l2_ctrl) {struct uvc_control_mapping *master_map = NULL;struct uvc_control *master_ctrl = NULL;struct uvc_menu_info *menu;unsigned int i;memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl);v4l2_ctrl->id = mapping->id;v4l2_ctrl->type = mapping->v4l2_type;strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);v4l2_ctrl->flags = 0;if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;if (mapping->master_id)__uvc_find_control(ctrl->entity, mapping->master_id,&master_map, &master_ctrl, 0);if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {s32 val;int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);if (ret < 0)return ret;if (val != mapping->master_manual)v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;}if (!ctrl->cached) {int ret = uvc_ctrl_populate_cache(chain, ctrl);if (ret < 0)return ret;}if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF,uvc_ctrl_data(ctrl,UVC_CTRL_DATA_DEF));}switch (mapping->v4l2_type) {case V4L2_CTRL_TYPE_MENU:v4l2_ctrl->minimum = 0;v4l2_ctrl->maximum = mapping->menu_count - 1;v4l2_ctrl->step = 1;menu = mapping->menu_info;for (i = 0; i < mapping->menu_count; ++i, ++menu) {if (menu->value == v4l2_ctrl->default_value) {v4l2_ctrl->default_value = i;break;}}return 0;case V4L2_CTRL_TYPE_BOOLEAN:v4l2_ctrl->minimum = 0;v4l2_ctrl->maximum = 1;v4l2_ctrl->step = 1;return 0;case V4L2_CTRL_TYPE_BUTTON:v4l2_ctrl->minimum = 0;v4l2_ctrl->maximum = 0;v4l2_ctrl->step = 0;return 0;default:break;}if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));return 0; }其中結(jié)構(gòu)體v4l2_queryctrl的定義在\include\uapi\linux\videodev2.h文件中,
struct v4l2_queryctrl {__u32 id;__u32 type; /* enum v4l2_ctrl_type */__u8 name[32]; /* Whatever */__s32 minimum; /* Note signedness */__s32 maximum;__s32 step;__s32 default_value;__u32 flags;__u32 reserved[2]; };包含了最大值,最小值,步長,默認(rèn)值,控制名稱等信息。
通過傳遞過來的控制id和類型遍歷,并將遍歷的結(jié)果保存在 v4l2_queryctrl中返回。
順帶說一下,在這個文件下面這個函數(shù)也很重要:
int uvc_ctrl_set(struct uvc_video_chain *chain,struct v4l2_ext_control *xctrl),這個函數(shù)掌控著控制的大權(quán)。其中v4l2_ext_control參數(shù)就是上層傳遞過來的控制參數(shù),非常重要。
從相關(guān)控制參數(shù)一路分析過來,大致明白了控制的過程和控制代碼的邏輯,下一步就是熟悉USB協(xié)議之后對這個參數(shù)做出定制,以適合不同廠商的攝像頭。
總結(jié)
以上是生活随笔為你收集整理的UVC系列2-探索Android UVC协议的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CoDeSys的前世今生
- 下一篇: Android VideoView无法播