3.3. 流式I/O (使用者指標)

當透過ioctl VIDIOC_QUERYCAP ioctl返回的v4l2_capability結構體的capabilities欄位中的V4L2_CAP_STREAMING標誌被設定時,輸入輸出裝置支援此I/O方法。如果支援特定的使用者指標方法(不僅僅是記憶體對映),則必須透過呼叫ioctl VIDIOC_REQBUFS ioctl並將其記憶體型別設定為V4L2_MEMORY_USERPTR來確定。

這種I/O方法結合了讀/寫和記憶體對映方法的優點。緩衝區(平面)由應用程式自身分配,例如可以駐留在虛擬記憶體或共享記憶體中。只交換資料指標,這些指標和元資訊在v4l2_buffer結構體中傳遞(在多平面API情況下則在v4l2_plane結構體中傳遞)。驅動程式必須透過呼叫ioctl VIDIOC_REQBUFS並指定所需的緩衝區型別來切換到使用者指標I/O模式。事先不分配任何緩衝區(平面),因此它們不會被索引,也不能像已對映的緩衝區那樣透過VIDIOC_QUERYBUF ioctl進行查詢。

3.3.1. 示例:使用使用者指標啟動流式I/O

struct v4l2_requestbuffers reqbuf;

memset (&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_USERPTR;

if (ioctl (fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
    if (errno == EINVAL)
        printf ("Video capturing or user pointer streaming is not supported\\n");
    else
        perror ("VIDIOC_REQBUFS");

    exit (EXIT_FAILURE);
}

緩衝區(平面)的地址和大小透過VIDIOC_QBUF ioctl即時傳遞。儘管緩衝區通常是迴圈使用的,但應用程式可以在每次VIDIOC_QBUF呼叫時傳遞不同的地址和大小。如果硬體要求,驅動程式會在物理記憶體中交換記憶體頁以建立連續的記憶體區域。這在核心的虛擬記憶體子系統中對應用程式是透明的。當緩衝區頁面被換出到磁碟時,它們會被重新載入並最終鎖定在物理記憶體中以供DMA使用。[1]

已填充或已顯示的緩衝區透過VIDIOC_DQBUF ioctl出隊。驅動程式可以在DMA完成到此ioctl呼叫之間的任何時間解鎖記憶體頁。當呼叫VIDIOC_STREAMOFFioctl VIDIOC_REQBUFS或裝置關閉時,記憶體也會被解鎖。應用程式必須注意不要在未出隊的情況下釋放緩衝區。首先,緩衝區會保持更長時間的鎖定狀態,浪費物理記憶體。其次,當記憶體返回到應用程式的空閒列表並隨後被用於其他目的時,驅動程式將不會收到通知,這可能導致已請求的DMA完成並覆蓋有價值的資料。

對於捕獲應用程式,通常的做法是入隊一些空緩衝區,然後開始捕獲並進入讀取迴圈。在此,應用程式會等待直到可以出隊一個已填充的緩衝區,並在資料不再需要時將其重新入隊。輸出應用程式填充併入隊緩衝區,當累積了足夠的緩衝區時,輸出就開始了。在寫入迴圈中,當應用程式用完空閒緩衝區時,它必須等待直到一個空緩衝區可以出隊並被重用。存在兩種方法來暫停應用程式的執行,直到一個或多個緩衝區可以出隊。預設情況下,當出隊佇列中沒有緩衝區時,VIDIOC_DQBUF 會阻塞。當O_NONBLOCK標誌傳遞給open()函式時,如果沒有可用緩衝區,VIDIOC_DQBUF 會立即返回EAGAIN錯誤程式碼。select()poll() 函式始終可用。

要啟動和停止捕獲或輸出應用程式,請呼叫VIDIOC_STREAMONVIDIOC_STREAMOFF ioctl。

注意

VIDIOC_STREAMOFF會從兩個佇列中移除所有緩衝區,並作為副作用解鎖所有緩衝區。由於在多工系統中沒有“立即”執行任何操作的概念,如果應用程式需要與另一個事件同步,它應該檢查捕獲或輸出緩衝區的v4l2_buffer結構體的timestamp

實現使用者指標I/O的驅動程式必須支援VIDIOC_REQBUFSVIDIOC_QBUFVIDIOC_DQBUFVIDIOC_STREAMONVIDIOC_STREAMOFF ioctl,以及select()poll()函式。[2]