← Documents Documentation/userspace-api/media/v4l/mmap.rst GitHub 원문 ↗

Linux 6.18.37 · Userspace API / Media / V4L

Streaming I/O: Memory Mapping

V4L2 MMAP buffer 할당, single/multi-planar 매핑, FIFO queue와 streaming lifecycle을 설명합니다.

Source pathDocumentation/userspace-api/media/v4l/mmap.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

mmap.rst:1-285

MMAP streaming은 데이터를 복사하지 않고 plane별 physical buffer를 주소 공간에 매핑하며 QBUF/DQBUF와 STREAMON/OFF로 두 FIFO를 운용합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
2 .. c:namespace:: V4L
3
4 .. _mmap:
5
6 ******************************
7 Streaming I/O (Memory Mapping)
8 ******************************
9
10 Input and output devices support this I/O method when the
11 ``V4L2_CAP_STREAMING`` flag in the ``capabilities`` field of struct
12 :c:type:`v4l2_capability` returned by the
13 :ref:`VIDIOC_QUERYCAP` ioctl is set. There are two
14 streaming methods, to determine if the memory mapping flavor is
15 supported applications must call the :ref:`VIDIOC_REQBUFS` ioctl
16 with the memory type set to ``V4L2_MEMORY_MMAP``.
17
18 Streaming is an I/O method where only pointers to buffers are exchanged
19 between application and driver, the data itself is not copied. Memory
20 mapping is primarily intended to map buffers in device memory into the
21 application's address space. Device memory can be for example the video
22 memory on a graphics card with a video capture add-on. However, being
23 the most efficient I/O method available for a long time, many other
24 drivers support streaming as well, allocating buffers in DMA-able main
25 memory.
26
27 A driver can support many sets of buffers. Each set is identified by a
28 unique buffer type value. The sets are independent and each set can hold
29 a different type of data. To access different sets at the same time
30 different file descriptors must be used. [#f1]_
31
32 To allocate device buffers applications call the
33 :ref:`VIDIOC_REQBUFS` ioctl with the desired number
34 of buffers and buffer type, for example ``V4L2_BUF_TYPE_VIDEO_CAPTURE``.
35 This ioctl can also be used to change the number of buffers or to free
36 the allocated memory, provided none of the buffers are still mapped.
37
38 Before applications can access the buffers they must map them into their
39 address space with the :c:func:`mmap()` function. The
40 location of the buffers in device memory can be determined with the
41 :ref:`VIDIOC_QUERYBUF` ioctl. In the single-planar
42 API case, the ``m.offset`` and ``length`` returned in a struct
43 :c:type:`v4l2_buffer` are passed as sixth and second
44 parameter to the :c:func:`mmap()` function. When using the
45 multi-planar API, struct :c:type:`v4l2_buffer` contains an
46 array of struct :c:type:`v4l2_plane` structures, each
47 containing its own ``m.offset`` and ``length``. When using the
48 multi-planar API, every plane of every buffer has to be mapped
49 separately, so the number of calls to :c:func:`mmap()` should
50 be equal to number of buffers times number of planes in each buffer. The
51 offset and length values must not be modified. Remember, the buffers are
52 allocated in physical memory, as opposed to virtual memory, which can be
53 swapped out to disk. Applications should free the buffers as soon as
54 possible with the :c:func:`munmap()` function.
55
56 Example: Mapping buffers in the single-planar API
57 =================================================
58
59 .. code-block:: c
60
61 struct v4l2_requestbuffers reqbuf;
62 struct {
63 void *start;
64 size_t length;
65 } *buffers;
66 unsigned int i;
67
68 memset(&reqbuf, 0, sizeof(reqbuf));
69 reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
70 reqbuf.memory = V4L2_MEMORY_MMAP;
71 reqbuf.count = 20;
72
73 if (-1 == ioctl (fd, VIDIOC_REQBUFS, &reqbuf)) {
74 if (errno == EINVAL)
75 printf("Video capturing or mmap-streaming is not supported\\n");
76 else
77 perror("VIDIOC_REQBUFS");
78
79 exit(EXIT_FAILURE);
80 }
81
82 /* We want at least five buffers. */
83
84 if (reqbuf.count < 5) {
85 /* You may need to free the buffers here. */
86 printf("Not enough buffer memory\\n");
87 exit(EXIT_FAILURE);
88 }
89
90 buffers = calloc(reqbuf.count, sizeof(*buffers));
91 assert(buffers != NULL);
92
93 for (i = 0; i < reqbuf.count; i++) {
94 struct v4l2_buffer buffer;
95
96 memset(&buffer, 0, sizeof(buffer));
97 buffer.type = reqbuf.type;
98 buffer.memory = V4L2_MEMORY_MMAP;
99 buffer.index = i;
100
101 if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buffer)) {
102 perror("VIDIOC_QUERYBUF");
103 exit(EXIT_FAILURE);
104 }
105
106 buffers[i].length = buffer.length; /* remember for munmap() */
107
108 buffers[i].start = mmap(NULL, buffer.length,
109 PROT_READ | PROT_WRITE, /* recommended */
110 MAP_SHARED, /* recommended */
111 fd, buffer.m.offset);
112
113 if (MAP_FAILED == buffers[i].start) {
114 /* If you do not exit here you should unmap() and free()
115 the buffers mapped so far. */
116 perror("mmap");
117 exit(EXIT_FAILURE);
118 }
119 }
120
121 /* Cleanup. */
122
123 for (i = 0; i < reqbuf.count; i++)
124 munmap(buffers[i].start, buffers[i].length);
125
126 Example: Mapping buffers in the multi-planar API
127 ================================================
128
129 .. code-block:: c
130
131 struct v4l2_requestbuffers reqbuf;
132 /* Our current format uses 3 planes per buffer */
133 #define FMT_NUM_PLANES = 3
134
135 struct {
136 void *start[FMT_NUM_PLANES];
137 size_t length[FMT_NUM_PLANES];
138 } *buffers;
139 unsigned int i, j;
140
141 memset(&reqbuf, 0, sizeof(reqbuf));
142 reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
143 reqbuf.memory = V4L2_MEMORY_MMAP;
144 reqbuf.count = 20;
145
146 if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
147 if (errno == EINVAL)
148 printf("Video capturing or mmap-streaming is not supported\\n");
149 else
150 perror("VIDIOC_REQBUFS");
151
152 exit(EXIT_FAILURE);
153 }
154
155 /* We want at least five buffers. */
156
157 if (reqbuf.count < 5) {
158 /* You may need to free the buffers here. */
159 printf("Not enough buffer memory\\n");
160 exit(EXIT_FAILURE);
161 }
162
163 buffers = calloc(reqbuf.count, sizeof(*buffers));
164 assert(buffers != NULL);
165
166 for (i = 0; i < reqbuf.count; i++) {
167 struct v4l2_buffer buffer;
168 struct v4l2_plane planes[FMT_NUM_PLANES];
169
170 memset(&buffer, 0, sizeof(buffer));
171 buffer.type = reqbuf.type;
172 buffer.memory = V4L2_MEMORY_MMAP;
173 buffer.index = i;
174 /* length in struct v4l2_buffer in multi-planar API stores the size
175 * of planes array. */
176 buffer.length = FMT_NUM_PLANES;
177 buffer.m.planes = planes;
178
179 if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0) {
180 perror("VIDIOC_QUERYBUF");
181 exit(EXIT_FAILURE);
182 }
183
184 /* Every plane has to be mapped separately */
185 for (j = 0; j < FMT_NUM_PLANES; j++) {
186 buffers[i].length[j] = buffer.m.planes[j].length; /* remember for munmap() */
187
188 buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length,
189 PROT_READ | PROT_WRITE, /* recommended */
190 MAP_SHARED, /* recommended */
191 fd, buffer.m.planes[j].m.mem_offset);
192
193 if (MAP_FAILED == buffers[i].start[j]) {
194 /* If you do not exit here you should unmap() and free()
195 the buffers and planes mapped so far. */
196 perror("mmap");
197 exit(EXIT_FAILURE);
198 }
199 }
200 }
201
202 /* Cleanup. */
203
204 for (i = 0; i < reqbuf.count; i++)
205 for (j = 0; j < FMT_NUM_PLANES; j++)
206 munmap(buffers[i].start[j], buffers[i].length[j]);
207
208 Conceptually streaming drivers maintain two buffer queues, an incoming
209 and an outgoing queue. They separate the synchronous capture or output
210 operation locked to a video clock from the application which is subject
211 to random disk or network delays and preemption by other processes,
212 thereby reducing the probability of data loss. The queues are organized
213 as FIFOs, buffers will be output in the order enqueued in the incoming
214 FIFO, and were captured in the order dequeued from the outgoing FIFO.
215
216 The driver may require a minimum number of buffers enqueued at all times
217 to function, apart of this no limit exists on the number of buffers
218 applications can enqueue in advance, or dequeue and process. They can
219 also enqueue in a different order than buffers have been dequeued, and
220 the driver can *fill* enqueued *empty* buffers in any order. [#f2]_ The
221 index number of a buffer (struct :c:type:`v4l2_buffer`
222 ``index``) plays no role here, it only identifies the buffer.
223
224 Initially all mapped buffers are in dequeued state, inaccessible by the
225 driver. For capturing applications it is customary to first enqueue all
226 mapped buffers, then to start capturing and enter the read loop. Here
227 the application waits until a filled buffer can be dequeued, and
228 re-enqueues the buffer when the data is no longer needed. Output
229 applications fill and enqueue buffers, when enough buffers are stacked
230 up the output is started with :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>`.
231 In the write loop, when the application runs out of free buffers, it
232 must wait until an empty buffer can be dequeued and reused.
233
234 To enqueue and dequeue a buffer applications use the
235 :ref:`VIDIOC_QBUF <VIDIOC_QBUF>` and :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`
236 ioctl. The status of a buffer being mapped, enqueued, full or empty can
237 be determined at any time using the :ref:`VIDIOC_QUERYBUF` ioctl. Two
238 methods exist to suspend execution of the application until one or more
239 buffers can be dequeued. By default :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`
240 blocks when no buffer is in the outgoing queue. When the ``O_NONBLOCK``
241 flag was given to the :c:func:`open()` function,
242 :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` returns immediately with an ``EAGAIN``
243 error code when no buffer is available. The :c:func:`select()`
244 or :c:func:`poll()` functions are always available.
245
246 To start and stop capturing or output applications call the
247 :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` and :ref:`VIDIOC_STREAMOFF
248 <VIDIOC_STREAMON>` ioctl.
249
250 .. note:::ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>`
251 removes all buffers from both queues as a side effect. Since there is
252 no notion of doing anything "now" on a multitasking system, if an
253 application needs to synchronize with another event it should examine
254 the struct ::c:type:`v4l2_buffer` ``timestamp`` of captured
255 or outputted buffers.
256
257 Drivers implementing memory mapping I/O must support the
258 :ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, :ref:`VIDIOC_QUERYBUF
259 <VIDIOC_QUERYBUF>`, :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`, :ref:`VIDIOC_DQBUF
260 <VIDIOC_QBUF>`, :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>`
261 and :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls, the :ref:`mmap()
262 <func-mmap>`, :c:func:`munmap()`, :ref:`select()
263 <func-select>` and :c:func:`poll()` function. [#f3]_
264
265 [capture example]
266
267 .. [#f1]
268 One could use one file descriptor and set the buffer type field
269 accordingly when calling :ref:`VIDIOC_QBUF` etc.,
270 but it makes the :c:func:`select()` function ambiguous. We also
271 like the clean approach of one file descriptor per logical stream.
272 Video overlay for example is also a logical stream, although the CPU
273 is not needed for continuous operation.
274
275 .. [#f2]
276 Random enqueue order permits applications processing images out of
277 order (such as video codecs) to return buffers earlier, reducing the
278 probability of data loss. Random fill order allows drivers to reuse
279 buffers on a LIFO-basis, taking advantage of caches holding
280 scatter-gather lists and the like.
281
282 .. [#f3]
283 At the driver level :c:func:`select()` and :c:func:`poll()` are
284 the same, and :c:func:`select()` is too important to be optional.
285 The rest should be evident.
286

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

MMAP streaming 협상과 plane 매핑

1-55

입출력 장치가 memory-mapped streaming을 지원하려면 `VIDIOC_QUERYCAP`으로 받은 `v4l2_capability.capabilities`에 `V4L2_CAP_STREAMING`이 있어야 합니다. 두 streaming 방식 가운데 MMAP 지원 여부는 `VIDIOC_REQBUFS`의 memory type을 `V4L2_MEMORY_MMAP`으로 설정해 확인합니다.

Streaming은 응용 프로그램과 드라이버가 buffer pointer만 교환하고 데이터를 복사하지 않는 방식입니다. 원래 장치 메모리, 예를 들어 캡처 확장 그래픽 카드의 video memory를 주소 공간에 매핑하려는 목적이지만 효율이 높아 DMA 가능한 main memory를 할당하는 드라이버도 널리 지원합니다.

드라이버는 고유 buffer type으로 식별되는 여러 독립 buffer set을 지원할 수 있고 set마다 데이터 종류가 다를 수 있습니다. 여러 set을 동시에 쓰려면 logical stream마다 별도 file descriptor를 사용합니다.

버퍼 할당과 매핑
항목설명
`VIDIOC_REQBUFS`원하는 buffer 수와 type을 지정해 장치 buffer를 할당합니다. 매핑된 buffer가 없으면 수를 바꾸거나 count=0으로 메모리를 해제할 수도 있습니다.
`VIDIOC_QUERYBUF`single-planar에서는 `v4l2_buffer.m.offset`과 `length`를 얻습니다.
single-planar `mmap()``length`를 두 번째, `m.offset`을 여섯 번째 인자로 그대로 전달합니다.
multi-planar QUERYBUF`v4l2_buffer.m.planes`의 각 `v4l2_plane`에서 `length`와 `m.mem_offset`을 얻습니다.
multi-planar `mmap()`모든 buffer의 모든 plane을 따로 매핑하므로 호출 수는 buffer 수 × buffer당 plane 수입니다.
값 보존드라이버가 반환한 offset과 length를 수정하면 안 됩니다.
해제buffer는 swap 가능한 virtual memory가 아니라 physical memory를 차지하므로 가능한 빨리 `munmap()`합니다.

REQBUFS부터 plane별 mmap까지의 필수 값입니다.

.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
.. c:namespace:: V4L

.. _mmap:

******************************
Streaming I/O (Memory Mapping)
******************************

Input and output devices support this I/O method when the
``V4L2_CAP_STREAMING`` flag in the ``capabilities`` field of struct
:c:type:`v4l2_capability` returned by the
:ref:`VIDIOC_QUERYCAP` ioctl is set. There are two
streaming methods, to determine if the memory mapping flavor is
supported applications must call the :ref:`VIDIOC_REQBUFS` ioctl
with the memory type set to ``V4L2_MEMORY_MMAP``.

Streaming is an I/O method where only pointers to buffers are exchanged
between application and driver, the data itself is not copied. Memory
mapping is primarily intended to map buffers in device memory into the
application's address space. Device memory can be for example the video
memory on a graphics card with a video capture add-on. However, being
the most efficient I/O method available for a long time, many other
drivers support streaming as well, allocating buffers in DMA-able main
memory.

A driver can support many sets of buffers. Each set is identified by a
unique buffer type value. The sets are independent and each set can hold
a different type of data. To access different sets at the same time
different file descriptors must be used. [#f1]_

To allocate device buffers applications call the
:ref:`VIDIOC_REQBUFS` ioctl with the desired number
of buffers and buffer type, for example ``V4L2_BUF_TYPE_VIDEO_CAPTURE``.
This ioctl can also be used to change the number of buffers or to free
the allocated memory, provided none of the buffers are still mapped.

Before applications can access the buffers they must map them into their
address space with the :c:func:`mmap()` function. The
location of the buffers in device memory can be determined with the
:ref:`VIDIOC_QUERYBUF` ioctl. In the single-planar
API case, the ``m.offset`` and ``length`` returned in a struct
:c:type:`v4l2_buffer` are passed as sixth and second
parameter to the :c:func:`mmap()` function. When using the
multi-planar API, struct :c:type:`v4l2_buffer` contains an
array of struct :c:type:`v4l2_plane` structures, each
containing its own ``m.offset`` and ``length``. When using the
multi-planar API, every plane of every buffer has to be mapped
separately, so the number of calls to :c:func:`mmap()` should
be equal to number of buffers times number of planes in each buffer. The
offset and length values must not be modified. Remember, the buffers are
allocated in physical memory, as opposed to virtual memory, which can be
swapped out to disk. Applications should free the buffers as soon as
possible with the :c:func:`munmap()` function.

Single-planar 매핑 예제

56-125

예제는 capture type과 `V4L2_MEMORY_MMAP`, 요청 count 20으로 `VIDIOC_REQBUFS`를 호출합니다. `EINVAL`이면 video capture 또는 mmap streaming 미지원으로 처리하고, 실제 할당 수가 필요한 최소 5개보다 적으면 중단합니다.

할당된 각 index에 `VIDIOC_QUERYBUF`를 호출해 length와 offset을 얻고 `PROT_READ | PROT_WRITE`, `MAP_SHARED`로 매핑합니다. length는 cleanup의 `munmap()`에 쓰도록 저장합니다. 중간 매핑이 실패했는데 프로세스를 끝내지 않는다면 그때까지 매핑한 buffer를 모두 해제하고 배열도 정리해야 합니다.

Single-planar 예제
REQBUFS로 VIDEO_CAPTURE MMAP buffer 20개 요청반환 count가 최소 5개인지 확인buffer descriptor 배열 할당각 index를 QUERYBUF각 `m.offset`/`length`로 shared mmap종료 때 모든 start/length를 munmap

원문 C 코드의 처리 순서입니다.

Example: Mapping buffers in the single-planar API
=================================================

.. code-block:: c

    struct v4l2_requestbuffers reqbuf;
    struct {
	void *start;
	size_t length;
    } *buffers;
    unsigned int i;

    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    reqbuf.count = 20;

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

	exit(EXIT_FAILURE);
    }

    /* We want at least five buffers. */

    if (reqbuf.count < 5) {
	/* You may need to free the buffers here. */
	printf("Not enough buffer memory\\n");
	exit(EXIT_FAILURE);
    }

    buffers = calloc(reqbuf.count, sizeof(*buffers));
    assert(buffers != NULL);

    for (i = 0; i < reqbuf.count; i++) {
	struct v4l2_buffer buffer;

	memset(&buffer, 0, sizeof(buffer));
	buffer.type = reqbuf.type;
	buffer.memory = V4L2_MEMORY_MMAP;
	buffer.index = i;

	if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buffer)) {
	    perror("VIDIOC_QUERYBUF");
	    exit(EXIT_FAILURE);
	}

	buffers[i].length = buffer.length; /* remember for munmap() */

	buffers[i].start = mmap(NULL, buffer.length,
		    PROT_READ | PROT_WRITE, /* recommended */
		    MAP_SHARED,             /* recommended */
		    fd, buffer.m.offset);

	if (MAP_FAILED == buffers[i].start) {
	    /* If you do not exit here you should unmap() and free()
	       the buffers mapped so far. */
	    perror("mmap");
	    exit(EXIT_FAILURE);
	}
    }

    /* Cleanup. */

    for (i = 0; i < reqbuf.count; i++)
	munmap(buffers[i].start, buffers[i].length);

Multi-planar 매핑 예제

126-207

Multi-planar 예제는 현재 형식이 buffer당 3개 plane을 쓴다고 가정하고 `V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE`으로 20개를 요청합니다. 응용 프로그램의 descriptor에는 plane별 start와 length 배열이 필요합니다.

QUERYBUF 전에 `v4l2_plane planes[FMT_NUM_PLANES]`를 준비하고 `v4l2_buffer.length`에 plane 배열 원소 수를, `m.planes`에 배열 포인터를 설정합니다. QUERYBUF 뒤에는 각 plane의 `length`와 `m.mem_offset`을 사용해 따로 `mmap()`합니다.

정리도 buffer와 plane의 이중 loop로 수행합니다. `MAP_FAILED` 뒤 계속 실행한다면 이미 매핑한 모든 buffer와 plane을 역으로 해제해야 합니다.

Multi-planar 예제
VIDEO_CAPTURE_MPLANE MMAP buffer 요청각 buffer용 plane 배열 준비`buffer.length=FMT_NUM_PLANES`, `buffer.m.planes=planes` 설정QUERYBUF로 plane별 length/mem_offset 조회각 plane을 독립적으로 shared mmap이중 loop로 모든 plane munmap

buffer × plane 구조가 single-planar와 다른 핵심입니다.

Example: Mapping buffers in the multi-planar API
================================================

.. code-block:: c

    struct v4l2_requestbuffers reqbuf;
    /* Our current format uses 3 planes per buffer */
    #define FMT_NUM_PLANES = 3

    struct {
	void *start[FMT_NUM_PLANES];
	size_t length[FMT_NUM_PLANES];
    } *buffers;
    unsigned int i, j;

    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    reqbuf.count = 20;

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

	exit(EXIT_FAILURE);
    }

    /* We want at least five buffers. */

    if (reqbuf.count < 5) {
	/* You may need to free the buffers here. */
	printf("Not enough buffer memory\\n");
	exit(EXIT_FAILURE);
    }

    buffers = calloc(reqbuf.count, sizeof(*buffers));
    assert(buffers != NULL);

    for (i = 0; i < reqbuf.count; i++) {
	struct v4l2_buffer buffer;
	struct v4l2_plane planes[FMT_NUM_PLANES];

	memset(&buffer, 0, sizeof(buffer));
	buffer.type = reqbuf.type;
	buffer.memory = V4L2_MEMORY_MMAP;
	buffer.index = i;
	/* length in struct v4l2_buffer in multi-planar API stores the size
	 * of planes array. */
	buffer.length = FMT_NUM_PLANES;
	buffer.m.planes = planes;

	if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0) {
	    perror("VIDIOC_QUERYBUF");
	    exit(EXIT_FAILURE);
	}

	/* Every plane has to be mapped separately */
	for (j = 0; j < FMT_NUM_PLANES; j++) {
	    buffers[i].length[j] = buffer.m.planes[j].length; /* remember for munmap() */

	    buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length,
		     PROT_READ | PROT_WRITE, /* recommended */
		     MAP_SHARED,             /* recommended */
		     fd, buffer.m.planes[j].m.mem_offset);

	    if (MAP_FAILED == buffers[i].start[j]) {
		/* If you do not exit here you should unmap() and free()
		   the buffers and planes mapped so far. */
		perror("mmap");
		exit(EXIT_FAILURE);
	    }
	}
    }

    /* Cleanup. */

    for (i = 0; i < reqbuf.count; i++)
	for (j = 0; j < FMT_NUM_PLANES; j++)
	    munmap(buffers[i].start[j], buffers[i].length[j]);

Incoming/outgoing FIFO와 buffer lifecycle

208-256

Streaming 드라이버는 개념적으로 incoming과 outgoing 두 FIFO queue를 유지합니다. video clock에 묶인 동기 capture/output과 디스크·네트워크 지연 및 다른 process의 preemption을 받는 응용 프로그램을 분리해 데이터 손실 가능성을 줄입니다.

출력 buffer는 incoming FIFO에 넣은 순서로 출력되고, 캡처 buffer는 outgoing FIFO에서 꺼내는 순서가 캡처 순서입니다. 드라이버가 항상 queue에 최소 buffer 수를 요구할 수 있지만 그 외에는 미리 넣거나 꺼내 처리할 수에 제한이 없습니다.

응용 프로그램은 dequeued 순서와 다른 순서로 다시 enqueue할 수 있고 드라이버는 enqueue된 빈 buffer를 임의 순서로 채울 수 있습니다. `v4l2_buffer.index`는 buffer 식별자일 뿐 처리 순서를 뜻하지 않습니다.

Capture와 output lifecycle
항목설명
Capture 시작보통 mapped buffer를 모두 QBUF한 뒤 STREAMON하고 filled buffer를 DQBUF하는 read loop에 들어갑니다.
Capture 재사용데이터가 더 필요 없으면 buffer를 다시 QBUF합니다.
Output 시작응용 프로그램이 buffer를 채워 QBUF하고 충분히 쌓이면 STREAMON합니다.
Output 재사용free buffer가 떨어지면 empty buffer를 DQBUF할 수 있을 때까지 기다린 뒤 다시 채웁니다.

