v4l2驅動編寫篇


博主按:介紹V4L2基礎的東西,不知道是哪位同志翻譯的,莫名的感動啊。這個必須轉!

                另,對未翻譯的部分博主加以補充。文中以藍色字體表示,如果有錯誤請高手指正。還有些圖片好像不能顯示,我從原文復制過來了。

 

v4l2驅動編寫篇一--介紹
原文網址:
http://lwn.net/Articles/203924/


大部分所需的信息都在這里。作為一個驅動作者,當挖掘頭文件的時候,你可能也得看看include/media/v4l2-dev.h,它定義了許多你將來要打交道的結構體。
一個視頻驅動很可能要有處理PCI總線,或USB總線的部分。這里我們不會花什么時間還接觸這些東西。通常會有一個內部一I2C接口,我們在這一系列的后續文章中會接觸到它。然后還有一個V4L2的子系統接口。這個子系統是圍繞video_device這個結構體建立的,它代表的是一個V4L2設備。講解進入這個結構體的一切,將會是這個系列中幾篇文章的主題。這里我們先有一個概覽。
video_device結構體的name字段是這一類設備的名字,它會出現在內核日志和sysfs中出現。這個名字通常與驅動的名字相同。
所表示的設備有兩個字段來描述。第一個字段(type)似乎是從V4L1的API中遺留下來的,它可以下列四個值之一:


  筆者最近有機會寫了一個攝像頭的驅動,是“One laptop per child”項目的中攝像頭專用的。這個驅動使用了為此目的而設計的內核API:the Video4Linux2 API。在寫這個驅動的過程中,筆者發現了一個驚人的問題:這個API的文檔工作做得並不是很好,而用戶層的文檔則寫的,實際上,相當不錯。為了補救現在的狀況,LWN將在未來的內個月里寫一系列文章,告訴大家如何寫V4L2接口的驅動。
V4L2有一段歷史了。大約在1998的秋天,它的光芒第一次出現在Bill Dirks的眼中。經過長足的發展,它於2002年11月,發布2.5.46 時,融入了內核主干之中。然而直到今天,仍有一部分內核驅不支持新的API,這種新舊API的轉換工作仍在進行。同時,V4L2 API也在發展,並在2.6.18版本中進行了一些重大的改變。支持V4L2的應用依舊相對較少。
V4L2在設計時,是要支持很多廣泛的設備的,它們之中只有一部分在本質上是真正的視頻設備:

•video capture interface (影像捕獲接口)從調諧器或是攝像頭上獲取視頻數據。對很多人來講,影像捕獲(video capture) 是V4L2的基本應用。由於筆者在這方面的經驗是強項,這一系列文章也趨於強調捕獲API,但V4L2不止這些。
•video output interface (視頻輸出接口)允許應用使用PC的外設,讓其提供視頻圖像。有可能是通過電視信號的形式。
•捕獲接口還有一個變體,存在於video overlay interface(視頻覆蓋接口) 之中。它的工作是方便視頻顯示設備直接從捕獲設備上獲取數據。視頻數據直接從捕獲設備傳到顯示設備,無需經過CPU。
•VBI interfaces (Vertical blanking interval interface,垂直消隱接口)提供垂直消隱期的數據接入。這個接口包括raw和sliced兩種接口,其分別在於硬件中處理的VBI數據量。(為什么要在消隱期間接入數據呢?看這里 )
•radio interface (廣播接口) 用於從AM或FM調諧器中獲得音頻數據。
也可能出現其它種類的設備。V4L2 API中還有一些關於編譯碼和效果設備的stub,他們都用來轉換視頻數據流。然而這塊的東西尚未完成確定,更不說應用了。還有“teletext” 和”radio data system”的接口,他們目前在V4L1 API中實現。他們沒有移動到V4L2的API中來,而且目前也沒有這方面的計划。
視頻驅動與其他驅動不同之處,在於它的配置方式多種多樣。因此大部分V4L2驅動都有一些特定的代碼,好讓應用可以知道給定的設備有什么功能,並配置設備,使其按期望的方式工作。V4L2的API定義了幾十個回調函數,用來配置如調諧頻率、窗口和裁剪、幀速率、視頻壓縮、圖像參數(亮度、對比度…)、視頻標准、視頻格式等參數。這一系列文章的很大部分都要用來考察這些配置的過程。
然后,還有一個小任務,就是有效地在視頻頻率下進行I/O操作。V4L2定義了三種方法來在用戶空間和外設之間移動視頻數據,其中有些會比較復雜。視頻I/O和視頻緩沖層,將會分成兩篇文章來寫,它們是用來處量一些共性的任務的。
隨后的文章每幾周發一篇,共會加入到下面的列表中。

 

v4l2驅動編寫篇二--注冊和打開 [復制鏈接]
原文網址:
http://lwn.net/Articles/204545/ 
  這篇文章是LWN寫V4L2接口的設備驅動系列文章的第二篇。沒看過介紹篇的,也許可以從那篇開始看。這一期文章將關注Video for Linux驅動的總體結構和設備注冊過程。
開始之前,有必要提一點,那就是對於搞視頻驅動的人來說,有兩份資料是非常有價值的。

•TheV4L2 API Specification . (V4L2 API說明)這份文檔涵蓋了用戶空間視角下的API,但在很大程度上,V4L2驅動直接實現的就是那些API。所以大部分結構體是相同的,而且V4L2調用的語義也表述很很明了。打印一份出來(可以考慮去掉自由文本協議的文本內容,以保護樹木[前面是作者原文,節省紙張就是保護樹木嘛 ]),放在容易夠到的地方。
•內核代碼中的vivi驅動,即drivers/media/video/vivi.c.這是一個虛擬驅動。它可以用來測試,卻不使用任何實際硬件。這樣,它就成一個教人如何寫V4L2驅動的非常好的實例。
首先,每個V4L2驅動都要包含一個必須的頭文件:

 

•VFL_TYPE_GRABBER 表明是一個圖像采集設備–包括攝像頭、調諧器,諸如此類。
•VFL_TYPE_VBI 代表的設備是從視頻消隱的時間段取得信息的設備。
•VFL_TYPE_RADIO 代表無線電設備。
•VFL_TYPE_VTX 代表視傳設備。
如果你的設備支持上面提到的不只一種功能,那就要為每個功能注冊一個V4L2設備。然而在V4L2中,注冊的每個設備都可以用作它實際支持的各種模式(就是說,你要為一個物理設備創建不多個設備節點,但你卻可以調用任意一個設備節點,來實現這個物理設備支持的任意功能)。實質上的問題是,在V4L2中,你實際上只需一個設備,注冊多個V4l2設備只是為了與V4l1兼容。
第二個字段是type2,它以掩碼的形式對設備的功能提供了更詳盡的描述。它可以包含以下值:

/* These defines are V4L1 specific and should not be used with the V4L2 API!
   They will be removed from this header in the future. */

•VID_TYPE_CAPTURE 它可以捕獲視頻數據
•VID_TYPE_TUNER 它可以接收不同的頻率
•VID_TYPE_TELETEXT 它可以抓取字幕
•VID_TYPE_OVERLAY 它可以將視頻數據直接覆蓋到顯示設備的幀緩沖區
•VID_TYPE_CHROMAKEY 一種特殊的覆蓋能力,覆蓋的僅是幀緩沖區中像素值為某特定值的區域
•VID_TYPE_CLIPPING 它可以剪輯覆蓋數據
•VID_TYPE_FRAMERAM 它使用幀緩沖區中的存儲器
•VID_TYPE_SCALES 它可以縮放視頻數據
•VID_TYPE_MONOCHROME 這個是一個純灰度設備
•VID_TYPE_SUBCAPTURE 它可以捕獲圖像的子區域
•VID_TYPE_MPEG_DECODER 它支持mpeg碼流解碼
•VID_TYPE_MPEG_ENCODER 它支持編碼mpeg碼流
•VID_TYPE_MJPEG_DECODER 它支持mjpeg解碼
•VID_TYPE_MJPEG_ENCODER 它支持mjpeg編碼
V4L2驅動還要初始化的一個字段是minor,它是你想要的子設備號。通常這個值都設為-1,這樣會讓video4linux子系統在注冊時自動分配一個子設備號。
在video_device結構體中,還有三組不同的函數指針集。第一組只包含一個函數,那就是 release(),如果驅動沒有release()函數,內核就會抱怨(筆者發現一個件有趣的事,就是這個抱怨涉及到冒犯一篇LWN文章的作者)。 release()函數很重要:由於多種原因,對video_device的引用可以在最后一個應用關閉文件描述符后很長一段時間依然保持。它們甚至可以在設備己經注銷后依然保持。因此,在release()函數調用前,釋放這個結構體是不安全的。所以這個函數通常要包含一個簡單的kfree()調用。
video_device的 file_operations結構體包含都是常規的函數指針。視頻設備通常都包括open()和release()函數。注意:這里所說的 release函數並非上面所講到的同名的release()函數,這個release() 函數只要設備關閉就要調用。通常都還要有read()和write()函數,這取決於設備的功能是輸入還是輸出。然而我們要注意的是,對於視頻流設備而言,傳輸數據還有別的方法。多數處理視頻流數據的設備還需要實現poll()和mmap();而且每個V4L2設備都要有ioctl()函數,但是也可以使用V4L2子系統的video_ioctl2();
第三組函數存在於video_device結構體本身里面,它們是V4L2 API的核心。這組函數有幾十個,處理不同的設備配置操作、流輸入輸出和其他操作。
最后,從一開始就要知道的一個字段就是debug.可以把它設成是V4L2_DEBUG_IOCTL或V4L2_DEBUG_IOCTL_ARG(或是兩個都設,這是個掩碼),可以生成很多的調試信息,它們可以幫助一個迷糊的程序員找到毛病,知道為什么驅動和應用誰也不知道對方在說什么。
視頻設備注冊
一旦video_device己經配置好,就可以下面的函數注冊了:

