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