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