1
 int video_register_device(struct video_device *vfd, int type, int nr);


這里vfd是設備的結構體(video_device),type的值與它的type字段值相同,nr也是一樣,想要的子設備號(為-1則注冊時自動分配)。返回值當為0,若返加的是負的
出錯碼,則表明出錯了,和通常一樣,我們要知道,設備一旦注冊,它的函數可能就會立即調用,所以不到一切准備就緒,不要調用video_register_device();
設備的注銷方法為:

1
 void video_unregister_device(struct video_device *vfd);


請繼續關注本系列的下篇文章,我們將會看看這些函數的具體實現。
open() 和 release()每個V4L2設備都需要open()函數,其原型也與常規的相同。
int (*open)(struct inode *inode, struct file *filp);

open()函數要做的第一件事是通過給定的inode找到內部設備,這是通過找到inode中存存儲的子設備號來完成的。這里還可以實現一定數量的初始化,如果有關閉電源選項的話,這個時間恰好可以用來開啟硬件電源。
V4L2規范還定義了一些相關的慣例。其一是:根據其設計,文件描述符可以在給定的任何時間重復打開。這樣設定的目的是當一個應用在顯示(或是產生)視頻信號時,另一個應用可以改變控制值。所以,雖然某些操作是獨占性質的(特別是數據讀、寫等),但是設備總體本身是要支持描述符復用的。
另一個值得一提的慣例是:open()函數,總體上講,不可以改變硬件中現行的操作參數。有些時候可能會有這樣的情況:通過命令行程序,根據一組參數(分辨率,視頻格式等)來改變攝像頭配置,然后運行一個完全不同的程序來,比如說,從攝像頭獲取上幀圖像。如果攝像頭在設置在中途改變了,這種模式就不好用。所以除非應用明確表示要改變設置(這種情況當然不包括在open函數中)V4L2驅動要盡量保持設定不變。
release()函數做一些必要清理工作。因為文件描述符可以重復打開,所以release函數中減小引用計數,並在徹底退出之前做檢查。如果關閉的文件描述符是用來傳輸數據的,release函數很可能要關掉DMA,並做一些其他的清理工作。
本系列的下一篇文章我們將進入查詢設備功能和設定系統模式的冗長過程之中,請續斷關注。

 

v4l2驅動編寫篇三--基本I/O處理
如果有人在video for linux API規范上花了我時間的話,他肯定已經注意到了一個問題,那就是V4L2大量使用了ioctl接口。視頻硬件有大量的可操作旋鈕,可能比其它任何處設都要多。視頻流要與許多參數相聯系,而且有很大一部分處理要通過硬件進行。不使用硬件有良好支持模式可能導致表現不好,甚至根本沒有表現。所以我們不得不揭露硬件的許多特性,而對最終應用表現得怪異一點。
傳統上來講,視頻驅動中包含的ioctl()函數一般會長得像一部小說,而函數所得到的結論也往往比小說更令人滿意來,他們往往在中間拖了很多(這句話完全不明白什么意思)。所以V4L2的API在2.6.18版本的內核開始做出了改變。冗長的ioctl函數被替換成了一個大的回調函數的集合,每個回調函數實現自己的ioctl函數。實際上,在2.6.19-rc3中,有79個這樣的回調函數。而幸運的是,多數驅動並不需實現所有的回調函數,甚至都不是大部分回調函數。
在ioctl()函數中發生的事情都放到了drivers/media/video/videodev.c里面。這部分代碼處理數據在內核和用戶空間之間的傳輸並把ioctl調用發送給驅動。要使用它的話,只要把video_device中的video_ioctl2()做為ioctl()來調用就行了。實際上,多數驅動也要把它當成unlocked_ioctl()來用。Video4Linux2層的鎖可以對其進行處理,而且驅動也應該在合適的地方加鎖。(這一段沒看明白,亂寫的)

 

你的驅動第一個可能要實現的回調函數是:

int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);

這個函數處理VIDIOC_QUERYCAP ioctl(), 只是簡單問問“你是誰?你能干什么?”實現它是V4L2驅動的責任。在這個函數中,和所有其他V4L2回調函數一樣, 參數priv是file->private_data域的內容;通常的實現是在open()的時候把它指向驅動中表示設備的內部結構。

 

驅動應該負責填充cap結構並且返回“0或者負的錯誤碼”值。如果成功返回,則V4L2層會負責把回復拷貝到用戶空間。

 

v4l2_capability結構(定義在<linux/videodev2.h>中)是這樣的:

struct v4l2_capability {
 __u8 driver[16]; /* i.e. "bttv" */
 __u8 card[32]; /* i.e. "Hauppauge WinTV" */
 __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
 __u32   version;        /* should use KERNEL_VERSION() */
 __u32 capabilities; /* Device capabilities */
 __u32 reserved[4];
};

 

其中driver域應該被填充設備驅動的名字,card域應該被填充這個設備的硬件描述信息。並不是所有的驅動都消耗精力去處理bus_info域,這些驅動通常使用下面這個方法:

   springf(cap->bus_info, "PCI:%s", pci_name(&my_dev));

 

version域用來保存驅動的版本號。capabilities域是一個位掩碼用來描述驅動能做的不同的事情:

/* Values for 'capabilities' field */
#define V4L2_CAP_VIDEO_CAPTURE  0x00000001  /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT  0x00000002  /* Is a video output device */
#define V4L2_CAP_VIDEO_OVERLAY  0x00000004  /* Can do video overlay */
#define V4L2_CAP_VBI_CAPTURE  0x00000010  /* Is a raw VBI capture device */
#define V4L2_CAP_VBI_OUTPUT  0x00000020  /* Is a raw VBI output device */
#define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040  /* Is a sliced VBI capture device */
#define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080  /* Is a sliced VBI output device */
#define V4L2_CAP_RDS_CAPTURE  0x00000100  /* RDS data capture */
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200  /* Can do video output overlay */
#define V4L2_CAP_HW_FREQ_SEEK  0x00000400  /* Can do hardware frequency seek  */

#define V4L2_CAP_TUNER   0x00010000  /* has a tuner */
#define V4L2_CAP_AUDIO   0x00020000  /* has audio support */
#define V4L2_CAP_RADIO   0x00040000  /* is a radio device */

#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */

 

最后一個域(reserved)是保留的。V4L2規則要求要求reserved被置為0, 但是, 因為video_ioctl2()設置了整個的結構體為0,所以這個就不用我們操心了。

 

可以在“vivi”這個驅動中找到一個典型的應用:

static int vidioc_querycap(struct file *file, void  *priv,
     struct v4l2_capability *cap)
{
 struct vivi_fh  *fh  = priv;
 struct vivi_dev *dev = fh->dev;

 strcpy(cap->driver, "vivi");
 strcpy(cap->card, "vivi");
 strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
 cap->version = VIVI_VERSION;
 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
    V4L2_CAP_STREAMING     |
    V4L2_CAP_READWRITE;
 return 0;
}

考慮到這個回調函數的出現,我們希望應用程序使用它,避免要求設備完成它們不可能完成的功能。然而,在你編程的有限經驗中,應用程序不會花太多的精力來關注VIDIOC_QUERYCAP調用。

 

另一個可選而又不常被是實現的回調函數是:

     int (*vidioc_log_status)       (struct file *file, void *fh);

這個函數用來實現VIDIOC_LOG_STATUS調用,作為視頻應用程序編寫者的調試助手。當調用時,它應該打印藐視驅動及其硬件的當前狀態信息。這個信息應該足夠充分以便幫助迷糊的應用程序開發者弄明白為什么視頻顯示一片空白。

 

 

下一部分開始剩下的77個回調函數。特別的,我們要開始看看與硬件協商一組操作模式的過程。

 

v4l2驅動編寫篇第四--輸入輸出


輸入和輸出
這是不定期發布的關於寫視頻驅動程序的LWN系統文章的第四篇.沒有看過介紹篇的,也許想從這里開始.本周的文章介紹的是應用程序如何確定在特定適配器上哪些輸入和輸出可用,並且它們之間做出選擇。

在很多情況下,視頻適配器並不能提供很多的輸入輸出選項.比如說攝像頭控制器,可能只是提供攝像頭,而沒什么別的功能.然而,在一些其他的情況下,事情將變得很復雜.一個電視卡可能對應板上不用的接頭有不同的輸入.他甚至可能有可以獨立發揮其功能的多路調諧器.有時,那些輸入會有不同的特性;有些調諧器可以支持比其他的更廣泛的視頻標准.對於輸出來說,也有同樣的問題.

很明顯,若想一個應用可以有效地利用視頻適配器,它必須有能力找到可用的輸入和輸出,而且他必須能找到他想操作的那一個.為此,Video4Linux2 API提供三種不同的ioctl()調用來處理輸入,相應地有三個來處理輸出.

