3.5. 緩衝區¶
緩衝區包含應用程式和驅動程式使用一種流式 I/O 方法交換的資料。在多平面 API 中,資料儲存在平面中,而緩衝區結構充當平面的容器。只交換指向緩衝區(平面)的指標,資料本身不復制。這些指標以及諸如時間戳或場奇偶校驗之類的元資訊儲存在結構體 v4l2_buffer 中,它是 ioctl VIDIOC_QUERYBUF、VIDIOC_QBUF 和 VIDIOC_DQBUF ioctl 的引數。在多平面 API 中,結構體 v4l2_buffer 的一些特定於平面的成員,例如每個平面的指標和大小,儲存在結構體 v4l2_plane 中。在這種情況下,結構體 v4l2_buffer 包含一個平面結構陣列。
出隊的影片緩衝區帶有時間戳。驅動程式決定在幀的哪個部分以及使用哪個時鐘獲取時間戳。請參閱 緩衝區標誌 中 V4L2_BUF_FLAG_TIMESTAMP_MASK 和 V4L2_BUF_FLAG_TSTAMP_SRC_MASK 掩碼中的標誌。這些標誌始終有效,並且在整個影片流中對所有緩衝區都是恆定的。這些標誌的更改可能會作為 VIDIOC_S_INPUT 或 VIDIOC_S_OUTPUT 的副作用發生。例如,在 mem-to-mem 裝置上使用的 V4L2_BUF_FLAG_TIMESTAMP_COPY 時間戳型別是此規則的例外:時間戳源標誌從 OUTPUT 影片緩衝區複製到 CAPTURE 影片緩衝區。
3.5.1. 格式、控制元件和緩衝區之間的互動¶
V4L2 公開了影響緩衝區大小或資料在緩衝區中佈局方式的引數。這些引數透過格式和控制元件公開。這種控制元件的一個例子是 V4L2_CID_ROTATE 控制元件,它修改了畫素儲存在緩衝區中的方向,以及當所選格式包含行尾的填充時修改了緩衝區大小。
解釋緩衝區內容所需的一組資訊(例如,畫素格式、行步幅、平鋪方向或旋轉)在本節的其餘部分中統稱為緩衝區佈局。
可以修改緩衝區佈局的控制元件應設定 V4L2_CTRL_FLAG_MODIFY_LAYOUT 標誌。
修改影響緩衝區大小或佈局的格式或控制元件需要停止流。在流活動時嘗試進行此類修改應導致設定格式或控制元件的 ioctl 返回 EBUSY 錯誤程式碼。在這種情況下,驅動程式在呼叫 VIDIOC_QUERYCTRL() 或 VIDIOC_QUERY_EXT_CTRL() 時,也應為流活動時此類控制元件設定 V4L2_CTRL_FLAG_GRABBED 標誌。
注意
VIDIOC_S_SELECTION() ioctl 可以根據硬體(例如,如果裝置不包含縮放器)修改格式以及選擇矩形。類似地,VIDIOC_S_INPUT()、VIDIOC_S_OUTPUT()、VIDIOC_S_STD() 和 VIDIOC_S_DV_TIMINGS() ioctl 也可以修改格式和選擇矩形。當這些 ioctl 導致緩衝區大小或佈局更改時,驅動程式應像處理 VIDIOC_S_FMT() ioctl 中描述的所有情況一樣處理這種情況。
僅影響緩衝區佈局的控制元件可以在停止流時隨時修改。由於它們不影響緩衝區大小,因此無需特殊處理即可將這些控制元件與緩衝區分配同步,並且一旦流停止,V4L2_CTRL_FLAG_GRABBED 標誌將被清除。
影響緩衝區大小的格式和控制元件與緩衝區分配互動。處理此問題的最簡單方法是讓驅動程式始終要求重新分配緩衝區,以便更改這些格式或控制元件。在這種情況下,要執行此類更改,使用者空間應用程式應首先使用 VIDIOC_STREAMOFF() ioctl 停止影片流(如果正在執行),並使用 VIDIOC_REQBUFS() ioctl 釋放所有緩衝區(如果已分配)。釋放所有緩衝區後,將清除控制元件的 V4L2_CTRL_FLAG_GRABBED 標誌。然後可以修改格式或控制元件,然後應重新分配緩衝區並重新啟動流。一個典型的 ioctl 序列是
VIDIOC_STREAMOFF
VIDIOC_REQBUFS(0)
VIDIOC_S_EXT_CTRLS
VIDIOC_S_FMT
VIDIOC_REQBUFS(n)
VIDIOC_QBUF
VIDIOC_STREAMON
第二個 VIDIOC_REQBUFS() 呼叫將考慮新的格式和控制元件值,以計算要分配的緩衝區大小。如果需要,應用程式還可以透過呼叫 VIDIOC_G_FMT() ioctl 來檢索大小。
注意
API 不強制執行上述控制(3.)和格式(4.)更改的順序。格式和控制元件可以根據裝置和用例以不同的順序設定,甚至可以交錯設定。例如,某些控制元件對於不同的畫素格式可能會表現不同,在這種情況下,可能需要首先設定格式。
當需要重新分配時,任何嘗試在分配緩衝區時修改影響緩衝區大小的格式或控制元件都應導致格式或控制元件設定 ioctl 返回 EBUSY 錯誤。任何嘗試排隊對於當前格式或控制元件而言太小的緩衝區都應導致 VIDIOC_QBUF() ioctl 返回 EINVAL 錯誤。
緩衝區重新分配是一項昂貴的操作。為了避免這種成本,鼓勵驅動程式允許在分配緩衝區的情況下更改影響緩衝區大小的格式或控制元件。在這種情況下,修改格式和控制元件的典型 ioctl 序列是
VIDIOC_STREAMOFF
VIDIOC_S_EXT_CTRLS
VIDIOC_S_FMT
VIDIOC_QBUF
VIDIOC_STREAMON
為了使此序列正確執行,排隊的緩衝區需要足夠大,以適應新的格式或控制元件。如果當前排隊的緩衝區對於新格式而言太小,則驅動程式應返回 ENOSPC 錯誤以響應格式更改 (VIDIOC_S_FMT()) 或控制元件更改 (VIDIOC_S_CTRL() 或 VIDIOC_S_EXT_CTRLS())。作為一種簡化,如果當前已排隊任何緩衝區,則允許驅動程式從這些 ioctl 返回 EBUSY 錯誤,而無需檢查排隊緩衝區的大小。
此外,如果正在排隊的緩衝區對於當前格式或控制元件而言太小,則驅動程式應從 VIDIOC_QBUF() ioctl 返回 EINVAL 錯誤。這些要求共同確保排隊的緩衝區始終足夠大,以適應配置的格式和控制元件。
使用者空間應用程式可以透過首先設定所需的控制元件值,然後嘗試所需的格式,來查詢給定格式和控制元件所需的緩衝區大小。VIDIOC_TRY_FMT() ioctl 將返回所需的緩衝區大小。
VIDIOC_S_EXT_CTRLS(x)
VIDIOC_S_EXT_CTRLS(y)
然後可以使用 VIDIOC_CREATE_BUFS() ioctl 根據查詢的大小分配緩衝區(例如,透過分配一組足夠大的緩衝區以適應所有所需的格式和控制元件,或者透過為每個用例分配單獨的一組大小合適的緩衝區)。
-
型別 v4l2_buffer¶
3.5.2. struct v4l2_buffer¶
__u32 |
|
緩衝區的編號,由應用程式設定,但在呼叫 VIDIOC_DQBUF 時除外,此時由驅動程式設定。該欄位的範圍可以從零到使用 ioctl VIDIOC_REQBUFS ioctl(struct |
__u32 |
|
緩衝區的型別,與 struct |
__u32 |
|
緩衝區中資料佔用的位元組數。它取決於協商的資料格式,並且對於壓縮的可變大小資料(如 JPEG 影像)可能會隨每個緩衝區而變化。當 |
__u32 |
|
由應用程式或驅動程式設定的標誌,請參閱 緩衝區標誌。 |
__u32 |
|
指示緩衝區中影像的場序,請參閱 |
struct timeval |
|
對於捕獲流,這是捕獲第一個資料位元組的時間,由相關時鐘 ID 的 |
struct |
|
當 |
__u32 |
|
由驅動程式設定,按順序計數幀(不是場!)。為輸入和輸出裝置設定此欄位。 |
|
在 注意 這可能會計數透過 USB 接收的幀,而無需考慮由於有限的壓縮吞吐量或匯流排頻寬而被遠端硬體丟棄的幀。這些裝置透過不列舉任何影片標準來識別,請參閱 影片標準。 |
||
__u32 |
|
必須由應用程式和/或驅動程式根據所選的 I/O 方法設定此欄位。請參閱 |
union { |
|
|
__u32 |
|
對於單平面 API,並且當 |
unsigned long |
|
對於單平面 API,並且當 |
|
使用多平面 API 時,包含指向 struct |
|
int |
|
對於單平面 API,並且當 |
} |
||
__u32 |
|
單平面 API 的緩衝區大小(不是有效負載),以位元組為單位。這是由驅動程式根據對 ioctl VIDIOC_REQBUFS 和/或 ioctl VIDIOC_CREATE_BUFS 的呼叫設定的。對於多平面 API,應用程式將其設定為 |
__u32 |
|
用於將來擴充套件的佔位符。驅動程式和應用程式必須將其設定為 0。 |
__u32 |
|
要將緩衝區排隊到的請求的檔案描述符。如果設定了標誌
對於除 VIDIOC_QBUF 之外的任何 ioctl,應用程式不應設定 如果裝置不支援請求,則將返回 |
-
型別 v4l2_plane¶
3.5.3. struct v4l2_plane¶
__u32 |
|
平面中資料佔用的位元組數(其有效負載)。當 注意 請注意,實際影像資料從 |
__u32 |
|
平面(而非其有效負載)的大小,以位元組為單位。 該值由驅動程式根據對 ioctl VIDIOC_REQBUFS 和/或 ioctl VIDIOC_CREATE_BUFS 的呼叫進行設定。 |
union { |
|
|
__u32 |
|
當包含的結構體 |
unsigned long |
|
當包含的結構體 |
int |
|
當包含的結構體 |
} |
||
__u32 |
|
平面中影片資料的偏移量(以位元組為單位)。 當 注意 該 data_offset 包含在 |
__u32 |
|
保留供將來使用。 驅動程式和應用程式應將其歸零。 |
-
type v4l2_buf_type¶
3.5.4. enum v4l2_buf_type¶
|
1 |
單平面影片捕獲流的緩衝區,請參閱 影片捕獲介面。 |
|
9 |
多平面影片捕獲流的緩衝區,請參閱 影片捕獲介面。 |
|
2 |
單平面影片輸出流的緩衝區,請參閱 影片輸出介面。 |
|
10 |
多平面影片輸出流的緩衝區,請參閱 影片輸出介面。 |
|
3 |
影片覆蓋的緩衝區,請參閱 影片覆蓋介面。 |
|
4 |
原始 VBI 捕獲流的緩衝區,請參閱 原始 VBI 資料介面。 |
|
5 |
原始 VBI 輸出流的緩衝區,請參閱 原始 VBI 資料介面。 |
|
6 |
切片 VBI 捕獲流的緩衝區,請參閱 切片 VBI 資料介面。 |
|
7 |
切片 VBI 輸出流的緩衝區,請參閱 切片 VBI 資料介面。 |
|
8 |
影片輸出覆蓋 (OSD) 的緩衝區,請參閱 影片輸出覆蓋介面。 |
|
11 |
軟體定義無線電 (SDR) 捕獲流的緩衝區,請參閱 軟體定義無線電介面 (SDR)。 |
|
12 |
軟體定義無線電 (SDR) 輸出流的緩衝區,請參閱 軟體定義無線電介面 (SDR)。 |
|
13 |
元資料捕獲的緩衝區,請參閱 元資料介面。 |
|
14 |
元資料輸出的緩衝區,請參閱 元資料介面。 |
3.5.5. 緩衝區標誌¶
|
0x00000001 |
該緩衝區位於裝置記憶體中,並且已對映到應用程式的地址空間中,有關詳細資訊,請參閱 流式 I/O (記憶體對映)。 呼叫 ioctl VIDIOC_QUERYBUF、ioctl VIDIOC_QBUF, VIDIOC_DQBUF 或 VIDIOC_DQBUF ioctl 時,驅動程式會設定或清除此標誌。 由驅動程式設定。 |
|
0x00000002 |
在內部,驅動程式維護兩個緩衝區佇列,一個傳入佇列和一個傳出佇列。 設定此標誌後,該緩衝區當前位於傳入佇列中。緩衝區填充(捕獲裝置)或顯示(輸出裝置)後,它會自動移至傳出佇列。 呼叫 |
|
0x00000004 |
設定此標誌後,該緩衝區當前位於傳出佇列中,可以從驅動程式中出隊。 呼叫 |
|
0x00000040 |
設定此標誌後,緩衝區已成功出隊,但資料可能已損壞。 這是可恢復的,流式傳輸可以像往常一樣繼續,並且可以正常重複使用該緩衝區。 呼叫 |
|
0x00000080 |
此緩衝區是尚未排隊的請求的一部分。 |
|
0x00000008 |
呼叫 |
|
0x00000010 |
與 |
|
0x00000020 |
與 |
|
0x00000100 |
|
|
0x00000400 |
該緩衝區已準備好進行 I/O,並且可以由應用程式排隊。 呼叫 VIDIOC_QUERYBUF、VIDIOC_PREPARE_BUF、VIDIOC_QBUF 或 VIDIOC_DQBUF ioctl 時,驅動程式會設定或清除此標誌。 |
|
0x00000800 |
不必使此緩衝區的快取失效。 通常,如果 CPU 不會接觸緩衝區中捕獲的資料,則應用程式應使用此標誌,相反,可能會將緩衝區傳遞給支援 DMA 的硬體單元以進行進一步處理或輸出。 除非佇列用於 記憶體對映 流式 I/O 並報告 V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS 功能,否則將忽略此標誌。 |
|
0x00001000 |
不必為此緩衝區清理快取。 通常,如果此緩衝區中的資料不是由 CPU 建立而是由某些支援 DMA 的單元建立的,在這種情況下未使用快取,則應用程式應將此標誌用於輸出緩衝區。 除非佇列用於 記憶體對映 流式 I/O 並報告 V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS 功能,否則將忽略此標誌。 |
|
0x00000200 |
僅當設定了結構體 |
|
0x00100000 |
硬體生成的最後一個緩衝區。 當呼叫 ioctl VIDIOC_QUERYBUF 或 VIDIOC_DQBUF ioctl 時,mem2mem 編解碼器驅動程式會在捕獲佇列上設定此標誌。 由於硬體限制,最後一個緩衝區可能為空。 在這種情況下,無論格式如何,驅動程式都會將 |
|
0x00800000 |
|
|
0x0000e000 |
用於以下時間戳型別的掩碼。 要測試時間戳型別,請透過對緩衝區標誌和時間戳掩碼執行邏輯與運算來遮蔽掉不屬於時間戳型別的位。 |
|
0x00000000 |
未知時間戳型別。 Linux 3.9 之前的驅動程式使用此型別,並且它可能是單調的(請參見下文)或即時的(掛鐘時間)。 單調時鐘在嵌入式系統中很受歡迎,而大多數驅動程式都使用即時時鐘。 兩種型別的時間戳都可透過使用時鐘 ID |
|
0x00002000 |
緩衝區時間戳取自 |
|
0x00004000 |
CAPTURE 緩衝區時間戳取自相應的 OUTPUT 緩衝區。 此標誌僅適用於 mem2mem 裝置。 |
|
0x00070000 |
用於以下時間戳源的掩碼。 時間戳源定義了時間戳相對於幀的獲取時間點。 |
|
0x00000000 |
幀結束。 當接收到幀的最後一個畫素或已傳輸幀的最後一個畫素時,將獲取緩衝區時間戳。 實際上,軟體生成的時間戳通常會在接收或傳輸最後一個畫素之後的一小段時間內從時鐘讀取,具體取決於系統及其中的其他活動。 |
|
0x00010000 |
曝光開始。 緩衝區時間戳是在幀的曝光開始時獲取的。 這僅對 |
3.5.6. enum v4l2_memory¶
|
1 |
該緩衝區用於 記憶體對映 I/O。 |
|
2 |
該緩衝區用於 使用者指標 I/O。 |
|
3 |
[待辦] |
|
4 |
該緩衝區用於 DMA 共享緩衝區 I/O。 |
3.5.7. 時間碼¶
v4l2_buffer_timecode 結構旨在儲存 SMPTE 12M 或類似的時間碼。(timeval 時間戳儲存在結構 v4l2_buffer timestamp 欄位中。)
-
type v4l2_timecode¶
3.5.7.1. struct v4l2_timecode¶
__u32 |
|
時間碼基於的幀速率,請參閱 時間碼型別。 |
__u32 |
|
時間碼標誌,請參閱 時間碼標誌。 |
__u8 |
|
幀計數,0 ... 23/24/29/49/59,具體取決於時間碼的型別。 |
__u8 |
|
秒數,0 ... 59。這是一個二進位制數,而不是 BCD 數字。 |
__u8 |
|
分鐘數,0 ... 59。這是一個二進位制數,而不是 BCD 數字。 |
__u8 |
|
小時數,0 ... 29。這是一個二進位制數,而不是 BCD 數字。 |
__u8 |
|
時間碼中的“使用者組”位。 |
3.5.7.2. 時間碼型別¶
|
1 |
每秒 24 幀,即電影。 |
|
2 |
每秒 25 幀,即 PAL 或 SECAM 影片。 |
|
3 |
每秒 30 幀,即 NTSC 影片。 |
|
4 |
|
|
5 |
3.5.7.3. 時間碼標誌¶
|
0x0001 |
表示 29.97 fps 素材中用於計算幀的“丟幀”語義。 設定後,從每分鐘開始,從計數中省略幀號 0 和 1,但 0、10、20、30、40、50 分鐘除外。 |
|
0x0002 |
“彩色幀”標誌。 |
|
0x000C |
“二進位制組標誌”的欄位掩碼。 |
|
0x0000 |
未指定的格式。 |
|
0x0008 |
8 位 ISO 字元。 |