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 ®istry_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