這三個(對於硬件支持的每一個功能)驅動都要支持.雖然如此,對於簡單的硬件而言,代碼還是很簡單的.驅動也要提供一此啟動時的默認值.然而,驅動不應該做的是,在應用退出時重置輸入輸出信息.對於其他視頻參數,在多次打開之間,參數應維持不變.

 

視頻標准

  在我們進入輸入輸出的細節之前,我們應該先了解一下視頻標准 .這些標准描述的是視頻為進行傳輸而做出的格式轉換–分辨率,幀頻率等.這些標准通常是由每一個國家的監管部門制定的。現在世界上使標准主要的有三個:NTSC(主要是北美使用),PAL(主要是歐洲,非洲和中國),和SECAM(法,俄和非洲部分地區).然而這在標准在國家之間都有變化,而且有些設備比其他設備能更加靈活,能與更多的標准變種協同工作.

V4L2使用v4l2_std_id來代表視頻標准,它是一個64位的掩碼。每個標准變種在掩碼中就是一位。所以標准NTSC就是V4L2_STD_NTSC_M, 值為0x1000,而日本的變種就是V4L2_STD_NTSC_M_JP
(0x2000)。如果一個設備可以處理所以有NTSC變種,它就可以設為V4L2_STD_NTSC,它可以所有相關位置位。對PAL和SECAM標准,也存在一組類似的位集。Seethis page for a complete list.

對於用戶空間而言,V4L2提供一個ioctl()命令(VIDIOC_ENUMSTD),它允許應用查詢設備實現了哪些標准。驅動卻無需直接回答查詢,而是將video_device結構體的tvnorm字段設置為它所支持的所有標准。然后V4L2層會向應用輸出所支持的標准。VIDIOC_G_STD命令,可以用來查詢現在哪種標准是激活的,它也是在V4L2層通過返回video_device結構的current_norm字段來處理的,驅動程序應在啟動時,初始化current_norm來反應現實情況。有些應用即使他並沒有設置過標准,發現標准沒有設置也會感到困惑。

當某個應用想要申請某個標准的時候,會發出一個VIDIOC_S_STD調用,該調用通過下面的函數傳到驅動:

  int (*vidioc_s_std) (struct file *file, void *private_data,                         v4l2_std_id std);


驅動要對硬件編程,以使用給定的標准,並返回0(或是負的出錯編碼).V4L2層需要把current_norm設為新的值。

應用可能想要知道硬件所看到的是何種信號,答案可以通過VIDIOC_QUERYSTD找到,它到了驅動里面就是:

    int (*vidioc_querystd) (struct file *file, void *private_data,                            v4l2_std_id *std);


驅動要盡可能地在這個字段填寫詳細信息。如果硬件沒有提供足夠的信息,std字段就會暗示任何可能出現的標准。

這里還有一點值得一提:所以的視頻設備必須支持(或是聲明支持)至少一種視頻標准。視頻標准對於攝像頭來說沒什么意義,它不與任何監管制度綁定。但是也不存一個標准說“我是個攝像頭,我什么都能做”,所以V4L2層有很多攝像頭聲明可以返回PAL或NTSC數據(實際只是如些聲明而己)。

 

輸入

  視頻捕獲的應用首先要通過VIDIOC_ENUMINPUT
命令來枚舉所有可用的輸入。在V4L2層,這個調用會轉換成調用一個驅動中對應的回調函數:

  int (*vidioc_enum_input)(struct file *file, void *private_data,                             struct v4l2_input *input);


在這個調用中,file 對就的是打開的視頻設備。private_data是驅動的私有字段,input字段是真正的傳遞的信息,它有如下幾個值得關注的字段:

•__u32 index:應用關注的輸入的索引號; 這是惟一一個用戶空間設定的字段. 驅動要分配索引號給輸入,從0開始,依次往上增加.想要知道所以可用的輸入的應用會調用VIDIOC_ENUMINPUT
調用過索引號從0開始,並開始遞增。 一旦返回EINVAL,應用就知道,輸入己經用光了.只要有輸入,輸入索引號0就一定要存在的.
•__u8 name[32]: 輸入的名字,由驅動設定.簡單起見,可以設為”Camera”,諸如此類;如果卡上有多個輸入,名稱就要與接口的打印相符合.
•__u32 type:輸入的類型,現在只有兩個值可選:V4L2_INPUT_TYPE_TUNER
和V4L2_INPUT_TYPE_CAMERA.
•__u32 audioset:描述哪個音頻輸入可以與些視頻輸入相關聯. 音頻輸入與視頻輸入一樣通過索引號枚舉 (我們會在另一篇文章中關注音頻),但並非所以的音頻與視頻的組合都是可用的.這個字段是一個掩碼,代表對於當前枚舉出的視頻而言,哪些音頻輸入是可以與之關聯的.如果沒有音頻輸入可以與之關聯,或是只有一個可選,那么就可以簡單地把這個字段置0.
•__u32 tuner: 如果輸入是一個調諧器 (type
字段置為V4L2_INPUT_TYPE_TUNER), 這個字段就是會包含一個相應的調諧設備的索引號.枚舉和調諧器的控制也將在未來的文章中講述.
•v4l2_std_id std: 描述設備支持哪個或哪些視頻標准.
•__u32 status: 給出輸入的狀態. 全整的標識符集合可以在V4L2的文檔中找到(即這里
);簡而言之,status
中設置的每一位都代表一個問題. 這些問題包括沒有電源,沒有信號,沒有同頻鎖,或 the presence of Macrovision(這個是什么意思?沒查到)或是其他一些不幸的問題.
•__u32 reserved[4]:保留字段,驅動應該將其置0.
通常驅動會設置上面所以的字段,並返回0。如果索引值超出支持的輸入范圍,應該返回-EINVAL.這個調用里可能出現的錯誤不多。

當應用想改變現行的輸入時,驅動會收到一個對回調函數vidioc_s_input()的調用。

int (*vidioc_s_input) (struct file *file, void *private_data,                           unsigned int index);


index的值與上面講到的意義相同 – 它相來確定哪個輸入是相要的.驅動要對硬件編輯,選擇那個輸入並返回0.也有可能要返回-EINVAL
(索引號不正確時) 或-EIO
(硬件有問題). 即使只有一路輸入,驅動也要實現這個回調函數.

還有另一個回調函數,指示哪一個輸入是激活狀態的:

  int (*vidioc_g_input) (struct file *file, void *private_data,                           unsigned int *index);

 


這里驅動把index值設為對應的輸入的索引號.

 

 

輸出

  枚舉和選擇輸出的過程與輸入的是十分相似的。所以這里的描述就從簡。輸入枚舉的回調函數是這樣的:

  int (*vidioc_enumoutput) (struct file *file, void *private_data                                  struct v4l2_output *output);


v4l2_output
結構的字段是:

•__u32 index:相關輸入的索引號.其工作方式與輸入的索引號相同:它從0開始遞增。
•__u8 name[32]: 輸出的名字.
•__u32 type: 輸入的類型.支持的輸出類型為:V4L2_OUTPUT_TYPE_MODULATOR
用於模擬電視調制器,V4L2_OUTPUT_TYPE_ANALOG
用於基本模擬視頻輸出,和V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY
用於模擬VGA覆蓋設備.
•__u32 audioset: 能與這個視頻協同工作的音頻集.
•__u32 modulator: 與此設備相關的調制器 (對於類型為V4L2_OUTPUT_TYPE_MODULATOR 的設備而言).
•v4l2_std_id std:輸出所支持的視頻標准.
•__u32 reserved[4]: 保留字段,要設置為0.
也有用於獲得和設定現行輸入設置的回調函數;他們與輸入的具體對稱性:

    int (*vidioc_g_output) (struct file *file, void *private_data,    unsigned int *index);   

    int (*vidioc_s_output) (struct file *file, void *private_data,     unsigned int index);


即便只有一個輸出,設備驅動也要定義所有上述的三個回調函數.

有了這些函數之后,V4L2應用就可以知道有哪些輸入和輸入,並在它們間進行選擇.然而選譯這些輸入輸出中所傳輸的是什么視頻數據流則是一件更加復雜的事情.本系列的下一期文章,我們將關注視頻數據流的格式,以及如何與用戶空間協定數據格式.

 

v4l2文檔第五A--顏色與格式
  顏色與格式這是不定期發布的關於寫視頻驅動程序的LWN系統文章的第五篇.沒有看過介紹篇的,也許想從這里 開始.

 


  應用在可以使視頻設備工作之前,它必須與驅動達成了解,知道視頻數據是何種格式的。這種協商將是一個非常復雜的過程,其原因有二:1、視頻硬件所支持的視頻格互不相同。2、在內核的格式轉換是令人難以接受的。所以應用在找出一種硬件支持的格式,並做出一種大家都可以接受的配置。這篇文章將會講述格式的基本描述方式;下期文章則會講述V4L2驅動與應用協商格式時所實現的API。


色域
  色域在廣義上來講,就是系統在描述色彩時所使用的坐標。V4L2規范中定義了好幾個,但只有兩個使用最為廣泛。它們是:

   •V4L2_COLORSPACE_SRGB.多數開發者所熟悉的[red,green,blue]數組包含在這個色域之中。它為每一種顏色提供了一個簡單的強度值,把它們混合在一起,從而產生了一種廣泛的顏色的效果。表示RGB值的方法有很多,我們在下面將會有所介紹。
