1 /* Spa
2 *
3 * Copyright © 2018 Wim Taymans
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sched.h>
28 #include <errno.h>
29 #include <sys/ioctl.h>
30 #include <sys/mman.h>
31 #include <poll.h>
32
33 static void v4l2_on_fd_events(struct spa_source *source);
34
xioctl(int fd,int request,void * arg)35 static int xioctl(int fd, int request, void *arg)
36 {
37 int err;
38
39 do {
40 err = ioctl(fd, request, arg);
41 } while (err == -1 && errno == EINTR);
42
43 return err;
44 }
45
46
spa_v4l2_open(struct spa_v4l2_device * dev,const char * path)47 int spa_v4l2_open(struct spa_v4l2_device *dev, const char *path)
48 {
49 struct stat st;
50 int err;
51
52 if (dev->fd != -1)
53 return 0;
54
55 if (path == NULL) {
56 spa_log_error(dev->log, "Device property not set");
57 return -EIO;
58 }
59
60 spa_log_info(dev->log, "Playback device is '%s'", path);
61
62 dev->fd = open(path, O_RDWR | O_NONBLOCK, 0);
63 if (dev->fd == -1) {
64 err = errno;
65 spa_log_error(dev->log, "Cannot open '%s': %d, %s",
66 path, err, strerror(err));
67 goto error;
68 }
69
70 if (fstat(dev->fd, &st) < 0) {
71 err = errno;
72 spa_log_error(dev->log, "Cannot identify '%s': %d, %s",
73 path, err, strerror(err));
74 goto error_close;
75 }
76
77 if (!S_ISCHR(st.st_mode)) {
78 spa_log_error(dev->log, "%s is no device", path);
79 err = ENODEV;
80 goto error_close;
81 }
82
83 if (xioctl(dev->fd, VIDIOC_QUERYCAP, &dev->cap) < 0) {
84 err = errno;
85 spa_log_error(dev->log, "'%s' QUERYCAP: %m", path);
86 goto error_close;
87 }
88 return 0;
89
90 error_close:
91 close(dev->fd);
92 dev->fd = -1;
93 error:
94 return -err;
95 }
96
spa_v4l2_is_capture(struct spa_v4l2_device * dev)97 int spa_v4l2_is_capture(struct spa_v4l2_device *dev)
98 {
99 uint32_t caps = dev->cap.capabilities;
100 if ((caps & V4L2_CAP_DEVICE_CAPS))
101 caps = dev->cap.device_caps;
102 return (caps & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE;
103 }
104
spa_v4l2_close(struct spa_v4l2_device * dev)105 int spa_v4l2_close(struct spa_v4l2_device *dev)
106 {
107 if (dev->fd == -1)
108 return 0;
109
110 if (dev->active || dev->have_format)
111 return 0;
112
113 spa_log_info(dev->log, "close");
114
115 if (close(dev->fd))
116 spa_log_warn(dev->log, "close: %m");
117
118 dev->fd = -1;
119 return 0;
120 }
121
spa_v4l2_buffer_recycle(struct impl * this,uint32_t buffer_id)122 static int spa_v4l2_buffer_recycle(struct impl *this, uint32_t buffer_id)
123 {
124 struct port *port = &this->out_ports[0];
125 struct buffer *b = &port->buffers[buffer_id];
126 struct spa_v4l2_device *dev = &port->dev;
127 int err;
128
129 if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING))
130 return 0;
131
132 SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUTSTANDING);
133 spa_log_trace(this->log, "v4l2 %p: recycle buffer %d", this, buffer_id);
134
135 if (xioctl(dev->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
136 err = errno;
137 spa_log_error(this->log, "'%s' VIDIOC_QBUF: %m", this->props.device);
138 return -err;
139 }
140
141 return 0;
142 }
143
spa_v4l2_clear_buffers(struct impl * this)144 static int spa_v4l2_clear_buffers(struct impl *this)
145 {
146 struct port *port = &this->out_ports[0];
147 struct v4l2_requestbuffers reqbuf;
148 uint32_t i;
149
150 if (port->n_buffers == 0)
151 return 0;
152
153 for (i = 0; i < port->n_buffers; i++) {
154 struct buffer *b;
155 struct spa_data *d;
156
157 b = &port->buffers[i];
158 d = b->outbuf->datas;
159
160 if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING)) {
161 spa_log_debug(this->log, "queueing outstanding buffer %p", b);
162 spa_v4l2_buffer_recycle(this, i);
163 }
164 if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_MAPPED)) {
165 munmap(b->ptr, d[0].maxsize);
166 }
167 if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_ALLOCATED)) {
168 spa_log_debug(this->log, "close %d", (int) d[0].fd);
169 close(d[0].fd);
170 }
171 d[0].type = SPA_ID_INVALID;
172 }
173
174 spa_zero(reqbuf);
175 reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
176 reqbuf.memory = port->memtype;
177 reqbuf.count = 0;
178
179 if (xioctl(port->dev.fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
180 spa_log_warn(this->log, "VIDIOC_REQBUFS: %m");
181 }
182 port->n_buffers = 0;
183
184 return 0;
185 }
186
187
188 struct format_info {
189 uint32_t fourcc;
190 uint32_t format;
191 uint32_t media_type;
192 uint32_t media_subtype;
193 };
194
195 #define VIDEO SPA_MEDIA_TYPE_video
196 #define IMAGE SPA_MEDIA_TYPE_image
197
198 #define RAW SPA_MEDIA_SUBTYPE_raw
199
200 #define BAYER SPA_MEDIA_SUBTYPE_bayer
201 #define MJPG SPA_MEDIA_SUBTYPE_mjpg
202 #define JPEG SPA_MEDIA_SUBTYPE_jpeg
203 #define DV SPA_MEDIA_SUBTYPE_dv
204 #define MPEGTS SPA_MEDIA_SUBTYPE_mpegts
205 #define H264 SPA_MEDIA_SUBTYPE_h264
206 #define H263 SPA_MEDIA_SUBTYPE_h263
207 #define MPEG1 SPA_MEDIA_SUBTYPE_mpeg1
208 #define MPEG2 SPA_MEDIA_SUBTYPE_mpeg2
209 #define MPEG4 SPA_MEDIA_SUBTYPE_mpeg4
210 #define XVID SPA_MEDIA_SUBTYPE_xvid
211 #define VC1 SPA_MEDIA_SUBTYPE_vc1
212 #define VP8 SPA_MEDIA_SUBTYPE_vp8
213
214 #define FORMAT_UNKNOWN SPA_VIDEO_FORMAT_UNKNOWN
215 #define FORMAT_ENCODED SPA_VIDEO_FORMAT_ENCODED
216 #define FORMAT_RGB15 SPA_VIDEO_FORMAT_RGB15
217 #define FORMAT_BGR15 SPA_VIDEO_FORMAT_BGR15
218 #define FORMAT_RGB16 SPA_VIDEO_FORMAT_RGB16
219 #define FORMAT_BGR SPA_VIDEO_FORMAT_BGR
220 #define FORMAT_RGB SPA_VIDEO_FORMAT_RGB
221 #define FORMAT_BGRA SPA_VIDEO_FORMAT_BGRA
222 #define FORMAT_BGRx SPA_VIDEO_FORMAT_BGRx
223 #define FORMAT_ARGB SPA_VIDEO_FORMAT_ARGB
224 #define FORMAT_xRGB SPA_VIDEO_FORMAT_xRGB
225 #define FORMAT_GRAY8 SPA_VIDEO_FORMAT_GRAY8
226 #define FORMAT_GRAY16_LE SPA_VIDEO_FORMAT_GRAY16_LE
227 #define FORMAT_GRAY16_BE SPA_VIDEO_FORMAT_GRAY16_BE
228 #define FORMAT_YVU9 SPA_VIDEO_FORMAT_YVU9
229 #define FORMAT_YV12 SPA_VIDEO_FORMAT_YV12
230 #define FORMAT_YUY2 SPA_VIDEO_FORMAT_YUY2
231 #define FORMAT_YVYU SPA_VIDEO_FORMAT_YVYU
232 #define FORMAT_UYVY SPA_VIDEO_FORMAT_UYVY
233 #define FORMAT_Y42B SPA_VIDEO_FORMAT_Y42B
234 #define FORMAT_Y41B SPA_VIDEO_FORMAT_Y41B
235 #define FORMAT_YUV9 SPA_VIDEO_FORMAT_YUV9
236 #define FORMAT_I420 SPA_VIDEO_FORMAT_I420
237 #define FORMAT_NV12 SPA_VIDEO_FORMAT_NV12
238 #define FORMAT_NV12_64Z32 SPA_VIDEO_FORMAT_NV12_64Z32
239 #define FORMAT_NV21 SPA_VIDEO_FORMAT_NV21
240 #define FORMAT_NV16 SPA_VIDEO_FORMAT_NV16
241 #define FORMAT_NV61 SPA_VIDEO_FORMAT_NV61
242 #define FORMAT_NV24 SPA_VIDEO_FORMAT_NV24
243
244 static const struct format_info format_info[] = {
245 /* RGB formats */
246 {V4L2_PIX_FMT_RGB332, FORMAT_UNKNOWN, VIDEO, RAW},
247 {V4L2_PIX_FMT_ARGB555, FORMAT_UNKNOWN, VIDEO, RAW},
248 {V4L2_PIX_FMT_XRGB555, FORMAT_RGB15, VIDEO, RAW},
249 {V4L2_PIX_FMT_ARGB555X, FORMAT_UNKNOWN, VIDEO, RAW},
250 {V4L2_PIX_FMT_XRGB555X, FORMAT_BGR15, VIDEO, RAW},
251 {V4L2_PIX_FMT_RGB565, FORMAT_RGB16, VIDEO, RAW},
252 {V4L2_PIX_FMT_RGB565X, FORMAT_UNKNOWN, VIDEO, RAW},
253 {V4L2_PIX_FMT_BGR666, FORMAT_UNKNOWN, VIDEO, RAW},
254 {V4L2_PIX_FMT_BGR24, FORMAT_BGR, VIDEO, RAW},
255 {V4L2_PIX_FMT_RGB24, FORMAT_RGB, VIDEO, RAW},
256 {V4L2_PIX_FMT_ABGR32, FORMAT_BGRA, VIDEO, RAW},
257 {V4L2_PIX_FMT_XBGR32, FORMAT_BGRx, VIDEO, RAW},
258 {V4L2_PIX_FMT_ARGB32, FORMAT_ARGB, VIDEO, RAW},
259 {V4L2_PIX_FMT_XRGB32, FORMAT_xRGB, VIDEO, RAW},
260
261 /* Deprecated Packed RGB Image Formats (alpha ambiguity) */
262 {V4L2_PIX_FMT_RGB444, FORMAT_UNKNOWN, VIDEO, RAW},
263 {V4L2_PIX_FMT_RGB555, FORMAT_RGB15, VIDEO, RAW},
264 {V4L2_PIX_FMT_RGB555X, FORMAT_BGR15, VIDEO, RAW},
265 {V4L2_PIX_FMT_BGR32, FORMAT_BGRx, VIDEO, RAW},
266 {V4L2_PIX_FMT_RGB32, FORMAT_xRGB, VIDEO, RAW},
267
268 /* Grey formats */
269 {V4L2_PIX_FMT_GREY, FORMAT_GRAY8, VIDEO, RAW},
270 {V4L2_PIX_FMT_Y4, FORMAT_UNKNOWN, VIDEO, RAW},
271 {V4L2_PIX_FMT_Y6, FORMAT_UNKNOWN, VIDEO, RAW},
272 {V4L2_PIX_FMT_Y10, FORMAT_UNKNOWN, VIDEO, RAW},
273 {V4L2_PIX_FMT_Y12, FORMAT_UNKNOWN, VIDEO, RAW},
274 {V4L2_PIX_FMT_Y16, FORMAT_GRAY16_LE, VIDEO, RAW},
275 {V4L2_PIX_FMT_Y16_BE, FORMAT_GRAY16_BE, VIDEO, RAW},
276 {V4L2_PIX_FMT_Y10BPACK, FORMAT_UNKNOWN, VIDEO, RAW},
277
278 /* Palette formats */
279 {V4L2_PIX_FMT_PAL8, FORMAT_UNKNOWN, VIDEO, RAW},
280
281 /* Chrominance formats */
282 {V4L2_PIX_FMT_UV8, FORMAT_UNKNOWN, VIDEO, RAW},
283
284 /* Luminance+Chrominance formats */
285 {V4L2_PIX_FMT_YVU410, FORMAT_YVU9, VIDEO, RAW},
286 {V4L2_PIX_FMT_YVU420, FORMAT_YV12, VIDEO, RAW},
287 {V4L2_PIX_FMT_YVU420M, FORMAT_UNKNOWN, VIDEO, RAW},
288 {V4L2_PIX_FMT_YUYV, FORMAT_YUY2, VIDEO, RAW},
289 {V4L2_PIX_FMT_YYUV, FORMAT_UNKNOWN, VIDEO, RAW},
290 {V4L2_PIX_FMT_YVYU, FORMAT_YVYU, VIDEO, RAW},
291 {V4L2_PIX_FMT_UYVY, FORMAT_UYVY, VIDEO, RAW},
292 {V4L2_PIX_FMT_VYUY, FORMAT_UNKNOWN, VIDEO, RAW},
293 {V4L2_PIX_FMT_YUV422P, FORMAT_Y42B, VIDEO, RAW},
294 {V4L2_PIX_FMT_YUV411P, FORMAT_Y41B, VIDEO, RAW},
295 {V4L2_PIX_FMT_Y41P, FORMAT_UNKNOWN, VIDEO, RAW},
296 {V4L2_PIX_FMT_YUV444, FORMAT_UNKNOWN, VIDEO, RAW},
297 {V4L2_PIX_FMT_YUV555, FORMAT_UNKNOWN, VIDEO, RAW},
298 {V4L2_PIX_FMT_YUV565, FORMAT_UNKNOWN, VIDEO, RAW},
299 {V4L2_PIX_FMT_YUV32, FORMAT_UNKNOWN, VIDEO, RAW},
300 {V4L2_PIX_FMT_YUV410, FORMAT_YUV9, VIDEO, RAW},
301 {V4L2_PIX_FMT_YUV420, FORMAT_I420, VIDEO, RAW},
302 {V4L2_PIX_FMT_YUV420M, FORMAT_I420, VIDEO, RAW},
303 {V4L2_PIX_FMT_HI240, FORMAT_UNKNOWN, VIDEO, RAW},
304 {V4L2_PIX_FMT_HM12, FORMAT_UNKNOWN, VIDEO, RAW},
305 {V4L2_PIX_FMT_M420, FORMAT_UNKNOWN, VIDEO, RAW},
306
307 /* two planes -- one Y, one Cr + Cb interleaved */
308 {V4L2_PIX_FMT_NV12, FORMAT_NV12, VIDEO, RAW},
309 {V4L2_PIX_FMT_NV12M, FORMAT_NV12, VIDEO, RAW},
310 {V4L2_PIX_FMT_NV12MT, FORMAT_NV12_64Z32, VIDEO, RAW},
311 {V4L2_PIX_FMT_NV12MT_16X16, FORMAT_UNKNOWN, VIDEO, RAW},
312 {V4L2_PIX_FMT_NV21, FORMAT_NV21, VIDEO, RAW},
313 {V4L2_PIX_FMT_NV21M, FORMAT_NV21, VIDEO, RAW},
314 {V4L2_PIX_FMT_NV16, FORMAT_NV16, VIDEO, RAW},
315 {V4L2_PIX_FMT_NV16M, FORMAT_NV16, VIDEO, RAW},
316 {V4L2_PIX_FMT_NV61, FORMAT_NV61, VIDEO, RAW},
317 {V4L2_PIX_FMT_NV61M, FORMAT_NV61, VIDEO, RAW},
318 {V4L2_PIX_FMT_NV24, FORMAT_NV24, VIDEO, RAW},
319 {V4L2_PIX_FMT_NV42, FORMAT_UNKNOWN, VIDEO, RAW},
320
321 /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
322 {V4L2_PIX_FMT_SBGGR8, FORMAT_UNKNOWN, VIDEO, BAYER},
323 {V4L2_PIX_FMT_SGBRG8, FORMAT_UNKNOWN, VIDEO, BAYER},
324 {V4L2_PIX_FMT_SGRBG8, FORMAT_UNKNOWN, VIDEO, BAYER},
325 {V4L2_PIX_FMT_SRGGB8, FORMAT_UNKNOWN, VIDEO, BAYER},
326
327 /* compressed formats */
328 {V4L2_PIX_FMT_MJPEG, FORMAT_ENCODED, VIDEO, MJPG},
329 {V4L2_PIX_FMT_JPEG, FORMAT_ENCODED, VIDEO, MJPG},
330 {V4L2_PIX_FMT_PJPG, FORMAT_ENCODED, VIDEO, MJPG},
331 {V4L2_PIX_FMT_DV, FORMAT_ENCODED, VIDEO, DV},
332 {V4L2_PIX_FMT_MPEG, FORMAT_ENCODED, VIDEO, MPEGTS},
333 {V4L2_PIX_FMT_H264, FORMAT_ENCODED, VIDEO, H264},
334 {V4L2_PIX_FMT_H264_NO_SC, FORMAT_ENCODED, VIDEO, H264},
335 {V4L2_PIX_FMT_H264_MVC, FORMAT_ENCODED, VIDEO, H264},
336 {V4L2_PIX_FMT_H263, FORMAT_ENCODED, VIDEO, H263},
337 {V4L2_PIX_FMT_MPEG1, FORMAT_ENCODED, VIDEO, MPEG1},
338 {V4L2_PIX_FMT_MPEG2, FORMAT_ENCODED, VIDEO, MPEG2},
339 {V4L2_PIX_FMT_MPEG4, FORMAT_ENCODED, VIDEO, MPEG4},
340 {V4L2_PIX_FMT_XVID, FORMAT_ENCODED, VIDEO, XVID},
341 {V4L2_PIX_FMT_VC1_ANNEX_G, FORMAT_ENCODED, VIDEO, VC1},
342 {V4L2_PIX_FMT_VC1_ANNEX_L, FORMAT_ENCODED, VIDEO, VC1},
343 {V4L2_PIX_FMT_VP8, FORMAT_ENCODED, VIDEO, VP8},
344
345 /* Vendor-specific formats */
346 {V4L2_PIX_FMT_WNVA, FORMAT_UNKNOWN, VIDEO, RAW},
347 {V4L2_PIX_FMT_SN9C10X, FORMAT_UNKNOWN, VIDEO, RAW},
348 {V4L2_PIX_FMT_PWC1, FORMAT_UNKNOWN, VIDEO, RAW},
349 {V4L2_PIX_FMT_PWC2, FORMAT_UNKNOWN, VIDEO, RAW},
350 };
351
fourcc_to_format_info(uint32_t fourcc)352 static const struct format_info *fourcc_to_format_info(uint32_t fourcc)
353 {
354 size_t i;
355
356 for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) {
357 if (format_info[i].fourcc == fourcc)
358 return &format_info[i];
359 }
360 return NULL;
361 }
362
363 #if 0
364 static const struct format_info *video_format_to_format_info(uint32_t format)
365 {
366 int i;
367
368 for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) {
369 if (format_info[i].format == format)
370 return &format_info[i];
371 }
372 return NULL;
373 }
374 #endif
375
find_format_info_by_media_type(uint32_t type,uint32_t subtype,uint32_t format,int startidx)376 static const struct format_info *find_format_info_by_media_type(uint32_t type,
377 uint32_t subtype,
378 uint32_t format,
379 int startidx)
380 {
381 size_t i;
382
383 for (i = startidx; i < SPA_N_ELEMENTS(format_info); i++) {
384 if ((format_info[i].media_type == type) &&
385 (format_info[i].media_subtype == subtype) &&
386 (format == 0 || format_info[i].format == format))
387 return &format_info[i];
388 }
389 return NULL;
390 }
391
392 static uint32_t
enum_filter_format(uint32_t media_type,int32_t media_subtype,const struct spa_pod * filter,uint32_t index)393 enum_filter_format(uint32_t media_type, int32_t media_subtype,
394 const struct spa_pod *filter, uint32_t index)
395 {
396 uint32_t video_format = 0;
397
398 switch (media_type) {
399 case SPA_MEDIA_TYPE_video:
400 case SPA_MEDIA_TYPE_image:
401 if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
402 const struct spa_pod_prop *p;
403 const struct spa_pod *val;
404 uint32_t n_values, choice;
405 const uint32_t *values;
406
407 if (!(p = spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_format)))
408 return SPA_VIDEO_FORMAT_UNKNOWN;
409
410 val = spa_pod_get_values(&p->value, &n_values, &choice);
411
412 if (val->type != SPA_TYPE_Id)
413 return SPA_VIDEO_FORMAT_UNKNOWN;
414
415 values = SPA_POD_BODY(val);
416
417 if (choice == SPA_CHOICE_None) {
418 if (index == 0)
419 video_format = values[0];
420 } else {
421 if (index + 1 < n_values)
422 video_format = values[index + 1];
423 }
424 } else {
425 if (index == 0)
426 video_format = SPA_VIDEO_FORMAT_ENCODED;
427 }
428 }
429 return video_format;
430 }
431
432 static bool
filter_framesize(struct v4l2_frmsizeenum * frmsize,const struct spa_rectangle * min,const struct spa_rectangle * max,const struct spa_rectangle * step)433 filter_framesize(struct v4l2_frmsizeenum *frmsize,
434 const struct spa_rectangle *min,
435 const struct spa_rectangle *max,
436 const struct spa_rectangle *step)
437 {
438 if (frmsize->type == V4L2_FRMSIZE_TYPE_DISCRETE) {
439 if (frmsize->discrete.width < min->width ||
440 frmsize->discrete.height < min->height ||
441 frmsize->discrete.width > max->width ||
442 frmsize->discrete.height > max->height) {
443 return false;
444 }
445 } else if (frmsize->type == V4L2_FRMSIZE_TYPE_CONTINUOUS ||
446 frmsize->type == V4L2_FRMSIZE_TYPE_STEPWISE) {
447 /* FIXME, use LCM */
448 frmsize->stepwise.step_width *= step->width;
449 frmsize->stepwise.step_height *= step->height;
450
451 if (frmsize->stepwise.max_width < min->width ||
452 frmsize->stepwise.max_height < min->height ||
453 frmsize->stepwise.min_width > max->width ||
454 frmsize->stepwise.min_height > max->height)
455 return false;
456
457 frmsize->stepwise.min_width = SPA_MAX(frmsize->stepwise.min_width, min->width);
458 frmsize->stepwise.min_height = SPA_MAX(frmsize->stepwise.min_height, min->height);
459 frmsize->stepwise.max_width = SPA_MIN(frmsize->stepwise.max_width, max->width);
460 frmsize->stepwise.max_height = SPA_MIN(frmsize->stepwise.max_height, max->height);
461 } else
462 return false;
463
464 return true;
465 }
466
compare_fraction(struct v4l2_fract * f1,const struct spa_fraction * f2)467 static int compare_fraction(struct v4l2_fract *f1, const struct spa_fraction *f2)
468 {
469 uint64_t n1, n2;
470
471 /* fractions are reduced when set, so we can quickly see if they're equal */
472 if (f1->denominator == f2->num && f1->numerator == f2->denom)
473 return 0;
474
475 /* extend to 64 bits */
476 n1 = ((int64_t) f1->denominator) * f2->denom;
477 n2 = ((int64_t) f1->numerator) * f2->num;
478 if (n1 < n2)
479 return -1;
480 return 1;
481 }
482
483 static bool
filter_framerate(struct v4l2_frmivalenum * frmival,const struct spa_fraction * min,const struct spa_fraction * max,const struct spa_fraction * step)484 filter_framerate(struct v4l2_frmivalenum *frmival,
485 const struct spa_fraction *min,
486 const struct spa_fraction *max,
487 const struct spa_fraction *step)
488 {
489 if (frmival->type == V4L2_FRMIVAL_TYPE_DISCRETE) {
490 if (compare_fraction(&frmival->discrete, min) < 0 ||
491 compare_fraction(&frmival->discrete, max) > 0)
492 return false;
493 } else if (frmival->type == V4L2_FRMIVAL_TYPE_CONTINUOUS ||
494 frmival->type == V4L2_FRMIVAL_TYPE_STEPWISE) {
495 /* FIXME, use LCM */
496 frmival->stepwise.step.denominator *= step->num;
497 frmival->stepwise.step.numerator *= step->denom;
498
499 if (compare_fraction(&frmival->stepwise.max, min) < 0 ||
500 compare_fraction(&frmival->stepwise.min, max) > 0)
501 return false;
502
503 if (compare_fraction(&frmival->stepwise.min, min) < 0) {
504 frmival->stepwise.min.denominator = min->num;
505 frmival->stepwise.min.numerator = min->denom;
506 }
507 if (compare_fraction(&frmival->stepwise.max, max) > 0) {
508 frmival->stepwise.max.denominator = max->num;
509 frmival->stepwise.max.numerator = max->denom;
510 }
511 } else
512 return false;
513
514 return true;
515 }
516
517 #define FOURCC_ARGS(f) (f)&0x7f,((f)>>8)&0x7f,((f)>>16)&0x7f,((f)>>24)&0x7f
518
519 static int
spa_v4l2_enum_format(struct impl * this,int seq,uint32_t start,uint32_t num,const struct spa_pod * filter)520 spa_v4l2_enum_format(struct impl *this, int seq,
521 uint32_t start, uint32_t num,
522 const struct spa_pod *filter)
523 {
524 struct port *port = &this->out_ports[0];
525 int res, n_fractions;
526 const struct format_info *info;
527 struct spa_pod_choice *choice;
528 uint32_t filter_media_type, filter_media_subtype, video_format;
529 struct spa_v4l2_device *dev = &port->dev;
530 uint8_t buffer[1024];
531 struct spa_pod_builder b = { 0 };
532 struct spa_pod_frame f[2];
533 struct spa_result_node_params result;
534 uint32_t count = 0;
535
536 if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
537 return res;
538
539 result.id = SPA_PARAM_EnumFormat;
540 result.next = start;
541
542 if (result.next == 0) {
543 spa_zero(port->fmtdesc);
544 port->fmtdesc.index = 0;
545 port->fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
546 port->next_fmtdesc = true;
547 spa_zero(port->frmsize);
548 port->next_frmsize = true;
549 spa_zero(port->frmival);
550 }
551
552 if (filter) {
553 if ((res = spa_format_parse(filter, &filter_media_type, &filter_media_subtype)) < 0)
554 return res;
555 }
556
557 if (false) {
558 next_fmtdesc:
559 port->fmtdesc.index++;
560 port->next_fmtdesc = true;
561 }
562
563 next:
564 result.index = result.next++;
565
566 while (port->next_fmtdesc) {
567 if (filter) {
568 struct v4l2_format fmt;
569
570 video_format = enum_filter_format(filter_media_type,
571 filter_media_subtype,
572 filter, port->fmtdesc.index);
573
574 if (video_format == SPA_VIDEO_FORMAT_UNKNOWN)
575 goto enum_end;
576
577 info = find_format_info_by_media_type(filter_media_type,
578 filter_media_subtype,
579 video_format, 0);
580 if (info == NULL)
581 goto next_fmtdesc;
582
583 port->fmtdesc.pixelformat = info->fourcc;
584
585 spa_zero(fmt);
586 fmt.type = port->fmtdesc.type;
587 fmt.fmt.pix.pixelformat = info->fourcc;
588 fmt.fmt.pix.field = V4L2_FIELD_ANY;
589 fmt.fmt.pix.width = 0;
590 fmt.fmt.pix.height = 0;
591
592 if ((res = xioctl(dev->fd, VIDIOC_TRY_FMT, &fmt)) < 0) {
593 spa_log_debug(this->log, "'%s' VIDIOC_TRY_FMT %08x: %m",
594 this->props.device, info->fourcc);
595 goto next_fmtdesc;
596 }
597 if (fmt.fmt.pix.pixelformat != info->fourcc) {
598 spa_log_debug(this->log, "'%s' VIDIOC_TRY_FMT wanted %.4s gave %.4s",
599 this->props.device, (char*)&info->fourcc,
600 (char*)&fmt.fmt.pix.pixelformat);
601 goto next_fmtdesc;
602 }
603
604 } else {
605 if ((res = xioctl(dev->fd, VIDIOC_ENUM_FMT, &port->fmtdesc)) < 0) {
606 if (errno == EINVAL)
607 goto enum_end;
608
609 res = -errno;
610 spa_log_error(this->log, "'%s' VIDIOC_ENUM_FMT: %m",
611 this->props.device);
612 goto exit;
613 }
614 }
615 port->next_fmtdesc = false;
616 port->frmsize.index = 0;
617 port->frmsize.pixel_format = port->fmtdesc.pixelformat;
618 port->next_frmsize = true;
619 }
620 if (!(info = fourcc_to_format_info(port->fmtdesc.pixelformat)))
621 goto next_fmtdesc;
622
623 next_frmsize:
624 while (port->next_frmsize) {
625 if (filter) {
626 const struct spa_pod_prop *p;
627 struct spa_pod *val;
628 uint32_t n_vals, choice;
629
630 /* check if we have a fixed frame size */
631 if (!(p = spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_size)))
632 goto do_frmsize;
633
634 val = spa_pod_get_values(&p->value, &n_vals, &choice);
635 if (val->type != SPA_TYPE_Rectangle)
636 goto enum_end;
637
638 if (choice == SPA_CHOICE_None) {
639 const struct spa_rectangle *values = SPA_POD_BODY(val);
640
641 if (port->frmsize.index > 0)
642 goto next_fmtdesc;
643
644 port->frmsize.type = V4L2_FRMSIZE_TYPE_DISCRETE;
645 port->frmsize.discrete.width = values[0].width;
646 port->frmsize.discrete.height = values[0].height;
647 goto have_size;
648 }
649 }
650 do_frmsize:
651 if ((res = xioctl(dev->fd, VIDIOC_ENUM_FRAMESIZES, &port->frmsize)) < 0) {
652 if (errno == EINVAL)
653 goto next_fmtdesc;
654
655 res = -errno;
656 spa_log_error(this->log, "'%s' VIDIOC_ENUM_FRAMESIZES: %m",
657 this->props.device);
658 goto exit;
659 }
660 if (filter) {
661 static const struct spa_rectangle step = {1, 1};
662
663 const struct spa_rectangle *values;
664 const struct spa_pod_prop *p;
665 struct spa_pod *val;
666 uint32_t choice, i, n_values;
667
668 /* check if we have a fixed frame size */
669 if (!(p = spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_size)))
670 goto have_size;
671
672 val = spa_pod_get_values(&p->value, &n_values, &choice);
673 if (val->type != SPA_TYPE_Rectangle)
674 goto have_size;
675
676 values = SPA_POD_BODY_CONST(val);
677
678 if (choice == SPA_CHOICE_Range && n_values > 2) {
679 if (filter_framesize(&port->frmsize, &values[1], &values[2], &step))
680 goto have_size;
681 } else if (choice == SPA_CHOICE_Step && n_values > 3) {
682 if (filter_framesize(&port->frmsize, &values[1], &values[2], &values[3]))
683 goto have_size;
684 } else if (choice == SPA_CHOICE_Enum) {
685 for (i = 1; i < n_values; i++) {
686 if (filter_framesize(&port->frmsize, &values[i], &values[i], &step))
687 goto have_size;
688 }
689 }
690 /* nothing matches the filter, get next frame size */
691 port->frmsize.index++;
692 continue;
693 }
694
695 have_size:
696 if (port->frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
697 /* we have a fixed size, use this to get the frame intervals */
698 port->frmival.index = 0;
699 port->frmival.pixel_format = port->frmsize.pixel_format;
700 port->frmival.width = port->frmsize.discrete.width;
701 port->frmival.height = port->frmsize.discrete.height;
702 port->next_frmsize = false;
703 } else if (port->frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS ||
704 port->frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
705 /* we have a non fixed size, fix to something sensible to get the
706 * framerate */
707 port->frmival.index = 0;
708 port->frmival.pixel_format = port->frmsize.pixel_format;
709 port->frmival.width = port->frmsize.stepwise.min_width;
710 port->frmival.height = port->frmsize.stepwise.min_height;
711 port->next_frmsize = false;
712 } else {
713 port->frmsize.index++;
714 }
715 }
716
717 spa_pod_builder_init(&b, buffer, sizeof(buffer));
718 spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
719 spa_pod_builder_add(&b,
720 SPA_FORMAT_mediaType, SPA_POD_Id(info->media_type),
721 SPA_FORMAT_mediaSubtype, SPA_POD_Id(info->media_subtype),
722 0);
723
724 if (info->media_subtype == SPA_MEDIA_SUBTYPE_raw) {
725 spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_format, 0);
726 spa_pod_builder_id(&b, info->format);
727 }
728 spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_size, 0);
729 if (port->frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
730 spa_pod_builder_rectangle(&b,
731 port->frmsize.discrete.width,
732 port->frmsize.discrete.height);
733 } else if (port->frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS ||
734 port->frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
735 spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0);
736 choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
737
738 spa_pod_builder_rectangle(&b,
739 port->frmsize.stepwise.min_width,
740 port->frmsize.stepwise.min_height);
741 spa_pod_builder_rectangle(&b,
742 port->frmsize.stepwise.min_width,
743 port->frmsize.stepwise.min_height);
744 spa_pod_builder_rectangle(&b,
745 port->frmsize.stepwise.max_width,
746 port->frmsize.stepwise.max_height);
747
748 if (port->frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
749 choice->body.type = SPA_CHOICE_Range;
750 } else {
751 choice->body.type = SPA_CHOICE_Step;
752 spa_pod_builder_rectangle(&b,
753 port->frmsize.stepwise.max_width,
754 port->frmsize.stepwise.max_height);
755 }
756 spa_pod_builder_pop(&b, &f[1]);
757 }
758
759 spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_framerate, 0);
760
761 n_fractions = 0;
762
763 spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0);
764 choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
765 port->frmival.index = 0;
766
767 while (true) {
768 if ((res = xioctl(dev->fd, VIDIOC_ENUM_FRAMEINTERVALS, &port->frmival)) < 0) {
769 res = -errno;
770 if (errno == EINVAL) {
771 port->frmsize.index++;
772 port->next_frmsize = true;
773 if (port->frmival.index == 0)
774 goto next_frmsize;
775 break;
776 }
777 spa_log_error(this->log, "'%s' VIDIOC_ENUM_FRAMEINTERVALS: %m",
778 this->props.device);
779 goto exit;
780 }
781 if (filter) {
782 static const struct spa_fraction step = {1, 1};
783
784 const struct spa_fraction *values;
785 const struct spa_pod_prop *p;
786 struct spa_pod *val;
787 uint32_t i, n_values, choice;
788
789 if (!(p = spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_framerate)))
790 goto have_framerate;
791
792 val = spa_pod_get_values(&p->value, &n_values, &choice);
793
794 if (val->type != SPA_TYPE_Fraction)
795 goto enum_end;
796
797 values = SPA_POD_BODY(val);
798
799 switch (choice) {
800 case SPA_CHOICE_None:
801 if (filter_framerate(&port->frmival, &values[0], &values[0], &step))
802 goto have_framerate;
803 break;
804
805 case SPA_CHOICE_Range:
806 if (n_values > 2 && filter_framerate(&port->frmival, &values[1], &values[2], &step))
807 goto have_framerate;
808 break;
809
810 case SPA_CHOICE_Step:
811 if (n_values > 3 && filter_framerate(&port->frmival, &values[1], &values[2], &values[3]))
812 goto have_framerate;
813 break;
814
815 case SPA_CHOICE_Enum:
816 for (i = 1; i < n_values; i++) {
817 if (filter_framerate(&port->frmival, &values[i], &values[i], &step))
818 goto have_framerate;
819 }
820 break;
821 default:
822 break;
823 }
824 port->frmival.index++;
825 continue;
826 }
827
828 have_framerate:
829
830 if (port->frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
831 choice->body.type = SPA_CHOICE_Enum;
832 if (n_fractions == 0)
833 spa_pod_builder_fraction(&b,
834 port->frmival.discrete.denominator,
835 port->frmival.discrete.numerator);
836 spa_pod_builder_fraction(&b,
837 port->frmival.discrete.denominator,
838 port->frmival.discrete.numerator);
839 port->frmival.index++;
840 } else if (port->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS ||
841 port->frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
842 if (n_fractions == 0)
843 spa_pod_builder_fraction(&b, 25, 1);
844 spa_pod_builder_fraction(&b,
845 port->frmival.stepwise.min.denominator,
846 port->frmival.stepwise.min.numerator);
847 spa_pod_builder_fraction(&b,
848 port->frmival.stepwise.max.denominator,
849 port->frmival.stepwise.max.numerator);
850
851 if (port->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
852 choice->body.type = SPA_CHOICE_Range;
853 } else {
854 choice->body.type = SPA_CHOICE_Step;
855 spa_pod_builder_fraction(&b,
856 port->frmival.stepwise.step.denominator,
857 port->frmival.stepwise.step.numerator);
858 }
859
860 port->frmsize.index++;
861 port->next_frmsize = true;
862 break;
863 }
864 n_fractions++;
865 }
866 if (n_fractions <= 1)
867 choice->body.type = SPA_CHOICE_None;
868
869 spa_pod_builder_pop(&b, &f[1]);
870 result.param = spa_pod_builder_pop(&b, &f[0]);
871
872 spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
873
874 if (++count != num)
875 goto next;
876
877 enum_end:
878 res = 0;
879 exit:
880 spa_v4l2_close(dev);
881 return res;
882 }
883
spa_v4l2_set_format(struct impl * this,struct spa_video_info * format,uint32_t flags)884 static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, uint32_t flags)
885 {
886 struct port *port = &this->out_ports[0];
887 struct spa_v4l2_device *dev = &port->dev;
888 int res, cmd;
889 struct v4l2_format reqfmt, fmt;
890 struct v4l2_streamparm streamparm;
891 const struct format_info *info = NULL;
892 uint32_t video_format;
893 struct spa_rectangle *size = NULL;
894 struct spa_fraction *framerate = NULL;
895 bool match;
896
897 spa_zero(fmt);
898 spa_zero(streamparm);
899 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
900 streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
901
902 switch (format->media_subtype) {
903 case SPA_MEDIA_SUBTYPE_raw:
904 video_format = format->info.raw.format;
905 size = &format->info.raw.size;
906 framerate = &format->info.raw.framerate;
907 break;
908 case SPA_MEDIA_SUBTYPE_mjpg:
909 case SPA_MEDIA_SUBTYPE_jpeg:
910 video_format = SPA_VIDEO_FORMAT_ENCODED;
911 size = &format->info.mjpg.size;
912 framerate = &format->info.mjpg.framerate;
913 break;
914 case SPA_MEDIA_SUBTYPE_h264:
915 video_format = SPA_VIDEO_FORMAT_ENCODED;
916 size = &format->info.h264.size;
917 framerate = &format->info.h264.framerate;
918 break;
919 default:
920 video_format = SPA_VIDEO_FORMAT_ENCODED;
921 break;
922 }
923
924 info = find_format_info_by_media_type(format->media_type,
925 format->media_subtype, video_format, 0);
926 if (info == NULL || size == NULL || framerate == NULL) {
927 spa_log_error(this->log, "unknown media type %d %d %d", format->media_type,
928 format->media_subtype, video_format);
929 return -EINVAL;
930 }
931
932
933 fmt.fmt.pix.pixelformat = info->fourcc;
934 fmt.fmt.pix.field = V4L2_FIELD_ANY;
935 fmt.fmt.pix.width = size->width;
936 fmt.fmt.pix.height = size->height;
937 streamparm.parm.capture.timeperframe.numerator = framerate->denom;
938 streamparm.parm.capture.timeperframe.denominator = framerate->num;
939
940 spa_log_debug(this->log, "set %.4s %dx%d %d/%d", (char *)&fmt.fmt.pix.pixelformat,
941 fmt.fmt.pix.width, fmt.fmt.pix.height,
942 streamparm.parm.capture.timeperframe.denominator,
943 streamparm.parm.capture.timeperframe.numerator);
944
945 reqfmt = fmt;
946
947 if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
948 return res;
949
950 cmd = (flags & SPA_NODE_PARAM_FLAG_TEST_ONLY) ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
951 if (xioctl(dev->fd, cmd, &fmt) < 0) {
952 res = -errno;
953 spa_log_error(this->log, "'%s' VIDIOC_S_FMT: %m",
954 this->props.device);
955 return res;
956 }
957
958 /* some cheap USB cam's won't accept any change */
959 if (xioctl(dev->fd, VIDIOC_S_PARM, &streamparm) < 0)
960 spa_log_warn(this->log, "VIDIOC_S_PARM: %m");
961
962 match = (reqfmt.fmt.pix.pixelformat == fmt.fmt.pix.pixelformat &&
963 reqfmt.fmt.pix.width == fmt.fmt.pix.width &&
964 reqfmt.fmt.pix.height == fmt.fmt.pix.height);
965
966 if (!match && !SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) {
967 spa_log_error(this->log, "wanted %.4s %dx%d, got %.4s %dx%d",
968 (char *)&reqfmt.fmt.pix.pixelformat,
969 reqfmt.fmt.pix.width, reqfmt.fmt.pix.height,
970 (char *)&fmt.fmt.pix.pixelformat,
971 fmt.fmt.pix.width, fmt.fmt.pix.height);
972 return -EINVAL;
973 }
974
975 if (flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)
976 return match ? 0 : 1;
977
978 spa_log_info(this->log, "'%s' got %.4s %dx%d %d/%d",
979 this->props.device, (char *)&fmt.fmt.pix.pixelformat,
980 fmt.fmt.pix.width, fmt.fmt.pix.height,
981 streamparm.parm.capture.timeperframe.denominator,
982 streamparm.parm.capture.timeperframe.numerator);
983
984 dev->have_format = true;
985 size->width = fmt.fmt.pix.width;
986 size->height = fmt.fmt.pix.height;
987 port->rate.denom = framerate->num = streamparm.parm.capture.timeperframe.denominator;
988 port->rate.num = framerate->denom = streamparm.parm.capture.timeperframe.numerator;
989
990 port->fmt = fmt;
991 port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_RATE;
992 port->info.flags = (port->alloc_buffers ? SPA_PORT_FLAG_CAN_ALLOC_BUFFERS : 0) |
993 SPA_PORT_FLAG_LIVE |
994 SPA_PORT_FLAG_PHYSICAL |
995 SPA_PORT_FLAG_TERMINAL;
996 port->info.rate = SPA_FRACTION(port->rate.num, port->rate.denom);
997
998 return match ? 0 : 1;
999 }
1000
query_ext_ctrl_ioctl(struct port * port,struct v4l2_query_ext_ctrl * qctrl)1001 static int query_ext_ctrl_ioctl(struct port *port, struct v4l2_query_ext_ctrl *qctrl)
1002 {
1003 struct spa_v4l2_device *dev = &port->dev;
1004 struct v4l2_queryctrl qc;
1005 int res;
1006
1007 if (port->have_query_ext_ctrl) {
1008 res = xioctl(dev->fd, VIDIOC_QUERY_EXT_CTRL, qctrl);
1009 if (errno != ENOTTY)
1010 return res;
1011 port->have_query_ext_ctrl = false;
1012 }
1013 spa_zero(qc);
1014 qc.id = qctrl->id;
1015 res = xioctl(dev->fd, VIDIOC_QUERYCTRL, &qc);
1016 if (res == 0) {
1017 qctrl->type = qc.type;
1018 memcpy(qctrl->name, qc.name, sizeof(qctrl->name));
1019 qctrl->minimum = qc.minimum;
1020 if (qc.type == V4L2_CTRL_TYPE_BITMASK) {
1021 qctrl->maximum = (__u32)qc.maximum;
1022 qctrl->default_value = (__u32)qc.default_value;
1023 } else {
1024 qctrl->maximum = qc.maximum;
1025 qctrl->default_value = qc.default_value;
1026 }
1027 qctrl->step = qc.step;
1028 qctrl->flags = qc.flags;
1029 qctrl->elems = 1;
1030 qctrl->nr_of_dims = 0;
1031 memset(qctrl->dims, 0, sizeof(qctrl->dims));
1032 switch (qctrl->type) {
1033 case V4L2_CTRL_TYPE_INTEGER64:
1034 qctrl->elem_size = sizeof(__s64);
1035 break;
1036 case V4L2_CTRL_TYPE_STRING:
1037 qctrl->elem_size = qc.maximum + 1;
1038 break;
1039 default:
1040 qctrl->elem_size = sizeof(__s32);
1041 break;
1042 }
1043 memset(qctrl->reserved, 0, sizeof(qctrl->reserved));
1044 }
1045 qctrl->id = qc.id;
1046 return res;
1047 }
1048
control_to_prop_id(struct impl * impl,uint32_t control_id)1049 static uint32_t control_to_prop_id(struct impl *impl, uint32_t control_id)
1050 {
1051 switch (control_id) {
1052 case V4L2_CID_BRIGHTNESS:
1053 return SPA_PROP_brightness;
1054 case V4L2_CID_CONTRAST:
1055 return SPA_PROP_contrast;
1056 case V4L2_CID_SATURATION:
1057 return SPA_PROP_saturation;
1058 case V4L2_CID_HUE:
1059 return SPA_PROP_hue;
1060 case V4L2_CID_GAMMA:
1061 return SPA_PROP_gamma;
1062 case V4L2_CID_EXPOSURE:
1063 return SPA_PROP_exposure;
1064 case V4L2_CID_GAIN:
1065 return SPA_PROP_gain;
1066 case V4L2_CID_SHARPNESS:
1067 return SPA_PROP_sharpness;
1068 default:
1069 return SPA_PROP_START_CUSTOM + control_id;
1070 }
1071 }
1072
1073 static int
spa_v4l2_enum_controls(struct impl * this,int seq,uint32_t start,uint32_t num,const struct spa_pod * filter)1074 spa_v4l2_enum_controls(struct impl *this, int seq,
1075 uint32_t start, uint32_t num,
1076 const struct spa_pod *filter)
1077 {
1078 struct port *port = &this->out_ports[0];
1079 struct spa_v4l2_device *dev = &port->dev;
1080 struct v4l2_query_ext_ctrl queryctrl;
1081 struct spa_pod *param;
1082 struct spa_pod_builder b = { 0 };
1083 uint32_t prop_id, ctrl_id;
1084 uint8_t buffer[1024];
1085 int res;
1086 const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
1087 struct spa_pod_frame f[2];
1088 struct spa_result_node_params result;
1089 uint32_t count = 0;
1090
1091 if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
1092 return res;
1093
1094 result.id = SPA_PARAM_PropInfo;
1095 result.next = start;
1096 next:
1097 result.index = result.next;
1098
1099 spa_zero(queryctrl);
1100
1101 if (result.next == 0) {
1102 result.next |= next_fl;
1103 port->n_controls = 0;
1104 }
1105
1106 queryctrl.id = result.next;
1107 spa_log_debug(this->log, "test control %08x", queryctrl.id);
1108
1109 if (query_ext_ctrl_ioctl(port, &queryctrl) != 0) {
1110 if (errno == EINVAL) {
1111 if (queryctrl.id != next_fl)
1112 goto enum_end;
1113
1114 if (result.next & next_fl)
1115 result.next = V4L2_CID_USER_BASE;
1116 else if (result.next >= V4L2_CID_USER_BASE && result.next < V4L2_CID_LASTP1)
1117 result.next++;
1118 else if (result.next >= V4L2_CID_LASTP1)
1119 result.next = V4L2_CID_PRIVATE_BASE;
1120 else
1121 goto enum_end;
1122 goto next;
1123 }
1124 res = -errno;
1125 spa_log_error(this->log, "'%s' VIDIOC_QUERYCTRL: %m", this->props.device);
1126 return res;
1127 }
1128 if (result.next & next_fl)
1129 result.next = queryctrl.id | next_fl;
1130 else
1131 result.next++;
1132
1133 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
1134 goto next;
1135
1136 if (port->n_controls >= MAX_CONTROLS)
1137 goto enum_end;
1138
1139 ctrl_id = queryctrl.id & ~next_fl;
1140
1141 spa_pod_builder_init(&b, buffer, sizeof(buffer));
1142
1143 prop_id = control_to_prop_id(this, ctrl_id);
1144
1145 port->controls[port->n_controls].id = prop_id;
1146 port->controls[port->n_controls].ctrl_id = ctrl_id;
1147 port->controls[port->n_controls].value = queryctrl.default_value;
1148
1149 spa_log_debug(this->log, "Control '%s' %d %d", queryctrl.name, prop_id, ctrl_id);
1150
1151 port->n_controls++;
1152
1153 switch (queryctrl.type) {
1154 case V4L2_CTRL_TYPE_INTEGER:
1155 param = spa_pod_builder_add_object(&b,
1156 SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
1157 SPA_PROP_INFO_id, SPA_POD_Id(prop_id),
1158 SPA_PROP_INFO_type, SPA_POD_CHOICE_STEP_Int(
1159 queryctrl.default_value,
1160 queryctrl.minimum,
1161 queryctrl.maximum,
1162 queryctrl.step),
1163 SPA_PROP_INFO_name, SPA_POD_String(queryctrl.name));
1164 break;
1165 case V4L2_CTRL_TYPE_BOOLEAN:
1166 param = spa_pod_builder_add_object(&b,
1167 SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
1168 SPA_PROP_INFO_id, SPA_POD_Id(prop_id),
1169 SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(queryctrl.default_value),
1170 SPA_PROP_INFO_name, SPA_POD_String(queryctrl.name));
1171 break;
1172 case V4L2_CTRL_TYPE_MENU:
1173 {
1174 struct v4l2_querymenu querymenu;
1175 struct spa_pod_builder_state state;
1176
1177 spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo);
1178 spa_pod_builder_add(&b,
1179 SPA_PROP_INFO_id, SPA_POD_Id(prop_id),
1180 SPA_PROP_INFO_type, SPA_POD_CHOICE_ENUM_Int(1, queryctrl.default_value),
1181 SPA_PROP_INFO_name, SPA_POD_String(queryctrl.name),
1182 0);
1183
1184 spa_zero(querymenu);
1185 querymenu.id = queryctrl.id;
1186
1187 spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0);
1188
1189 spa_pod_builder_get_state(&b, &state);
1190 spa_pod_builder_push_struct(&b, &f[1]);
1191 for (querymenu.index = queryctrl.minimum;
1192 querymenu.index <= queryctrl.maximum;
1193 querymenu.index++) {
1194 if (xioctl(dev->fd, VIDIOC_QUERYMENU, &querymenu) == 0) {
1195 spa_pod_builder_int(&b, querymenu.index);
1196 spa_pod_builder_string(&b, (const char *)querymenu.name);
1197 }
1198 }
1199 if (spa_pod_builder_pop(&b, &f[1]) == NULL) {
1200 spa_log_warn(this->log, "can't create Control '%s' overflow %d",
1201 queryctrl.name, b.state.offset);
1202 spa_pod_builder_reset(&b, &state);
1203 spa_pod_builder_none(&b);
1204 }
1205 param = spa_pod_builder_pop(&b, &f[0]);
1206 break;
1207 }
1208 case V4L2_CTRL_TYPE_INTEGER_MENU:
1209 case V4L2_CTRL_TYPE_BITMASK:
1210 case V4L2_CTRL_TYPE_BUTTON:
1211 case V4L2_CTRL_TYPE_INTEGER64:
1212 case V4L2_CTRL_TYPE_STRING:
1213 default:
1214 goto next;
1215
1216 }
1217 if (spa_pod_filter(&b, &result.param, param, filter) < 0)
1218 goto next;
1219
1220 spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
1221
1222 if (++count != num)
1223 goto next;
1224
1225 enum_end:
1226 res = 0;
1227 spa_v4l2_close(dev);
1228 return res;
1229 }
1230
mmap_read(struct impl * this)1231 static int mmap_read(struct impl *this)
1232 {
1233 struct port *port = &this->out_ports[0];
1234 struct spa_v4l2_device *dev = &port->dev;
1235 struct v4l2_buffer buf;
1236 struct buffer *b;
1237 struct spa_data *d;
1238 int64_t pts;
1239
1240 spa_zero(buf);
1241 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1242 buf.memory = port->memtype;
1243
1244 if (xioctl(dev->fd, VIDIOC_DQBUF, &buf) < 0)
1245 return -errno;
1246
1247 pts = SPA_TIMEVAL_TO_NSEC(&buf.timestamp);
1248 spa_log_trace(this->log, "v4l2 %p: have output %d", this, buf.index);
1249
1250 if (this->clock) {
1251 this->clock->nsec = pts;
1252 this->clock->rate = port->rate;
1253 this->clock->position = buf.sequence;
1254 this->clock->duration = 1;
1255 this->clock->delay = 0;
1256 this->clock->rate_diff = 1.0;
1257 this->clock->next_nsec = pts + 1000000000LL / port->rate.denom;
1258 }
1259
1260 b = &port->buffers[buf.index];
1261 if (b->h) {
1262 b->h->flags = 0;
1263 if (buf.flags & V4L2_BUF_FLAG_ERROR)
1264 b->h->flags |= SPA_META_HEADER_FLAG_CORRUPTED;
1265 b->h->offset = 0;
1266 b->h->seq = buf.sequence;
1267 b->h->pts = pts;
1268 b->h->dts_offset = 0;
1269 }
1270
1271 d = b->outbuf->datas;
1272 d[0].chunk->offset = 0;
1273 d[0].chunk->size = buf.bytesused;
1274 d[0].chunk->stride = port->fmt.fmt.pix.bytesperline;
1275 d[0].chunk->flags = 0;
1276 if (buf.flags & V4L2_BUF_FLAG_ERROR)
1277 d[0].chunk->flags |= SPA_CHUNK_FLAG_CORRUPTED;
1278
1279 spa_list_append(&port->queue, &b->link);
1280 return 0;
1281 }
1282
v4l2_on_fd_events(struct spa_source * source)1283 static void v4l2_on_fd_events(struct spa_source *source)
1284 {
1285 struct impl *this = source->data;
1286 struct spa_io_buffers *io;
1287 struct port *port = &this->out_ports[0];
1288 struct buffer *b;
1289
1290 if (source->rmask & SPA_IO_ERR) {
1291 struct port *port = &this->out_ports[0];
1292 spa_log_error(this->log, "'%p' error %08x", this->props.device, source->rmask);
1293 if (port->source.loop)
1294 spa_loop_remove_source(this->data_loop, &port->source);
1295 return;
1296 }
1297
1298 if (!(source->rmask & SPA_IO_IN)) {
1299 spa_log_warn(this->log, "v4l2 %p: spurious wakeup %d", this, source->rmask);
1300 return;
1301 }
1302
1303 if (mmap_read(this) < 0)
1304 return;
1305
1306 if (spa_list_is_empty(&port->queue))
1307 return;
1308
1309 io = port->io;
1310 if (io != NULL && io->status != SPA_STATUS_HAVE_DATA) {
1311 if (io->buffer_id < port->n_buffers)
1312 spa_v4l2_buffer_recycle(this, io->buffer_id);
1313
1314 b = spa_list_first(&port->queue, struct buffer, link);
1315 spa_list_remove(&b->link);
1316 SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING);
1317
1318 io->buffer_id = b->id;
1319 io->status = SPA_STATUS_HAVE_DATA;
1320 spa_log_trace(this->log, "v4l2 %p: now queued %d", this, b->id);
1321 }
1322 spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA);
1323 }
1324
spa_v4l2_use_buffers(struct impl * this,struct spa_buffer ** buffers,uint32_t n_buffers)1325 static int spa_v4l2_use_buffers(struct impl *this, struct spa_buffer **buffers, uint32_t n_buffers)
1326 {
1327 struct port *port = &this->out_ports[0];
1328 struct spa_v4l2_device *dev = &port->dev;
1329 struct v4l2_requestbuffers reqbuf;
1330 unsigned int i;
1331 struct spa_data *d;
1332
1333 if (n_buffers > 0) {
1334 d = buffers[0]->datas;
1335
1336 if (d[0].type == SPA_DATA_MemFd ||
1337 (d[0].type == SPA_DATA_MemPtr && d[0].data != NULL)) {
1338 port->memtype = V4L2_MEMORY_USERPTR;
1339 } else if (d[0].type == SPA_DATA_DmaBuf) {
1340 port->memtype = V4L2_MEMORY_DMABUF;
1341 } else {
1342 spa_log_error(this->log, "can't use buffers of type %d", d[0].type);
1343 return -EINVAL;
1344 }
1345 }
1346
1347 spa_zero(reqbuf);
1348 reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1349 reqbuf.memory = port->memtype;
1350 reqbuf.count = n_buffers;
1351
1352 if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
1353 spa_log_error(this->log, "'%s' VIDIOC_REQBUFS %m", this->props.device);
1354 return -errno;
1355 }
1356 spa_log_debug(this->log, "got %d buffers", reqbuf.count);
1357 if (reqbuf.count < n_buffers) {
1358 spa_log_error(this->log, "'%s' can't allocate enough buffers %d < %d",
1359 this->props.device, reqbuf.count, n_buffers);
1360 return -ENOMEM;
1361 }
1362
1363 for (i = 0; i < reqbuf.count; i++) {
1364 struct buffer *b;
1365
1366 b = &port->buffers[i];
1367 b->id = i;
1368 b->outbuf = buffers[i];
1369 b->flags = BUFFER_FLAG_OUTSTANDING;
1370 b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h));
1371
1372 spa_log_debug(this->log, "import buffer %p", buffers[i]);
1373
1374 if (buffers[i]->n_datas < 1) {
1375 spa_log_error(this->log, "invalid memory on buffer %p", buffers[i]);
1376 return -EINVAL;
1377 }
1378 d = buffers[i]->datas;
1379
1380 spa_zero(b->v4l2_buffer);
1381 b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1382 b->v4l2_buffer.memory = port->memtype;
1383 b->v4l2_buffer.index = i;
1384
1385 if (port->memtype == V4L2_MEMORY_USERPTR) {
1386 if (d[0].data == NULL) {
1387 void *data;
1388
1389 data = mmap(NULL,
1390 d[0].maxsize,
1391 PROT_READ | PROT_WRITE, MAP_SHARED,
1392 d[0].fd,
1393 d[0].mapoffset);
1394 if (data == MAP_FAILED)
1395 return -errno;
1396
1397 b->ptr = data;
1398 SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
1399 }
1400 else
1401 b->ptr = d[0].data;
1402
1403 b->v4l2_buffer.m.userptr = (unsigned long) b->ptr;
1404 b->v4l2_buffer.length = d[0].maxsize;
1405 }
1406 else if (port->memtype == V4L2_MEMORY_DMABUF) {
1407 b->v4l2_buffer.m.fd = d[0].fd;
1408 }
1409 else {
1410 spa_log_error(this->log, "invalid port memory %d",
1411 port->memtype);
1412 return -EIO;
1413 }
1414
1415 spa_v4l2_buffer_recycle(this, i);
1416 }
1417 port->n_buffers = reqbuf.count;
1418
1419 return 0;
1420 }
1421
1422 static int
mmap_init(struct impl * this,struct spa_buffer ** buffers,uint32_t n_buffers)1423 mmap_init(struct impl *this,
1424 struct spa_buffer **buffers, uint32_t n_buffers)
1425 {
1426 struct port *port = &this->out_ports[0];
1427 struct spa_v4l2_device *dev = &port->dev;
1428 struct v4l2_requestbuffers reqbuf;
1429 unsigned int i;
1430 bool use_expbuf = false;
1431
1432 port->memtype = V4L2_MEMORY_MMAP;
1433
1434 spa_zero(reqbuf);
1435 reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1436 reqbuf.memory = port->memtype;
1437 reqbuf.count = n_buffers;
1438
1439 if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
1440 spa_log_error(this->log, "'%s' VIDIOC_REQBUFS: %m", this->props.device);
1441 return -errno;
1442 }
1443
1444 spa_log_debug(this->log, "got %d buffers", reqbuf.count);
1445 n_buffers = reqbuf.count;
1446
1447 if (n_buffers < 2) {
1448 spa_log_error(this->log, "'%s' can't allocate enough buffers (%d)",
1449 this->props.device, n_buffers);
1450 return -ENOMEM;
1451 }
1452
1453 for (i = 0; i < n_buffers; i++) {
1454 struct buffer *b;
1455 struct spa_data *d;
1456
1457 if (buffers[i]->n_datas < 1) {
1458 spa_log_error(this->log, "invalid buffer data");
1459 return -EINVAL;
1460 }
1461
1462 b = &port->buffers[i];
1463 b->id = i;
1464 b->outbuf = buffers[i];
1465 b->flags = BUFFER_FLAG_OUTSTANDING;
1466 b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h));
1467
1468 spa_zero(b->v4l2_buffer);
1469 b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1470 b->v4l2_buffer.memory = port->memtype;
1471 b->v4l2_buffer.index = i;
1472
1473 if (xioctl(dev->fd, VIDIOC_QUERYBUF, &b->v4l2_buffer) < 0) {
1474 spa_log_error(this->log, "'%s' VIDIOC_QUERYBUF: %m", this->props.device);
1475 return -errno;
1476 }
1477
1478 if (b->v4l2_buffer.flags & V4L2_BUF_FLAG_QUEUED) {
1479 /* some drivers can give us an already queued buffer. */
1480 spa_log_warn(this->log, "buffer %d was already queued", i);
1481 n_buffers = i;
1482 break;
1483 }
1484
1485 d = buffers[i]->datas;
1486 d[0].mapoffset = 0;
1487 d[0].maxsize = b->v4l2_buffer.length;
1488 d[0].chunk->offset = 0;
1489 d[0].chunk->size = 0;
1490 d[0].chunk->stride = port->fmt.fmt.pix.bytesperline;
1491 d[0].chunk->flags = 0;
1492
1493 spa_log_debug(this->log, "data types %08x", d[0].type);
1494
1495 if (port->have_expbuf &&
1496 d[0].type != SPA_ID_INVALID &&
1497 (d[0].type & ((1u << SPA_DATA_DmaBuf)|(1u<<SPA_DATA_MemFd)))) {
1498 struct v4l2_exportbuffer expbuf;
1499
1500 spa_zero(expbuf);
1501 expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1502 expbuf.index = i;
1503 expbuf.flags = O_CLOEXEC | O_RDONLY;
1504 if (xioctl(dev->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
1505 if (errno == ENOTTY || errno == EINVAL) {
1506 spa_log_debug(this->log, "'%s' VIDIOC_EXPBUF not supported: %m",
1507 this->props.device);
1508 port->have_expbuf = false;
1509 goto fallback;
1510 }
1511 spa_log_error(this->log, "'%s' VIDIOC_EXPBUF: %m", this->props.device);
1512 return -errno;
1513 }
1514 if (d[0].type & (1u<<SPA_DATA_DmaBuf))
1515 d[0].type = SPA_DATA_DmaBuf;
1516 else
1517 d[0].type = SPA_DATA_MemFd;
1518 d[0].flags = SPA_DATA_FLAG_READABLE;
1519 d[0].fd = expbuf.fd;
1520 d[0].data = NULL;
1521 SPA_FLAG_SET(b->flags, BUFFER_FLAG_ALLOCATED);
1522 spa_log_debug(this->log, "EXPBUF fd:%d", expbuf.fd);
1523 use_expbuf = true;
1524 } else if (d[0].type & (1u << SPA_DATA_MemPtr)) {
1525 fallback:
1526 d[0].type = SPA_DATA_MemPtr;
1527 d[0].flags = SPA_DATA_FLAG_READABLE;
1528 d[0].fd = -1;
1529 d[0].mapoffset = b->v4l2_buffer.m.offset;
1530 d[0].data = mmap(NULL,
1531 b->v4l2_buffer.length,
1532 PROT_READ, MAP_SHARED,
1533 dev->fd,
1534 b->v4l2_buffer.m.offset);
1535 if (d[0].data == MAP_FAILED) {
1536 spa_log_error(this->log, "'%s' mmap: %m", this->props.device);
1537 return -errno;
1538 }
1539 b->ptr = d[0].data;
1540 SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
1541 spa_log_debug(this->log, "mmap offset:%u data:%p", d[0].mapoffset, b->ptr);
1542 use_expbuf = false;
1543 } else {
1544 spa_log_error(this->log, "unsupported data type:%08x", d[0].type);
1545 return -ENOTSUP;
1546 }
1547 spa_v4l2_buffer_recycle(this, i);
1548 }
1549 spa_log_info(this->log, "have %u buffers using %s", n_buffers,
1550 use_expbuf ? "EXPBUF" : "MMAP");
1551
1552 port->n_buffers = n_buffers;
1553
1554 return 0;
1555 }
1556
userptr_init(struct impl * this)1557 static int userptr_init(struct impl *this)
1558 {
1559 return -ENOTSUP;
1560 }
1561
read_init(struct impl * this)1562 static int read_init(struct impl *this)
1563 {
1564 return -ENOTSUP;
1565 }
1566
1567 static int
spa_v4l2_alloc_buffers(struct impl * this,struct spa_buffer ** buffers,uint32_t n_buffers)1568 spa_v4l2_alloc_buffers(struct impl *this,
1569 struct spa_buffer **buffers,
1570 uint32_t n_buffers)
1571 {
1572 int res;
1573 struct port *port = &this->out_ports[0];
1574 struct spa_v4l2_device *dev = &port->dev;
1575
1576 if (port->n_buffers > 0)
1577 return -EIO;
1578
1579 if (dev->cap.capabilities & V4L2_CAP_STREAMING) {
1580 if ((res = mmap_init(this, buffers, n_buffers)) < 0)
1581 if ((res = userptr_init(this)) < 0)
1582 return res;
1583 } else if (dev->cap.capabilities & V4L2_CAP_READWRITE) {
1584 if ((res = read_init(this)) < 0)
1585 return res;
1586 } else {
1587 spa_log_error(this->log, "invalid capabilities %08x",
1588 dev->cap.capabilities);
1589 return -EIO;
1590 }
1591
1592 return 0;
1593 }
1594
spa_v4l2_stream_on(struct impl * this)1595 static int spa_v4l2_stream_on(struct impl *this)
1596 {
1597 struct port *port = &this->out_ports[0];
1598 struct spa_v4l2_device *dev = &port->dev;
1599 enum v4l2_buf_type type;
1600
1601 if (dev->fd == -1)
1602 return -EIO;
1603
1604 if (!dev->have_format)
1605 return -EIO;
1606
1607 if (dev->active)
1608 return 0;
1609
1610 spa_log_debug(this->log, "starting");
1611
1612 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1613 if (xioctl(dev->fd, VIDIOC_STREAMON, &type) < 0) {
1614 spa_log_error(this->log, "'%s' VIDIOC_STREAMON: %m", this->props.device);
1615 return -errno;
1616 }
1617
1618 port->source.func = v4l2_on_fd_events;
1619 port->source.data = this;
1620 port->source.fd = dev->fd;
1621 port->source.mask = SPA_IO_IN | SPA_IO_ERR;
1622 port->source.rmask = 0;
1623 spa_loop_add_source(this->data_loop, &port->source);
1624
1625 dev->active = true;
1626
1627 return 0;
1628 }
1629
do_remove_source(struct spa_loop * loop,bool async,uint32_t seq,const void * data,size_t size,void * user_data)1630 static int do_remove_source(struct spa_loop *loop,
1631 bool async,
1632 uint32_t seq,
1633 const void *data,
1634 size_t size,
1635 void *user_data)
1636 {
1637 struct port *port = user_data;
1638 if (port->source.loop)
1639 spa_loop_remove_source(loop, &port->source);
1640 return 0;
1641 }
1642
spa_v4l2_stream_off(struct impl * this)1643 static int spa_v4l2_stream_off(struct impl *this)
1644 {
1645 struct port *port = &this->out_ports[0];
1646 struct spa_v4l2_device *dev = &port->dev;
1647 enum v4l2_buf_type type;
1648 uint32_t i;
1649
1650 if (!dev->active)
1651 return 0;
1652
1653 if (dev->fd == -1)
1654 return -EIO;
1655
1656 spa_log_debug(this->log, "stopping");
1657
1658 spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, port);
1659
1660 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1661 if (xioctl(dev->fd, VIDIOC_STREAMOFF, &type) < 0) {
1662 spa_log_error(this->log, "'%s' VIDIOC_STREAMOFF: %m", this->props.device);
1663 return -errno;
1664 }
1665 for (i = 0; i < port->n_buffers; i++) {
1666 struct buffer *b;
1667
1668 b = &port->buffers[i];
1669 if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING)) {
1670 if (xioctl(dev->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0)
1671 spa_log_warn(this->log, "VIDIOC_QBUF: %s", strerror(errno));
1672 }
1673 }
1674 spa_list_init(&port->queue);
1675 dev->active = false;
1676
1677 return 0;
1678 }
1679