Android項(xiàng)目復(fù)盤4

個(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的源碼:

https://android.googlesource.com/kernel/goldfish/

git clone https://android.googlesource.com/kernel/goldfish

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é)議文檔里面的定義:

image

攝像頭傳感器,僅用于描述攝像頭終端榛泛。那么代碼里面的描述是:

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.bmControlscamera.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_CONTROLUVC_CT_PANTILT_RELATIVE_CONTROL控制唧席,看看在協(xié)議文檔中的定義:
    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-q0hHvuSC-1593603543313)(https://ftp.bmp.ovh/imgs/2020/06/7b49813393d6df2a.png)]
image

對(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_mappingsuvc_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帖池,而entityselector都對(duì)應(yīng)著uvc_control_info uvc_ctrls中定義的entityselector

另外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_infomappings作為鏈表頭晤斩,將map->list添加到后面焕檬。

1.3 總結(jié)

在初始化的過程中可以將UVC協(xié)議的文檔跟代碼建立聯(lián)系,以幫助理解代碼的邏輯澳泵。待理解了各種數(shù)據(jù)類型定義的原理及流程之后实愚,發(fā)現(xiàn)其實(shí)現(xiàn)了UVC與V4L2的連接,這樣下一步的工作就比較好開展了兔辅。

UVC初始化的部分到這里告一段落腊敲,接下來要根據(jù)具體需求做一些定制的工作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末维苔,一起剝皮案震驚了整個(gè)濱河市碰辅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌介时,老刑警劉巖没宾,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凌彬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡循衰,警方通過查閱死者的電腦和手機(jī)铲敛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來会钝,“玉大人伐蒋,你說我怎么就攤上這事∏ㄋ幔” “怎么了先鱼?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)胁出。 經(jīng)常有香客問我型型,道長(zhǎng),這世上最難降的妖魔是什么全蝶? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任闹蒜,我火速辦了婚禮,結(jié)果婚禮上抑淫,老公的妹妹穿的比我還像新娘绷落。我一直安慰自己,他們只是感情好始苇,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布砌烁。 她就那樣靜靜地躺著,像睡著了一般催式。 火紅的嫁衣襯著肌膚如雪函喉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天荣月,我揣著相機(jī)與錄音管呵,去河邊找鬼。 笑死哺窄,一個(gè)胖子當(dāng)著我的面吹牛捐下,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播萌业,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼坷襟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了生年?” 一聲冷哼從身側(cè)響起婴程,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抱婉,沒想到半個(gè)月后排抬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懂从,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蹲蒲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了番甩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡届搁,死狀恐怖缘薛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卡睦,我是刑警寧澤宴胧,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站表锻,受9級(jí)特大地震影響恕齐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瞬逊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一悍手、第九天 我趴在偏房一處隱蔽的房頂上張望未斑。 院中可真熱鬧,春花似錦、人聲如沸逸绎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)糟秘。三九已至,卻和暖如春旨巷,著一層夾襖步出監(jiān)牢的瞬間巨缘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工采呐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留带猴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓懈万,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親靶病。 傳聞我的和親對(duì)象是個(gè)殘疾皇子会通,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容