這個色域也包含YUV和YCbCr的表示方法,這個表示方法最早是為了早期的彩色電視信號可以在黑白電視中的播放,所以Y(或說亮度)值只是一個簡單的亮度值,單獨播放時可以產生灰度圖像。U和V(或Cb和Cr)色度值描述的是色彩中藍色和紅色的分量。綠色可以通過從亮度中減去這些分量而得到。YUV和RGB之間的轉換並不簡單,但是我們有一些成形的公式 可選。
注意:YUV和YCbCr並非完成一樣,雖然有時他們的名字會替代使用。

   •V4L2_COLORSPACE_SMPTE170M 這個是NTSC或PAL等電視信號的模擬色彩表示方法,電視調諧器通常產生的色域都屬於這個色域。
還存在很多其他的色域,他們多數都是電視相關標准的變種。點擊查看V4L2規范 中的詳細列表。

 

密集存儲和平面存儲

  如汝所見,像素值是以數組的方式表示的,通常由RGB或YUV值組成。要把這數組組織成圖像,通常有兩種常用的方法。

  •Packed 格式把一個像素的所有值存領教在一起.
  •Planar 格式把每一個分量單獨存儲成一個陣列,這樣在YUV格式中,所有Y值都連續地一起存儲在一個陣列中,U值存儲在另一個中,V值存在第三個中.這些平面常常都存儲在一個緩沖區中,但並不一定非要這樣.
緊密型存儲方式可能使用更為廣泛,特別是RGB格式,但這兩種存儲方式都可以由硬件產生並由應用程序請求。如果設備可以產生緊密型和平面型兩種,那么驅動就要讓兩種都在用戶空間可見。


四字符碼(four Charactor Code:FourCC)

V4L2 API中表示色彩格式采用的是廣受好評的四字符碼(fourcc)機制。這些編碼都是32位的值,由四個ASCII碼產生。如此一來,它就有一個優點就是,易於傳遞,對人可讀。當一個色彩格式讀作,例如,”RGB4″就沒有必要去查表了。
  注意:四字符碼在很多不同的設定中都會使用,有些還是早於linux的.Mplayer中內部使用它們,然而,fourcc只是說明一種編碼機制,並不說明使用何種編碼。Mplayer有一個轉換函數,用於在它自己的fourcc碼和v4l2用的fourcc碼之間做出轉換。

RGB格式

在下面的格式描述中,字節是按存儲順序列出的。在小端模式下,LSByte在前面。每字節的LSbit在右側。每種色域中,輕陰影位是最高有效的。

 

Name

fourcc

Byte 0

Byte 1

Byte 2

Byte 3

V4L2_PIX_FORMAT_RGB332

RGB1

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB444

R444

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB555

RGB0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB565

RGBP

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB555X

RGBQ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB565X

RGBR

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_BGR24

BGR3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB24

RGB3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_BGR32

BGR4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB32

RGB4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_SBGGR8

BA81

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

當使用有空位(上圖中灰色部分)的格式 , 應用可以使用空位作alpha(透明度)值.
上面的最后一個格式是”Bayer(人名好像是)”格式,此格式與多數攝像機感光器所得到的真實數據非常接近。每個像素都有綠色分量,但藍和紅只是隔一個像素才有分量。本質上講,綠色帶有更重的強度信息,而藍紅色則在丟失時以相隔像素的內插值替換。這種模式我們交在YUV格式中再次見到。

YUV格式

YUV的緊密型模式在下面首先展示,看表的關鍵處如下:

  •  

 

 

 

 

 

 

 

 

  • = Y (intensity)
  •  

 

 

 

 

 

 

 

 

  • = U (Cb)
  •  

 

 

 

 

 

 

 

 

  • = V (Cr)

 

 

 

也有幾中平面型的YUV格式在用,但把它們全畫出來並沒有什么大的幫助,所以我們只是在下面舉一下例子,常用”YUV 4:2:2″(V4L2_PIX_FMT_YUV422, fourcc422P)格式使用三組陣列,一幅4X4的圖片將如下表示:

Y plane:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

U plane:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V plane:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

對於Bayer格式, YUV 4:2:2 每隔一個Y值有一個U和一個V值,展示圖像需要以內插值替換的丟失的值。其他的平面YUV格式有:


•V4L2_PIX_FMT_YUV420: YUV 4:2:0格式,每四個Y值才有一個U值一個V值.U和V都要在水平和垂直兩個方向上都以內插值替換.平面是以Y-U-V的順序存儲的,與上面的例子一致.
•V4L2_PIX_FMT_YVU420: 與YUV 4:2:0格式類似,只是U,V值調換了位置.
•V4L2_PIX_FMT_YUV410: 每16個Y值才有一個U值和V值.陣列的順序是 Y-U-V.
•V4L2_PIX_FMT_YVU410: 每16個Y 值才有一個U值和V值.陣列的順序是Y-V-U.

 

其他格式

還有一些可能對驅動有用的格式如下:

•V4L2_PIX_FMT_JPEG: 一種定義模糊的JPEG流;更多信息請看這里 .
•V4L2_PIX_FMT_MPEG: MPEG流.還有一些MPEG流格式的變種;未來的文章中將討論流的控制.

[p=21, null, left]還有一些其他的混雜的格式,其中一些還是有傳利保護的;這里 有一張列表.
格式的描述[p=21, null, left]現在我們己經了解了顏色的格式,下面將要看看下V4L2 API中是如何描述圖像格式的了。這里的主要的結構體是struct v4l2_pix_format (定義於<linux/videodev2.h>),它包含如下字段:


•__u32 width: 圖片寬度,以像素為單位.
•__u32 height:圖片高度,以像素為單位.
•__u32 pixelformat: 描述圖片格式的四字符碼.
•enum v4l2_field field:很多圖片的源會使數據交錯 -先傳輸奇數行,然后是偶到行.真正的攝像頭設備是不會做數據的交錯的。 V4L2 API 允許應用使用很多種方式交錯字段.常用的值為V4L2_FIELD_NONE (字段不交錯),V4l2_FIELD_TOP (只交錯頂部字面),或V4L2_FIELD_ANY (無所謂). 詳情見這里 .
•__u32 bytesperline: 相臨掃描行之間的字節數.這包括各種設備可能會加入的填充字節.對於平面格式,這個值描述的是最大的 (Y) 平面.
•__u32 sizeimage: 存儲圖片所需的緩沖區的大小.
•enum v4l2_colorspace colorspace: 使用的色域.
加到一起,這些參數以合理而完整的方式描述了視頻數據緩沖區。應用可以填充 v4l2_pix_format 請求用戶空間開發者所能想到的幾乎任何格式. 然而,在驅動層面上,驅動開發者則要限制在硬件所能支持的格式上. 所以每一個 V4L2 應用都必須經歷一個與驅動協定的過程,以便於使用一個硬件支持並且能滿足應用需要的圖像格式。下一期文章,我們將從驅動角度,描述這種協定是什么進行的。

 

v4l2驅動編寫篇第五B--格式的協定

 
 這是不定期發布的關於寫視頻驅動程序的LWN系統文章的一篇續篇.介紹篇 包含了對整個系統的描述,並且包含對本篇的上一篇的鏈接,在上一集,我們關注了V4L2 API是如何描述視頻格式的:圖片的大小,和像素在其內部的表示方式。這篇文章將完成對這個問題的討論,它將描述如就硬件所支持的實際視頻格與應用達到協議。
如我們在上一篇中所見,在存儲器中表示圖像有很多種方法。市場幾乎找不到可以處理所有V4L2所理解的視頻格式的設備。驅動不應支持底層硬件不懂的視頻格式。實際上在內核中進行格式的轉換是令人難以接受的。所以驅動必須可以應用選擇一個硬件可以支持的格式。
第一步就是簡單的允許應用查詢所支持的格式。VIDIOC_ENUM_FMT ioctl()就是為此目的而提供的。在驅動內部這個調用會轉化為這樣一個回調函數(如果查詢的是視頻捕獲設備)。
  int (*vidioc_enum_fmt_cap)(struct file *file, void *private_data,
                               struct v4l2_fmtdesc *f);

這個回返調函數要求視頻捕獲設備描述其支持的格式。應用會傳遞一個v4l2_fmtdesc 結構體:
   struct v4l2_fmtdesc
    {
        __u32                    index;
        enum v4l2_buf_type  type;
        __u32               flags;
        __u8                    description[32];
        __u32                    pixelformat;
        __u32                    reserved[4];
    };

