3.4. 流式 I/O (DMA 緩衝區匯入)¶
DMABUF 框架提供了一種在多個裝置之間共享緩衝區的通用方法。 支援 DMABUF 的裝置驅動程式可以將 DMA 緩衝區作為檔案描述符匯出到使用者空間(稱為匯出者角色),使用先前為不同或相同裝置匯出的檔案描述符從使用者空間匯入 DMA 緩衝區(稱為匯入者角色),或者兩者兼而有之。 本節介紹 V4L2 中的 DMABUF 匯入器角色 API。
有關將 V4L2 緩衝區匯出為 DMABUF 檔案描述符的詳細資訊,請參閱 DMABUF 匯出。
當 VIDIOC_QUERYCAP ioctl 返回的 struct v4l2_capability 的 capabilities 欄位中的 V4L2_CAP_STREAMING 標誌已設定時,輸入和輸出裝置支援流式 I/O 方法。 是否支援透過 DMABUF 檔案描述符匯入 DMA 緩衝區由呼叫 VIDIOC_REQBUFS ioctl 並將記憶體型別設定為 V4L2_MEMORY_DMABUF 來確定。
此 I/O 方法專用於在不同裝置之間共享 DMA 緩衝區,這些裝置可能是 V4L 裝置或其他與影片相關的裝置(例如 DRM)。 緩衝區(平面)由驅動程式代表應用程式分配。 接下來,這些緩衝區使用特定於分配器驅動程式的 API 作為檔案描述符匯出到應用程式。 僅交換此類檔案描述符。 描述符和元資訊在 struct v4l2_buffer 中(或者在多平面 API 的情況下,在 struct v4l2_plane 中)傳遞。 驅動程式必須透過呼叫 VIDIOC_REQBUFS 並設定所需的緩衝區型別來切換到 DMABUF I/O 模式。
3.4.1. 示例:使用 DMABUF 檔案描述符啟動流式 I/O¶
struct v4l2_requestbuffers reqbuf;
memset(&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_DMABUF;
reqbuf.count = 1;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
if (errno == EINVAL)
printf("Video capturing or DMABUF streaming is not supported\\n");
else
perror("VIDIOC_REQBUFS");
exit(EXIT_FAILURE);
}
緩衝區(平面)檔案描述符透過 VIDIOC_QBUF ioctl 動態傳遞。 在多平面緩衝區的情況下,每個平面都可以與不同的 DMABUF 描述符關聯。 儘管緩衝區通常是迴圈的,但應用程式可以在每次 VIDIOC_QBUF 呼叫時傳遞不同的 DMABUF 描述符。
3.4.2. 示例:使用單平面 API 佇列 DMABUF¶
int buffer_queue(int v4lfd, int index, int dmafd)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = index;
buf.m.fd = dmafd;
if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
}
return 0;
}
3.4.3. 示例 3.6. 使用多平面 API 佇列 DMABUF¶
int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes)
{
struct v4l2_buffer buf;
struct v4l2_plane planes[VIDEO_MAX_PLANES];
int i;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = index;
buf.m.planes = planes;
buf.length = n_planes;
memset(&planes, 0, sizeof planes);
for (i = 0; i < n_planes; ++i)
buf.m.planes[i].m.fd = dmafd[i];
if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
}
return 0;
}
捕獲或顯示的緩衝區使用 VIDIOC_DQBUF ioctl 出隊。 驅動程式可以在 DMA 完成和此 ioctl 之間的任何時間解鎖緩衝區。 當呼叫 VIDIOC_STREAMOFF、VIDIOC_REQBUFS 或關閉裝置時,記憶體也會被解鎖。
對於捕獲應用程式,習慣做法是排隊多個空緩衝區,開始捕獲並進入讀取迴圈。 在這裡,應用程式等待直到可以出隊一個填充的緩衝區,並在不再需要資料時重新排隊該緩衝區。 輸出應用程式填充和排隊緩衝區,當堆積足夠的緩衝區時,開始輸出。 在寫入迴圈中,當應用程式用完空閒緩衝區時,它必須等待直到可以出隊和重用空緩衝區。 存在兩種方法來暫停應用程式的執行,直到可以出隊一個或多個緩衝區。 預設情況下,當傳出佇列中沒有緩衝區時,VIDIOC_DQBUF 會阻塞。 當 open() 函式被賦予 O_NONBLOCK 標誌時,當沒有可用的緩衝區時,VIDIOC_DQBUF 立即返回並顯示 EAGAIN 錯誤程式碼。 select() 和 poll() 函式始終可用。
要啟動和停止捕獲或顯示應用程式,請呼叫 VIDIOC_STREAMON 和 VIDIOC_STREAMOFF ioctl。
注意
VIDIOC_STREAMOFF 會從兩個佇列中刪除所有緩衝區,並解鎖所有緩衝區作為副作用。 由於在多工處理系統中沒有“現在”執行任何操作的概念,因此如果應用程式需要與其他事件同步,它應該檢查捕獲或輸出緩衝區的 struct v4l2_buffer timestamp。
實現 DMABUF 匯入 I/O 的驅動程式必須支援 VIDIOC_REQBUFS、VIDIOC_QBUF、VIDIOC_DQBUF、VIDIOC_STREAMON 和 VIDIOC_STREAMOFF ioctl,以及 select() 和 poll() 函式。