UVC大概是我看過比較複雜的協定。基本上它複雜不是協定本身,而是裡面一堆[用語]非常的不人性化,導致看的人超痛苦。
基本上UVC跟CDC-ACM類似,它一定要支援兩個Interface,一個用來控制用,一個用來傳資料。控制用的Interface叫做Video Control Interface,另一個叫Video Streaming Interface。這兩個Interface得用IAD把他們接合在一起。
Video Control Interface,顧名思義,就是拿來控制用的,它裡面可以包含幾種元件(我亂翻的):
- Input Terminal:基本上是描述這個裝置的輸入端,以及它支援的功能。
- Output Terminal:基本上是描述這個裝置的輸出端,以及它支援的功能。
- Camera Terminal:基本上是描述這個裝置的相機功能,像是Focus或Zoom等等的功能。
- Select Unit:簡而言之就是MUX。
- Processing Unit:基本上是描述這個裝置的影像處理功能,像是Hue或Brightness等等的功能。
- Extension Unit:描述這個裝置支援的延伸性功能,可有可無。
請注意,每個元件都需要一個獨一無二的ID,並請他們要構成一個Topology(Topology真的不知道怎麼翻)。下圖是某個範例:
這個範例說明某個裝置的Topology,它有兩個輸入,可以用Select Unit選擇其中一個輸入,輸入的資料可以透過Processing Unit做一些處理,然後輸出到Output Terminal。
至於如何把你的裝置的Topology告訴HOST?當然是透過Descriptor啦。每個元件都有對應的Descriptor,基本上只要照規格的範例抄就可以了。
Video Streaming Interface主要用來控制資料的傳輸以及資料的格式。
先來講資料傳輸。UVC支援兩種資料傳輸模式,第一個是Isochronous模式,第二個是bulk模式。這邊不講bulk模式,因為市面上99.9999%的UVC裝置都是用Isochronous模式。
Isochronous模式有個特點,就是必須使用Alternate Setting,如果你忘了Alternate Setting的話,記得再去瞄一下USB2.0規格。基本上只要宣告Alternate Setting 0跟1就可以了。其中Alternate Setting 0是不包含任何Endpoint宣告的,而Alternate Setting 1則會包含一個Isochronous Endpoint宣告。當HOST想要接收影像資料時,會用Set Interface去選擇Alternate Setting 1,而這時DEVICE就得開始輸出資料。相同地,Alternate Setting 0就是不要傳資料。
資料的格式部分,因為UVC目的是支援大部分的裝置,所以裝置端必須提供它支援的格式。資料格式分兩種,第一種是Video Format,其下又分Video Frame。這很好理解,基本上你得先讓HOST選擇影像格式,才能再選擇它的解析度、bitrate跟frame rate。Video Format對應的就是影像格式,而Video Frame則是對應到解析度、bitrate跟frame rate。這樣做的好處是每個裝置可以支援多種影像格式,像是RGB或 MJPEG。
題外話,UVC要支援MJPEG或H264的原因很簡單,因為傳統的RGB格式真的佔用太多USB頻寬了,加上現在沒有HD賣不動,用RGB格式根本沒辦法達到720p30。1280*720*30*3 = 82,944,000 bytes,直接爆掉USB頻寬,所以你會看到有些Webcam號稱支援720p或1080p RGB格式,但是它的frame rate是可憐的1或5,這有意義嗎?
每個Video Format有對應的規格要K,這邊就不介紹了,基本上UVC規格都有給範例,照抄就是了。至於要怎麼跟HOST講這些資訊,當然還是Descriptor啦。他們對應到的分別是Class-specific VS Format Descriptor跟Class-specific VS Frame Descriptor。
整個UVC descriptor的結構如下:
這個結構很重要,基本上所有USB class規格都會提供類似的結構圖,了解整個結構是快速理解整個通訊協定的關鍵。
接下來是Class Request的部分。UVC的Class Request有點多,而且它牽涉到你的裝置所宣告的內容。每個在Video Control Interface宣告的元件都會有對應的Class Request。在看這些Class Request之前,得先搞清楚它的格式,不然會看不懂。下面是Set Request的格式:
這個比較簡單,Entity ID就是每個元件的ID。
下面是Get Request的格式:
有沒有看到每個元件可以支援這麼多bRequest?其實沒有很難,會這樣子的原因是,還是UVC是個通用型的協定的關係。所以它基本上不知道每個控制項可以支援的範圍,它只能訂這類泛用的bRequest,然後讓每個裝置提供不同的數值。舉例來說,HOST可以透過GET_DEF取得Hue控制項的預設值、透過GET_MAX跟GET_MIN取得Hue控制項的支援範圍,可以透過GET_CUR取得Hue控制項的現在值。
下面是Hue Control(Processing Unit)的Class Request:
Class Request還分Video Control Interface跟Video Streaming Interface。Video Control Interface基本上沒有甚麼大困難,Video Streaming Interface就有點複雜了。
Video Streaming Interface只需要支援兩種Class Request,一個是Video Probe,另外一個是Video Commit。他們是用來讓HOST跟DEVICE協調出一組共通的影像傳輸格式。如前所述,Video Streaming Interface描述這個裝置所支援的影像格式,HOST得透過Video Probe跟DEVICE協調出一組雙方都可以接受的參數,然後透過Video Commit指定這種參數。下面是示意圖:
根據我的經驗,HOST幾乎都會發出符合Descriptor宣告的參數給DEVICE端,所以如果HOST遲遲不發COMMIT_CONTROL,那就表示DEVICE回PROBE_CONTROL(GET_CUR)的內容有誤。我遇過的那次是structure的endian問題造成的。