應用會設置index 和type 字段.index 是用來確定格式的一個簡單的整型 數;與其他V4L2所使用的索引(indexes)一樣,這個也是從0開始遞增,至最大允許的值為止.應用可以通過一直遞增索引值(index)直到返回 EINVAL的方式枚舉所有支持的格式.type 字段描述的是數據流類型 ;對於視頻捕獲設備來說(攝像頭或調諧器就是V4L2_BUF_TYPE_VIDEO_CAPTURE.
如果index 對就某個支持的格式,驅動應該填寫結構體的其他字段.pixelformat字段應該是描述視頻表現方式的fourcc編碼,而 description 是對這個格式的一種簡短的字符串描述.flags 字段只定義了一個值即V4L2_FMT_FLAG_COMPRESSED,它表示是一個壓縮了的視頻格式.
上述的函數是視頻捕獲函數;只有當type 字段的是值是V4L2_BUF_TYPE_VIDEO_CAPTURE時才會調用. VIDIOC_ENUM_FMT 調用將根據type字段的值的不同解釋成不同的回調函數。
    /* V4L2_BUF_TYPE_VIDEO_OUTPUT */
    int (*vidioc_enum_fmt_video_output)(file, private_date, f);

    /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
    int (*vidioc_enum_fmt_overlay)(file, private_date, f);

    /* V4L2_BUF_TYPE_VBI_CAPTURE */
    int (*vidioc_enum_fmt_vbi)(file, private_date, f);

    /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ */
    int (*vidioc_enum_fmt_vbi_capture)(file, private_date, f);

    /* V4L2_BUF_TYPE_VBI_OUTPUT */
    /* V4L2_BUF_TYPE_SLICED_VBI_OUTPUT */
    int (*vidioc_enum_fmt_vbi_output)(file, private_date, f);

    /* V4L2_BUF_TYPE_VIDEO_PRIVATE */
    int (*vidioc_enum_fmt_type_private)(file, private_date, f);

參數類似對於所以的調用都是一樣. 對於以V4L2_BUF_TYPE_PRIVATE開頭的解碼器,驅動可以支持特殊的緩沖類型是沒有意義的。, 但在應用端卻需要一個清楚的認識. 對這篇文章的目的而言,我們更加關心的是視頻捕獲和輸出設備; 其他的視頻設備我們會在將來某期的文章中講述.
應用可以通過調用VIDIOC_G_FMT知道硬件現在的配置如何。這種情況下傳遞的參數是一個v4l2_format 結構體:
    struct v4l2_format
    {
        enum v4l2_buf_type type;
        union
        {
                struct v4l2_pix_format                pix;
                struct v4l2_window                win;
                struct v4l2_vbi_format                vbi;
                struct v4l2_sliced_vbi_format        sliced;
                __u8        raw_data[200];
        } fmt;
    };

同樣,type 描述的是緩沖區類型;V4L2層會根據type的不同,將調用解釋成不同的驅動的回調函數. 對於視頻捕獲設備而言,這個回調函數就是:
  int (*vidioc_g_fmt_cap)(struct file *file, void *private_data,
                                struct v4l2_format *f);

對於視頻捕獲(和輸出)設備, 聯合體中pix 字段是我們關注的重點. 這是我們在上一期中見過的v4l2_pix_format 結構體;驅動應該用現在的硬件設置填充那個結構體並且返回。這個調用通常不會失敗,除非是硬件出現了非常嚴重的問題。
其他的回調函數還有:
    int (*vidioc_s_fmt_overlay)(file, private_data, f);
    int (*vidioc_s_fmt_video_output)(file, private_data, f);
    int (*vidioc_s_fmt_vbi)(file, private_data, f);
    int (*vidioc_s_fmt_vbi_output)(file, private_data, f);
    int (*vidioc_s_fmt_vbi_capture)(file, private_data, f);
    int (*vidioc_s_fmt_type_private)(file, private_data, f);

vidioc_s_fmt_video_output()與捕獲接口一樣使用相同的方式使用同一個pix字段。
多數應用都想最終對硬件進行配置以使其為應用提供一種符合其目的的格式。改變視頻格有兩個接口。第一個是VIDIOC_TRY_FMT 調用,它在V4L2驅動中轉化為下面的回調函數:
    int (*vidioc_try_fmt_cap)(struct file *file, void *private_data,
                              struct v4l2_format *f);
    int (*vidioc_try_fmt_video_output)(struct file *file, void *private_data,
                                             struct v4l2_format *f);
    /* And so on for the other buffer types */

要處理這個調用,驅動會查看請求的視頻格式,然后斷定硬件是否支持這個格式。如果應用請求的格式是不能支持的,就會返回-EINVAL.所以,例如,一個描述了一個不支持格的fourcc編碼或者請求了一個隔行掃描的視頻,而設備只支持逐行掃描的就會失敗。在另一方面,驅動可以調整size字段,以與硬件支持的圖像大小相適應。普便的做法是可能的話就將大小調小。所以一個只能處理VGA分辨率的設備驅動會根據情況相應地調整width和height參數而成功返回。v4l2_format 結構體會在調用后復制給用戶空間;驅動應該更新這個結構體以反映改變的參數,這樣應用才可以知道它真正得到就是什么。
VIDIOC_TRY_FMT 這個處理對於驅動來說是可選的,但是不推薦忽略這個功能.如果提供了的話,這個函數可以在任何時候調用,甚至時設備正在工作的時候。它不可以對實質上的硬件參數做任何改變,只是讓應用知道都可以做什么的一種方式。
如果應用要真正的改變硬件的格式,它使用VIDIOC_S_FMT 調用,它以下面的方式到達驅動:
    int (*vidioc_s_fmt_cap)(struct file *file, void *private_data,
                                struct v4l2_format *f);
    int (*vidioc_s_fmt_video_output)(struct file *file, void *private_data,
                                         struct v4l2_format *f);

與VIDIOC_TRY_FMT不同,這個調用是不能隨時調用的.如果硬件正在工作,或者有流緩沖器己經開辟了(未來另一篇文章的),改變格式會帶來無盡的麻煩。想想會發生什么,比如說,一個新的格式比現在使的緩沖區大的時候。所以驅動要一直保證硬件是空閑的,如果不空閑就對請求返回失敗 (-EBUSY).
格式的改變應該是原子的 – 它或者改變所以的參數以實現請求否則就必須一個也不改變.同樣,驅動在必要時是可以改變圖像的大小的,通常的回調函數格式與下面的差不多:
    int my_s_fmt_cap(struct file *file, void *private,
                     struct v4l2_format *f)
    {
        struct mydev *dev = (struct mydev *) private;
        int ret;

        if (hardware_busy(mydev))
            return -EBUSY;
        ret = my_try_fmt_cap(file, private, f);
        if (ret != 0)
            return ret;
        return tweak_hardware(mydev, &f->fmt.pix);
    }

使用VIDIOC_TRY_FMT 句柄可以避免代碼重寫而且可以避免任何沒有先實現那個函數的借口. 如果”try”函數成功返回,結果格式就己知並且可以直接編程進硬件。
還有很多其他調用也用影響視頻I/O的完成方式。將來的文章將會討論他們中的一部分。支持設置格式就足以讓應用開始傳輸圖像了,而且那也這個結構體的最終目的.所以下一篇文章,(希望會在這次之后的時間不會太久)我們會來關注對視頻數據的讀和寫的支持。

 

v4l2驅動編寫篇第六A--基本的幀輸入輸出
基本的幀輸入輸出
關於視頻驅動的這一系列文章己經更新了好幾期,但是我們還沒有傳輸過一幀的視頻數據。雖然在這一點上,我們己經了解了足夠多的關於格式協定方面的細節,我們可以看一下視頻幀是如何在應用和設備之間傳輸的了。

V4L2 API定義了三種不同的傳輸視頻幀的方法,現在有兩種是可以實現的:

read() 和write() 系統調用這種普通的方法. 根據硬件和驅動的不同,這種方法可能會非常慢 -但也不是一定會那樣.
將幀直接以視頻流的方法送到應用可以訪問的緩沖區. 視頻流這際上是傳輸視頻數據的最有效的方法;這種接口還允許在圖像幀中附帶一些其他信息. 視頻流的方法有兩種變種,其分別在於緩沖的開辟是在用戶空間還是內核空間。
Video4Linux2 API規范提供一種異頻的輸入輸出機制用於幀的傳輸。然而這種模式還沒有實現,因此不能使用。
這一篇將關注的是簡單的read()和write()接口,視頻流的方式將在下一期來講解。

read() 和 write()
Video4Linux2 規范並沒有規定要實現read()和write(),然而很多簡單的應用希望這種系統調用可用,所以可能的話,驅動的作者應該使之工作。如果驅動沒有實現這些系統調用,它應該在保證V4L2_CAP_READWRITE置位,來回應VIDIOC_QUERYCAP調用。然而以筆者的經驗,多數的應用在使用調用之前,根本就不會是費心查看調用是否可用。

驅動的read()和/或write()方法必須存在相關的video_device結構中的fops字段里。注意:V4L2規范要求實現這些方法,從而也提供poll()操作。

在一下視頻捕獲設備上實現read()操作是非常直接的:驅動告訴硬件開始捕獲幀,發送一幀到用戶空間緩沖,然后關停硬件並返回。如果可能的話,驅動應該安排DMA操作直接將數據傳送到目的緩沖區,但這種方式只有在控制器可以處理分散/聚集I/O的時候可能。否則,驅動應該在內核里啟用幀緩沖區。同樣,寫操作也是盡可能直接傳到設備,否則啟用幀緩沖區。

不那么簡單的操作也是可以的。例如筆者的”cafe”驅動會在read()操作后讓攝像頭控制器工作在一種投機的狀態,在一秒鍾的下一部分,攝像頭中的后續幀將會存儲在內核的緩沖區中,如果應用發出了另一個讀的調用,它將會更快的反應,無續再次啟動硬件。經過一定數目的幀都沒有讀的話,控制器就會被放回空閑的狀態。同理,寫操作時,也會廷時幾十毫秒,意在幫助應用與硬件幀同步。

流參數
VIDIOC_G_PARM 和VIDIOC_S_PARM ioctl()系統調用會調整一些read(),write()專用的參數,其中一些更加普便。它看起來像是一個設置沒有明顯歸屬的雜項的調用。我們在這里就了解一下,雖然有些參數同時會影響流輸入輸出的參數。

支持這些調用的Video4Linux2驅動提供如下兩個方法:

int (*vidioc_g_parm) (struct file *file, void *private_data,
                              struct v4l2_streamparm *parms);
    int (*vidioc_s_parm) (struct file *file, void *private_data,
                          struct v4l2_streamparm *parms);
v4l2_streamparm結構包含下面的聯合,這一系列文章的讀者到現在應該對它們己經很熟悉了。

struct v4l2_streamparm
    {
        enum v4l2_buf_type type;
        union
        {
                struct v4l2_captureparm        capture;
                struct v4l2_outputparm        output;
                __u8 raw_data[200];
        } parm;
    };
type字段描述的是在涉及的操作的類型。對於視頻捕獲設備,應該為V4L2_BUF_TYPE_VIDEO_CAPTURE。對於輸出設備應該為 V4L2_BUF_TYPE_VIDEO_OUTPUT。它的值也可以是V4L2_BUF_TYPE_PRIVATE,在這種情況下,raw_data字段用來傳遞一些私有的,不可移植的,甚至是不鼓勵的數據給內核 。

對於捕獲設備而言,parm.capture字段是要關注的內容,這個結構體如下:

struct v4l2_captureparm
    {
        __u32                   capability;
        __u32                   capturemode;
        struct v4l2_fract  timeperframe;
        __u32                   extendedmode;
        __u32              readbuffers;
        __u32                   reserved[4];
    };
capability字段是一組功能標簽。目前為止已經定義的只有一個V4L2_CAP_TIMEPERFRAME,它代表可以改變幀頻率。 capturemode也是一個只定義了一個標簽的字段:V4L2_MODE_HIGHQUALITY,這個標簽意在使硬件在高清模式下工作,實現單幀的捕獲。這個模式可以做出任何的犧牲(包括支持的格式,曝光時間等),以達到設備可以處理的最佳圖片質量。

timeperframe字段用於指定想要使用的幀頻率,它又是一個結構體:

    struct v4l2_fract {
        __u32   numerator;
        __u32   denominator;
    };
numerator 和denominator所描述的系數給出的是成功的幀之間的時間間隔。另一個驅動相關的字段是:extendedmode,它在API中沒有明確的意義。readbuffers字段是read()操作被調用時內核應為輸入的幀准備的緩沖區數量。

對於輸出設備,其結構體如下:

struct v4l2_outputparm
    {
        __u32                   capability;
        __u32                   outputmode;
        struct v4l2_fract  timeperframe;
        __u32                   extendedmode;
        __u32              writebuffers;
        __u32                   reserved[4];
    };
capability, timeperframe, 和 extendedmode字段與捕獲設備中的意義相同。outputmode 和writebuffers與capturemode 和readbuffers對應相同。

當應用想要查詢現在的參數時,它會發出一個VIDIOC_G_PARM調用,因而調用驅動的vidioc_g_parm()方法。驅動應該提供現在的設置,不用的話確保將extendedmode設為0,並且把reserved字段永遠設為0.

設置參數將調用vidioc_s_parm()。在這種情況下,驅動將參數設為與應用所提供的參數盡可能近的值,並調整v4l2_streamparm結構體以反應現行使用的值 。例如,應用可以會請求一個比硬件所能提供的更高的幀頻率,在這種情況下,幀頻率會設為最高,並把imeperframe設為這個最高的幀頻率。

如果應用提供timeperframe為0,the driver should program the nominal frame rate associated with the current video norm.(這句不懂)。如果readbuffers 或writebuffers是0,驅動應返回現行設置而不是刪除緩沖區。

到現在為止,我們已經可以寫一個支持read()和write()方式幀傳輸的簡單的驅動了。然而多數正式的應用要使用流輸入輸出方式:流的方式使高性能變得更簡單;幀可以打包帶上附加信息如幀序號,請繼續關注本系列的下篇文章,我們將討論如何在視頻驅動中實現流API.
v4l2驅動編寫篇第六B--流輸入輸出
在本系列文章的上一期中,我們討論了如何通過read()和write()的方式實現視頻幀的傳輸,這樣的實現可以完成基本的工作,卻並不是普便上用來實現視頻輸入輸出大家偏愛的方法。為了實現最高的性能和最好的信息傳輸,視頻驅動應該支持V4L2 流輸入輸出。

使用read()和write()方法,每一幀都要通過I/O操作在用戶和內核空間之間拷貝數據。然而,當使用流輸入輸出的方式時,這種情況就不會發生。替代的方案是用戶與內核空間之間交換緩沖區的指針,這些緩沖區將被映射到應用的地址空間,這也就使零幀復制數成為可能。有兩種流輸入輸出緩沖區:

    * 內存映謝緩沖區(memory-mapped buffers) (typeV4L2_MEMORY_MMAP) 是在內核空間開辟的;應用通過themmap()系統調用將其映謝到地址空間。這些緩沖區可以是大而相臨DMA緩沖區,通過vmalloc()創建的虛擬緩沖區,或者(如果硬件支持的話)直接在設備的輸入輸出存儲器中開辟的。
    * 用戶空間緩沖區 (V4L2_MEMORY_USERPTR) 是在用戶空間的應用中開辟的.很明顯,在這種情況下,是不需要mmap()調用的,但驅動在有效地支持用戶空間緩沖區上的工作將會更難一些。

注意:驅動支持流輸入輸出的方式並非必需,即便做了實現,驅動也不必兩種緩沖區類型都做處理。一個靈活的驅動可以支持更多的應用。在實際應用中,似乎多數應用都是使用內存映射緩沖區的。同時使用兩種緩沖區是不可能的。

現在,我們將要探索一下支持流輸入輸出的眾多而邋遢的細節。任何Video4Linux2驅動的作者都要了解這部分API。然而值得指出的是,有一個更高層次的API,它能夠幫助驅動作者寫流驅動。當底層設備可以支持分散/聚集I/O的時候,這一層(稱為video-buf)可以使事情變得容易。關於 video-buf API我們將在將來的某期討論。

支持流輸入輸出的驅動應該通知應用這一事實,方法是在vidioc_querycap()方法中設置V4L2_CAP_STREAMING標簽。注意:並沒有辦法來描述支持的是哪一種緩沖區,那是后話。
v4l2_buffer結構體

當流輸入輸出是有效的,幀是以v4l2_buffer的形式在應用和驅動之間傳輸的。這個結構體是一個復雜的--畜生?--,要用很長的時間才能描述完。一個很好的開頭要大家知道一個緩沖區可以有三種基本的狀態:

    * 在驅動的傳入隊列中.如果驅動不用它做什么有用事的話,應用就可以把緩沖區放在這個隊列里。對於一個視頻捕獲設備還講,傳入隊列中的緩沖區是空的,等待驅動向其內填入視頻數據。對於輸入設備來講,這些緩沖區內是要送入設備的幀數據。
    * 在驅動的傳出隊列中.這些緩沖區已經經過驅動的處理,正等待應用來認領。對於捕獲設備而言,傳出緩沖區內是新的幀數據;對出輸出設備而言,這個緩沖區是空的。
    * 不在上述兩個隊列里.在這種狀態時,緩沖區是由用戶空間擁有的,驅動無法訪問。這是應用可以對緩沖區進行操作的唯一的時間。我們稱其為用戶空間狀態。

這些狀態和造成他們之間傳輸的操作都放在一起,在下圖中示出:

實際上的v4l2_buffer結構體如下:

    struct v4l2_buffer
    {
        __u32                        index;
        enum v4l2_buf_type      type;
        __u32                        bytesused;
        __u32                        flags;
        enum v4l2_field                field;
        struct timeval                timestamp;
        struct v4l2_timecode        timecode;
        __u32                        sequence;

        /* memory location */
        enum v4l2_memory        memory;
        union {
                __u32           offset;
                unsigned long   userptr;
        } m;
        __u32                        length;
        __u32                        input;
        __u32                        reserved;
    };

index 字段是鑒別緩沖區的序號;它只在內存映射緩沖區中使用。與其它可以在V4L2接口中枚舉的對相一樣,內存映射緩沖區的index從0開始,依次遞增。 type字段描述的是緩沖區的類型 ,通常是V4L2_BUF_TYPE_VIDEO_CAPTURE 或V4L2_BUF_TYPE_VIDEO_OUTPUT.

緩沖區的大小是論長度給定的,單位為byte.緩沖區中的圖像數據大小可以在bytesused字段中找到。很明顯,bytesused<=length.對於捕獲設備而言,驅動會設置bytesused; 對輸出設備而言,應用必須設置這個字段。

field字段描述的是圖像存在緩沖區的那一個區域。這些區域在這系統文章中的part5a 中可以找到。

timestamp(時間戳)字段,對於輸入設備來說,代表幀捕獲的時間.對輸出設備來說,在沒有到達時間戳所代表的時間前,驅動不可以把幀發送出去;時間戳值為0代表越快越好。驅動會把時間戳設為幀的第一個字節傳送到設備的時間,或者說是驅動所能達到的最接近的時間。timecode 字段可以用來存放時間編碼,對於視頻編輯類的應用是非常有用的。關於時間編碼詳情見表 .

驅動對傳過設備的幀維護了一個遞增的計數; 每一幀傳送時,它都會在sequence字段中存入現行序號。對於輸入設備來講,應用可以觀察這一字段來檢測幀。

memory 字段表示的是緩沖是內存映射的還是用戶空間的。對於內存映謝的緩沖區,m.offset 描述的是緩沖區的位置. 規范將它描述為“從設備存儲器開發的緩沖區偏移”,但其實質卻是一個 magic cookie,應用可以將其傳給mmap(),以確定那一個緩沖區被映射了。然而對於用戶空間緩沖區而言,m.userptr是緩沖區的用戶空間地址。

input 字段可以用來快速切換捕獲設備的輸入 – 當然,這要得設備支持幀與幀音的快速切換才來。reserved字段應置0.

最后,還有幾個標簽定義:

    * V4L2_BUF_FLAG_MAPPED 暗示緩沖區己映射到用戶空間。它只應用於內存映射緩沖區。

    * V4L2_BUF_FLAG_QUEUED: the buffer is in the driver’s incoming queue.
    * V4L2_BUF_FLAG_DONE: 緩沖區在驅動的傳出隊列.
    * V4L2_BUF_FLAG_KEYFRAME: 緩沖區包含一個關鍵幀,它在壓縮流中是非常有用的.
    * V4L2_BUF_FLAG_PFRAME 和V4L2_BUF_FLAG_BFRAME 也是應用於壓縮流中;他們代表的是預測的或者說是差分的幀.
    * V4L2_BUF_FLAG_TIMECODE: timecode 字段有效.
    * V4L2_BUF_FLAG_INPUT: input字段有效.

緩沖區設定

一旦流應用已經完成了基本的設置,它將轉去執行組織I/O緩沖區的任務。第一步就是使用VIDIOC_REQBUFS ioctl()來建立一組緩沖區,它將由V4L2轉換成對驅動的vidioc_reqbufs()方法的調用。

    int (*vidioc_reqbufs) (struct file *file, void *private_data,
                           struct v4l2_requestbuffers *req);

我們要關注的所以內容都在v4l2_requestbuffers結構體中,如下所示:

    struct v4l2_requestbuffers
    {
        __u32                        count;
        enum v4l2_buf_type      type;
        enum v4l2_memory        memory;
        __u32                        reserved[2];
    };

type 字段描述的是完成的I/O操作的類型。通常它的值要么是視頻獲得設備的V4L2_BUF_TYPE_VIDEO_CAPTURE ,要么是輸出設備的V4L2_BUF_TYPE_VIDEO_OUTPUT.也有其它的類型,但在這里我們不予討論。

如果應用想要使用內存映謝的緩沖區,它將會把memory字段置為V4L2_MEMORY_MMAP,count置為它想要使用的緩沖區的數目。如果驅動不支持內存映射,它就該返回-EINVAL.否則它將在內部開辟請求的緩沖區並返回0.返回之后,應用就會認為緩沖區是存在的,所以任何可以失敗的任務都在在這個階段進行處理 (比如說內存開辟)。

注意:驅動並不一定要開辟與請求的一樣數目的緩沖區.在很多情況下,只有一個緩沖區數的最小值有意義。如果應用請求的比最小值小,它可以能實際須要的要多一些。以筆者的經驗,mplayer要用兩個緩沖區,如果用戶空間速度慢下來的話,這將很容易超支(因而丟失幀)。通過強制一個大一點的最小緩沖區數(通過模塊能數可以調整),cafe_ccic驅動可以使流輸入輸出通道更加強壯。count 字段應設為方法返回前實際開辟的緩沖區數。

應用可以通設置count字段為0的方式來釋放掉所有已存在的緩沖區。在這種情況下,驅動必須在釋放緩沖前停止所有的DMA操作,否則會發生非常嚴重的事情。如果緩沖區已映射到用戶空間,則釋放緩沖區是不可能的。

相反,如果用的是用戶空間緩沖區,則有意義的字段只有緩沖區的type和只有V4L2_MEMORY_USERPTR這個值可用的memory 字段。應用不需要指定它想用的緩沖區的數目。因為內存是在用戶空間開辟的,驅動無須操心。如果驅動支持用戶空間緩沖區,它只須注意應用會使用這一特性,返回0就可以了,否則通常的-EINVAL 返回值會被調用到.

VIDIOC_REQBUFS 命令是應用得知驅動支持的流輸入輸出緩沖區類型的唯一方法。
將緩沖區映射到用戶空間

如果使用了用戶空間,在應用向傳入隊列放置緩沖區之前,驅動看不到任何緩沖區相關的調用。內存映射緩沖區需要更多的設置。應用通常會查看每一個開辟了的緩沖區,並將其映射到地址空間。第一站是VIDIOC_QUERYBUF命令,它將轉換成驅動中的vidioc_querybuf() 方法:

    int (*vidioc_querybuf)(struct file *file, void *private_data,
                           struct v4l2_buffer *buf);

進入這個方法時,buf 字段中要設置的字段有type (在緩沖區開辟時,它將被檢查是否與給定的類型相同)和index,它們可以確定一個特定的緩沖區。驅動要保證index有意義,並添充buf中的其余字段。通常來說,驅動內部存儲着一個v4l2_buffer結構體的數組, 所以vidioc_querybuf()方法的核心只是一個結構體的賦值。

應用訪問內存映射緩沖區的唯一方法就是將其映射到它們的地址空間,所以vidioc_querybuf() 調用后面通常會跟着一個驅動的mmap()方法 -這個方法,大家要記住,是存儲在相關設備的video_device結構體中的fops字段中的。 設備如何處理mmap() 將依賴於內核中緩沖區是如何設置的。如果緩沖區可以在remap_pfn_range() 或remap_vmalloc_range()之前映射,那就應該在這個時間來做.對於內核空間的緩沖區,頁也可以在頁錯誤時通過常規的使用 nopage()方法的方式單獨被映射,對於需要的人來說,在Linux Device Drivers 可以找到一個關於handlingmmap()的一個很好的討論。

mmap() 被調用時,傳遞的VMA結構應該含有vm_pgoff字段中的某個緩沖區的地址-當然是經過PAGE_SHIFT右移過的。特別是,它應該是你的驅動對於 VIDIOC_QUERYBUF調用返回值的楄移。請您遍歷緩沖區列表,並確保傳入地址匹配其中之一。視頻驅動程序不應該是一個可以讓惡意程序映射內存的任意區域手段。

你所提供的偏移值可幾乎所有的東西.有些驅動只是返回(index<<PAGE_SHIFT),意思是說傳入的vm_pgoff字段應該正好是緩沖區索引。有一件事你不可以做的就是把緩沖區的內核實際地址存儲到offset字段,把內核地址泄露給用戶空間永遠也不是一個好注意。

當用戶空間映射緩沖區時,驅動應該在相關的v4l2_buffer結構體中調置V4L2_BUF_FLAG_MAPPED標簽.它也必須設定open() 和close() VMA 操作,這樣它才可以跟蹤映謝了緩沖區的進程數。只要緩沖區在哪里映射了,它就不可以在內核里釋放掉。如果一個或多個緩沖區的映謝計算降為0,驅動就應該停止正在進行的輸入輸出,因為沒有進程要用它。
流輸入輸出

到現為止,我們己經看了很多設置,卻沒有傳輸過一幀的數據,我們離這步己經很近了,但在些之前還有一個步驟要做。當應用通過 VIDIOC_REQBUFS獲得了緩沖區后,那個緩沖區都處於用戶空間狀態。如果他們是用戶空間緩沖區,他們甚至還並不真的存在。在應用可以開始流的輸入輸出之前,它必須至少將一個緩沖區放到驅動傳入隊列,對出輸出設備,那些緩沖區當然還要先添完有效的幀數據。

要把一個緩沖區加入隊列,應用首先要發出一個VIDIOC_QBUF ioctl()調用,這個調用V4L2會映射為對驅動的vidioc_qbuf()方法的調用。

    int (*vidioc_qbuf) (struct file *file, void *private_data,
                        struct v4l2_buffer *buf);

對於內存映射緩沖而言,還是那樣,只有buf的type和 index字段有效. 驅動只能進行一些明顯的檢查(type 和index 有意義,緩沖區還沒有在驅動的隊列里,緩沖區己映射等。),把緩沖區放在傳入隊列里 (設置V4L2_BUF_FLAG_QUEUED 標簽),並返回.

在這點上,用戶空間緩沖區可能會更加復雜,因為驅動可能從來沒看過緩沖區的樣子。使用這個方法時,允許應用在每次向隊列傳入緩沖區時,傳遞不同的地址,所以驅動不能提前做任何的設置。如果你的驅動通過內核空間緩沖區將幀送回,它只須記錄一下應用提供的用戶空間地址就可以了。然而如果你正嘗試將通過 DMA直接將數據傳送到用戶空間,那將會非常的具有挑戰性。

要想把數據直接傳送到用戶空間,驅必須先fault in緩沖區的所以的頁,並將其鎖定。get_user_pages() 可以做這件事.注意這個函數會在開辟很大的內存空間和硬盤I/O-它可能會卡住很長時間。你得注意要保證重要的驅動功能不能在 get_user_pages()時停止,因為它可能停止很長時間等待許多視頻幀通過。

下面就是要告訴設備把圖像數據傳到用戶空間緩沖區(或是相反的方向)了。緩沖區在物理上不是相臨的,相反,它會被分散成很多很多單獨的4096字節的頁(在大部分架構上如此)。很明顯,設備得可以實現分散/聚集DMA操作才行。如果設備立即傳輸一個完整的視頻幀,它就需要接受一個包含很多頁的分散列表(scatterlist)。一個 16位格式的VGA解決方案的圖像需要150個頁,隨着圖像大小的增加,分散列表的大小也會增加.V4L2規范說:

    如果硬件需要,驅動要與物理內存交換內存頁,以產生相臨的內存區。這對內核子系統的虛擬內存中的應用來說是透明的。

然而,筆者卻不推薦驅動作者嘗試這種深層的虛擬內存技巧。有一個更有前途的方法就是要求用戶空間緩沖區分配成大的tlb頁,但是現在的驅動不那么做。

如果你的設備轉輸的是小圖像(如USB攝像頭),直接從DMA到用戶空間的設定就簡單些。在任何情況下,不得不支持到用戶空間緩沖區的直接I/O 時,驅動作者都應該(1)確定的確值得這么大的麻煩,因為應用更趨向於使用內存映射緩沖區。(2)使用video_buf屋,它可以幫你解決一些痛苦的難題 。

一旦流輸入輸出開始,驅動就要從它的傳入隊列里抓取緩沖區,讓設備更快地實現轉送請求,然后把緩沖區移動到傳出隊列。轉輸開始時,緩沖區標簽也要相應調整。像序號和時間戳這樣的字段必需在這個時候添充。最后,應用會在傳出隊列中認領緩沖區,讓它變回為用戶空間狀態。這是VIDIOC_DQBUF的工作,它最終變為如下調用:

    int (*vidioc_dqbuf) (struct file *file, void *private_data,
                         struct v4l2_buffer *buf);

這里,驅動會從傳出隊列中移除第一個緩沖區,把相關的信息存入buf.通常,傳出隊列是空的,這個調用會處於阻塞狀態直到有緩沖區可用。然而V4L2是用來處理非阻塞I/O的,所以如果視頻設備是以O_NONBLOCK方式打開的,在隊列為空的情況下驅動就該返回-EAGAIN .當然,這個要求也暗示驅動必須為流I/O支持poll();

剩下最后的一個步驟實際上就是告訴設備開始流輸入輸出操作。這個任務的 Video4Linux2驅動方法是:

    int (*vidioc_streamon) (struct file *file, void *private_data,
                            enum v4l2_buf_type type);
    int (*vidioc_streamoff)(struct file *file, void *private_data,
                                enum v4l2_buf_type type);

對vidioc_streamon()的調用應該在檢查類型有意義之后讓設備開始工作。如查需要的話,驅動可以請求等傳入隊列中有一定數目的緩沖區后再開始流的轉輸.

當應用關閉時,它應發出一個對vidioc_streamoff()的調用,這個調用要停止設備。驅動還應從傳入還傳出隊列是移除所有的緩沖區,使它們都處於用戶空間狀態。當然,驅動必須准備好,應用可能在沒有停流轉輸的情況下關閉設備。

v4l2驅動編寫篇第七--控制方法
剛剛完成了這一系列文章的第六部分,我們現在知道如何設置視頻設備,並來回傳輸幀了。然而,有一個眾所周知的事實,那就是用戶永遠也不會滿意,不會滿足於能從攝像頭上看到視頻,他們馬上就會問我可不可以調參數啊?像亮度、對比度等等。這些參數可以視頻應用中調整,有時也的確會這樣做,但是當硬件支持時,在硬件中進行調整有其優勢。比如說亮度調整,如果不這樣做的話,可能會丟失動態范圍,但是基於硬件的調整可以完整保持傳感器可以傳遞的動態范圍。很明顯,基於硬件的調整也可以讓主機的處理器壓力輕些。

現代的硬件中通常都可以在運行的時候調整很多參數。然而現在,在不同設備之間這些參數差別很大。簡單的亮度調整可以直觀地設置一個寄存器,也可以需要處理一個沒有頭緒的矩陣變換。最好是能盡可能多的把諸多細節對應用隱藏,但能隱藏到什么程序卻受到很多限制。一個過於抽象的接口會使硬件的控制無法發揮其極限。

V4L2的控制接口試圖使事情盡可能地簡單,同時還能完全發揮硬件的功能。它開始於定義一個標准控制名字的集合,包括 V4L2_CID_BRIGHTNESS,V4L2_CID_CONTRAST,V4L2_CID_SATURATION,還有許多其他的。對於白平衡、水平,垂直鏡像等特性,還提供了一些布爾型的控制。定義的控制ID值的完整列表請見the V4L2 API spec 。還有一個驅動程序特定的控制,但這些,顯然,一般只能由專用的應用程序使用。私有的控制從V4L2_CID_PRIVATE_BASE開始往后都是。

一種典型的做法,V4L2 API提供一種機制可以讓應用可以枚舉可用的控制操作。為此,他們要發出最終要實現為驅動中的vidioc_queryctrl()方法的ioctl()調用。

   int (*vidioc_queryctrl)(struct file *file, void *private_data,
                            struct v4l2_queryctrl *qc);

驅動通常會用所關心的控制信息來添充qc結構體,或是當控制操作不支持時返回EINVAL,這個結構體有很多個字段:

    struct v4l2_queryctrl
    {
        __u32                     id;
        enum v4l2_ctrl_type  type;
        __u8                     name[32];
        __s32                     minimum;
        __s32                     maximum;
        __s32                     step;
        __s32                     default_value;
        __u32                flags;
        __u32                     reserved[2];
    };

被查詢的控制操作將會通過id傳送。作為一個特殊的情況,應用可以通過設定V4L2_CTRL_FLAG_NEXT_CTRL位的方式傳遞控制id.當這種情況發生時,驅動會返回關於下一個所支持的控制id的信息,這比應用給出的ID要高。無論在何種情況下,id都應設為實際上被描述的控制操作的id.

其他所有字段都由驅動設定,用來描述所選的控制操作。控制的數據類型在type字段中給定。這可以是V4L2_CTRL_TYPE_INTEGER、 V4L2_CTRL_TYPE_BOOLEAN、V4L2_CTRL_TYPE_MENU (針對一組固定的擇項) 或V4L2_CTRL_TYPE_BUTTON (針對一些設定時會忽略任何給出的值的控制操作).name字段用來描述控制操作;它可以在展現給用戶的應用接口中使用。 對於整型的控制來說(僅針對這種控制),minimum和maximum 描述的是控制所實現的值的范圍,step 給出的是此范圍下的粒度大小。default_value顧名思義就是默認值 – 僅管他只對整型,布爾型和菜單控制適用。驅動只應在初始化時將控制參數設為默認。至於其它設備參數,他們應該從open()到close()保持不變。結果,default_value 很可能不是現在的控制參數值。

不可避免地,還有一組值進一步描述控制操作。V4L2_CTRL_FLAG_DISABLED 意為控制操作不可用,就用應該忽略它。V4L2_CTRL_FLAG_GRABBED 意為控制暫進不可變,可能會是因為另一個應用正在使用它。V4L2_CTRL_FLAG_READ_ONLY 意為可以查看,但不可以改變的控制操作。V4L2_CTRL_FLAG_UPDATE意為調整這個參數可以會對其他控制操作造成影響。 V4L2_CTRL_FLAG_INACTIVE 意為與當前設備配置無關的控制操作。V4L2_CTRL_FLAG_SLIDER 意在暗示應用在表現這個操作的時候可以使用類似於滾動條的接口。

應用可以只是查詢幾個特別編程過的控制操作,或者他們也想枚舉整個集合。對后而來講,他們會從開始V4L2_CID_BASE 至V4L2_CID_LASTP1結束,過程中可能會用到V4L2_CTRL_FLAG_NEXT_CTRL標簽.對於菜單型的諸多控制操作 (type=V4L2_CTRL_TYPE_MENU)而言, 應用很可能想要枚舉可能的值,相關的回調函數是:

int (*vidioc_querymenu)(struct file *file, void *private_data,
                            struct v4l2_querymenu *qm);

v4l2_querymenu 結構體如下:

    struct v4l2_querymenu
    {
        __u32                id;
        __u32                index;
        __u8                name[32];
        __u32                reserved;
    };

在輸入中,id 是相關的菜單控制操作的ID值,index 某特定菜單ID值的索引值.索引值從0開始,依次遞增到vidioc_queryctrl()返回的最大值.驅動會填充菜單項的name字段。reserved字段恆設為0.

一旦應用知道了可用的控制操作,它就很可以開始查詢並改變其值。這種情況下相關的結構體是:

    struct v4l2_control
    {
        __u32 id;
        __s32 value;
    };

要查詢某一給定控制操作,應用應將id字段設為對應的控制的ID,並發出一個調用,這個調最終實現為:

    int (*vidioc_g_ctrl)(struct file *file, void *private_data,
                             struct v4l2_control *ctrl);

驅動應將值設為當前控制的設定,還要保證它知道這個特定的控制操作並在應用試圖查詢不存在的控制操作時返回EINVAL,試圖訪問按鍵控制時也應返回EINVAL.

一個試圖改變控制操作的請求實現為:

    int (*vidioc_s_ctrl)(struct file *file, void *private_data,
                         struct v4l2_control *ctrl);

驅動應該驗證id,保證其值在允許的區間。如果一切都沒有問題的話,就將新值寫入硬件。

最后, 值得注意的是,還有一個單獨的擴展控制接口 也為V4L2所支持.這個API是一組相當復雜的控制操作。實際上,它的主要應用就是 MPEG編解碼的參數。擴展控制可以分門歸類, 而且支持64位整型值。其接口與常規的控制接口類似。詳見API規范。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM