요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
.. c:namespace:: V4L
.. _userp:
*****************************
Streaming I/O (User Pointers)
*****************************
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. If the
particular user pointer method (not only memory mapping) is supported
must be determined by calling the :ref:`VIDIOC_REQBUFS` ioctl
with the memory type set to ``V4L2_MEMORY_USERPTR``.
This I/O method combines advantages of the read/write and memory mapping
methods. Buffers (planes) are allocated by the application itself, and
can reside for example in virtual or shared memory. Only pointers to
data are exchanged, these pointers and meta-information are passed in
struct :c:type:`v4l2_buffer` (or in struct
:c:type:`v4l2_plane` in the multi-planar API case). The
driver must be switched into user pointer I/O mode by calling the
:ref:`VIDIOC_REQBUFS` with the desired buffer type.
No buffers (planes) are allocated beforehand, consequently they are not
indexed and cannot be queried like mapped buffers with the
:ref:`VIDIOC_QUERYBUF <VIDIOC_QUERYBUF>` ioctl.
Example: Initiating streaming I/O with user pointers
====================================================
.. code-block:: c
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);
}
Buffer (plane) addresses and sizes are passed on the fly with the
:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` ioctl. Although buffers are commonly
cycled, applications can pass different addresses and sizes at each
:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` call. If required by the hardware the
driver swaps memory pages within physical memory to create a continuous
area of memory. This happens transparently to the application in the
virtual memory subsystem of the kernel. When buffer pages have been
swapped out to disk they are brought back and finally locked in physical
memory for DMA. [#f1]_
Filled or displayed buffers are dequeued with the
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. The driver can unlock the
memory pages at any time between the completion of the DMA and this
ioctl. The memory is also unlocked when
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` is called,
:ref:`VIDIOC_REQBUFS`, or when the device is closed.
Applications must take care not to free buffers without dequeuing.
Firstly, the buffers remain locked for longer, wasting physical memory.
Secondly the driver will not be notified when the memory is returned to
the application's free list and subsequently reused for other purposes,
possibly completing the requested DMA and overwriting valuable data.
For capturing applications it is customary to enqueue a number of empty
buffers, 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 output is
started. In the write loop, when the application runs out of free
buffers it must wait until an empty buffer can be dequeued and reused.
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 :ref:`select()
<func-select>` or :c:func:`poll()` function 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 and unlocks all buffers 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.
Drivers implementing user pointer I/O must support the
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`,
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`, :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>`
and :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls, the
:c:func:`select()` and :c:func:`poll()` function. [#f2]_
.. [#f1]
We expect that frequently used buffers are typically not swapped out.
Anyway, the process of swapping, locking or generating scatter-gather
lists may be time consuming. The delay can be masked by the depth of
the incoming buffer queue, and perhaps by maintaining caches assuming
a buffer will be soon enqueued again. On the other hand, to optimize
memory usage drivers can limit the number of buffers locked in
advance and recycle the most recently used buffers first. Of course,
the pages of empty buffers in the incoming queue need not be saved to
disk. Output buffers must be saved on the incoming and outgoing queue
because an application may share them with other processes.
.. [#f2]
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.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
USERPTR 지원 확인과 메모리 소유권
1-29입출력 장치는 `VIDIOC_QUERYCAP`의 `v4l2_capability.capabilities`에 `V4L2_CAP_STREAMING`이 설정되어 있을 때 스트리밍 I/O를 지원합니다. 그중 사용자 포인터 방식의 실제 지원 여부는 `VIDIOC_REQBUFS`의 메모리 유형을 `V4L2_MEMORY_USERPTR`로 지정해 확인해야 합니다.
USERPTR은 read/write 방식과 메모리 매핑 방식의 장점을 결합합니다. 응용 프로그램이 가상 또는 공유 메모리 등에 버퍼와 평면을 직접 할당하고, 드라이버와는 `v4l2_buffer` 또는 다중 평면 API의 `v4l2_plane`을 통해 포인터와 메타정보만 교환합니다.
`VIDIOC_REQBUFS`로 원하는 버퍼 유형과 USERPTR 모드를 선택하지만 드라이버가 버퍼를 미리 할당하지는 않습니다. 따라서 버퍼에는 고정 인덱스가 없고 mmap 버퍼처럼 `VIDIOC_QUERYBUF`로 조회할 수 없습니다.
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
.. c:namespace:: V4L
.. _userp:
*****************************
Streaming I/O (User Pointers)
*****************************
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. If the
particular user pointer method (not only memory mapping) is supported
must be determined by calling the :ref:`VIDIOC_REQBUFS` ioctl
with the memory type set to ``V4L2_MEMORY_USERPTR``.
This I/O method combines advantages of the read/write and memory mapping
methods. Buffers (planes) are allocated by the application itself, and
can reside for example in virtual or shared memory. Only pointers to
data are exchanged, these pointers and meta-information are passed in
struct :c:type:`v4l2_buffer` (or in struct
:c:type:`v4l2_plane` in the multi-planar API case). The
driver must be switched into user pointer I/O mode by calling the
:ref:`VIDIOC_REQBUFS` with the desired buffer type.
No buffers (planes) are allocated beforehand, consequently they are not
indexed and cannot be queried like mapped buffers with the
:ref:`VIDIOC_QUERYBUF <VIDIOC_QUERYBUF>` ioctl.
USERPTR 모드 시작과 DMA 메모리 준비
30-58`v4l2_requestbuffers`를 0으로 초기화하고 `type`에 스트림 유형, `memory`에 `V4L2_MEMORY_USERPTR`을 지정한 뒤 `VIDIOC_REQBUFS`를 호출합니다. `EINVAL`이면 해당 장치가 캡처 또는 사용자 포인터 스트리밍을 지원하지 않는다는 뜻입니다.
struct v4l2_requestbuffers reqbuf = { 0 };
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_USERPTR;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
if (errno == EINVAL)
printf("USERPTR streaming is not supported\n");
}
버퍼 주소와 크기는 `VIDIOC_QBUF` 호출마다 전달하므로 매번 다른 주소와 크기를 사용할 수 있습니다. 하드웨어가 연속 물리 메모리를 요구하면 드라이버와 커널 가상 메모리 계층이 페이지를 투명하게 재배치하고, 스왑 아웃된 페이지를 불러와 DMA 동안 물리 메모리에 고정합니다.
Example: Initiating streaming I/O with user pointers
====================================================
.. code-block:: c
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);
}
Buffer (plane) addresses and sizes are passed on the fly with the
:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` ioctl. Although buffers are commonly
cycled, applications can pass different addresses and sizes at each
:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` call. If required by the hardware the
driver swaps memory pages within physical memory to create a continuous
area of memory. This happens transparently to the application in the
virtual memory subsystem of the kernel. When buffer pages have been
swapped out to disk they are brought back and finally locked in physical
memory for DMA. [#f1]_
큐 수명주기와 블로킹 동작
59-100처리된 버퍼는 `VIDIOC_DQBUF`로 꺼냅니다. 드라이버는 DMA 완료와 dequeue 사이에 페이지 잠금을 해제할 수 있으며, `VIDIOC_STREAMOFF`, `VIDIOC_REQBUFS` 재호출 또는 장치 닫기 때도 잠금이 풀립니다. 응용 프로그램은 dequeue하지 않은 버퍼를 해제해서는 안 됩니다. 그렇지 않으면 물리 메모리를 오래 점유하거나, 재사용된 메모리를 뒤늦은 DMA가 덮어쓸 수 있습니다.
캡처는 빈 버퍼 여러 개를 enqueue하고 스트림을 시작한 뒤 채워진 버퍼를 dequeue하고 사용이 끝나면 다시 enqueue합니다. 출력은 데이터를 채운 버퍼를 쌓아 스트림을 시작하고, 빈 버퍼가 부족하면 처리 완료 버퍼를 dequeue해 재사용합니다.
기본 `VIDIOC_DQBUF`는 출력 큐에 버퍼가 없으면 블록합니다. 장치를 `O_NONBLOCK`으로 열었다면 즉시 `EAGAIN`을 반환하며, `select()`와 `poll()`은 항상 사용할 수 있습니다. `VIDIOC_STREAMON`과 `VIDIOC_STREAMOFF`로 스트림을 시작하고 중지합니다.
`VIDIOC_STREAMOFF`는 양쪽 큐에서 모든 버퍼를 제거하고 잠금을 해제합니다. 다른 사건과 동기화해야 한다면 멀티태스킹 환경의 '지금'에 의존하지 말고 캡처 또는 출력 버퍼의 `v4l2_buffer.timestamp`를 검사해야 합니다.
응용 프로그램 소유 버퍼가 드라이버 큐를 오가는 흐름입니다.
Filled or displayed buffers are dequeued with the
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. The driver can unlock the
memory pages at any time between the completion of the DMA and this
ioctl. The memory is also unlocked when
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` is called,
:ref:`VIDIOC_REQBUFS`, or when the device is closed.
Applications must take care not to free buffers without dequeuing.
Firstly, the buffers remain locked for longer, wasting physical memory.
Secondly the driver will not be notified when the memory is returned to
the application's free list and subsequently reused for other purposes,
possibly completing the requested DMA and overwriting valuable data.
For capturing applications it is customary to enqueue a number of empty
buffers, 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 output is
started. In the write loop, when the application runs out of free
buffers it must wait until an empty buffer can be dequeued and reused.
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 :ref:`select()
<func-select>` or :c:func:`poll()` function 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 and unlocks all buffers 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.
드라이버 필수 호출과 성능 고려
101-122USERPTR I/O 드라이버는 `VIDIOC_REQBUFS`, `VIDIOC_QBUF`, `VIDIOC_DQBUF`, `VIDIOC_STREAMON`, `VIDIOC_STREAMOFF`, `select()`와 `poll()`을 지원해야 합니다. 드라이버 수준에서 select와 poll은 같은 동작이며 select는 선택 사항으로 둘 수 없을 만큼 중요합니다.
자주 쓰는 버퍼는 대개 스왑되지 않지만 페이지 스왑, 잠금, scatter-gather 목록 생성에는 시간이 걸릴 수 있습니다. 입력 큐 깊이와 재사용 캐시로 지연을 숨길 수 있고, 메모리 절약이 필요하면 미리 잠그는 버퍼 수를 제한하고 최근 사용 버퍼부터 재활용할 수 있습니다. 출력 버퍼는 다른 프로세스와 공유될 수 있으므로 입력·출력 큐 양쪽에서 보존해야 합니다.
Drivers implementing user pointer I/O must support the
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`,
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`, :ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>`
and :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls, the
:c:func:`select()` and :c:func:`poll()` function. [#f2]_
.. [#f1]
We expect that frequently used buffers are typically not swapped out.
Anyway, the process of swapping, locking or generating scatter-gather
lists may be time consuming. The delay can be masked by the depth of
the incoming buffer queue, and perhaps by maintaining caches assuming
a buffer will be soon enqueued again. On the other hand, to optimize
memory usage drivers can limit the number of buffers locked in
advance and recycle the most recently used buffers first. Of course,
the pages of empty buffers in the incoming queue need not be saved to
disk. Output buffers must be saved on the incoming and outgoing queue
because an application may share them with other processes.
.. [#f2]
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.
요약·해설
userp.rst:1-122응용 프로그램 소유 버퍼를 사용자 포인터로 큐에 전달하는 스트리밍 I/O를 설명합니다.