처음 모든 mapped buffer는 driver가 접근하지 못하는 dequeued 상태입니다.

QBUF/DQBUF로 queue를 조작하고 QUERYBUF로 mapped/enqueued/full/empty 상태를 확인합니다. 기본 DQBUF는 outgoing queue가 비면 block합니다. `open()`에 `O_NONBLOCK`을 썼다면 즉시 `EAGAIN`을 반환하며 `select()`와 `poll()`은 항상 사용할 수 있습니다.

STREAMON/STREAMOFF로 capture 또는 output을 시작·중지합니다. STREAMOFF는 두 queue의 모든 buffer를 제거합니다. 멀티태스킹 환경에서 정확한 '지금'은 없으므로 다른 event와 동기화하려면 capture/output buffer의 `timestamp`를 검사합니다.

Conceptually streaming drivers maintain two buffer queues, an incoming
and an outgoing queue. They separate the synchronous capture or output
operation locked to a video clock from the application which is subject
to random disk or network delays and preemption by other processes,
thereby reducing the probability of data loss. The queues are organized
as FIFOs, buffers will be output in the order enqueued in the incoming
FIFO, and were captured in the order dequeued from the outgoing FIFO.

The driver may require a minimum number of buffers enqueued at all times
to function, apart of this no limit exists on the number of buffers
applications can enqueue in advance, or dequeue and process. They can
also enqueue in a different order than buffers have been dequeued, and
the driver can *fill* enqueued *empty* buffers in any order.  [#f2]_ The
index number of a buffer (struct :c:type:`v4l2_buffer`
``index``) plays no role here, it only identifies the buffer.

Initially all mapped buffers are in dequeued state, inaccessible by the
driver. For capturing applications it is customary to first enqueue all
mapped buffers, then to start capturing and enter the read loop. Here
the application waits until a filled buffer can be dequeued, and
re-enqueues the buffer when the data is no longer needed. Output
applications fill and enqueue buffers, when enough buffers are stacked
up the output is started with :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>`.
In the write loop, when the application runs out of free buffers, it
must wait until an empty buffer can be dequeued and reused.

To enqueue and dequeue a buffer applications use the
:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` and :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`
ioctl. The status of a buffer being mapped, enqueued, full or empty can
be determined at any time using the :ref:`VIDIOC_QUERYBUF` ioctl. Two
methods exist to suspend execution of the application until one or more
buffers can be dequeued.  By default :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`
blocks when no buffer is in the outgoing queue. When the ``O_NONBLOCK``
flag was given to the :c:func:`open()` function,
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` returns immediately with an ``EAGAIN``
error code when no buffer is available. The :c:func:`select()`
or :c:func:`poll()` functions are always available.

To start and stop capturing or output applications call the
:ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` and :ref:`VIDIOC_STREAMOFF
<VIDIOC_STREAMON>` ioctl.

.. note:::ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>`
   removes all buffers from both queues as a side effect. Since there is
   no notion of doing anything "now" on a multitasking system, if an
   application needs to synchronize with another event it should examine
   the struct ::c:type:`v4l2_buffer` ``timestamp`` of captured
   or outputted buffers.

필수 연산과 설계 주석

257-285
MMAP 드라이버 필수 지원
항목설명
ioctl`VIDIOC_REQBUFS`, `VIDIOC_QUERYBUF`, `VIDIOC_QBUF`, `VIDIOC_DQBUF`, `VIDIOC_STREAMON`, `VIDIOC_STREAMOFF`
memory`mmap()`, `munmap()`
대기`select()`, `poll()`

memory mapping I/O 구현에 필요한 ioctl과 함수입니다.

한 descriptor에서 QBUF 때마다 buffer type을 바꾸는 것도 이론상 가능하지만 `select()` 의미가 모호해집니다. 오버레이도 CPU를 계속 쓰지 않을 뿐 logical stream이므로 stream마다 descriptor 하나를 쓰는 방식이 명확합니다.

무작위 re-enqueue 순서는 codec처럼 영상을 순서 밖에서 처리하는 응용 프로그램이 buffer를 일찍 반환해 손실을 줄이게 합니다. 무작위 fill 순서는 드라이버가 LIFO로 buffer를 재사용해 cache에 남은 scatter-gather list 등을 활용하게 합니다. 드라이버 수준에서 select와 poll은 같은 연산이며 select는 선택 기능이 아니라 필수 기능입니다.

Drivers implementing memory mapping I/O must support the
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, :ref:`VIDIOC_QUERYBUF
<VIDIOC_QUERYBUF>`, :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`, :ref:`VIDIOC_DQBUF
<VIDIOC_QBUF>`, :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>`
and :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls, the :ref:`mmap()
<func-mmap>`, :c:func:`munmap()`, :ref:`select()
<func-select>` and :c:func:`poll()` function. [#f3]_

[capture example]

.. [#f1]
   One could use one file descriptor and set the buffer type field
   accordingly when calling :ref:`VIDIOC_QBUF` etc.,
   but it makes the :c:func:`select()` function ambiguous. We also
   like the clean approach of one file descriptor per logical stream.
   Video overlay for example is also a logical stream, although the CPU
   is not needed for continuous operation.

.. [#f2]
   Random enqueue order permits applications processing images out of
   order (such as video codecs) to return buffers earlier, reducing the
   probability of data loss. Random fill order allows drivers to reuse
   buffers on a LIFO-basis, taking advantage of caches holding
   scatter-gather lists and the like.

.. [#f3]
   At the driver level :c:func:`select()` and :c:func:`poll()` are
   the same, and :c:func:`select()` is too important to be optional.
   The rest should be evident.