Skip to content

Commit 02be3ec

Browse files
committed
v4l2: use custom xioctl as in reference impl
+ reference the example in vcap/ As far as I know, no problem was observed so far, but it matches the example. In theory, if VIDIOC_QBUF is interrupted (vcap), the buffer will "leak" (won't be enqued). If eg. from the default 3 bufs 2 are dismissed in that way, the stream would perhaps stop working. But just theoretically speaking, QBUF should not be blocking much so the probability is low, anyways. DQBUF might be interrupted more likely but that won't harm. + update inline comments for mmap from example
1 parent c8dc68b commit 02be3ec

3 files changed

Lines changed: 59 additions & 46 deletions

File tree

src/v4l2_common.h

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,24 @@ struct v4l2_buffer_data {
127127
size_t length;
128128
};
129129

130+
static int xioctl(int fh, int request, void *arg)
131+
{
132+
int r;
133+
134+
do {
135+
r = ioctl(fh, request, arg);
136+
} while (-1 == r && EINTR == errno);
137+
138+
return r;
139+
}
140+
130141
static _Bool set_v4l2_buffers(int fd, struct v4l2_requestbuffers *reqbuf, struct v4l2_buffer_data *buffers) {
131-
if (ioctl (fd, VIDIOC_REQBUFS, reqbuf) != 0) {
142+
if (xioctl (fd, VIDIOC_REQBUFS, reqbuf) != 0) {
132143
if (errno == EINVAL)
133144
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Video capturing or mmap-streaming is not supported\n");
134145
else
135146
log_perror(LOG_LEVEL_ERROR, MOD_NAME "VIDIOC_REQBUFS");
136147
return 0;
137-
138148
}
139149

140150
if (reqbuf->count < 2) {
@@ -152,17 +162,17 @@ static _Bool set_v4l2_buffers(int fd, struct v4l2_requestbuffers *reqbuf, struct
152162
buf.memory = V4L2_MEMORY_MMAP;
153163
buf.index = i;
154164

155-
if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) {
165+
if (xioctl(fd, VIDIOC_QUERYBUF, &buf) != 0) {
156166
log_perror(LOG_LEVEL_ERROR, MOD_NAME "VIDIOC_QUERYBUF");
157167
return 0;
158168
}
159169

160170
buffers[i].length = buf.length; /* remember for munmap() */
161-
162-
buffers[i].start = mmap(NULL, buf.length,
163-
PROT_READ | PROT_WRITE, /* recommended */
164-
MAP_SHARED, /* recommended */
165-
fd, buf.m.offset);
171+
buffers[i].start = mmap(NULL /* start anywhere */,
172+
buf.length,
173+
PROT_READ | PROT_WRITE /* required */,
174+
MAP_SHARED /* recommended */,
175+
fd, buf.m.offset);
166176

167177
if (MAP_FAILED == buffers[i].start) {
168178
/* If you do not exit here you should unmap() and free()
@@ -173,7 +183,7 @@ static _Bool set_v4l2_buffers(int fd, struct v4l2_requestbuffers *reqbuf, struct
173183

174184
buf.flags = 0;
175185

176-
if(ioctl(fd, VIDIOC_QBUF, &buf) != 0) {
186+
if (xioctl(fd, VIDIOC_QBUF, &buf) != 0) {
177187
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to enqueue buffer");
178188
return 0;
179189
}
@@ -194,7 +204,7 @@ static int try_open_v4l2_device(int log_level, const char *dev_name, int cap) {
194204

195205
struct v4l2_capability capability;
196206
memset(&capability, 0, sizeof(capability));
197-
if (ioctl(fd, VIDIOC_QUERYCAP, &capability) != 0) {
207+
if (xioctl(fd, VIDIOC_QUERYCAP, &capability) != 0) {
198208
log_perror(log_level, MOD_NAME "ioctl VIDIOC_QUERYCAP");
199209
close(fd);
200210
return -1;
@@ -215,7 +225,7 @@ static int try_open_v4l2_device(int log_level, const char *dev_name, int cap) {
215225

216226
int index = 0;
217227

218-
if (ioctl(fd, VIDIOC_S_INPUT, &index) != 0) {
228+
if (xioctl(fd, VIDIOC_S_INPUT, &index) != 0) {
219229
log_perror(log_level, MOD_NAME "Could not enable input (VIDIOC_S_INPUT)");
220230
close(fd);
221231
return -1;

src/video_capture/v4l2.c

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
* @author Milos Liska <xliska@fi.muni.cz>
44
* @author Martin Piatka <piatka@cesnet.cz>
55
* @author Martin Pulec <martin.pulec@cesnet.cz>
6+
*
7+
* The implementation is based on capture.c.rst example in
8+
* Documentation/userspace-api/media/v4l subdir of Linux kernel
9+
* tree (the IO_METHOD_MMAP variant).
610
*/
7-
/*
11+
/*
812
* Copyright (c) 2012-2026 CESNET, zájmové sdružení právických osob
913
* All rights reserved.
1014
*
@@ -54,7 +58,6 @@
5458
#include <stdio.h>
5559
#include <stdlib.h>
5660
#include <string.h>
57-
#include <sys/ioctl.h>
5861
#include <sys/mman.h>
5962
#include <unistd.h>
6063

@@ -114,7 +117,7 @@ static void enqueue_all_finished_frames(struct vidcap_v4l2_state *s) {
114117
struct v4l2_dispose_deq_buffer_data *dequeue_data;
115118
while ((dequeue_data = simple_linked_list_pop(s->buffers_to_enqueue)) != NULL) {
116119
s->dequeued_buffers -= 1;
117-
if (ioctl(s->fd, VIDIOC_QBUF, &dequeue_data->buf) != 0) {
120+
if (xioctl(s->fd, VIDIOC_QBUF, &dequeue_data->buf) != 0) {
118121
log_perror(LOG_LEVEL_ERROR, "Unable to enqueue buffer");
119122
}
120123
free(dequeue_data);
@@ -127,7 +130,7 @@ static void vidcap_v4l2_common_cleanup(struct vidcap_v4l2_state *s) {
127130
}
128131

129132
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
130-
if (s->fd != -1 && ioctl(s->fd, VIDIOC_STREAMOFF, &type) != 0) {
133+
if (s->fd != -1 && xioctl(s->fd, VIDIOC_STREAMOFF, &type) != 0) {
131134
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Stream stopping error");
132135
};
133136

@@ -164,7 +167,7 @@ static void vidcap_v4l2_common_cleanup(struct vidcap_v4l2_state *s) {
164167
}
165168

166169
static void print_fps(int fd, struct v4l2_frmivalenum *param) {
167-
int res = ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, param);
170+
int res = xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, param);
168171

169172
if(res == -1) {
170173
log_perror(LOG_LEVEL_WARNING, MOD_NAME "Unable to get FPS");
@@ -173,7 +176,7 @@ static void print_fps(int fd, struct v4l2_frmivalenum *param) {
173176

174177
switch (param->type) {
175178
case V4L2_FRMIVAL_TYPE_DISCRETE:
176-
while(ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, param) == 0) {
179+
while(xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, param) == 0) {
177180
printf("%u/%u ", param->discrete.numerator,
178181
param->discrete.denominator);
179182
param->index++;
@@ -249,7 +252,7 @@ static void show_help(_Bool full)
249252

250253
struct v4l2_capability capab;
251254
memset(&capab, 0, sizeof capab);
252-
if (ioctl(fd, VIDIOC_QUERYCAP, &capab) != 0) {
255+
if (xioctl(fd, VIDIOC_QUERYCAP, &capab) != 0) {
253256
log_perror(LOG_LEVEL_WARNING, MOD_NAME "Unable to query device capabilities");
254257
}
255258

@@ -268,12 +271,12 @@ static void show_help(_Bool full)
268271
struct v4l2_format fmt;
269272
memset(&fmt, 0, sizeof(fmt));
270273
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
271-
if (ioctl(fd, VIDIOC_G_FMT, &fmt) != 0) {
274+
if (xioctl(fd, VIDIOC_G_FMT, &fmt) != 0) {
272275
log_perror(LOG_LEVEL_WARNING, MOD_NAME "Unable to get video format");
273276
goto next_device;
274277
}
275278

276-
while (full && ioctl(fd, VIDIOC_ENUM_FMT, &format) == 0) {
279+
while (full && xioctl(fd, VIDIOC_ENUM_FMT, &format) == 0) {
277280
printf("\t\t");
278281
if(fmt.fmt.pix.pixelformat == format.pixelformat) {
279282
printf("(*) ");
@@ -287,7 +290,7 @@ static void show_help(_Bool full)
287290
memset(&size, 0, sizeof(size));
288291
size.pixel_format = format.pixelformat;
289292

290-
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == -1) {
293+
if (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == -1) {
291294
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to get frame size iterator");
292295
goto next_device;
293296
}
@@ -299,7 +302,7 @@ static void show_help(_Bool full)
299302

300303
switch (size.type) {
301304
case V4L2_FRMSIZE_TYPE_DISCRETE:
302-
while(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == 0) {
305+
while(xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == 0) {
303306
printf("\t\t\t");
304307
if(fmt.fmt.pix.width == size.discrete.width &&
305308
fmt.fmt.pix.height == size.discrete.height) {
@@ -403,7 +406,7 @@ static void vidcap_v4l2_probe(struct device_info **available_cards, int *count,
403406

404407
struct v4l2_capability capab;
405408
memset(&capab, 0, sizeof capab);
406-
if (ioctl(fd, VIDIOC_QUERYCAP, &capab) != 0) {
409+
if (xioctl(fd, VIDIOC_QUERYCAP, &capab) != 0) {
407410
log_perror(LOG_LEVEL_WARNING, MOD_NAME "Unable to query device capabilities");
408411
}
409412

@@ -423,12 +426,12 @@ static void vidcap_v4l2_probe(struct device_info **available_cards, int *count,
423426
format.index = 0;
424427

425428
int fmt_idx = 0;
426-
while(ioctl(fd, VIDIOC_ENUM_FMT, &format) == 0) {
429+
while(xioctl(fd, VIDIOC_ENUM_FMT, &format) == 0) {
427430
struct v4l2_frmsizeenum size;
428431
memset(&size, 0, sizeof(size));
429432
size.pixel_format = format.pixelformat;
430433

431-
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == -1) {
434+
if (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == -1) {
432435
log_perror(LOG_LEVEL_WARNING, MOD_NAME "Unable to get frame size iterator");
433436
goto next_device;
434437
}
@@ -440,19 +443,19 @@ static void vidcap_v4l2_probe(struct device_info **available_cards, int *count,
440443

441444
switch (size.type) {
442445
case V4L2_FRMSIZE_TYPE_DISCRETE:
443-
while(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == 0) {
446+
while(xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) == 0) {
444447
frame_int.width = size.discrete.width;
445448
frame_int.height = size.discrete.height;
446449
frame_int.index = 0;
447450

448-
if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frame_int) == -1) {
451+
if (xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frame_int) == -1) {
449452
log_perror(LOG_LEVEL_WARNING, MOD_NAME "Unable to get FPS");
450453
goto next_device;
451454
}
452455

453456
switch (frame_int.type) {
454457
case V4L2_FRMIVAL_TYPE_DISCRETE:
455-
while(ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frame_int) == 0) {
458+
while(xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frame_int) == 0) {
456459
write_mode(&cards[card_count - 1].modes[fmt_idx++],
457460
size.discrete.width, size.discrete.height,
458461
frame_int.discrete.numerator, frame_int.discrete.denominator,
@@ -635,13 +638,13 @@ static int vidcap_v4l2_init(const struct vidcap_params *params, void **state)
635638
}
636639

637640
struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
638-
if (ioctl(s->fd, VIDIOC_G_FMT, &fmt) != 0) {
641+
if (xioctl(s->fd, VIDIOC_G_FMT, &fmt) != 0) {
639642
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to get video format");
640643
goto error;
641644
}
642645

643646
struct v4l2_streamparm stream_params = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
644-
if (ioctl(s->fd, VIDIOC_G_PARM, &stream_params) != 0) {
647+
if (xioctl(s->fd, VIDIOC_G_PARM, &stream_params) != 0) {
645648
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to get stream params");
646649
goto error;
647650
}
@@ -659,7 +662,7 @@ static int vidcap_v4l2_init(const struct vidcap_params *params, void **state)
659662
fmt.fmt.pix.bytesperline = 0;
660663

661664
struct v4l2_format req_fmt = fmt;
662-
if (ioctl(s->fd, VIDIOC_S_FMT, &fmt) != 0) {
665+
if (xioctl(s->fd, VIDIOC_S_FMT, &fmt) != 0) {
663666
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to set video format");
664667
goto error;
665668
}
@@ -671,7 +674,7 @@ static int vidcap_v4l2_init(const struct vidcap_params *params, void **state)
671674
}
672675
struct v4l2_streamparm req_stream_params = stream_params;
673676
if (opts.numerator != 0 && opts.denominator != 0 &&
674-
ioctl(s->fd, VIDIOC_S_PARM, &stream_params) != 0) {
677+
xioctl(s->fd, VIDIOC_S_PARM, &stream_params) != 0) {
675678
log_perror(LOG_LEVEL_ERROR,
676679
MOD_NAME "Unable to set stream params");
677680
goto error;
@@ -686,7 +689,7 @@ static int vidcap_v4l2_init(const struct vidcap_params *params, void **state)
686689
s->dst_fmt.fmt.pix.bytesperline = 0;
687690
s->dst_fmt.fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
688691

689-
if(ioctl(s->fd, VIDIOC_G_PARM, &stream_params) != 0) {
692+
if(xioctl(s->fd, VIDIOC_G_PARM, &stream_params) != 0) {
690693
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to get stream params");
691694
goto error;
692695
}
@@ -758,7 +761,7 @@ static int vidcap_v4l2_init(const struct vidcap_params *params, void **state)
758761
}
759762
s->buffer_count = reqbuf.count;
760763

761-
if(ioctl(s->fd, VIDIOC_STREAMON, &reqbuf.type) != 0) {
764+
if(xioctl(s->fd, VIDIOC_STREAMON, &reqbuf.type) != 0) {
762765
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to start stream");
763766
goto error;
764767
};
@@ -824,7 +827,7 @@ static struct video_frame * vidcap_v4l2_grab(void *state, struct audio_frame **a
824827
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
825828
buf.memory = V4L2_MEMORY_MMAP;
826829

827-
if(ioctl(s->fd, VIDIOC_DQBUF, &buf) != 0) {
830+
if(xioctl(s->fd, VIDIOC_DQBUF, &buf) != 0) {
828831
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to dequeue buffer");
829832
return NULL;
830833
};
@@ -847,7 +850,7 @@ static struct video_frame * vidcap_v4l2_grab(void *state, struct audio_frame **a
847850
out->tiles[0].data_len);
848851

849852
// we do not need the driver buffer any more
850-
if (ioctl(s->fd, VIDIOC_QBUF, &buf) != 0) {
853+
if (xioctl(s->fd, VIDIOC_QBUF, &buf) != 0) {
851854
log_perror(LOG_LEVEL_ERROR, "[V4L2 capture] Unable to enqueue buffer");
852855
} else {
853856
s->dequeued_buffers -= 1;

src/video_display/v4l2.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @author Martin Pulec <pulec@cesnet.cz>
44
*/
55
/*
6-
* Copyright (c) 2022-2023 CESNET z.s.p.o.
6+
* Copyright (c) 2022-2026 CESNET, zájmové sdružení právnických osob
77
* All rights reserved.
88
*
99
* Redistribution and use in source and binary forms, with or without
@@ -89,7 +89,7 @@ static void display_v4l2_probe(struct device_info **available_cards, int *count,
8989

9090
struct v4l2_capability capab;
9191
memset(&capab, 0, sizeof capab);
92-
if (ioctl(fd, VIDIOC_QUERYCAP, &capab) != 0) {
92+
if (xioctl(fd, VIDIOC_QUERYCAP, &capab) != 0) {
9393
log_msg(LOG_LEVEL_WARNING, MOD_NAME "Unable to query device capabilities for %s: %s",
9494
name, ug_strerror(errno));
9595
}
@@ -183,7 +183,7 @@ static void deinit_device(struct display_v4l2_state *s) {
183183
return;
184184
}
185185
int type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
186-
if (ioctl(s->fd, VIDIOC_STREAMOFF, &type) != 0) {
186+
if (xioctl(s->fd, VIDIOC_STREAMOFF, &type) != 0) {
187187
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Stream stopping error");
188188
}
189189

@@ -210,7 +210,7 @@ static void display_v4l2_done(void *state)
210210
static struct video_frame *display_v4l2_getf(void *state)
211211
{
212212
struct display_v4l2_state *s = state;
213-
int ret = ioctl(s->fd, VIDIOC_DQBUF, &s->buf);
213+
int ret = xioctl(s->fd, VIDIOC_DQBUF, &s->buf);
214214
if (ret != 0) {
215215
log_perror(LOG_LEVEL_WARNING, MOD_NAME "QBUF");
216216
return NULL;
@@ -229,7 +229,7 @@ static bool display_v4l2_putf(void *state, struct video_frame *frame, long long
229229
if (frame == NULL) {
230230
return true;
231231
}
232-
int ret = ioctl(s->fd, VIDIOC_QBUF, &s->buf);
232+
int ret = xioctl(s->fd, VIDIOC_QBUF, &s->buf);
233233
if (ret != 0) {
234234
log_perror(LOG_LEVEL_WARNING, MOD_NAME "QBUF");
235235
}
@@ -278,7 +278,7 @@ static bool display_v4l2_reconfigure(void *state, struct video_desc desc)
278278
deinit_device(s);
279279

280280
struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT };
281-
CHECK(ioctl(s->fd, VIDIOC_G_FMT, &fmt));
281+
CHECK(xioctl(s->fd, VIDIOC_G_FMT, &fmt));
282282
fmt.fmt.pix.width = desc.width;
283283
fmt.fmt.pix.height = desc.height;
284284
for (unsigned i = 0; i < sizeof v4l2_ug_map / sizeof v4l2_ug_map[0]; i++) {
@@ -293,14 +293,14 @@ static bool display_v4l2_reconfigure(void *state, struct video_desc desc)
293293
break;
294294
}
295295
}
296-
CHECK(ioctl(s->fd, VIDIOC_S_FMT, &fmt));
296+
CHECK(xioctl(s->fd, VIDIOC_S_FMT, &fmt));
297297

298298
struct v4l2_streamparm parm = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT };
299-
CHECK(ioctl(s->fd, VIDIOC_G_PARM, &parm));
299+
CHECK(xioctl(s->fd, VIDIOC_G_PARM, &parm));
300300
parm.parm.output.capability = V4L2_CAP_TIMEPERFRAME;
301301
parm.parm.output.timeperframe.numerator = get_framerate_d(desc.fps);
302302
parm.parm.output.timeperframe.denominator = get_framerate_n(desc.fps);
303-
CHECK(ioctl(s->fd, VIDIOC_S_PARM, &parm));
303+
CHECK(xioctl(s->fd, VIDIOC_S_PARM, &parm));
304304

305305
struct v4l2_requestbuffers reqbuf = { 0 };
306306
reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
@@ -310,7 +310,7 @@ static bool display_v4l2_reconfigure(void *state, struct video_desc desc)
310310
return false;
311311
}
312312

313-
if (ioctl(s->fd, VIDIOC_STREAMON, &reqbuf.type) != 0) {
313+
if (xioctl(s->fd, VIDIOC_STREAMON, &reqbuf.type) != 0) {
314314
log_perror(LOG_LEVEL_ERROR, MOD_NAME "Unable to start stream");
315315
return false;
316316
};

0 commit comments

Comments
 (0)