1 /* PipeWire
2  *
3  * Copyright © 2021 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 
25 #include "config.h"
26 
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <dlfcn.h>
30 #include <stdarg.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <pthread.h>
36 #include <limits.h>
37 #include <linux/videodev2.h>
38 
39 #include "pipewire-v4l2.h"
40 
41 #include <spa/utils/result.h>
42 #include <spa/pod/iter.h>
43 #include <spa/pod/parser.h>
44 #include <spa/pod/filter.h>
45 #include <spa/param/video/format-utils.h>
46 
47 #include <pipewire/pipewire.h>
48 
49 PW_LOG_TOPIC_STATIC(v4l2_log_topic, "v4l2");
50 #define PW_LOG_TOPIC_DEFAULT v4l2_log_topic
51 
52 #define MIN_BUFFERS	2u
53 #define MAX_BUFFERS	32u
54 #define DEFAULT_TIMEOUT	30
55 
56 #define DEFAULT_DRIVER		"PipeWire"
57 #define DEFAULT_CARD		"PipeWire Camera"
58 #define DEFAULT_BUS_INFO	"PipeWire"
59 
60 struct file_map {
61 	void *addr;
62 	struct file *file;
63 };
64 
65 struct fd_map {
66 	int fd;
67 	struct file *file;
68 };
69 
70 struct globals {
71 	struct fops old_fops;
72 
73 	pthread_mutex_t lock;
74 	struct pw_array fd_maps;
75 	struct pw_array file_maps;
76 };
77 
78 static struct globals globals;
79 
80 struct global;
81 
82 struct buffer_map {
83 	void *addr;
84 	uint32_t id;
85 };
86 
87 struct buffer {
88 	struct v4l2_buffer v4l2;
89 	struct pw_buffer *buf;
90 	uint32_t id;
91 };
92 
93 struct file {
94 	int ref;
95 
96 	struct pw_properties *props;
97 	struct pw_thread_loop *loop;
98 	struct pw_loop *l;
99 	struct pw_context *context;
100 
101 	struct pw_core *core;
102 	struct spa_hook core_listener;
103 
104 	int last_seq;
105 	int pending_seq;
106 	int error;
107 
108 	struct pw_registry *registry;
109 	struct spa_hook registry_listener;
110 
111 	struct spa_list globals;
112 	struct global *node;
113 
114 	struct pw_stream *stream;
115 	struct spa_hook stream_listener;
116 
117 	struct v4l2_format v4l2_format;
118 	uint32_t reqbufs;
119 
120 	struct buffer buffers[MAX_BUFFERS];
121 	uint32_t n_buffers;
122 	uint32_t size;
123 
124 	struct pw_array buffer_maps;
125 
126 	uint32_t last_fourcc;
127 
128 	unsigned int running:1;
129 	int fd;
130 };
131 
132 #define MAX_PARAMS	32
133 
134 struct global_info {
135 	const char *type;
136 	uint32_t version;
137 	const void *events;
138 	pw_destroy_t destroy;
139 	int (*init) (struct global *g);
140 };
141 
142 struct global {
143 	struct spa_list link;
144 
145 	struct file *file;
146 
147 	const struct global_info *ginfo;
148 
149 	uint32_t id;
150 	uint32_t permissions;
151 	struct pw_properties *props;
152 
153 	struct pw_proxy *proxy;
154 	struct spa_hook proxy_listener;
155 	struct spa_hook object_listener;
156 
157 	int changed;
158 	void *info;
159 	struct spa_list param_list;
160 	int param_seq[MAX_PARAMS];
161 
162 	union {
163 		struct {
164 #define NODE_FLAG_SOURCE	(1<<0)
165 #define NODE_FLAG_SINK		(1<<0)
166 			uint32_t flags;
167 			uint32_t device_id;
168 			int priority;
169 		} node;
170 	};
171 };
172 
173 struct param {
174 	struct spa_list link;
175 	uint32_t id;
176 	struct spa_pod *param;
177 };
178 
clear_params(struct spa_list * param_list,uint32_t id)179 static uint32_t clear_params(struct spa_list *param_list, uint32_t id)
180 {
181 	struct param *p, *t;
182 	uint32_t count = 0;
183 
184 	spa_list_for_each_safe(p, t, param_list, link) {
185 		if (id == SPA_ID_INVALID || p->id == id) {
186 			spa_list_remove(&p->link);
187 			free(p);
188 			count++;
189 		}
190 	}
191 	return count;
192 }
193 
add_param(struct spa_list * params,int seq,int * param_seq,uint32_t id,const struct spa_pod * param)194 static struct param *add_param(struct spa_list *params,
195 		int seq, int *param_seq, uint32_t id, const struct spa_pod *param)
196 {
197 	struct param *p;
198 
199 	if (id == SPA_ID_INVALID) {
200 		if (param == NULL || !spa_pod_is_object(param)) {
201 			errno = EINVAL;
202 			return NULL;
203 		}
204 		id = SPA_POD_OBJECT_ID(param);
205 	}
206 
207 	if (id >= MAX_PARAMS) {
208 		pw_log_error("too big param id %d", id);
209 		errno = EINVAL;
210 		return NULL;
211 	}
212 
213 	if (seq != param_seq[id]) {
214 		pw_log_debug("ignoring param %d, seq:%d != current_seq:%d",
215 				id, seq, param_seq[id]);
216 		errno = EBUSY;
217 		return NULL;
218 	}
219 
220 	p = malloc(sizeof(*p) + (param != NULL ? SPA_POD_SIZE(param) : 0));
221 	if (p == NULL)
222 		return NULL;
223 
224 	p->id = id;
225 	if (param != NULL) {
226 		p->param = SPA_PTROFF(p, sizeof(*p), struct spa_pod);
227 		memcpy(p->param, param, SPA_POD_SIZE(param));
228 	} else {
229 		clear_params(params, id);
230 		p->param = NULL;
231 	}
232 	spa_list_append(params, &p->link);
233 
234 	return p;
235 }
236 
237 #define ATOMIC_DEC(s)                   __atomic_sub_fetch(&(s), 1, __ATOMIC_SEQ_CST)
238 #define ATOMIC_INC(s)                   __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST)
239 
make_file(void)240 static struct file *make_file(void)
241 {
242 	struct file *file;
243 
244 	file = calloc(1, sizeof(*file));
245 	if (file == NULL)
246 		return NULL;
247 
248 	file->ref = 1;
249 	file->fd = -1;
250 	spa_list_init(&file->globals);
251 	pw_array_init(&file->buffer_maps, sizeof(struct buffer_map) * MAX_BUFFERS);
252 	return file;
253 }
254 
free_file(struct file * file)255 static void free_file(struct file *file)
256 {
257 	if (file->loop)
258 		pw_thread_loop_stop(file->loop);
259 
260 	if (file->registry) {
261 		spa_hook_remove(&file->registry_listener);
262 		pw_proxy_destroy((struct pw_proxy*)file->registry);
263 	}
264 	if (file->stream) {
265 		spa_hook_remove(&file->stream_listener);
266 		pw_stream_destroy(file->stream);
267 	}
268 	if (file->core) {
269 		spa_hook_remove(&file->core_listener);
270 		pw_core_disconnect(file->core);
271 	}
272 	if (file->context)
273 		pw_context_destroy(file->context);
274 	if (file->fd != -1)
275 		spa_system_close(file->l->system, file->fd);
276 	if (file->loop)
277 		pw_thread_loop_destroy(file->loop);
278 
279 	pw_array_clear(&file->buffer_maps);
280 	free(file);
281 }
282 
unref_file(struct file * file)283 static void unref_file(struct file *file)
284 {
285 	if (ATOMIC_DEC(file->ref) <= 0)
286 		free_file(file);
287 }
288 
add_fd_map(int fd,struct file * file)289 static int add_fd_map(int fd, struct file *file)
290 {
291 	struct fd_map *map;
292 	pthread_mutex_lock(&globals.lock);
293 	map = pw_array_add(&globals.fd_maps, sizeof(*map));
294 	if (map != NULL) {
295 		map->fd = fd;
296 		map->file = file;
297 		ATOMIC_INC(file->ref);
298 	}
299 	pthread_mutex_unlock(&globals.lock);
300 	return 0;
301 }
302 
303 /* must be called with `globals.lock` held */
find_fd_map_unlocked(int fd)304 static struct fd_map *find_fd_map_unlocked(int fd)
305 {
306 	struct fd_map *map;
307 
308 	pw_array_for_each(map, &globals.fd_maps) {
309 		if (map->fd == fd) {
310 			ATOMIC_INC(map->file->ref);
311 			return map;
312 		}
313 	}
314 
315 	return NULL;
316 }
317 
find_file(int fd)318 static struct file *find_file(int fd)
319 {
320 	pthread_mutex_lock(&globals.lock);
321 
322 	struct fd_map *map = find_fd_map_unlocked(fd);
323 	struct file *file = NULL;
324 
325 	if (map != NULL)
326 		file = map->file;
327 
328 	pthread_mutex_unlock(&globals.lock);
329 
330 	return file;
331 }
332 
remove_fd_map(int fd)333 static struct file *remove_fd_map(int fd)
334 {
335 	pthread_mutex_lock(&globals.lock);
336 
337 	struct fd_map *map = find_fd_map_unlocked(fd);
338 	struct file *file = NULL;
339 
340 	if (map != NULL) {
341 		file = map->file;
342 		pw_array_remove(&globals.fd_maps, map);
343 	}
344 
345 	pthread_mutex_unlock(&globals.lock);
346 
347 	if (file != NULL)
348 		unref_file(file);
349 
350 	return file;
351 }
352 
add_file_map(void * addr,struct file * file)353 static int add_file_map(void *addr, struct file *file)
354 {
355 	struct file_map *map;
356 	pthread_mutex_lock(&globals.lock);
357 	map = pw_array_add(&globals.file_maps, sizeof(*map));
358 	if (map != NULL) {
359 		map->addr = addr;
360 		map->file = file;
361 	}
362 	pthread_mutex_unlock(&globals.lock);
363 	return 0;
364 }
365 
366 /* must be called with `globals.lock` held */
find_file_map_unlocked(void * addr)367 static struct file_map *find_file_map_unlocked(void *addr)
368 {
369 	struct file_map *map;
370 
371 	pw_array_for_each(map, &globals.file_maps) {
372 		if (map->addr == addr)
373 			return map;
374 	}
375 
376 	return NULL;
377 }
remove_file_map(void * addr)378 static struct file *remove_file_map(void *addr)
379 {
380 	pthread_mutex_lock(&globals.lock);
381 
382 	struct file_map *map = find_file_map_unlocked(addr);
383 	struct file *file = NULL;
384 
385 	if (map != NULL) {
386 		file = map->file;
387 		pw_array_remove(&globals.file_maps, map);
388 	}
389 
390 	pthread_mutex_unlock(&globals.lock);
391 
392 	return file;
393 }
394 
add_buffer_map(struct file * file,void * addr,uint32_t id)395 static int add_buffer_map(struct file *file, void *addr, uint32_t id)
396 {
397 	struct buffer_map *map;
398 	map = pw_array_add(&file->buffer_maps, sizeof(*map));
399 	if (map != NULL) {
400 		map->addr = addr;
401 		map->id = id;
402 	}
403 	return 0;
404 }
find_buffer_map(struct file * file,void * addr)405 static struct buffer_map *find_buffer_map(struct file *file, void *addr)
406 {
407 	struct buffer_map *map;
408 	pw_array_for_each(map, &file->buffer_maps) {
409 		if (map->addr == addr)
410 			return map;
411 	}
412 	return NULL;
413 }
remove_buffer_map(struct file * file,struct buffer_map * map)414 static void remove_buffer_map(struct file *file, struct buffer_map *map)
415 {
416 	pw_array_remove(&file->buffer_maps, map);
417 }
418 
do_resync(struct file * file)419 static void do_resync(struct file *file)
420 {
421 	file->pending_seq = pw_core_sync(file->core, PW_ID_CORE, file->pending_seq);
422 }
423 
wait_resync(struct file * file)424 static int wait_resync(struct file *file)
425 {
426 	int res;
427 	do_resync(file);
428 
429 	while (true) {
430 		pw_thread_loop_wait(file->loop);
431 
432 		res = file->error;
433 		if (res < 0) {
434 			file->error = 0;
435 			return res;
436 		}
437 		if (file->pending_seq == file->last_seq)
438 			break;
439 	}
440 	return 0;
441 }
442 
on_sync_reply(void * data,uint32_t id,int seq)443 static void on_sync_reply(void *data, uint32_t id, int seq)
444 {
445 	struct file *file = data;
446 
447 	if (id != PW_ID_CORE)
448 		return;
449 
450 	file->last_seq = seq;
451 	if (file->pending_seq == seq)
452 		pw_thread_loop_signal(file->loop, false);
453 }
454 
on_error(void * data,uint32_t id,int seq,int res,const char * message)455 static void on_error(void *data, uint32_t id, int seq, int res, const char *message)
456 {
457 	struct file *file = data;
458 
459 	pw_log_warn("%p: error id:%u seq:%d res:%d (%s): %s", file,
460 			id, seq, res, spa_strerror(res), message);
461 
462 	if (id == PW_ID_CORE) {
463 		switch (res) {
464 		case -ENOENT:
465 			break;
466 		default:
467 			file->error = res;
468 		}
469 	}
470 	pw_thread_loop_signal(file->loop, false);
471 }
472 
473 static const struct pw_core_events core_events = {
474 	PW_VERSION_CORE_EVENTS,
475 	.done = on_sync_reply,
476 	.error = on_error,
477 };
478 
479 /** node */
node_event_info(void * object,const struct pw_node_info * info)480 static void node_event_info(void *object, const struct pw_node_info *info)
481 {
482 	struct global *g = object;
483 	struct file *file = g->file;
484 	const char *str;
485 	uint32_t i;
486 
487 	info = g->info = pw_node_info_merge(g->info, info, g->changed == 0);
488 
489 	pw_log_debug("update %d %"PRIu64, g->id, info->change_mask);
490 
491 	if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS && info->props) {
492 		if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)))
493 			g->node.device_id = atoi(str);
494 		else
495 			g->node.device_id = SPA_ID_INVALID;
496 
497 		if ((str = spa_dict_lookup(info->props, PW_KEY_PRIORITY_SESSION)))
498 			g->node.priority = atoi(str);
499 		if ((str = spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS))) {
500 			if (spa_streq(str, "Video/Sink"))
501 				g->node.flags |= NODE_FLAG_SINK;
502 			else if (spa_streq(str, "Video/Source"))
503 				g->node.flags |= NODE_FLAG_SOURCE;
504 		}
505 	}
506 	if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) {
507 		for (i = 0; i < info->n_params; i++) {
508 			uint32_t id = info->params[i].id;
509 			int res;
510 
511 			if (info->params[i].user == 0)
512 				continue;
513 			info->params[i].user = 0;
514 
515 			if (id >= MAX_PARAMS) {
516 				pw_log_error("too big param id %d", id);
517 				continue;
518 			}
519 
520 			if (id != SPA_PARAM_EnumFormat)
521 				continue;
522 
523 			if (!(info->params[i].flags & SPA_PARAM_INFO_READ))
524 				continue;
525 
526 			res = pw_node_enum_params((struct pw_node*)g->proxy,
527 					++g->param_seq[id], id, 0, -1, NULL);
528 			if (SPA_RESULT_IS_ASYNC(res))
529 				g->param_seq[id] = res;
530 		}
531 	}
532 	do_resync(file);
533 }
534 
node_event_param(void * object,int seq,uint32_t id,uint32_t index,uint32_t next,const struct spa_pod * param)535 static void node_event_param(void *object, int seq,
536                 uint32_t id, uint32_t index, uint32_t next,
537                 const struct spa_pod *param)
538 {
539 	struct global *g = object;
540 
541 	pw_log_debug("update param %d %d %d %d", g->id, id, seq, g->param_seq[id]);
542 	add_param(&g->param_list, seq, g->param_seq, id, param);
543 }
544 
545 static const struct pw_node_events node_events = {
546 	PW_VERSION_NODE_EVENTS,
547 	.info = node_event_info,
548 	.param = node_event_param,
549 };
550 
551 static const struct global_info node_info = {
552 	.type = PW_TYPE_INTERFACE_Node,
553 	.version = PW_VERSION_NODE,
554 	.events = &node_events,
555 };
556 
557 /** proxy */
proxy_removed(void * data)558 static void proxy_removed(void *data)
559 {
560 	struct global *g = data;
561 	pw_proxy_destroy(g->proxy);
562 }
563 
proxy_destroy(void * data)564 static void proxy_destroy(void *data)
565 {
566 	struct global *g = data;
567 	spa_list_remove(&g->link);
568 	g->proxy = NULL;
569 }
570 
571 static const struct pw_proxy_events proxy_events = {
572 	PW_VERSION_PROXY_EVENTS,
573 	.removed = proxy_removed,
574 	.destroy = proxy_destroy
575 };
576 
registry_event_global(void * data,uint32_t id,uint32_t permissions,const char * type,uint32_t version,const struct spa_dict * props)577 static void registry_event_global(void *data, uint32_t id,
578 		uint32_t permissions, const char *type, uint32_t version,
579 		const struct spa_dict *props)
580 {
581 	struct file *file = data;
582 	const struct global_info *info = NULL;
583 	struct pw_proxy *proxy;
584 	const char *str;
585 
586 	pw_log_debug("got %d %s", id, type);
587 
588 	if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
589 		if (file->node != NULL)
590 			return;
591 
592 		if (props == NULL ||
593 		    ((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL) ||
594 		    ((!spa_streq(str, "Video/Sink")) &&
595 		     (!spa_streq(str, "Video/Source"))))
596 			return;
597 
598 		pw_log_debug("found node %d type:%s", id, str);
599 		info = &node_info;
600 	}
601 	if (info) {
602 		struct global *g;
603 
604 		proxy = pw_registry_bind(file->registry,
605 				id, info->type, info->version,
606 				sizeof(struct global));
607 
608 		g = pw_proxy_get_user_data(proxy);
609 		g->file = file;
610 		g->ginfo = info;
611 		g->id = id;
612 		g->permissions = permissions;
613 		g->props = props ? pw_properties_new_dict(props) : NULL;
614 		g->proxy = proxy;
615 		spa_list_init(&g->param_list);
616 		spa_list_append(&file->globals, &g->link);
617 
618 		pw_proxy_add_listener(proxy,
619 				&g->proxy_listener,
620 				&proxy_events, g);
621 
622 		if (info->events) {
623 			pw_proxy_add_object_listener(proxy,
624 					&g->object_listener,
625 					info->events, g);
626 		}
627 		if (info->init)
628 			info->init(g);
629 
630 		file->node = g;
631 
632 		do_resync(file);
633 	}
634 }
635 
find_global(struct file * file,uint32_t id)636 static struct global *find_global(struct file *file, uint32_t id)
637 {
638 	struct global *g;
639 	spa_list_for_each(g, &file->globals, link) {
640 		if (g->id == id)
641 			return g;
642 	}
643 	return NULL;
644 }
645 
registry_event_global_remove(void * data,uint32_t id)646 static void registry_event_global_remove(void *data, uint32_t id)
647 {
648 	struct file *file = data;
649 	struct global *g;
650 
651 	if ((g = find_global(file, id)) == NULL)
652 		return;
653 
654 	pw_proxy_destroy(g->proxy);
655 }
656 
657 static const struct pw_registry_events registry_events = {
658 	PW_VERSION_REGISTRY_EVENTS,
659 	.global = registry_event_global,
660 	.global_remove = registry_event_global_remove,
661 };
662 
v4l2_openat(int dirfd,const char * path,int oflag,mode_t mode)663 static int v4l2_openat(int dirfd, const char *path, int oflag, mode_t mode)
664 {
665 	int res;
666 	struct file *file;
667 
668 	if (!spa_strstartswith(path, "/dev/video0"))
669 		return globals.old_fops.openat(dirfd, path, oflag, mode);
670 
671 	if ((file = make_file()) == NULL)
672 		goto error;
673 
674 	file->props = pw_properties_new(
675 			PW_KEY_CLIENT_API, "v4l2",
676 			NULL);
677 	file->loop = pw_thread_loop_new("v4l2", NULL);
678 	if (file->loop == NULL)
679 		goto error;
680 
681 	file->l = pw_thread_loop_get_loop(file->loop);
682 	file->context = pw_context_new(file->l,
683 			pw_properties_copy(file->props), 0);
684 	if (file->context == NULL)
685 		goto error;
686 
687 	pw_thread_loop_start(file->loop);
688 
689 	pw_thread_loop_lock(file->loop);
690 
691 	file->core = pw_context_connect(file->context,
692 			pw_properties_copy(file->props), 0);
693 	if (file->core == NULL)
694 		goto error_unlock;
695 
696 	pw_core_add_listener(file->core,
697 			&file->core_listener,
698 			&core_events, file);
699 	file->registry = pw_core_get_registry(file->core,
700 			PW_VERSION_REGISTRY, 0);
701 	if (file->registry == NULL)
702 		goto error_unlock;
703 
704 	pw_registry_add_listener(file->registry,
705 			&file->registry_listener,
706 			&registry_events, file);
707 
708 	res = wait_resync(file);
709 	if (res < 0) {
710 		errno = -res;
711 		goto error_unlock;
712 	}
713 	if (file->node == NULL) {
714 		errno = -ENOENT;
715 		goto error_unlock;
716 	}
717 	pw_thread_loop_unlock(file->loop);
718 
719 	res = file->fd = spa_system_eventfd_create(file->l->system,
720 		SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
721 	if (res < 0)
722 		goto error;
723 
724 	pw_log_info("path:%s oflag:%d mode:%d -> %d (%s)", path, oflag, mode,
725 			res, strerror(res < 0 ? errno : 0));
726 
727 	add_fd_map(res, file);
728 
729 	return res;
730 
731 error_unlock:
732 	pw_thread_loop_unlock(file->loop);
733 error:
734 	if (file)
735 		free_file(file);
736 	return -1;
737 }
738 
v4l2_dup(int oldfd)739 static int v4l2_dup(int oldfd)
740 {
741 	int res;
742 	struct file *file;
743 
744 	res = globals.old_fops.dup(oldfd);
745 	if (res < 0)
746 		return res;
747 
748 	if ((file = find_file(oldfd)) != NULL) {
749 		add_fd_map(res, file);
750 		unref_file(file);
751 		pw_log_info("fd:%d -> %d (%s)", oldfd,
752 				res, strerror(res < 0 ? errno : 0));
753 	}
754 	return res;
755 }
756 
v4l2_close(int fd)757 static int v4l2_close(int fd)
758 {
759 	struct file *file;
760 
761 	if ((file = remove_fd_map(fd)) == NULL)
762 		return globals.old_fops.close(fd);
763 
764 	if (fd != file->fd)
765 		spa_system_close(file->l->system, fd);
766 
767 	unref_file(file);
768 
769 	return 0;
770 }
771 
772 #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
773 
vidioc_querycap(struct file * file,struct v4l2_capability * arg)774 static int vidioc_querycap(struct file *file, struct v4l2_capability *arg)
775 {
776 	int res = 0;
777 
778 	spa_scnprintf((char*)arg->driver, sizeof(arg->driver), "%s", DEFAULT_DRIVER);
779 	spa_scnprintf((char*)arg->card, sizeof(arg->card), "%s", DEFAULT_CARD);
780 	spa_scnprintf((char*)arg->bus_info, sizeof(arg->bus_info), "%s:%d", DEFAULT_BUS_INFO, 1);
781 
782 	arg->version = KERNEL_VERSION(5, 2, 0);
783 	arg->device_caps = V4L2_CAP_VIDEO_CAPTURE
784 		| V4L2_CAP_STREAMING
785 		| V4L2_CAP_EXT_PIX_FORMAT;
786 	arg->capabilities = arg->device_caps | V4L2_CAP_DEVICE_CAPS;
787 	memset(arg->reserved, 0, sizeof(arg->reserved));
788 
789 	pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
790 	return res;
791 }
792 
793 struct format_info {
794 	uint32_t fourcc;
795 	uint32_t media_type;
796 	uint32_t media_subtype;
797 	uint32_t format;
798 	uint32_t bpp;
799 	const char *desc;
800 };
801 
802 #define MAKE_FORMAT(fcc,mt,mst,bpp,fmt)	\
803 	{ V4L2_PIX_FMT_ ## fcc, SPA_MEDIA_TYPE_ ## mt, SPA_MEDIA_SUBTYPE_ ## mst, SPA_VIDEO_FORMAT_ ## fmt, bpp, #fcc }
804 
805 static const struct format_info format_info[] = {
806 	/* RGB formats */
807 	MAKE_FORMAT(RGB332, video, raw, 4, UNKNOWN),
808 	MAKE_FORMAT(ARGB555, video, raw, 4, UNKNOWN),
809 	MAKE_FORMAT(XRGB555, video, raw, 4, RGB15),
810 	MAKE_FORMAT(ARGB555X, video, raw, 4, UNKNOWN),
811 	MAKE_FORMAT(XRGB555X, video, raw, 4, BGR15),
812 	MAKE_FORMAT(RGB565, video, raw, 4, RGB16),
813 	MAKE_FORMAT(RGB565X, video, raw, 4, UNKNOWN),
814 	MAKE_FORMAT(BGR666, video, raw, 4, UNKNOWN),
815 	MAKE_FORMAT(BGR24, video, raw, 4, BGR),
816 	MAKE_FORMAT(RGB24, video, raw, 4, RGB),
817 	MAKE_FORMAT(ABGR32, video, raw, 4, BGRA),
818 	MAKE_FORMAT(XBGR32, video, raw, 4, BGRx),
819 	MAKE_FORMAT(ARGB32, video, raw, 4, ARGB),
820 	MAKE_FORMAT(XRGB32, video, raw, 4, xRGB),
821 
822 	/* Deprecated Packed RGB Image Formats (alpha ambiguity) */
823 	MAKE_FORMAT(RGB444, video, raw, 2, UNKNOWN),
824 	MAKE_FORMAT(RGB555, video, raw, 2, RGB15),
825 	MAKE_FORMAT(RGB555X, video, raw, 2, BGR15),
826 	MAKE_FORMAT(BGR32, video, raw, 4, BGRx),
827 	MAKE_FORMAT(RGB32, video, raw, 4, xRGB),
828 
829         /* Grey formats */
830 	MAKE_FORMAT(GREY, video, raw, 1, GRAY8),
831 	MAKE_FORMAT(Y4, video, raw, 1, UNKNOWN),
832 	MAKE_FORMAT(Y6, video, raw, 1, UNKNOWN),
833 	MAKE_FORMAT(Y10, video, raw, 2, UNKNOWN),
834 	MAKE_FORMAT(Y12, video, raw, 2, UNKNOWN),
835 	MAKE_FORMAT(Y16, video, raw, 2, GRAY16_LE),
836 	MAKE_FORMAT(Y16_BE, video, raw, 2, GRAY16_BE),
837 	MAKE_FORMAT(Y10BPACK, video, raw, 2, UNKNOWN),
838 
839 	/* Palette formats */
840 	MAKE_FORMAT(PAL8, video, raw, 1, UNKNOWN),
841 
842 	/* Chrominance formats */
843 	MAKE_FORMAT(UV8, video, raw, 2, UNKNOWN),
844 
845 	/* Luminance+Chrominance formats */
846 	MAKE_FORMAT(YVU410, video, raw, 1, YVU9),
847 	MAKE_FORMAT(YVU420, video, raw, 1, YV12),
848 	MAKE_FORMAT(YVU420M, video, raw, 1, UNKNOWN),
849 	MAKE_FORMAT(YUYV, video, raw, 2, YUY2),
850 	MAKE_FORMAT(YYUV, video, raw, 2, UNKNOWN),
851 	MAKE_FORMAT(YVYU, video, raw, 2, YVYU),
852 	MAKE_FORMAT(UYVY, video, raw, 2, UYVY),
853 	MAKE_FORMAT(VYUY, video, raw, 2, UNKNOWN),
854 	MAKE_FORMAT(YUV422P, video, raw, 1, Y42B),
855 	MAKE_FORMAT(YUV411P, video, raw, 1, Y41B),
856 	MAKE_FORMAT(Y41P, video, raw, 1, UNKNOWN),
857 	MAKE_FORMAT(YUV444, video, raw, 1, UNKNOWN),
858 	MAKE_FORMAT(YUV555, video, raw, 1, UNKNOWN),
859 	MAKE_FORMAT(YUV565, video, raw, 1, UNKNOWN),
860 	MAKE_FORMAT(YUV32, video, raw, 1, UNKNOWN),
861 	MAKE_FORMAT(YUV410, video, raw, 1, YUV9),
862 	MAKE_FORMAT(YUV420, video, raw, 1, I420),
863 	MAKE_FORMAT(YUV420M, video, raw, 1, I420),
864 	MAKE_FORMAT(HI240, video, raw, 1, UNKNOWN),
865 	MAKE_FORMAT(HM12, video, raw, 1, UNKNOWN),
866 	MAKE_FORMAT(M420, video, raw, 1, UNKNOWN),
867 
868 	/* two planes -- one Y, one Cr + Cb interleaved  */
869 	MAKE_FORMAT(NV12, video, raw, 1, NV12),
870 	MAKE_FORMAT(NV12M, video, raw, 1,  NV12),
871 	MAKE_FORMAT(NV12MT, video, raw, 1,  NV12_64Z32),
872 	MAKE_FORMAT(NV12MT_16X16, video, raw, 1, UNKNOWN),
873 	MAKE_FORMAT(NV21, video, raw, 1, NV21),
874 	MAKE_FORMAT(NV21M, video, raw, 1, NV21),
875 	MAKE_FORMAT(NV16, video, raw, 1, NV16),
876 	MAKE_FORMAT(NV16M, video, raw, 1, NV16),
877 	MAKE_FORMAT(NV61, video, raw, 1, NV61),
878 	MAKE_FORMAT(NV61M, video, raw, 1, NV61),
879 	MAKE_FORMAT(NV24, video, raw, 1, NV24),
880 	MAKE_FORMAT(NV42, video, raw, 1, UNKNOWN),
881 
882 	/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
883 	MAKE_FORMAT(SBGGR8, video, bayer, 1, UNKNOWN),
884 	MAKE_FORMAT(SGBRG8, video, bayer, 1, UNKNOWN),
885 	MAKE_FORMAT(SGRBG8, video, bayer, 1, UNKNOWN),
886 	MAKE_FORMAT(SRGGB8, video, bayer, 1, UNKNOWN),
887 
888 	/* compressed formats */
889 	MAKE_FORMAT(MJPEG, video, mjpg, 1, ENCODED),
890 	MAKE_FORMAT(JPEG, video, mjpg, 1, ENCODED),
891 	MAKE_FORMAT(PJPG, video, mjpg, 1, ENCODED),
892 	MAKE_FORMAT(DV, video, dv, 1, ENCODED),
893 	MAKE_FORMAT(MPEG, video, mpegts, 1, ENCODED),
894 	MAKE_FORMAT(H264, video, h264, 1, ENCODED),
895 	MAKE_FORMAT(H264_NO_SC, video, h264, 1, ENCODED),
896 	MAKE_FORMAT(H264_MVC, video, h264, 1, ENCODED),
897 	MAKE_FORMAT(H263, video, h263, 1, ENCODED),
898 	MAKE_FORMAT(MPEG1, video, mpeg1, 1, ENCODED),
899 	MAKE_FORMAT(MPEG2, video, mpeg2, 1, ENCODED),
900 	MAKE_FORMAT(MPEG4, video, mpeg4, 1, ENCODED),
901 	MAKE_FORMAT(XVID, video, xvid, 1, ENCODED),
902 	MAKE_FORMAT(VC1_ANNEX_G, video, vc1, 1, ENCODED),
903 	MAKE_FORMAT(VC1_ANNEX_L, video, vc1, 1, ENCODED),
904 	MAKE_FORMAT(VP8, video, vp8, 1, ENCODED),
905 
906 	/*  Vendor-specific formats   */
907 	MAKE_FORMAT(WNVA, video, raw, 1, UNKNOWN),
908 	MAKE_FORMAT(SN9C10X, video, raw, 1, UNKNOWN),
909 	MAKE_FORMAT(PWC1, video, raw, 1, UNKNOWN),
910 	MAKE_FORMAT(PWC2, video, raw, 1, UNKNOWN),
911 };
912 
format_info_from_media_type(uint32_t type,uint32_t subtype,uint32_t format)913 static const struct format_info *format_info_from_media_type(uint32_t type,
914 		uint32_t subtype, uint32_t format)
915 {
916 	size_t i;
917 	for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) {
918 		if ((format_info[i].media_type == type) &&
919 		    (format_info[i].media_subtype == subtype) &&
920 		    (format == 0 || format_info[i].format == format))
921 			return &format_info[i];
922 	}
923 	return NULL;
924 }
925 
format_info_from_fourcc(uint32_t fourcc)926 static const struct format_info *format_info_from_fourcc(uint32_t fourcc)
927 {
928 	size_t i;
929 	for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) {
930 		if (format_info[i].fourcc == fourcc)
931 			return &format_info[i];
932 	}
933 	return NULL;
934 }
935 
format_to_info(const struct v4l2_format * arg,struct spa_video_info * info)936 static int format_to_info(const struct v4l2_format *arg, struct spa_video_info *info)
937 {
938 	const struct format_info *fi;
939 
940 	pw_log_info("type: %u", arg->type);
941 	pw_log_info("width: %u", arg->fmt.pix.width);
942 	pw_log_info("height: %u", arg->fmt.pix.height);
943 	pw_log_info("fmt: %u", arg->fmt.pix.pixelformat);
944 
945 	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
946 		return -EINVAL;
947 
948 	fi = format_info_from_fourcc(arg->fmt.pix.pixelformat);
949 	if (fi == NULL)
950 		return -EINVAL;
951 
952 	spa_zero(*info);
953 	info->media_type = fi->media_type;
954 	info->media_subtype = fi->media_subtype;
955 
956 	switch (info->media_subtype) {
957 	case SPA_MEDIA_SUBTYPE_raw:
958 		info->info.raw.format = fi->format;
959 		info->info.raw.size.width = arg->fmt.pix.width;
960 		info->info.raw.size.height = arg->fmt.pix.height;
961 		break;
962 	case SPA_MEDIA_SUBTYPE_h264:
963 		info->info.h264.size.width = arg->fmt.pix.width;
964 		info->info.h264.size.height = arg->fmt.pix.height;
965 		break;
966 	case SPA_MEDIA_SUBTYPE_mjpg:
967 	case SPA_MEDIA_SUBTYPE_jpeg:
968 		info->info.mjpg.size.width = arg->fmt.pix.width;
969 		info->info.mjpg.size.height = arg->fmt.pix.height;
970 		break;
971 	default:
972 		return -EINVAL;
973 	}
974 	return 0;
975 }
976 
info_to_param(struct spa_pod_builder * builder,uint32_t id,struct spa_video_info * info)977 static struct spa_pod *info_to_param(struct spa_pod_builder *builder, uint32_t id,
978 		struct spa_video_info *info)
979 {
980 	struct spa_pod *pod;
981 
982 	if (info->media_type != SPA_MEDIA_TYPE_video)
983 		return NULL;
984 
985 	switch (info->media_subtype) {
986 	case SPA_MEDIA_SUBTYPE_raw:
987 		pod = spa_format_video_raw_build(builder, id, &info->info.raw);
988 		break;
989 	case SPA_MEDIA_SUBTYPE_mjpg:
990 	case SPA_MEDIA_SUBTYPE_jpeg:
991 		pod = spa_format_video_mjpg_build(builder, id, &info->info.mjpg);
992 		break;
993 	case SPA_MEDIA_SUBTYPE_h264:
994 		pod = spa_format_video_h264_build(builder, id, &info->info.h264);
995 		break;
996 	default:
997 		return NULL;
998 	}
999 	return pod;
1000 }
1001 
fmt_to_param(struct spa_pod_builder * builder,uint32_t id,const struct v4l2_format * fmt)1002 static struct spa_pod *fmt_to_param(struct spa_pod_builder *builder, uint32_t id,
1003 		const struct v4l2_format *fmt)
1004 {
1005 	struct spa_video_info info;
1006 	if (format_to_info(fmt, &info) < 0)
1007 		return NULL;
1008 	return info_to_param(builder, id, &info);
1009 }
1010 
param_to_info(const struct spa_pod * param,struct spa_video_info * info)1011 static int param_to_info(const struct spa_pod *param, struct spa_video_info *info)
1012 {
1013 	int res;
1014 
1015 	spa_zero(*info);
1016 	if (spa_format_parse(param, &info->media_type, &info->media_subtype) < 0)
1017 		return -EINVAL;
1018 
1019 	if (info->media_type != SPA_MEDIA_TYPE_video)
1020 		return -EINVAL;
1021 
1022 	switch (info->media_subtype) {
1023 	case SPA_MEDIA_SUBTYPE_raw:
1024 		res = spa_format_video_raw_parse(param, &info->info.raw);
1025 		break;
1026 	case SPA_MEDIA_SUBTYPE_h264:
1027 		res = spa_format_video_h264_parse(param, &info->info.h264);
1028 		break;
1029 	case SPA_MEDIA_SUBTYPE_mjpg:
1030 	case SPA_MEDIA_SUBTYPE_jpeg:
1031 		res = spa_format_video_mjpg_parse(param, &info->info.mjpg);
1032 		break;
1033 	default:
1034 		return -EINVAL;
1035 	}
1036 	return res;
1037 }
1038 
info_to_fmt(const struct spa_video_info * info,struct v4l2_format * fmt)1039 static int info_to_fmt(const struct spa_video_info *info, struct v4l2_format *fmt)
1040 {
1041 	const struct format_info *fi;
1042 	uint32_t format;
1043 
1044 	if (info->media_type != SPA_MEDIA_TYPE_video)
1045 		return -EINVAL;
1046 
1047 	if (info->media_subtype == SPA_MEDIA_SUBTYPE_raw) {
1048 		format = info->info.raw.format;
1049 	} else {
1050 		format = SPA_VIDEO_FORMAT_ENCODED;
1051 	}
1052 
1053 	fi = format_info_from_media_type(info->media_type, info->media_subtype,
1054 			format);
1055 	if (fi == NULL)
1056 		return -EINVAL;
1057 
1058 	spa_zero(*fmt);
1059 	fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1060 	fmt->fmt.pix.pixelformat = fi->fourcc;
1061 	fmt->fmt.pix.field = V4L2_FIELD_NONE;
1062 
1063 	switch (info->media_subtype) {
1064 	case SPA_MEDIA_SUBTYPE_raw:
1065 		fmt->fmt.pix.width = info->info.raw.size.width;
1066 		fmt->fmt.pix.height = info->info.raw.size.height;
1067 		break;
1068 	case SPA_MEDIA_SUBTYPE_mjpg:
1069 	case SPA_MEDIA_SUBTYPE_jpeg:
1070 		fmt->fmt.pix.width = info->info.mjpg.size.width;
1071 		fmt->fmt.pix.height = info->info.mjpg.size.height;
1072 		break;
1073 	case SPA_MEDIA_SUBTYPE_h264:
1074 		fmt->fmt.pix.width = info->info.h264.size.width;
1075 		fmt->fmt.pix.height = info->info.h264.size.height;
1076 		break;
1077 	default:
1078 		return -EINVAL;
1079 	}
1080 	fmt->fmt.pix.bytesperline = SPA_ROUND_UP_N(fmt->fmt.pix.width, 4) * fi->bpp;
1081 	fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline *
1082 		SPA_ROUND_UP_N(fmt->fmt.pix.height, 2);
1083 	return 0;
1084 }
1085 
param_to_fmt(const struct spa_pod * param,struct v4l2_format * fmt)1086 static int param_to_fmt(const struct spa_pod *param, struct v4l2_format *fmt)
1087 {
1088 	struct spa_video_info info;
1089 	struct spa_pod *copy;
1090 	int res;
1091 
1092 	copy = spa_pod_copy(param);
1093 	spa_pod_fixate(copy);
1094 	res = param_to_info(copy, &info);
1095 	free(copy);
1096 
1097 	if (res < 0)
1098 		return -EINVAL;
1099 	if (info_to_fmt(&info, fmt) < 0)
1100 		return -EINVAL;
1101 	return 0;
1102 }
1103 
on_stream_param_changed(void * data,uint32_t id,const struct spa_pod * param)1104 static void on_stream_param_changed(void *data, uint32_t id, const struct spa_pod *param)
1105 {
1106 	struct file *file = data;
1107 	const struct spa_pod *params[4];
1108 	uint32_t n_params = 0;
1109 	uint8_t buffer[4096];
1110 	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
1111 	uint32_t buffers, size;
1112 	struct v4l2_format fmt;
1113 
1114 	if (param == NULL || id != SPA_PARAM_Format)
1115 		return;
1116 
1117 	if (param_to_fmt(param, &fmt) < 0)
1118 		return;
1119 
1120 	file->v4l2_format = fmt;
1121 
1122 	buffers = file->reqbufs;
1123 	size = 0;
1124 
1125 	params[n_params++] = spa_pod_builder_add_object(&b,
1126 			SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
1127 			SPA_PARAM_BUFFERS_buffers, SPA_POD_Int(buffers),
1128 			SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
1129 			SPA_PARAM_BUFFERS_size,    SPA_POD_CHOICE_RANGE_Int(size, 0, INT_MAX),
1130 			SPA_PARAM_BUFFERS_stride,  SPA_POD_CHOICE_RANGE_Int(0, 0, INT_MAX),
1131 			SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int((1<<SPA_DATA_MemFd)));
1132 
1133 	pw_stream_update_params(file->stream, params, n_params);
1134 
1135 }
1136 
on_stream_state_changed(void * data,enum pw_stream_state old,enum pw_stream_state state,const char * error)1137 static void on_stream_state_changed(void *data, enum pw_stream_state old,
1138 		enum pw_stream_state state, const char *error)
1139 {
1140 	struct file *file = data;
1141 
1142 	pw_log_info("%p: state %s", file, pw_stream_state_as_string(state));
1143 
1144 	switch (state) {
1145 	case PW_STREAM_STATE_ERROR:
1146 		break;
1147 	case PW_STREAM_STATE_UNCONNECTED:
1148 		break;
1149 	case PW_STREAM_STATE_CONNECTING:
1150 	case PW_STREAM_STATE_PAUSED:
1151 	case PW_STREAM_STATE_STREAMING:
1152 		break;
1153 	}
1154 	pw_thread_loop_signal(file->loop, false);
1155 }
1156 
on_stream_add_buffer(void * data,struct pw_buffer * b)1157 static void on_stream_add_buffer(void *data, struct pw_buffer *b)
1158 {
1159 	struct file *file = data;
1160 	uint32_t id = file->n_buffers;
1161 	struct buffer *buf = &file->buffers[id];
1162 	struct v4l2_buffer vb;
1163 	struct spa_data *d = &b->buffer->datas[0];
1164 
1165 	file->size = d->maxsize;
1166 
1167 	pw_log_info("%p: id:%d fd:%"PRIi64" size:%u", file, id, d->fd, file->size);
1168 
1169 	spa_zero(vb);
1170 	vb.index = id;
1171 	vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1172 	vb.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1173 	vb.memory = V4L2_MEMORY_MMAP;
1174 	vb.m.offset = id * file->size;
1175 	vb.length = file->size;
1176 
1177 	buf->v4l2 = vb;
1178 	buf->id = id;
1179 	buf->buf = b;
1180 	b->user_data = buf;
1181 
1182 	file->n_buffers++;
1183 }
1184 
on_stream_remove_buffer(void * data,struct pw_buffer * b)1185 static void on_stream_remove_buffer(void *data, struct pw_buffer *b)
1186 {
1187 	struct file *file = data;
1188 	file->n_buffers--;
1189 }
1190 
on_stream_process(void * data)1191 static void on_stream_process(void *data)
1192 {
1193 	struct file *file = data;
1194         spa_system_eventfd_write(file->l->system, file->fd, 1);
1195 }
1196 
1197 static const struct pw_stream_events stream_events = {
1198 	PW_VERSION_STREAM_EVENTS,
1199 	.param_changed = on_stream_param_changed,
1200 	.state_changed = on_stream_state_changed,
1201 	.add_buffer = on_stream_add_buffer,
1202 	.remove_buffer = on_stream_remove_buffer,
1203 	.process = on_stream_process,
1204 };
1205 
vidioc_enum_framesizes(struct file * file,struct v4l2_frmsizeenum * arg)1206 static int vidioc_enum_framesizes(struct file *file, struct v4l2_frmsizeenum *arg)
1207 {
1208 	uint32_t count = 0;
1209 	struct global *g = file->node;
1210 	struct param *p;
1211 	bool found = false;
1212 
1213 	pw_log_info("index: %u", arg->index);
1214 	pw_log_info("format: %.4s", (char*)&arg->pixel_format);
1215 
1216 	pw_thread_loop_lock(file->loop);
1217 	spa_list_for_each(p, &g->param_list, link) {
1218 		const struct format_info *fi;
1219 		uint32_t media_type, media_subtype, format;
1220 		struct spa_rectangle size;
1221 
1222 		if (p->id != SPA_PARAM_EnumFormat || p->param == NULL)
1223 			continue;
1224 
1225 		if (spa_format_parse(p->param, &media_type, &media_subtype) < 0)
1226 			continue;
1227 		if (media_type != SPA_MEDIA_TYPE_video)
1228 			continue;
1229 		if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
1230 			if (spa_pod_parse_object(p->param,
1231 					SPA_TYPE_OBJECT_Format, NULL,
1232 					SPA_FORMAT_VIDEO_format, SPA_POD_Id(&format)) < 0)
1233 				continue;
1234 		} else {
1235 			format = SPA_VIDEO_FORMAT_ENCODED;
1236 		}
1237 
1238 		fi = format_info_from_media_type(media_type, media_subtype, format);
1239 		if (fi == NULL)
1240 			continue;
1241 
1242 		if (fi->fourcc != arg->pixel_format)
1243 			continue;
1244 		if (spa_pod_parse_object(p->param,
1245 				SPA_TYPE_OBJECT_Format, NULL,
1246 				SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&size)) < 0)
1247 			continue;
1248 
1249 		arg->type = V4L2_FRMSIZE_TYPE_DISCRETE;
1250 		arg->discrete.width = size.width;
1251 		arg->discrete.height = size.height;
1252 
1253 		pw_log_debug("count:%d %d %dx%d", count, fi->fourcc,
1254 				size.width, size.height);
1255 		if (count == arg->index) {
1256 			found = true;
1257 			break;
1258 		}
1259 		count++;
1260 	}
1261 	pw_thread_loop_unlock(file->loop);
1262 
1263 	if (!found)
1264 		return -EINVAL;
1265 
1266 	switch (arg->type) {
1267 	case V4L2_FRMSIZE_TYPE_DISCRETE:
1268 		pw_log_info("type: discrete");
1269 		pw_log_info("width: %u", arg->discrete.width);
1270 		pw_log_info("height: %u", arg->discrete.height);
1271 		break;
1272 	case V4L2_FRMSIZE_TYPE_CONTINUOUS:
1273 	case V4L2_FRMSIZE_TYPE_STEPWISE:
1274 		pw_log_info("type: stepwise");
1275 		pw_log_info("min-width: %u", arg->stepwise.min_width);
1276 		pw_log_info("max-width: %u", arg->stepwise.max_width);
1277 		pw_log_info("step-width: %u", arg->stepwise.step_width);
1278 		pw_log_info("min-height: %u", arg->stepwise.min_height);
1279 		pw_log_info("max-height: %u", arg->stepwise.max_height);
1280 		pw_log_info("step-height: %u", arg->stepwise.step_height);
1281 		break;
1282 	}
1283 
1284 	memset(arg->reserved, 0, sizeof(arg->reserved));
1285 
1286 	return 0;
1287 }
1288 
vidioc_enum_fmt(struct file * file,struct v4l2_fmtdesc * arg)1289 static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *arg)
1290 {
1291 	uint32_t count = 0, last_fourcc = 0;
1292 	struct global *g = file->node;
1293 	struct param *p;
1294 	bool found = false;
1295 
1296 	pw_log_info("index: %u", arg->index);
1297 	pw_log_info("type: %u", arg->type);
1298 
1299 	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1300 		return -EINVAL;
1301 
1302 	pw_thread_loop_lock(file->loop);
1303 	spa_list_for_each(p, &g->param_list, link) {
1304 		const struct format_info *fi;
1305 		uint32_t media_type, media_subtype, format;
1306 
1307 		if (p->id != SPA_PARAM_EnumFormat || p->param == NULL)
1308 			continue;
1309 
1310 		if (spa_format_parse(p->param, &media_type, &media_subtype) < 0)
1311 			continue;
1312 		if (media_type != SPA_MEDIA_TYPE_video)
1313 			continue;
1314 		if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
1315 			if (spa_pod_parse_object(p->param,
1316 					SPA_TYPE_OBJECT_Format, NULL,
1317 					SPA_FORMAT_VIDEO_format, SPA_POD_Id(&format)) < 0)
1318 				continue;
1319 		} else {
1320 			format = SPA_VIDEO_FORMAT_ENCODED;
1321 		}
1322 
1323 		fi = format_info_from_media_type(media_type, media_subtype, format);
1324 		if (fi == NULL)
1325 			continue;
1326 
1327 		if (fi->fourcc == last_fourcc)
1328 			continue;
1329 		pw_log_info("count:%d %d %d", count, fi->fourcc, last_fourcc);
1330 
1331 		arg->flags = fi->format == SPA_VIDEO_FORMAT_ENCODED ? V4L2_FMT_FLAG_COMPRESSED : 0;
1332 		arg->pixelformat = fi->fourcc;
1333 		last_fourcc = fi->fourcc;
1334 		if (count == arg->index) {
1335 			found = true;
1336 			break;
1337 		}
1338 		count++;
1339 	}
1340 	pw_thread_loop_unlock(file->loop);
1341 
1342 	if (!found)
1343 		return -EINVAL;
1344 
1345 	pw_log_info("format: %u", arg->pixelformat);
1346 	pw_log_info("flags: %u", arg->type);
1347 	memset(arg->reserved, 0, sizeof(arg->reserved));
1348 
1349 	return 0;
1350 }
1351 
vidioc_g_fmt(struct file * file,struct v4l2_format * arg)1352 static int vidioc_g_fmt(struct file *file, struct v4l2_format *arg)
1353 {
1354 	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1355 		return -EINVAL;
1356 
1357 	*arg = file->v4l2_format;
1358 	return 0;
1359 }
1360 
score_diff(struct v4l2_format * fmt,struct v4l2_format * tmp)1361 static int score_diff(struct v4l2_format *fmt, struct v4l2_format *tmp)
1362 {
1363 	int score = 0;
1364 	if (fmt->fmt.pix.pixelformat != tmp->fmt.pix.pixelformat)
1365 		score += 20000;
1366 	score += SPA_ABS((int)fmt->fmt.pix.width - (int)tmp->fmt.pix.width);
1367 	score += SPA_ABS((int)fmt->fmt.pix.height - (int)tmp->fmt.pix.height);
1368 	return score;
1369 }
1370 
try_format(struct file * file,struct v4l2_format * fmt)1371 static int try_format(struct file *file, struct v4l2_format *fmt)
1372 {
1373 	struct param *p;
1374 	struct global *g = file->node;
1375 	struct v4l2_format best_fmt = *fmt;
1376 	int best = -1;
1377 
1378 	pw_log_info("in: type: %u", fmt->type);
1379 	pw_log_info("in: format: %.4s", (char*)&fmt->fmt.pix.pixelformat);
1380 	pw_log_info("in: width: %u", fmt->fmt.pix.width);
1381 	pw_log_info("in: height: %u", fmt->fmt.pix.height);
1382 	pw_log_info("in: field: %u", fmt->fmt.pix.field);
1383 	spa_list_for_each(p, &g->param_list, link) {
1384 		struct v4l2_format tmp;
1385 		int score;
1386 
1387 		if (p->id != SPA_PARAM_EnumFormat || p->param == NULL)
1388 			continue;
1389 
1390 		if (param_to_fmt(p->param, &tmp) < 0)
1391 			continue;
1392 
1393 		score = score_diff(fmt, &tmp);
1394 		pw_log_debug("check: type: %u", tmp.type);
1395 		pw_log_debug("check: format: %.4s", (char*)&tmp.fmt.pix.pixelformat);
1396 		pw_log_debug("check: width: %u", tmp.fmt.pix.width);
1397 		pw_log_debug("check: height: %u", tmp.fmt.pix.height);
1398 		pw_log_debug("check: score: %d best:%d", score, best);
1399 
1400 		if (best == -1 || score < best) {
1401 			best = score;
1402 			best_fmt = tmp;
1403 		}
1404 	}
1405 	*fmt = best_fmt;
1406 	pw_log_info("out: format: %.4s", (char*)&fmt->fmt.pix.pixelformat);
1407 	pw_log_info("out: width: %u", fmt->fmt.pix.width);
1408 	pw_log_info("out: height: %u", fmt->fmt.pix.height);
1409 	pw_log_info("out: field: %u", fmt->fmt.pix.field);
1410 	pw_log_info("out: size: %u", fmt->fmt.pix.sizeimage);
1411 	return 0;
1412 }
1413 
disconnect_stream(struct file * file)1414 static int disconnect_stream(struct file *file)
1415 {
1416 	if (file->stream != NULL) {
1417 		pw_stream_destroy(file->stream);
1418 		file->stream = NULL;
1419 		file->n_buffers = 0;
1420 	}
1421 	return 0;
1422 }
1423 
connect_stream(struct file * file)1424 static int connect_stream(struct file *file)
1425 {
1426 	int res;
1427 	struct global *g = file->node;
1428 	const char *str;
1429 	struct timespec abstime;
1430 	const char *error = NULL;
1431 	uint8_t buffer[1024];
1432 	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
1433 	const struct spa_pod *params[1];
1434 	struct pw_properties *props;
1435 
1436 	params[0] = fmt_to_param(&b, SPA_PARAM_EnumFormat, &file->v4l2_format);
1437 	if (params[0] == NULL) {
1438 		res = -EINVAL;
1439 		goto exit;
1440 	}
1441 
1442 	disconnect_stream(file);
1443 
1444 	props = NULL;
1445 	if ((str = getenv("PIPEWIRE_PROPS")) != NULL)
1446 		props = pw_properties_new_string(str);
1447 	if (props == NULL)
1448 		props = pw_properties_new(NULL, NULL);
1449 	if (props == NULL) {
1450 		res = -errno;
1451 		goto exit;
1452 	}
1453 
1454 	pw_properties_set(props, PW_KEY_CLIENT_API, "v4l2");
1455 	pw_properties_setf(props, PW_KEY_APP_NAME, "%s", pw_get_prgname());
1456 
1457 	if (pw_properties_get(props, PW_KEY_MEDIA_TYPE) == NULL)
1458 		pw_properties_set(props, PW_KEY_MEDIA_TYPE, "Video");
1459 	if (pw_properties_get(props, PW_KEY_MEDIA_CATEGORY) == NULL)
1460 		pw_properties_set(props, PW_KEY_MEDIA_CATEGORY, "Capture");
1461 
1462 	file->stream = pw_stream_new(file->core, "v4l2 capture", props);
1463 	if (file->stream == NULL) {
1464 		res = -errno;
1465 		goto exit;
1466 	}
1467 
1468 	pw_stream_add_listener(file->stream,
1469 			&file->stream_listener,
1470 			&stream_events, file);
1471 
1472 	file->error = 0;
1473 
1474 	pw_stream_connect(file->stream,
1475 			PW_DIRECTION_INPUT,
1476 			g->id,
1477 			PW_STREAM_FLAG_DONT_RECONNECT |
1478 			PW_STREAM_FLAG_AUTOCONNECT |
1479 			PW_STREAM_FLAG_RT_PROCESS,
1480 			params, 1);
1481 
1482 	pw_thread_loop_get_time (file->loop, &abstime,
1483 			DEFAULT_TIMEOUT * SPA_NSEC_PER_SEC);
1484 
1485 	while (true) {
1486 		enum pw_stream_state state = pw_stream_get_state(file->stream, &error);
1487 
1488 		if (state == PW_STREAM_STATE_STREAMING)
1489 			break;
1490 
1491 		if (state == PW_STREAM_STATE_ERROR) {
1492 			res = -EIO;
1493 			goto exit;
1494 		}
1495 		if (file->error < 0) {
1496 			res = file->error;
1497 			goto exit;
1498 		}
1499 		if (pw_thread_loop_timed_wait_full(file->loop, &abstime) < 0) {
1500 			res = -ETIMEDOUT;
1501 			goto exit;
1502 		}
1503 	}
1504 	/* pause stream */
1505 	res = pw_stream_set_active(file->stream, false);
1506 exit:
1507 	return res;
1508 }
1509 
vidioc_s_fmt(struct file * file,struct v4l2_format * arg)1510 static int vidioc_s_fmt(struct file *file, struct v4l2_format *arg)
1511 {
1512 	int res;
1513 
1514 	pw_thread_loop_lock(file->loop);
1515 	if ((res = try_format(file, arg)) < 0)
1516 		goto exit_unlock;
1517 
1518 	file->v4l2_format = *arg;
1519 
1520 exit_unlock:
1521 	pw_thread_loop_unlock(file->loop);
1522 	return res;
1523 }
vidioc_try_fmt(struct file * file,struct v4l2_format * arg)1524 static int vidioc_try_fmt(struct file *file, struct v4l2_format *arg)
1525 {
1526 	int res;
1527 
1528 	pw_thread_loop_lock(file->loop);
1529 	res = try_format(file, arg);
1530 	pw_thread_loop_unlock(file->loop);
1531 	return res;
1532 }
1533 
vidioc_enuminput(struct file * file,struct v4l2_input * arg)1534 static int vidioc_enuminput(struct file *file, struct v4l2_input *arg)
1535 {
1536 	if (arg->index != 0)
1537 		return -EINVAL;
1538 
1539 	spa_zero(*arg);
1540 	spa_scnprintf((char*)arg->name, sizeof(arg->name), "%s", DEFAULT_CARD);
1541         arg->type = V4L2_INPUT_TYPE_CAMERA;
1542 
1543         return 0;
1544 }
vidioc_g_input(struct file * file,int * arg)1545 static int vidioc_g_input(struct file *file, int *arg)
1546 {
1547 	*arg = 0;
1548         return 0;
1549 }
vidioc_s_input(struct file * file,int * arg)1550 static int vidioc_s_input(struct file *file, int *arg)
1551 {
1552 	if (*arg != 0)
1553 		return -EINVAL;
1554         return 0;
1555 }
1556 
vidioc_reqbufs(struct file * file,struct v4l2_requestbuffers * arg)1557 static int vidioc_reqbufs(struct file *file, struct v4l2_requestbuffers *arg)
1558 {
1559 	int res;
1560 
1561 	pw_log_info("count: %u", arg->count);
1562 	pw_log_info("type: %u", arg->type);
1563 	pw_log_info("memory: %u", arg->memory);
1564 
1565 	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1566 		return -EINVAL;
1567 	if (arg->memory != V4L2_MEMORY_MMAP)
1568 		return -EINVAL;
1569 
1570 	pw_thread_loop_lock(file->loop);
1571 
1572 	if (arg->count == 0) {
1573 		if (pw_array_get_len(&file->buffer_maps, struct buffer_map) != 0) {
1574 			res = -EBUSY;
1575 			goto exit_unlock;
1576 		}
1577 		if (file->running) {
1578 			res = -EBUSY;
1579 			goto exit_unlock;
1580 		}
1581 		file->reqbufs = 0;
1582 		res = disconnect_stream(file);
1583 	} else {
1584 		file->reqbufs = arg->count;
1585 
1586 		if ((res = connect_stream(file)) < 0)
1587 			goto exit_unlock;
1588 
1589 		arg->count = file->n_buffers;
1590 	}
1591 #ifdef V4L2_BUF_CAP_SUPPORTS_MMAP
1592 	arg->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;
1593 #endif
1594 	memset(arg->reserved, 0, sizeof(arg->reserved));
1595 
1596 	pw_log_info("result count: %u", arg->count);
1597 
1598 exit_unlock:
1599 	pw_thread_loop_unlock(file->loop);
1600 	return res;
1601 }
1602 
vidioc_querybuf(struct file * file,struct v4l2_buffer * arg)1603 static int vidioc_querybuf(struct file *file, struct v4l2_buffer *arg)
1604 {
1605 	int res;
1606 
1607 	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1608 		return -EINVAL;
1609 
1610 	pw_thread_loop_lock(file->loop);
1611 	if (arg->index >= file->n_buffers) {
1612 		res = -EINVAL;
1613 		goto exit_unlock;
1614 	}
1615 	*arg = file->buffers[arg->index].v4l2;
1616 
1617 	res = 0;
1618 
1619 exit_unlock:
1620 	pw_thread_loop_unlock(file->loop);
1621 
1622 	return res;
1623 }
1624 
vidioc_qbuf(struct file * file,struct v4l2_buffer * arg)1625 static int vidioc_qbuf(struct file *file, struct v4l2_buffer *arg)
1626 {
1627 	int res = 0;
1628 	struct buffer *buf;
1629 
1630 	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1631 		return -EINVAL;
1632 	if (arg->memory != V4L2_MEMORY_MMAP)
1633 		return -EINVAL;
1634 
1635 	pw_thread_loop_lock(file->loop);
1636 	if (arg->index >= file->n_buffers) {
1637 		res = -EINVAL;
1638 		goto exit;
1639 	}
1640 	buf = &file->buffers[arg->index];
1641 
1642 	if (SPA_FLAG_IS_SET(buf->v4l2.flags, V4L2_BUF_FLAG_QUEUED)) {
1643 		res = -EINVAL;
1644 		goto exit;
1645 	}
1646 
1647 	SPA_FLAG_SET(buf->v4l2.flags, V4L2_BUF_FLAG_QUEUED);
1648 	arg->flags = buf->v4l2.flags;
1649 
1650 	pw_stream_queue_buffer(file->stream, buf->buf);
1651 	pw_log_debug("file:%p %d -> %d (%s)", file, arg->index, res, spa_strerror(res));
1652 
1653 exit:
1654 	pw_thread_loop_unlock(file->loop);
1655 
1656 	return res;
1657 }
vidioc_dqbuf(struct file * file,struct v4l2_buffer * arg)1658 static int vidioc_dqbuf(struct file *file, struct v4l2_buffer *arg)
1659 {
1660 	int res = 0;
1661 	struct pw_buffer *b;
1662 	struct buffer *buf;
1663 	uint64_t val;
1664 	struct spa_data *d;
1665 
1666 	if (arg->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1667 		return -EINVAL;
1668 	if (arg->memory != V4L2_MEMORY_MMAP)
1669 		return -EINVAL;
1670 
1671 	pw_thread_loop_lock(file->loop);
1672 	if (arg->index >= file->n_buffers) {
1673 		res = -EINVAL;
1674 		goto exit_unlock;
1675 	}
1676 	if (!file->running) {
1677 		res = -EINVAL;
1678 		goto exit_unlock;
1679 	}
1680 
1681 	b = pw_stream_dequeue_buffer(file->stream);
1682 	if (b == NULL) {
1683 		res = -EAGAIN;
1684 		goto exit_unlock;
1685 	}
1686 	spa_system_eventfd_read(file->l->system, file->fd, &val);
1687 
1688 	buf = b->user_data;
1689 	d = &buf->buf->buffer->datas[0];
1690 	SPA_FLAG_CLEAR(buf->v4l2.flags, V4L2_BUF_FLAG_QUEUED);
1691 
1692 	SPA_FLAG_UPDATE(buf->v4l2.flags, V4L2_BUF_FLAG_ERROR,
1693 		SPA_FLAG_IS_SET(d->chunk->flags, SPA_CHUNK_FLAG_CORRUPTED));
1694 
1695 	buf->v4l2.bytesused = d->chunk->size;
1696 	*arg = buf->v4l2;
1697 
1698 exit_unlock:
1699 	pw_thread_loop_unlock(file->loop);
1700 
1701 	pw_log_debug("file:%p %d -> %d (%s)", file, arg->index, res, spa_strerror(res));
1702 	return res;
1703 }
1704 
vidioc_streamon(struct file * file,int * arg)1705 static int vidioc_streamon(struct file *file, int *arg)
1706 {
1707 	int res;
1708 
1709 	pw_log_info("file:%p -> %d", file, *arg);
1710 
1711 	if (*arg != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1712 		return -EINVAL;
1713 
1714 	pw_thread_loop_lock(file->loop);
1715 	if (file->n_buffers == 0) {
1716 		res = -EINVAL;
1717 		goto exit_unlock;
1718 	}
1719 	if (file->running) {
1720 		res = 0;
1721 		goto exit_unlock;
1722 	}
1723 	res = pw_stream_set_active(file->stream, true);
1724 	if (res >= 0)
1725 		file->running = true;
1726 exit_unlock:
1727 	pw_thread_loop_unlock(file->loop);
1728 
1729 	pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
1730 	return res;
1731 }
vidioc_streamoff(struct file * file,int * arg)1732 static int vidioc_streamoff(struct file *file, int *arg)
1733 {
1734 	int res;
1735 
1736 	if (*arg != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1737 		return -EINVAL;
1738 
1739 	pw_thread_loop_lock(file->loop);
1740 	if (!file->running) {
1741 		res = 0;
1742 		goto exit_unlock;
1743 	}
1744 	res = pw_stream_set_active(file->stream, false);
1745 	file->running = false;
1746 
1747 exit_unlock:
1748 	pw_thread_loop_unlock(file->loop);
1749 
1750 	pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
1751 	return res;
1752 }
1753 
v4l2_ioctl(int fd,unsigned long int request,void * arg)1754 static int v4l2_ioctl(int fd, unsigned long int request, void *arg)
1755 {
1756 	int res;
1757 	struct file *file;
1758 
1759 	if ((file = find_file(fd)) == NULL)
1760 		return globals.old_fops.ioctl(fd, request, arg);
1761 
1762 #ifdef __FreeBSD__
1763 	if (arg == NULL && (request & IOC_DIRMASK != IOC_VOID)) {
1764 #else
1765 	if (arg == NULL && (_IOC_DIR(request) & (_IOC_WRITE | _IOC_READ))) {
1766 #endif
1767 		res = -EFAULT;
1768 		goto done;
1769 	}
1770 
1771 	switch (request & 0xffffffff) {
1772 	case VIDIOC_QUERYCAP:
1773 		res = vidioc_querycap(file, (struct v4l2_capability *)arg);
1774 		break;
1775 	case VIDIOC_ENUM_FRAMESIZES:
1776 		res = vidioc_enum_framesizes(file, (struct v4l2_frmsizeenum *)arg);
1777 		break;
1778 	case VIDIOC_ENUM_FMT:
1779 		res = vidioc_enum_fmt(file, (struct v4l2_fmtdesc *)arg);
1780 		break;
1781 	case VIDIOC_G_FMT:
1782 		res = vidioc_g_fmt(file, (struct v4l2_format *)arg);
1783 		break;
1784 	case VIDIOC_S_FMT:
1785 		res = vidioc_s_fmt(file, (struct v4l2_format *)arg);
1786 		break;
1787 	case VIDIOC_TRY_FMT:
1788 		res = vidioc_try_fmt(file, (struct v4l2_format *)arg);
1789 		break;
1790 	case VIDIOC_ENUMINPUT:
1791 		res = vidioc_enuminput(file, (struct v4l2_input *)arg);
1792 		break;
1793 	case VIDIOC_G_INPUT:
1794 		res = vidioc_g_input(file, (int *)arg);
1795 		break;
1796 	case VIDIOC_S_INPUT:
1797 		res = vidioc_s_input(file, (int *)arg);
1798 		break;
1799 	case VIDIOC_REQBUFS:
1800 		res = vidioc_reqbufs(file, (struct v4l2_requestbuffers *)arg);
1801 		break;
1802 	case VIDIOC_QUERYBUF:
1803 		res = vidioc_querybuf(file, (struct v4l2_buffer *)arg);
1804 		break;
1805 	case VIDIOC_QBUF:
1806 		res = vidioc_qbuf(file, (struct v4l2_buffer *)arg);
1807 		break;
1808 	case VIDIOC_DQBUF:
1809 		res = vidioc_dqbuf(file, (struct v4l2_buffer *)arg);
1810 		break;
1811 	case VIDIOC_STREAMON:
1812 		res = vidioc_streamon(file, (int *)arg);
1813 		break;
1814 	case VIDIOC_STREAMOFF:
1815 		res = vidioc_streamoff(file, (int *)arg);
1816 		break;
1817 	default:
1818 		res = -ENOTTY;
1819 		break;
1820 	}
1821 done:
1822 	if (res < 0) {
1823 		errno = -res;
1824 		res = -1;
1825 	}
1826 	pw_log_debug("fd:%d request:%lx nr:%d arg:%p -> %d (%s)",
1827 			fd, request, (int)_IOC_NR(request), arg,
1828 			res, strerror(res < 0 ? errno : 0));
1829 
1830 	unref_file(file);
1831 
1832 	return res;
1833 }
1834 
1835 static void *v4l2_mmap(void *addr, size_t length, int prot,
1836 			int flags, int fd, off64_t offset)
1837 {
1838 	void *res;
1839 	struct file *file;
1840 	off64_t id;
1841 	struct pw_map_range range;
1842 	struct buffer *buf;
1843 	struct spa_data *data;
1844 
1845 	if ((file = find_file(fd)) == NULL)
1846 		return globals.old_fops.mmap(addr, length, prot, flags, fd, offset);
1847 
1848 	pw_thread_loop_lock(file->loop);
1849 	if (file->size == 0) {
1850 		errno = EIO;
1851 		res = MAP_FAILED;
1852 		goto error_unlock;
1853 	}
1854 	id = offset / file->size;
1855 	if ((id * file->size) != offset || file->size != length) {
1856 		errno = EINVAL;
1857 		res = MAP_FAILED;
1858 		goto error_unlock;
1859 	}
1860 	buf = &file->buffers[id];
1861 	data = &buf->buf->buffer->datas[0];
1862 
1863         pw_map_range_init(&range, data->mapoffset, data->maxsize, 1024);
1864 
1865 	if (!SPA_FLAG_IS_SET(data->flags, SPA_DATA_FLAG_READABLE))
1866 		prot &= ~PROT_READ;
1867 	if (!SPA_FLAG_IS_SET(data->flags, SPA_DATA_FLAG_WRITABLE))
1868 		prot &= ~PROT_WRITE;
1869 
1870 	res = globals.old_fops.mmap(addr, range.size, prot, flags, data->fd, range.offset);
1871 
1872 	add_file_map(file, addr);
1873 	add_buffer_map(file, addr, id);
1874 	SPA_FLAG_SET(buf->v4l2.flags, V4L2_BUF_FLAG_MAPPED);
1875 
1876 	pw_log_info("addr:%p length:%u prot:%d flags:%d fd:%"PRIi64" offset:%u -> %p (%s)" ,
1877 			addr, range.size, prot, flags, data->fd, range.offset,
1878 			res, strerror(res == MAP_FAILED ? errno : 0));
1879 
1880 error_unlock:
1881 	pw_thread_loop_unlock(file->loop);
1882 	unref_file(file);
1883 	return res;
1884 }
1885 
1886 static int v4l2_munmap(void *addr, size_t length)
1887 {
1888 	int res;
1889 	struct buffer_map *bmap;
1890 	struct file *file;
1891 
1892 	if ((file = remove_file_map(addr)) == NULL)
1893 		return globals.old_fops.munmap(addr, length);
1894 
1895 	pw_thread_loop_lock(file->loop);
1896 
1897 	bmap = find_buffer_map(file, addr);
1898 	if (bmap == NULL) {
1899 		res = -EINVAL;
1900 		goto exit_unlock;
1901 	}
1902 	res = globals.old_fops.munmap(addr, length);
1903 
1904 	pw_log_info("addr:%p length:%zu -> %d (%s)", addr, length,
1905 			res, strerror(res < 0 ? errno : 0));
1906 
1907 	file->buffers[bmap->id].v4l2.flags &= ~V4L2_BUF_FLAG_MAPPED;
1908 	remove_buffer_map(file, bmap);
1909 
1910 exit_unlock:
1911 	pw_thread_loop_unlock(file->loop);
1912 
1913 	return res;
1914 }
1915 
1916 const struct fops fops = {
1917 	.openat = v4l2_openat,
1918 	.dup = v4l2_dup,
1919 	.close = v4l2_close,
1920 	.ioctl = v4l2_ioctl,
1921 	.mmap = v4l2_mmap,
1922 	.munmap = v4l2_munmap,
1923 };
1924 
1925 static void initialize(void)
1926 {
1927 	globals.old_fops.openat = dlsym(RTLD_NEXT, "openat64");
1928 	globals.old_fops.dup = dlsym(RTLD_NEXT, "dup");
1929 	globals.old_fops.close = dlsym(RTLD_NEXT, "close");
1930 	globals.old_fops.ioctl = dlsym(RTLD_NEXT, "ioctl");
1931 	globals.old_fops.mmap = dlsym(RTLD_NEXT, "mmap64");
1932 	globals.old_fops.munmap = dlsym(RTLD_NEXT, "munmap");
1933 
1934 	pw_init(NULL, NULL);
1935 	PW_LOG_TOPIC_INIT(v4l2_log_topic);
1936 
1937 	pthread_mutex_init(&globals.lock, NULL);
1938 	pw_array_init(&globals.file_maps, 1024);
1939 	pw_array_init(&globals.fd_maps, 256);
1940 }
1941 
1942 const struct fops *get_fops(void)
1943 {
1944 	static pthread_once_t initialized = PTHREAD_ONCE_INIT;
1945         pthread_once(&initialized, initialize);
1946 	return &fops;
1947 }
1948