From e7edf2f55b7eb71b69984110f3c8e31b8ac6285e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 5 Aug 2022 18:42:22 +0200 Subject: [PATCH 001/151] Backported dkms-patch from Ubuntu Closes: https://github.com/umlaeute/v4l2loopback/issues/498 --- dkms.conf | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dkms.conf b/dkms.conf index f697db2b..7d5ed75c 100644 --- a/dkms.conf +++ b/dkms.conf @@ -3,8 +3,15 @@ PACKAGE_VERSION="0.12.7" if [ -f $kernel_source_dir/.config ]; then . $kernel_source_dir/.config - if [ "${CONFIG_VIDEO_V4L2:-n}" = "n" ]; then - BUILD_EXCLUSIVE_KERNEL="REQUIRES CONFIG_VIDEO_V4L2" + if ! { echo "$kernelver"; echo 5.18; } | sort -V -C; then + # for linux>=5.18, CONFIG_VIDEO_V4L2 has been renamed to CONFIG_VIDEO_DEV + if [ "${CONFIG_VIDEO_DEV:-n}" = "n" ]; then + BUILD_EXCLUSIVE_KERNEL="REQUIRES CONFIG_VIDEO_DEV" + fi + else + if [ "${CONFIG_VIDEO_V4L2:-n}" = "n" ]; then + BUILD_EXCLUSIVE_KERNEL="REQUIRES CONFIG_VIDEO_V4L2" + fi fi fi From ad95466986c18247bab481b2ceec7d075af2c515 Mon Sep 17 00:00:00 2001 From: Oleksandr Natalenko Date: Tue, 1 Nov 2022 18:39:59 +0100 Subject: [PATCH 002/151] v4l2loopback: un-confine v4l2loopback_cleanup_module() @TheArcaneBrony reported this happening when building the `v4l2loopback` as a built-in: ``` drivers/media/v4l2-core/v4l2loopback.c:2969:13: error: use of undeclared identifier 'v4l2loopback_cleanup_module'; did you mean 'v4l2loopback_init_module'? ``` The reason for this is that 550c240585 introduced extra `ifdef MODULE` around `v4l2loopback_cleanup_module()`, but subsequently 1fcd767eb3 opted into using `module_init()`/`module_exit()` instead, which made the `ifdef` excessive. Hence, remove extra `ifdef MODULE`. Fixes: 1fcd767eb3 ("use module_init()/module_exit() rather than '#ifdef MODULE'") Reported-by: The Arcane Brony Tested-by: The Arcane Brony Signed-off-by: Oleksandr Natalenko --- v4l2loopback.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d5dfe2c2..ed6cfea7 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2951,7 +2951,6 @@ static int __init v4l2loopback_init_module(void) return err; } -#ifdef MODULE static void v4l2loopback_cleanup_module(void) { MARK(); @@ -2961,7 +2960,6 @@ static void v4l2loopback_cleanup_module(void) misc_deregister(&v4l2loopback_misc); dprintk("module removed\n"); } -#endif MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR); From 8902b3f11413166e7823c377dbf876bae1fab137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 9 Nov 2022 13:23:15 +0100 Subject: [PATCH 003/151] quote FOURCC-codes to show the space also print the hex-representation. and align the table. Closes: https://github.com/umlaeute/v4l2loopback/issues/510 --- utils/v4l2loopback-ctl.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 9a6f5b67..435f5853 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -310,7 +310,9 @@ static void help_setcaps(const char *program, int brief, int argc, char **argv) "\n"); if (!argc) { dprintf(2, "\nknown fourcc-codes" - "\n------------------" + "\n==================" + "\nFOURCC\thex \tdec \tdescription" + "\n------\t----------\t------------\t-----------" "\n"); char fourcc[5]; const size_t num_formats = sizeof(formats) / sizeof(*formats); @@ -318,8 +320,8 @@ static void help_setcaps(const char *program, int brief, int argc, char **argv) for (i = 0; i < num_formats; i++) { const struct v4l2l_format *fmt = formats + i; memset(fourcc, 0, 5); - dprintf(2, "%4s\t%d\t%s\n", - fourcc2str(fmt->fourcc, fourcc), fmt->fourcc, + dprintf(2, "'%4s'\t0x%08X\t%12d\t%s\n", + fourcc2str(fmt->fourcc, fourcc), fmt->fourcc, fmt->fourcc, fmt->name); } } From f3f40c1be83abe1dd8f805519b293a85e17778ce Mon Sep 17 00:00:00 2001 From: Jianhui Dai Date: Mon, 21 Nov 2022 15:04:01 +0800 Subject: [PATCH 004/151] Reset V4L2_BUF_FLAG_MAPPED if use_count <= 0 --- v4l2loopback.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d5dfe2c2..6d3a36ac 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1866,6 +1866,8 @@ static void vm_open(struct vm_area_struct *vma) buf = vma->vm_private_data; buf->use_count++; + + buf->buffer.flags |= V4L2_BUF_FLAG_MAPPED; } static void vm_close(struct vm_area_struct *vma) @@ -1875,6 +1877,9 @@ static void vm_close(struct vm_area_struct *vma) buf = vma->vm_private_data; buf->use_count--; + + if (buf->use_count <= 0) + buf->buffer.flags &= ~V4L2_BUF_FLAG_MAPPED; } static struct vm_operations_struct vm_ops = { @@ -1953,7 +1958,6 @@ static int v4l2_loopback_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_ops = &vm_ops; vma->vm_private_data = buffer; - buffer->buffer.flags |= V4L2_BUF_FLAG_MAPPED; vm_open(vma); From 7dbf775363a7bce23b56f3563014f75baba5a98a Mon Sep 17 00:00:00 2001 From: Jianhui Dai Date: Mon, 21 Nov 2022 15:17:37 +0800 Subject: [PATCH 005/151] log pid for debug --- v4l2loopback.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d5dfe2c2..4c8ec3cb 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -73,8 +73,8 @@ MODULE_LICENSE("GPL"); #define dprintk(fmt, args...) \ do { \ if (debug > 0) { \ - printk(KERN_INFO "v4l2-loopback[" STRINGIFY2( \ - __LINE__) "]: " fmt, \ + printk(KERN_INFO "pid(%d): v4l2-loopback[" STRINGIFY2( \ + __LINE__) "]: " fmt, task_pid_nr(current), \ ##args); \ } \ } while (0) @@ -82,7 +82,7 @@ MODULE_LICENSE("GPL"); #define MARK() \ do { \ if (debug > 1) { \ - printk(KERN_INFO "%s:%d[%s]\n", __FILE__, __LINE__, \ + printk(KERN_INFO "pid(%d): %s:%d[%s]\n", task_pid_nr(current), __FILE__, __LINE__, \ __func__); \ } \ } while (0) @@ -90,8 +90,8 @@ MODULE_LICENSE("GPL"); #define dprintkrw(fmt, args...) \ do { \ if (debug > 2) { \ - printk(KERN_INFO "v4l2-loopback[" STRINGIFY2( \ - __LINE__) "]: " fmt, \ + printk(KERN_INFO "pid(%d): v4l2-loopback[" STRINGIFY2( \ + __LINE__) "]: " fmt, task_pid_nr(current), \ ##args); \ } \ } while (0) From 70babe4ffcd76855745a3ad26cb8a525a5c6d6a0 Mon Sep 17 00:00:00 2001 From: tudor Date: Fri, 16 Dec 2022 13:49:37 +0100 Subject: [PATCH 006/151] DKMS section clarification --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 23a9fd96..9382c60b 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,9 @@ dkms install -m v4l2loopback -v ${version} | Debian, Ubuntu,... | dkms | +Note that using this method will NOT install the v4l2loopback-ctl tool, you will have to do it yourself! + + # LOAD THE MODULE AT BOOT One can avoid manually loading the module by letting systemd load the module From 31901e9e0420e780d22d94d8a0c9cc3c90fa0a00 Mon Sep 17 00:00:00 2001 From: tudor Date: Fri, 16 Dec 2022 13:50:38 +0100 Subject: [PATCH 007/151] too many empty lines --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9382c60b..9b5aa1cb 100644 --- a/README.md +++ b/README.md @@ -314,7 +314,6 @@ dkms install -m v4l2loopback -v ${version} Note that using this method will NOT install the v4l2loopback-ctl tool, you will have to do it yourself! - # LOAD THE MODULE AT BOOT One can avoid manually loading the module by letting systemd load the module From cc389462180f1d91401b51c415c54c9aa930d04e Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Mon, 19 Oct 2020 23:11:42 +0800 Subject: [PATCH 008/151] Track active readers Introduce the device reference counter. If the V4L2_BUF_TYPE_VIDEO_CAPTURE is called, the reference counter is increased. Also, it will be decreased when the device is closed. Signed-off-by: Kate Hsuan --- v4l2loopback.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d5dfe2c2..0d3548b4 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -414,6 +414,7 @@ struct v4l2_loopback_device { int ready_for_output; /* set to true when no writer is currently attached * this differs slightly from !ready_for_capture, * e.g. when using fallback images */ + int active_readers; /* increase if any reader starts streaming */ int announce_all_caps; /* set to false, if device caps (OUTPUT/CAPTURE) * should only be announced if the resp. "ready" * flag is set; default=TRUE */ @@ -1800,6 +1801,7 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (!dev->ready_for_capture) return -EIO; opener->type = READER; + dev->active_readers++; return 0; default: return -EINVAL; @@ -1814,17 +1816,23 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct v4l2_loopback_device *dev; + struct v4l2_loopback_opener *opener; + MARK(); dprintk("%d\n", type); dev = v4l2loopback_getdevice(file); - + opener = fh_to_opener(fh); switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (dev->ready_for_capture > 0) dev->ready_for_capture--; return 0; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (opener->type == READER) { + opener->type = 0; + dev->active_readers--; + } return 0; default: return -EINVAL; @@ -2052,14 +2060,16 @@ static int v4l2_loopback_close(struct file *file) { struct v4l2_loopback_opener *opener; struct v4l2_loopback_device *dev; - int iswriter = 0; + int is_writer = 0, is_reader = 0; MARK(); opener = fh_to_opener(file->private_data); dev = v4l2loopback_getdevice(file); if (WRITER == opener->type) - iswriter = 1; + is_writer = 1; + if (READER == opener->type) + is_reader = 1; atomic_dec(&dev->open_count); if (dev->open_count.counter == 0) { @@ -2072,9 +2082,10 @@ static int v4l2_loopback_close(struct file *file) v4l2_fh_exit(&opener->fh); kfree(opener); - if (iswriter) { - dev->ready_for_output = 1; - } + if (is_writer) + dev->ready_for_output = 1; + if (is_reader) + dev->active_readers--; MARK(); return 0; } From ceed405e453a6ab746d94334c03b4679dfb93ac6 Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Thu, 13 Aug 2020 23:21:05 +0800 Subject: [PATCH 009/151] Support V4L2_EVENT_PRI_CLIENT_USAGE Add V4L2_EVENT_PRI_CLIENT_USAGE event to report the current device reference count. When the device is opened, closed, and stream on/off, the event will be sent to the application to perform the necessary actions against the event. Signed-off-by: Kate Hsuan --- v4l2loopback.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 0d3548b4..7abc0764 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -665,6 +665,14 @@ static void v4l2loopback_create_sysfs(struct video_device *vdev) dev_err(&vdev->dev, "%s error: %d\n", __func__, res); } +/* Event APIs */ + +#define V4L2_EVENT_PRI_CLIENT_USAGE V4L2_EVENT_PRIVATE_START + +struct v4l2_event_client_usage { + __u32 count; +}; + /* global module data */ /* find a device based on it's device-number (e.g. '3' for /dev/video3) */ struct v4l2loopback_lookup_cb_data { @@ -719,6 +727,7 @@ static struct v4l2_loopback_device *v4l2loopback_getdevice(struct file *f) } /* forward declarations */ +static void client_usage_queue_event(struct video_device *vdev); static void init_buffers(struct v4l2_loopback_device *dev); static int allocate_buffers(struct v4l2_loopback_device *dev); static void free_buffers(struct v4l2_loopback_device *dev); @@ -1802,6 +1811,7 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type type) return -EIO; opener->type = READER; dev->active_readers++; + client_usage_queue_event(dev->vdev); return 0; default: return -EINVAL; @@ -1832,6 +1842,7 @@ static int vidioc_streamoff(struct file *file, void *fh, if (opener->type == READER) { opener->type = 0; dev->active_readers--; + client_usage_queue_event(dev->vdev); } return 0; default: @@ -1855,12 +1866,60 @@ static int vidiocgmbuf(struct file *file, void *fh, struct video_mbuf *p) } #endif +static void client_usage_queue_event(struct video_device *vdev) +{ + struct v4l2_event ev; + struct v4l2_loopback_device *dev; + + dev = container_of(vdev->v4l2_dev, + struct v4l2_loopback_device, v4l2_dev); + + memset(&ev, 0, sizeof(ev)); + ev.type = V4L2_EVENT_PRI_CLIENT_USAGE; + ((struct v4l2_event_client_usage*)&ev.u)->count = + dev->active_readers; + + v4l2_event_queue(vdev, &ev); +} + +static int client_usage_ops_add(struct v4l2_subscribed_event *sev, + unsigned elems) +{ + if (!(sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) + return 0; + + client_usage_queue_event(sev->fh->vdev); + return 0; +} + +static void client_usage_ops_replace(struct v4l2_event *old, + const struct v4l2_event *new) +{ + *((struct v4l2_event_client_usage*)&old->u) = + *((struct v4l2_event_client_usage*)&new->u); +} + +static void client_usage_ops_merge(const struct v4l2_event *old, + struct v4l2_event *new) +{ + *((struct v4l2_event_client_usage*)&new->u) = + *((struct v4l2_event_client_usage*)&old->u); +} + +const struct v4l2_subscribed_event_ops client_usage_ops = { + .add = client_usage_ops_add, + .replace = client_usage_ops_replace, + .merge = client_usage_ops_merge, +}; + static int vidioc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { switch (sub->type) { case V4L2_EVENT_CTRL: return v4l2_ctrl_subscribe_event(fh, sub); + case V4L2_EVENT_PRI_CLIENT_USAGE: + return v4l2_event_subscribe(fh, sub, 0, &client_usage_ops); } return -EINVAL; @@ -2084,8 +2143,10 @@ static int v4l2_loopback_close(struct file *file) kfree(opener); if (is_writer) dev->ready_for_output = 1; - if (is_reader) - dev->active_readers--; + if (is_reader) { + dev->active_readers--; + client_usage_queue_event(dev->vdev); + } MARK(); return 0; } From 7c4ca0d207ab1338db2aae8e4a78a04a35fe25c8 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Wed, 28 Dec 2022 18:59:01 +0000 Subject: [PATCH 010/151] Fix signed integer overflows in increments. It could happen that position increments overflow after a long time of operation. Signed integer overflows are undefined behaviour. In this case they also lead to illegal index values and subsequent out of boundary access. Reviewed-by: Benny Baumann Signed-off-by: Tobias Stoeckmann --- v4l2loopback.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d5dfe2c2..d48aba29 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -147,6 +147,16 @@ static inline void v4l2_device_unregister(struct v4l2_device *v4l2_dev) } #endif /* HAVE__V4L2_DEVICE */ +/* TODO: Make sure that function is never interrupted. */ +static inline int mod_inc(int *number, int mod) { + int result; + result = (*number + 1) % mod; + if (unlikely(result < 0)) + result += mod; + *number = result; + return result; +} + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) #define v4l2_file_operations file_operations #endif @@ -1539,9 +1549,8 @@ static int vidioc_reqbufs(struct file *file, void *fh, i = dev->write_position; list_for_each_entry (pos, &dev->outbufs_list, list_head) { - dev->bufpos2index[i % b->count] = + dev->bufpos2index[mod_inc(&i, b->count)] = pos->buffer.index; - ++i; } } @@ -1605,10 +1614,9 @@ static void buffer_written(struct v4l2_loopback_device *dev, del_timer_sync(&dev->timeout_timer); spin_lock_bh(&dev->lock); - dev->bufpos2index[dev->write_position % dev->used_buffers] = + dev->bufpos2index[mod_inc(&dev->write_position, dev->used_buffers)] = buf->buffer.index; list_move_tail(&buf->list_head, &dev->outbufs_list); - ++dev->write_position; dev->reread_count = 0; check_timers(dev); @@ -1703,8 +1711,7 @@ static int get_capture_buffer(struct file *file) if (dev->write_position > opener->read_position + dev->used_buffers) opener->read_position = dev->write_position - 1; - pos = opener->read_position % dev->used_buffers; - ++opener->read_position; + pos = mod_inc(&opener->read_position, dev->used_buffers); } timeout_happened = dev->timeout_happened; dev->timeout_happened = 0; From c7a5cd4c06b41370790eec3a2a9884b764330074 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Wed, 4 Jan 2023 15:03:04 +0800 Subject: [PATCH 011/151] v4l2loopback: a new offset for V4L2_EVENT_PRI_CLIENT_USAGE The original ID of V4L2_EVENT_PRI_CLIENT_USAGE is V4L2_EVENT_PRIVATE_START. To prevent misunderstanding between V4L2_EVENT_PRI_CLIENT_USAGE and V4L2_EVENT_PRIVATE_START, the new ID of V4L2_EVENT_PRI_CLIENT_USAGE is (V4L2_EVENT_PRIVATE_START+0x08E00000+1) Signed-off-by: Kate Hsuan --- v4l2loopback.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 7abc0764..6ed23de7 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -667,7 +667,11 @@ static void v4l2loopback_create_sysfs(struct video_device *vdev) /* Event APIs */ -#define V4L2_EVENT_PRI_CLIENT_USAGE V4L2_EVENT_PRIVATE_START +#define V4L2LOOPBACK_EVENT_BASE (V4L2_EVENT_PRIVATE_START) +#define V4L2LOOPBACK_EVENT_OFFSET 0x08E00000 +#define V4L2_EVENT_PRI_CLIENT_USAGE (V4L2LOOPBACK_EVENT_BASE + \ + V4L2LOOPBACK_EVENT_OFFSET + \ + 1) struct v4l2_event_client_usage { __u32 count; From 2fe4e20fbb4be62998895b632e2c35d7c4b36102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 08:53:49 +0100 Subject: [PATCH 012/151] Allow a minimum of 0 fps Closes: https://github.com/umlaeute/v4l2loopback/issues/521 --- v4l2loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d5dfe2c2..c102e1be 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -281,7 +281,7 @@ static DEFINE_IDR(v4l2loopback_index_idr); static DEFINE_MUTEX(v4l2loopback_ctl_mutex); /* frame intervals */ -#define V4L2LOOPBACK_FPS_MIN 1 +#define V4L2LOOPBACK_FPS_MIN 0 #define V4L2LOOPBACK_FPS_MAX 1000 /* control IDs */ From 0503055f1cfb1d1f74a7ddf61a350c083127c907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 10:07:02 +0100 Subject: [PATCH 013/151] clang-format --- utils/v4l2loopback-ctl.c | 4 ++-- v4l2loopback.c | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 435f5853..8b5e7fa5 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -321,8 +321,8 @@ static void help_setcaps(const char *program, int brief, int argc, char **argv) const struct v4l2l_format *fmt = formats + i; memset(fourcc, 0, 5); dprintf(2, "'%4s'\t0x%08X\t%12d\t%s\n", - fourcc2str(fmt->fourcc, fourcc), fmt->fourcc, fmt->fourcc, - fmt->name); + fourcc2str(fmt->fourcc, fourcc), fmt->fourcc, + fmt->fourcc, fmt->name); } } } diff --git a/v4l2loopback.c b/v4l2loopback.c index 48a8f5e9..d48b3cbe 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -414,7 +414,7 @@ struct v4l2_loopback_device { int ready_for_output; /* set to true when no writer is currently attached * this differs slightly from !ready_for_capture, * e.g. when using fallback images */ - int active_readers; /* increase if any reader starts streaming */ + int active_readers; /* increase if any reader starts streaming */ int announce_all_caps; /* set to false, if device caps (OUTPUT/CAPTURE) * should only be announced if the resp. "ready" * flag is set; default=TRUE */ @@ -669,9 +669,8 @@ static void v4l2loopback_create_sysfs(struct video_device *vdev) #define V4L2LOOPBACK_EVENT_BASE (V4L2_EVENT_PRIVATE_START) #define V4L2LOOPBACK_EVENT_OFFSET 0x08E00000 -#define V4L2_EVENT_PRI_CLIENT_USAGE (V4L2LOOPBACK_EVENT_BASE + \ - V4L2LOOPBACK_EVENT_OFFSET + \ - 1) +#define V4L2_EVENT_PRI_CLIENT_USAGE \ + (V4L2LOOPBACK_EVENT_BASE + V4L2LOOPBACK_EVENT_OFFSET + 1) struct v4l2_event_client_usage { __u32 count; @@ -1875,13 +1874,12 @@ static void client_usage_queue_event(struct video_device *vdev) struct v4l2_event ev; struct v4l2_loopback_device *dev; - dev = container_of(vdev->v4l2_dev, - struct v4l2_loopback_device, v4l2_dev); + dev = container_of(vdev->v4l2_dev, struct v4l2_loopback_device, + v4l2_dev); memset(&ev, 0, sizeof(ev)); ev.type = V4L2_EVENT_PRI_CLIENT_USAGE; - ((struct v4l2_event_client_usage*)&ev.u)->count = - dev->active_readers; + ((struct v4l2_event_client_usage *)&ev.u)->count = dev->active_readers; v4l2_event_queue(vdev, &ev); } @@ -1899,15 +1897,15 @@ static int client_usage_ops_add(struct v4l2_subscribed_event *sev, static void client_usage_ops_replace(struct v4l2_event *old, const struct v4l2_event *new) { - *((struct v4l2_event_client_usage*)&old->u) = - *((struct v4l2_event_client_usage*)&new->u); + *((struct v4l2_event_client_usage *)&old->u) = + *((struct v4l2_event_client_usage *)&new->u); } static void client_usage_ops_merge(const struct v4l2_event *old, struct v4l2_event *new) { - *((struct v4l2_event_client_usage*)&new->u) = - *((struct v4l2_event_client_usage*)&old->u); + *((struct v4l2_event_client_usage *)&new->u) = + *((struct v4l2_event_client_usage *)&old->u); } const struct v4l2_subscribed_event_ops client_usage_ops = { @@ -2146,9 +2144,9 @@ static int v4l2_loopback_close(struct file *file) kfree(opener); if (is_writer) - dev->ready_for_output = 1; + dev->ready_for_output = 1; if (is_reader) { - dev->active_readers--; + dev->active_readers--; client_usage_queue_event(dev->vdev); } MARK(); From 9d397325a20889f6182ca963077222d30c5aa955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 09:37:56 +0100 Subject: [PATCH 014/151] Propagate snapshot version to v4l2loopback-ctl --- Makefile | 5 +++-- utils/Makefile | 3 +++ utils/v4l2loopback-ctl.c | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cd318c57..8cab9bfa 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ ifneq ($(wildcard .gitversion),) # building a snapshot version -override KCPPFLAGS += -DSNAPSHOT_VERSION='"$(shell git describe --always --dirty 2>/dev/null || shell git describe --always 2>/dev/null || echo snapshot)"' +V4L2LOOPBACK_SNAPSHOT_VERSION=$(shell git describe --always --dirty 2>/dev/null || shell git describe --always 2>/dev/null || echo snapshot) +override KCPPFLAGS += -DSNAPSHOT_VERSION='"$(V4L2LOOPBACK_SNAPSHOT_VERSION)"' endif include Kbuild @@ -87,7 +88,7 @@ man/v4l2loopback-ctl.1: utils/v4l2loopback-ctl utils: utils/v4l2loopback-ctl utils/v4l2loopback-ctl: utils/v4l2loopback-ctl.c - $(MAKE) -C utils + $(MAKE) -C utils V4L2LOOPBACK_SNAPSHOT_VERSION=$(V4L2LOOPBACK_SNAPSHOT_VERSION) .clang-format: curl "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/.clang-format" > $@ diff --git a/utils/Makefile b/utils/Makefile index 19e5bdde..f662a4b3 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -1,4 +1,7 @@ CPPFLAGS += -I.. +ifneq ($(V4L2LOOPBACK_SNAPSHOT_VERSION),) +CPPFLAGS += -DSNAPSHOT_VERSION='"$(V4L2LOOPBACK_SNAPSHOT_VERSION)"' +endif .PHONY: default clean diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 8b5e7fa5..44031169 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -1127,8 +1127,12 @@ int main(int argc, char **argv) } break; case VERSION: +#ifdef SNAPSHOT_VERSION + printf("%s %s\n", argv[0], SNAPSHOT_VERSION); +#else printf("%s v%d.%d.%d\n", argv[0], V4L2LOOPBACK_VERSION_MAJOR, V4L2LOOPBACK_VERSION_MINOR, V4L2LOOPBACK_VERSION_BUGFIX); +#endif break; default: dprintf(2, "not implemented '%s'\n", argv[1]); From ae04a4ba72c631689346830f36e694033609d694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 10:00:52 +0100 Subject: [PATCH 015/151] strip leading 'v' from git snapshot version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8cab9bfa..dd76a18b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ ifneq ($(wildcard .gitversion),) # building a snapshot version -V4L2LOOPBACK_SNAPSHOT_VERSION=$(shell git describe --always --dirty 2>/dev/null || shell git describe --always 2>/dev/null || echo snapshot) +V4L2LOOPBACK_SNAPSHOT_VERSION=$(patsubst v%,%,$(shell git describe --always --dirty 2>/dev/null || shell git describe --always 2>/dev/null || echo snapshot)) override KCPPFLAGS += -DSNAPSHOT_VERSION='"$(V4L2LOOPBACK_SNAPSHOT_VERSION)"' endif From a6dce262429df729547b66ac2c97f60ed8dea59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 10:01:10 +0100 Subject: [PATCH 016/151] v4l2loopback-ctl: compat flag "--version" --- utils/v4l2loopback-ctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 44031169..0d7d8401 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -923,6 +923,8 @@ static t_command get_command(const char *command) return HELP; if (!strncmp(command, "-v", 2)) return VERSION; + if (!strncmp(command, "--version", 9)) + return VERSION; if (!strncmp(command, "add", 4)) return ADD; if (!strncmp(command, "del", 3)) From 5e3f8724ca699cb0bc5fa648bf01b32d5f34360f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 10:01:57 +0100 Subject: [PATCH 017/151] calculate MODULE_VERSION from V4L2LOOPBACK_VERSION and make it overridable with SNAPSHOT_VERSION --- v4l2loopback.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d48b3cbe..aaa1daee 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -39,6 +39,12 @@ #include #include "v4l2loopback.h" +/* + * helpers + */ +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) + #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 1) #define kstrtoul strict_strtoul #endif @@ -61,15 +67,17 @@ MODULE_AUTHOR("Vasily Levin, " "Stefan Diewald," "Anton Novikov" "et al."); -MODULE_VERSION("0.12.5"); +#ifdef SNAPSHOT_VERSION +MODULE_VERSION(STRINGIFY2(SNAPSHOT_VERSION)); +#else +MODULE_VERSION("" STRINGIFY2(V4L2LOOPBACK_VERSION_MAJOR) "." STRINGIFY2( + V4L2LOOPBACK_VERSION_MINOR) "." STRINGIFY2(V4L2LOOPBACK_VERSION_BUGFIX)); +#endif MODULE_LICENSE("GPL"); /* * helpers */ -#define STRINGIFY(s) #s -#define STRINGIFY2(s) STRINGIFY(s) - #define dprintk(fmt, args...) \ do { \ if (debug > 0) { \ From 52cf4dc8518299c2f9c7f13917ae74efe0eac69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 10:05:17 +0100 Subject: [PATCH 018/151] use linux' __stringify instead of our own STRINGIFY2 --- v4l2loopback.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index aaa1daee..87a62c90 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -39,12 +39,6 @@ #include #include "v4l2loopback.h" -/* - * helpers - */ -#define STRINGIFY(s) #s -#define STRINGIFY2(s) STRINGIFY(s) - #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 1) #define kstrtoul strict_strtoul #endif @@ -68,10 +62,10 @@ MODULE_AUTHOR("Vasily Levin, " "Anton Novikov" "et al."); #ifdef SNAPSHOT_VERSION -MODULE_VERSION(STRINGIFY2(SNAPSHOT_VERSION)); +MODULE_VERSION(__stringify(SNAPSHOT_VERSION)); #else -MODULE_VERSION("" STRINGIFY2(V4L2LOOPBACK_VERSION_MAJOR) "." STRINGIFY2( - V4L2LOOPBACK_VERSION_MINOR) "." STRINGIFY2(V4L2LOOPBACK_VERSION_BUGFIX)); +MODULE_VERSION("" __stringify(V4L2LOOPBACK_VERSION_MAJOR) "." __stringify( + V4L2LOOPBACK_VERSION_MINOR) "." __stringify(V4L2LOOPBACK_VERSION_BUGFIX)); #endif MODULE_LICENSE("GPL"); @@ -81,7 +75,7 @@ MODULE_LICENSE("GPL"); #define dprintk(fmt, args...) \ do { \ if (debug > 0) { \ - printk(KERN_INFO "v4l2-loopback[" STRINGIFY2( \ + printk(KERN_INFO "v4l2-loopback[" __stringify( \ __LINE__) "]: " fmt, \ ##args); \ } \ @@ -98,7 +92,7 @@ MODULE_LICENSE("GPL"); #define dprintkrw(fmt, args...) \ do { \ if (debug > 2) { \ - printk(KERN_INFO "v4l2-loopback[" STRINGIFY2( \ + printk(KERN_INFO "v4l2-loopback[" __stringify( \ __LINE__) "]: " fmt, \ ##args); \ } \ @@ -224,7 +218,7 @@ MODULE_PARM_DESC(debug, "debugging level (higher values == more verbose)"); static int max_buffers = V4L2LOOPBACK_DEFAULT_MAX_BUFFERS; module_param(max_buffers, int, S_IRUGO); MODULE_PARM_DESC(max_buffers, - "how many buffers should be allocated [DEFAULT: " STRINGIFY2( + "how many buffers should be allocated [DEFAULT: " __stringify( V4L2LOOPBACK_DEFAULT_MAX_BUFFERS) "]"); /* how many times a device can be opened @@ -240,7 +234,7 @@ static int max_openers = V4L2LOOPBACK_DEFAULT_MAX_OPENERS; module_param(max_openers, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC( max_openers, - "how many users can open the loopback device [DEFAULT: " STRINGIFY2( + "how many users can open the loopback device [DEFAULT: " __stringify( V4L2LOOPBACK_DEFAULT_MAX_OPENERS) "]"); static int devices = -1; @@ -263,7 +257,7 @@ module_param_array(exclusive_caps, bool, NULL, 0444); /* FIXXME: wording */ MODULE_PARM_DESC( exclusive_caps, - "whether to announce OUTPUT/CAPTURE capabilities exclusively or not [DEFAULT: " STRINGIFY2( + "whether to announce OUTPUT/CAPTURE capabilities exclusively or not [DEFAULT: " __stringify( V4L2LOOPBACK_DEFAULT_EXCLUSIVECAPS) "]"); /* format specifications */ @@ -277,12 +271,13 @@ MODULE_PARM_DESC( static int max_width = V4L2LOOPBACK_SIZE_DEFAULT_MAX_WIDTH; module_param(max_width, int, S_IRUGO); -MODULE_PARM_DESC(max_width, "maximum allowed frame width [DEFAULT: " STRINGIFY2( - V4L2LOOPBACK_SIZE_DEFAULT_MAX_WIDTH) "]"); +MODULE_PARM_DESC(max_width, + "maximum allowed frame width [DEFAULT: " __stringify( + V4L2LOOPBACK_SIZE_DEFAULT_MAX_WIDTH) "]"); static int max_height = V4L2LOOPBACK_SIZE_DEFAULT_MAX_HEIGHT; module_param(max_height, int, S_IRUGO); MODULE_PARM_DESC(max_height, - "maximum allowed frame height [DEFAULT: " STRINGIFY2( + "maximum allowed frame height [DEFAULT: " __stringify( V4L2LOOPBACK_SIZE_DEFAULT_MAX_HEIGHT) "]"); static DEFINE_IDR(v4l2loopback_index_idr); @@ -3020,7 +3015,7 @@ static int __init v4l2loopback_init_module(void) (V4L2LOOPBACK_VERSION_CODE >> 8) & 0xff, (V4L2LOOPBACK_VERSION_CODE ) & 0xff, #ifdef SNAPSHOT_VERSION - " (" STRINGIFY2(SNAPSHOT_VERSION) ")" + " (" __stringify(SNAPSHOT_VERSION) ")" #else "" #endif From a66968647843f57448b59cf98d0318f1e98e072c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 10:27:55 +0100 Subject: [PATCH 019/151] re-ordered 'struct v4l2_loopback_config' so "announce_all_caps" comes last --- v4l2loopback.h | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/v4l2loopback.h b/v4l2loopback.h index f9db61c9..10f8e662 100644 --- a/v4l2loopback.h +++ b/v4l2loopback.h @@ -23,6 +23,9 @@ struct v4l2_loopback_config { * setting this to a value<0, will allocate an available one * if nr>=0 and the device already exists, the ioctl will EEXIST * if output_nr and capture_nr are the same, only a single device will be created + * NOTE: currently split-devices (where output_nr and capture_nr differ) + * are not implemented yet. + * until then, requesting different device-IDs will result in EINVAL. * * V4L2LOOPBACK_CTL_QUERY: * either both output_nr and capture_nr must refer to the same loopback, @@ -45,14 +48,6 @@ struct v4l2_loopback_config { int max_width; int max_height; - /** - * whether to announce OUTPUT/CAPTURE capabilities exclusively - * for this device or not - * (!exclusive_caps) - * FIXXME: this ought to be removed (if superseded by output_nr vs capture_nr) - */ - int announce_all_caps; - /** * number of buffers to allocate for the queue * if set to <=0, default values are used @@ -69,6 +64,15 @@ struct v4l2_loopback_config { * set the debugging level for this device */ int debug; + + /** + * whether to announce OUTPUT/CAPTURE capabilities exclusively + * for this device or not + * (!exclusive_caps) + * NOTE: this is going to be removed once separate output/capture + * devices are implemented + */ + int announce_all_caps; }; /* a pointer to a (struct v4l2_loopback_config) that has all values you wish to impose on the From 3cba62c078b13f806cb1eacf6fd60c02f3724e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 17 Feb 2023 12:14:12 +0100 Subject: [PATCH 020/151] logging: move "pid(%d)" after the v4l2-loopback identifier --- v4l2loopback.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 1f22f2c5..3426cb96 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -75,9 +75,8 @@ MODULE_LICENSE("GPL"); #define dprintk(fmt, args...) \ do { \ if (debug > 0) { \ - printk(KERN_INFO \ - "pid(%d): v4l2-loopback[" __stringify( \ - __LINE__) "]: " fmt, \ + printk(KERN_INFO "v4l2-loopback[" __stringify( \ + __LINE__) "], pid(%d): " fmt, \ task_pid_nr(current), ##args); \ } \ } while (0) @@ -85,18 +84,16 @@ MODULE_LICENSE("GPL"); #define MARK() \ do { \ if (debug > 1) { \ - printk(KERN_INFO "pid(%d): %s:%d[%s]\n", \ - task_pid_nr(current), __FILE__, __LINE__, \ - __func__); \ + printk(KERN_INFO "%s:%d[%s], pid(%d)\n", __FILE__, \ + __LINE__, __func__, task_pid_nr(current)); \ } \ } while (0) #define dprintkrw(fmt, args...) \ do { \ if (debug > 2) { \ - printk(KERN_INFO \ - "pid(%d): v4l2-loopback[" __stringify( \ - __LINE__) "]: " fmt, \ + printk(KERN_INFO "v4l2-loopback[" __stringify( \ + __LINE__) "], pid(%d): " fmt, \ task_pid_nr(current), ##args); \ } \ } while (0) From e2973791b35d1f1474ab0bfce839241908987343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sun, 19 Feb 2023 18:37:16 +0100 Subject: [PATCH 021/151] [ci] use Environment-File rather than set-output --- .github/workflows/kmod-compatibility-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 84f785f9..8fa5c3f3 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -25,7 +25,7 @@ jobs: # - drop some releases (e.g. 'oldstable') # - format as a JSON array run: | - echo ::set-output name=matrix::$(true; (curl -s "https://hub.docker.com/v2/repositories/library/buildpack-deps/tags?page_size=1000" | jq -r '.results[] | .name' | sed -e '/[^a-z]/d' -e '/stable$/d' -e '/^\(testing\|experimental\)$/d' | sort -u; for url in http://archive.ubuntu.com/ubuntu http://deb.debian.org/debian; do curl -s "${url}/dists/" | grep "href=" | sed -e 's|.*a href="||' -e 's|".*||' -e 's|/$||'; done | sed -e '/[^a-z]/d' -e '/stable$/d' -e '/^\(testing\|experimental\)$/d' | sort -u) | sort | uniq -d | while read x; do echo -n '"'${x}'" '; done | sed -e 's|^ *|[|' -e 's| *$|]|' -e 's| *|,|g') + echo matrix=$(true; (curl -s "https://hub.docker.com/v2/repositories/library/buildpack-deps/tags?page_size=1000" | jq -r '.results[] | .name' | sed -e '/[^a-z]/d' -e '/stable$/d' -e '/^\(testing\|experimental\)$/d' | sort -u; for url in http://archive.ubuntu.com/ubuntu http://deb.debian.org/debian; do curl -s "${url}/dists/" | grep "href=" | sed -e 's|.*a href="||' -e 's|".*||' -e 's|/$||'; done | sed -e '/[^a-z]/d' -e '/stable$/d' -e '/^\(testing\|experimental\)$/d' | sort -u) | sort | uniq -d | while read x; do echo -n '"'${x}'" '; done | sed -e 's|^ *|[|' -e 's| *$|]|' -e 's| *|,|g') | tee -a $GITHUB_OUTPUT id: set-matrix outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} From 0302d3f23b530a9154387b6852179b8bf689a17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sun, 19 Feb 2023 20:13:46 +0100 Subject: [PATCH 022/151] Drop support for Linux<2.6.37 --- README.md | 2 +- v4l2loopback.c | 164 +------------------------------------------------ 2 files changed, 2 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index 9b5aa1cb..062b3460 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ Support: - >= 4.0.0 should work - >= 3.0.0 might work - << 3.0.0 may work (has not been tested in ages) -- <= 2.6.27 will definitely NOT work +- <= 2.6.37 will definitely NOT work # DISTRIBUTIONS v4l2loopack is now (since 2010-10-13) available as a Debian-package. diff --git a/v4l2loopback.c b/v4l2loopback.c index 5d8a0c25..94e1a96a 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -26,14 +26,8 @@ #include #include #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) -#define HAVE__V4L2_DEVICE #include -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) -#define HAVE__V4L2_CTRLS #include -#endif #include #include @@ -98,57 +92,6 @@ MODULE_LICENSE("GPL"); } \ } while (0) -/* - * compatibility hacks - */ - -#ifndef HAVE__V4L2_CTRLS -struct v4l2_ctrl_handler { - int error; -}; -struct v4l2_ctrl_config { - void *ops; - u32 id; - const char *name; - int type; - s32 min; - s32 max; - u32 step; - s32 def; -}; -int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, - unsigned nr_of_controls_hint) -{ - hdl->error = 0; - return 0; -} -void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) -{ -} -void *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_config *conf, void *priv) -{ - return NULL; -} -#endif /* HAVE__V4L2_CTRLS */ - -#ifndef HAVE__V4L2_DEVICE -/* dummy v4l2_device struct/functions */ -#define V4L2_DEVICE_NAME_SIZE (20 + 16) -struct v4l2_device { - char name[V4L2_DEVICE_NAME_SIZE]; - struct v4l2_ctrl_handler *ctrl_handler; -}; -static inline int v4l2_device_register(void *dev, void *v4l2_dev) -{ - return 0; -} -static inline void v4l2_device_unregister(struct v4l2_device *v4l2_dev) -{ - return; -} -#endif /* HAVE__V4L2_DEVICE */ - /* TODO: Make sure that function is never interrupted. */ static inline int mod_inc(int *number, int mod) { @@ -160,21 +103,6 @@ static inline int mod_inc(int *number, int mod) return result; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -#define v4l2_file_operations file_operations -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) -void *v4l2l_vzalloc(unsigned long size) -{ - void *data = vmalloc(size); - - memset(data, 0, size); - return data; -} -#else -#define v4l2l_vzalloc vzalloc -#endif - static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) { /* ktime_get_ts is considered deprecated, so use ktime_get_ts64 if possible */ @@ -299,11 +227,7 @@ static DEFINE_MUTEX(v4l2loopback_ctl_mutex); #define V4L2LOOPBACK_FPS_MAX 1000 /* control IDs */ -#ifndef HAVE__V4L2_CTRLS -#define V4L2LOOPBACK_CID_BASE (V4L2_CID_PRIVATE_BASE) -#else #define V4L2LOOPBACK_CID_BASE (V4L2_CID_USER_BASE | 0xf000) -#endif #define CID_KEEP_FORMAT (V4L2LOOPBACK_CID_BASE + 0) #define CID_SUSTAIN_FRAMERATE (V4L2LOOPBACK_CID_BASE + 1) #define CID_TIMEOUT (V4L2LOOPBACK_CID_BASE + 2) @@ -1272,67 +1196,6 @@ static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) } #endif /* V4L2LOOPBACK_WITH_STD */ -/* get ctrls info - * called on VIDIOC_QUERYCTRL - */ -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *q) -{ - const struct v4l2_ctrl_config *cnf = 0; - switch (q->id) { - case CID_KEEP_FORMAT: - cnf = &v4l2loopback_ctrl_keepformat; - break; - case CID_SUSTAIN_FRAMERATE: - cnf = &v4l2loopback_ctrl_sustainframerate; - break; - case CID_TIMEOUT: - cnf = &v4l2loopback_ctrl_timeout; - break; - case CID_TIMEOUT_IMAGE_IO: - cnf = &v4l2loopback_ctrl_timeoutimageio; - break; - default: - return -EINVAL; - } - if (!cnf) - BUG(); - - strlcpy(q->name, cnf->name, sizeof(q->name)); - q->default_value = cnf->def; - q->type = cnf->type; - q->minimum = cnf->min; - q->maximum = cnf->max; - q->step = cnf->step; - - memset(q->reserved, 0, sizeof(q->reserved)); - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); - - switch (c->id) { - case CID_KEEP_FORMAT: - c->value = dev->keep_format; - break; - case CID_SUSTAIN_FRAMERATE: - c->value = dev->sustain_framerate; - break; - case CID_TIMEOUT: - c->value = jiffies_to_msecs(dev->timeout_jiffies); - break; - case CID_TIMEOUT_IMAGE_IO: - c->value = dev->timeout_image_io; - break; - default: - return -EINVAL; - } - - return 0; -} - static int v4l2loopback_set_ctrl(struct v4l2_loopback_device *dev, u32 id, s64 val) { @@ -1378,11 +1241,6 @@ static int v4l2loopback_s_ctrl(struct v4l2_ctrl *ctrl) ctrl->handler, struct v4l2_loopback_device, ctrl_handler); return v4l2loopback_set_ctrl(dev, ctrl->id, ctrl->val); } -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); - return v4l2loopback_set_ctrl(dev, c->id, c->value); -} /* returns set of device outputs, in our case there is only one * called on VIDIOC_ENUMOUTPUT @@ -2384,7 +2242,7 @@ static int allocate_timeout_image(struct v4l2_loopback_device *dev) return -EINVAL; if (dev->timeout_image == NULL) { - dev->timeout_image = v4l2l_vzalloc(dev->buffer_size); + dev->timeout_image = vzalloc(dev->buffer_size); if (dev->timeout_image == NULL) return -ENOMEM; } @@ -2868,16 +2726,8 @@ static const struct v4l2_file_operations v4l2_loopback_fops = { static const struct v4l2_ioctl_ops v4l2_loopback_ioctl_ops = { // clang-format off .vidioc_querycap = &vidioc_querycap, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) .vidioc_enum_framesizes = &vidioc_enum_framesizes, .vidioc_enum_frameintervals = &vidioc_enum_frameintervals, -#endif - -#ifndef HAVE__V4L2_CTRLS - .vidioc_queryctrl = &vidioc_queryctrl, - .vidioc_g_ctrl = &vidioc_g_ctrl, - .vidioc_s_ctrl = &vidioc_s_ctrl, -#endif /* HAVE__V4L2_CTRLS */ .vidioc_enum_output = &vidioc_enum_output, .vidioc_g_output = &vidioc_g_output, @@ -3054,15 +2904,3 @@ MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR); module_init(v4l2loopback_init_module); module_exit(v4l2loopback_cleanup_module); - -/* - * fake usage of unused functions - */ -#ifdef HAVE__V4L2_CTRLS -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *q) __attribute__((unused)); -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) - __attribute__((unused)); -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) - __attribute__((unused)); -#endif /* HAVE__V4L2_CTRLS */ From fa7c76045f19ac1a9e2689d38b5cd96939b919df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sun, 19 Feb 2023 20:26:49 +0100 Subject: [PATCH 023/151] v4l2loopback-ctl: print module version as well --- utils/v4l2loopback-ctl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 0d7d8401..4c60df43 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -1130,11 +1130,21 @@ int main(int argc, char **argv) break; case VERSION: #ifdef SNAPSHOT_VERSION - printf("%s %s\n", argv[0], SNAPSHOT_VERSION); + printf("%s v%s\n", argv[0], SNAPSHOT_VERSION); #else printf("%s v%d.%d.%d\n", argv[0], V4L2LOOPBACK_VERSION_MAJOR, V4L2LOOPBACK_VERSION_MINOR, V4L2LOOPBACK_VERSION_BUGFIX); #endif + fd = open("/sys/module/v4l2loopback/version", O_RDONLY); + if (fd >= 0) { + char buf[1024]; + int len = read(fd, buf, sizeof(buf)); + if (len > 0) { + if (len < sizeof(buf)) + buf[len] = 0; + printf("v4l2loopback module v%s", buf); + } + } break; default: dprintf(2, "not implemented '%s'\n", argv[1]); From 439f078ffda6ae7aaaafe11a29900bd763941ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sun, 19 Feb 2023 21:04:42 +0100 Subject: [PATCH 024/151] [ci] Job for code-linting --- .github/workflows/lint.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..ca8e69e2 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: Code Linters +on: + pull_request: + branches: + - develop + - main + push: + branches: + - develop + - main + +jobs: + clang-format: + runs-on: ubuntu-latest + steps: + - name: install dependencies + run: | + sudo apt-get update + sudo apt-get -y install clang-format make + - name: Checkout code + uses: actions/checkout@v2 + + - name: clang-format + shell: bash + run: | + make clang-format + git status --porcelain + git diff --exit-code + From ccc4f7bf56bc41f468b94ae3b2dc237a0aae8b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sun, 19 Feb 2023 21:11:58 +0100 Subject: [PATCH 025/151] [ci] Fixup lint-job --- .github/workflows/lint.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ca8e69e2..5fcfcd79 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,13 +1,5 @@ name: Code Linters -on: - pull_request: - branches: - - develop - - main - push: - branches: - - develop - - main +on: [push, pull_request] jobs: clang-format: @@ -18,10 +10,8 @@ jobs: sudo apt-get update sudo apt-get -y install clang-format make - name: Checkout code - uses: actions/checkout@v2 - - - name: clang-format - shell: bash + uses: actions/checkout@v2 + - name: Check Code Formatting run: | make clang-format git status --porcelain From 56d39b58516caddebb63e43f7c203167552e2161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sun, 19 Feb 2023 21:16:25 +0100 Subject: [PATCH 026/151] Re-formatted code with updated .clang-format --- v4l2loopback.c | 64 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 94e1a96a..2ab1f760 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -66,30 +66,30 @@ MODULE_LICENSE("GPL"); /* * helpers */ -#define dprintk(fmt, args...) \ - do { \ - if (debug > 0) { \ - printk(KERN_INFO "v4l2-loopback[" __stringify( \ - __LINE__) "], pid(%d): " fmt, \ - task_pid_nr(current), ##args); \ - } \ +#define dprintk(fmt, args...) \ + do { \ + if (debug > 0) { \ + printk(KERN_INFO "v4l2-loopback[" __stringify( \ + __LINE__) "], pid(%d): " fmt, \ + task_pid_nr(current), ##args); \ + } \ } while (0) -#define MARK() \ - do { \ - if (debug > 1) { \ - printk(KERN_INFO "%s:%d[%s], pid(%d)\n", __FILE__, \ - __LINE__, __func__, task_pid_nr(current)); \ - } \ +#define MARK() \ + do { \ + if (debug > 1) { \ + printk(KERN_INFO "%s:%d[%s], pid(%d)\n", __FILE__, \ + __LINE__, __func__, task_pid_nr(current)); \ + } \ } while (0) -#define dprintkrw(fmt, args...) \ - do { \ - if (debug > 2) { \ - printk(KERN_INFO "v4l2-loopback[" __stringify( \ - __LINE__) "], pid(%d): " fmt, \ - task_pid_nr(current), ##args); \ - } \ +#define dprintkrw(fmt, args...) \ + do { \ + if (debug > 2) { \ + printk(KERN_INFO "v4l2-loopback[" __stringify( \ + __LINE__) "], pid(%d): " fmt, \ + task_pid_nr(current), ##args); \ + } \ } while (0) /* TODO: Make sure that function is never interrupted. */ @@ -585,9 +585,9 @@ static void v4l2loopback_create_sysfs(struct video_device *vdev) { int res = 0; -#define V4L2_SYSFS_CREATE(x) \ - res = device_create_file(&vdev->dev, &dev_attr_##x); \ - if (res < 0) \ +#define V4L2_SYSFS_CREATE(x) \ + res = device_create_file(&vdev->dev, &dev_attr_##x); \ + if (res < 0) \ break if (!vdev) return; @@ -607,7 +607,7 @@ static void v4l2loopback_create_sysfs(struct video_device *vdev) #define V4L2LOOPBACK_EVENT_BASE (V4L2_EVENT_PRIVATE_START) #define V4L2LOOPBACK_EVENT_OFFSET 0x08E00000 -#define V4L2_EVENT_PRI_CLIENT_USAGE \ +#define V4L2_EVENT_PRI_CLIENT_USAGE \ (V4L2LOOPBACK_EVENT_BASE + V4L2LOOPBACK_EVENT_OFFSET + 1) struct v4l2_event_client_usage { @@ -1413,8 +1413,8 @@ static int vidioc_reqbufs(struct file *file, void *fh, if (b->count < dev->used_buffers) { struct v4l2l_buffer *pos, *n; - list_for_each_entry_safe (pos, n, &dev->outbufs_list, - list_head) { + list_for_each_entry_safe(pos, n, &dev->outbufs_list, + list_head) { if (pos->buffer.index >= b->count) list_del(&pos->list_head); } @@ -1422,8 +1422,8 @@ static int vidioc_reqbufs(struct file *file, void *fh, /* after we update dev->used_buffers, buffers in outbufs_list will * correspond to dev->write_position + [0;b->count-1] range */ i = dev->write_position; - list_for_each_entry (pos, &dev->outbufs_list, - list_head) { + list_for_each_entry(pos, &dev->outbufs_list, + list_head) { dev->bufpos2index[mod_inc(&i, b->count)] = pos->buffer.index; } @@ -2273,8 +2273,8 @@ static void init_vdev(struct video_device *vdev, int nr) #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 20, 0) vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; #else - vdev->dev_debug = - V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG; + vdev->dev_debug = V4L2_DEV_DEBUG_IOCTL | + V4L2_DEV_DEBUG_IOCTL_ARG; #endif /* since kernel-3.7, there is a new field 'vfl_dir' that has to be @@ -2640,8 +2640,8 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, if ((ret = copy_from_user(&conf, (void *)parm, sizeof(conf))) < 0) break; - device_nr = - (conf.output_nr < 0) ? conf.capture_nr : conf.output_nr; + device_nr = (conf.output_nr < 0) ? conf.capture_nr : + conf.output_nr; MARK(); /* get the device from either capture_nr or output_nr (whatever is valid) */ if ((ret = v4l2loopback_lookup(device_nr, &dev)) < 0) From fb410fc7af40e972058809a191fae9517b9313af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sun, 19 Feb 2023 21:30:49 +0100 Subject: [PATCH 027/151] [ci] hardcode Debian's supported releases to their rolling names - unstable - testing - stable - oldstable and drop all that codename foo --- .github/workflows/kmod-compatibility-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 8fa5c3f3..03aaaba1 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -25,7 +25,7 @@ jobs: # - drop some releases (e.g. 'oldstable') # - format as a JSON array run: | - echo matrix=$(true; (curl -s "https://hub.docker.com/v2/repositories/library/buildpack-deps/tags?page_size=1000" | jq -r '.results[] | .name' | sed -e '/[^a-z]/d' -e '/stable$/d' -e '/^\(testing\|experimental\)$/d' | sort -u; for url in http://archive.ubuntu.com/ubuntu http://deb.debian.org/debian; do curl -s "${url}/dists/" | grep "href=" | sed -e 's|.*a href="||' -e 's|".*||' -e 's|/$||'; done | sed -e '/[^a-z]/d' -e '/stable$/d' -e '/^\(testing\|experimental\)$/d' | sort -u) | sort | uniq -d | while read x; do echo -n '"'${x}'" '; done | sed -e 's|^ *|[|' -e 's| *$|]|' -e 's| *|,|g') | tee -a $GITHUB_OUTPUT + echo matrix=$(true; (curl -s "https://hub.docker.com/v2/repositories/library/buildpack-deps/tags?page_size=1000" | jq -r '.results[] | .name' | sort -u; for url in http://archive.ubuntu.com/ubuntu; do curl -s "${url}/dists/" | grep "href=" | sed -e 's|.*a href="||' -e 's|".*||' -e 's|/$||'; done | sort -u; for d in unstable testing stable oldstable; do echo $d; done) | sort | uniq -d | while read x; do echo -n '"'${x}'" '; done | sed -e 's|^ *|[|' -e 's| *$|]|' -e 's| *|,|g') | tee -a $GITHUB_OUTPUT id: set-matrix outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} From c50689b18312b0241236d3d969400b2280a2d1b0 Mon Sep 17 00:00:00 2001 From: sanbrother Date: Mon, 3 Apr 2023 20:32:43 +0800 Subject: [PATCH 028/151] Fix compilation issues under AOSP --- v4l2loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 2ab1f760..1a02f570 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -118,7 +118,7 @@ static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) b->timestamp.tv_usec = (ts.tv_nsec / NSEC_PER_USEC); } -#if !defined(__poll_t) +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) typedef unsigned __poll_t; #endif From d9277249064a04cee6e3784573cf36162e09bf3e Mon Sep 17 00:00:00 2001 From: mzihlmann <68678250+mzihlmann@users.noreply.github.com> Date: Tue, 4 Apr 2023 09:15:16 +0000 Subject: [PATCH 029/151] mutex lock access to outbufs_list in vidioc_dqbuf --- v4l2loopback.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index 2ab1f760..6fc41f44 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1640,9 +1640,11 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) *buf = dev->buffers[index].buffer; return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: + spin_lock_bh(&dev->lock); b = list_entry(dev->outbufs_list.prev, struct v4l2l_buffer, list_head); list_move_tail(&b->list_head, &dev->outbufs_list); + spin_unlock_bh(&dev->lock); dprintkrw("output DQBUF index: %d\n", b->buffer.index); unset_flags(b); *buf = b->buffer; From b92e9ce7cccd30cd28743a3fe57eb7827909aeab Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sat, 8 Apr 2023 16:38:22 +0900 Subject: [PATCH 030/151] Revert "Fix signed integer overflows in increments." This reverts commit 7c4ca0d207ab1338db2aae8e4a78a04a35fe25c8. The commit introduced a bug where mod_inc returned the incremented value, while the places it is used in expected the previous value. Additionally, the rest of the logic for tracking buffer positions does not work when the read_position and write_position wrap around, since they are directly compared. In particular, in that model, it is impossible to distinguish the case of all buffers full vs. all buffers empty, since both pointers will be equal in both cases. So a more thorough refactor is necessary to fix that... Let's revert this for now, since it breaks everything as it stands. Fixes: #535 Signed-off-by: Asahi Lina --- v4l2loopback.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 2ab1f760..2514f093 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -92,17 +92,6 @@ MODULE_LICENSE("GPL"); } \ } while (0) -/* TODO: Make sure that function is never interrupted. */ -static inline int mod_inc(int *number, int mod) -{ - int result; - result = (*number + 1) % mod; - if (unlikely(result < 0)) - result += mod; - *number = result; - return result; -} - static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) { /* ktime_get_ts is considered deprecated, so use ktime_get_ts64 if possible */ @@ -1424,8 +1413,9 @@ static int vidioc_reqbufs(struct file *file, void *fh, i = dev->write_position; list_for_each_entry(pos, &dev->outbufs_list, list_head) { - dev->bufpos2index[mod_inc(&i, b->count)] = + dev->bufpos2index[i % b->count] = pos->buffer.index; + ++i; } } @@ -1489,9 +1479,10 @@ static void buffer_written(struct v4l2_loopback_device *dev, del_timer_sync(&dev->timeout_timer); spin_lock_bh(&dev->lock); - dev->bufpos2index[mod_inc(&dev->write_position, dev->used_buffers)] = + dev->bufpos2index[dev->write_position % dev->used_buffers] = buf->buffer.index; list_move_tail(&buf->list_head, &dev->outbufs_list); + ++dev->write_position; dev->reread_count = 0; check_timers(dev); @@ -1586,7 +1577,8 @@ static int get_capture_buffer(struct file *file) if (dev->write_position > opener->read_position + dev->used_buffers) opener->read_position = dev->write_position - 1; - pos = mod_inc(&opener->read_position, dev->used_buffers); + pos = opener->read_position % dev->used_buffers; + ++opener->read_position; } timeout_happened = dev->timeout_happened; dev->timeout_happened = 0; From 9f512816dfad3beb1d329bcb1d5955f6c5ddda99 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sat, 8 Apr 2023 16:47:30 +0900 Subject: [PATCH 031/151] Change the type of read_position and write_position to 64-bit This basically solves the same problem that 7c4ca0d207a tried to solve. It is practically impossible for a 64-bit type to actually overflow (it would take 4 billion years of 60 FPS frames), and this avoids having to do a much more thorough refactor to switch to wrapping buffer indices with the extra requirement of tracking buffer fullness separately. I checked that all of the usages should promote/truncate properly: (s64) (OP) (s32 or u32) should always promote to 64-bit, and all of the references to read_position and write_position are either direct comparisons or apply a modulo before storing into a smaller type, except for one which this commit fixes. Signed-off-by: Asahi Lina --- v4l2loopback.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 2514f093..33a9d0da 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -313,7 +313,7 @@ struct v4l2_loopback_device { int used_buffers; /* number of the actually used buffers */ int max_openers; /* how many times can this device be opened */ - int write_position; /* number of last written frame + 1 */ + s64 write_position; /* number of last written frame + 1 */ struct list_head outbufs_list; /* buffers in output DQBUF order */ int bufpos2index [MAX_BUFFERS]; /* mapping of (read/write_position % used_buffers) @@ -367,7 +367,7 @@ enum opener_type { /* struct keeping state and type of opener */ struct v4l2_loopback_opener { enum opener_type type; - int read_position; /* number of last processed frame + 1 or + s64 read_position; /* number of last processed frame + 1 or * write_position - 1 if reader went out of sync */ unsigned int reread_count; struct v4l2_buffer *buffers; @@ -1410,7 +1410,7 @@ static int vidioc_reqbufs(struct file *file, void *fh, /* after we update dev->used_buffers, buffers in outbufs_list will * correspond to dev->write_position + [0;b->count-1] range */ - i = dev->write_position; + i = dev->write_position % b->count; list_for_each_entry(pos, &dev->outbufs_list, list_head) { dev->bufpos2index[i % b->count] = @@ -1516,8 +1516,8 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) set_queued(b); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - dprintkrw("output QBUF pos: %d index: %d\n", - dev->write_position, index); + dprintkrw("output QBUF pos: %lld index: %d\n", + (long long)dev->write_position, index); if (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0) v4l2l_get_timestamp(&b->buffer); else @@ -1621,8 +1621,8 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) index = get_capture_buffer(file); if (index < 0) return index; - dprintkrw("capture DQBUF pos: %d index: %d\n", - opener->read_position - 1, index); + dprintkrw("capture DQBUF pos: %lld index: %d\n", + (long long)(opener->read_position - 1), index); if (!(dev->buffers[index].buffer.flags & V4L2_BUF_FLAG_MAPPED)) { dprintk("trying to return not mapped buf[%d]\n", index); @@ -2314,7 +2314,7 @@ static void sustain_timer_clb(unsigned long nr) spin_lock(&dev->lock); if (dev->sustain_framerate) { dev->reread_count++; - dprintkrw("reread: %d %d\n", dev->write_position, + dprintkrw("reread: %lld %d\n", (long long)dev->write_position, dev->reread_count); if (dev->reread_count == 1) mod_timer(&dev->sustain_timer, From 6b9ac6ca106eed2ee01fb2b66960c1ea10a10f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 12 Apr 2023 09:57:30 +0200 Subject: [PATCH 032/151] Use separate spinlocks for protecting list access --- v4l2loopback.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 0a666b35..7aa37739 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -352,7 +352,7 @@ struct v4l2_loopback_device { char card_label[32]; wait_queue_head_t read_event; - spinlock_t lock; + spinlock_t lock, list_lock; }; /* types of opener shows what opener wants to do with loopback */ @@ -1632,11 +1632,11 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) *buf = dev->buffers[index].buffer; return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - spin_lock_bh(&dev->lock); + spin_lock_bh(&dev->list_lock); b = list_entry(dev->outbufs_list.prev, struct v4l2l_buffer, list_head); list_move_tail(&b->list_head, &dev->outbufs_list); - spin_unlock_bh(&dev->lock); + spin_unlock_bh(&dev->list_lock); dprintkrw("output DQBUF index: %d\n", b->buffer.index); unset_flags(b); *buf = b->buffer; @@ -2476,6 +2476,7 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) MARK(); spin_lock_init(&dev->lock); + spin_lock_init(&dev->list_lock); INIT_LIST_HEAD(&dev->outbufs_list); if (list_empty(&dev->outbufs_list)) { int i; From 2e4dcae2a832d9195d38bfd8745f0e5d1b941493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 7 Mar 2023 16:57:46 +0100 Subject: [PATCH 033/151] Make VIDIOC_ENUMINPUT return V4L2_IN_ST_NO_SIGNAL if there's no producer Closes: https://github.com/umlaeute/v4l2loopback/issues/511 --- v4l2loopback.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index 7aa37739..3e0aaccb 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1300,6 +1300,7 @@ static int vidioc_s_output(struct file *file, void *fh, unsigned int i) static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *inp) { + struct v4l2_loopback_device *dev; __u32 index = inp->index; MARK(); @@ -1323,6 +1324,12 @@ static int vidioc_enum_input(struct file *file, void *fh, #endif #endif /* V4L2LOOPBACK_WITH_STD */ + dev = v4l2loopback_getdevice(file); + if (!dev->ready_for_capture) { + inp->status |= V4L2_IN_ST_NO_SIGNAL; + } + + return 0; } From 1d01a4156cd22ff25d015079cbb731078fa89ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 12 Apr 2023 12:38:30 +0200 Subject: [PATCH 034/151] code formatting --- v4l2loopback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 3e0aaccb..da6ead1e 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1329,7 +1329,6 @@ static int vidioc_enum_input(struct file *file, void *fh, inp->status |= V4L2_IN_ST_NO_SIGNAL; } - return 0; } From 45c067cac4810bda026f0d3b0fbad366adc885ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 11:19:22 +0200 Subject: [PATCH 035/151] Always protect access to dev->outbufs_list with the list_lock mutex --- v4l2loopback.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index da6ead1e..f86b91b6 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1483,11 +1483,14 @@ static void buffer_written(struct v4l2_loopback_device *dev, { del_timer_sync(&dev->sustain_timer); del_timer_sync(&dev->timeout_timer); - spin_lock_bh(&dev->lock); + spin_lock_bh(&dev->list_lock); + list_move_tail(&buf->list_head, &dev->outbufs_list); + spin_unlock_bh(&dev->list_lock); + + spin_lock_bh(&dev->lock); dev->bufpos2index[dev->write_position % dev->used_buffers] = buf->buffer.index; - list_move_tail(&buf->list_head, &dev->outbufs_list); ++dev->write_position; dev->reread_count = 0; From fa0b246ea3a58746299fb02870efbc865528d06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 11:42:33 +0200 Subject: [PATCH 036/151] v4l2loopback-ctl help: use 'detail' level rather than 'brief' flag so we can suppress the printing of the FOURCCs codes unless the user asks for 'set-caps' help specifically --- utils/v4l2loopback-ctl.c | 64 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 4c60df43..070efdb7 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -225,21 +225,21 @@ typedef enum { _UNKNOWN } t_command; -static int help_shortcmdline(int brief, const char *program, +static int help_shortcmdline(int detail, const char *program, const char *argstring) { dprintf(2, "\n"); - //if(!brief)dprintf(2, " -->"); + //if(detail)dprintf(2, " -->"); dprintf(2, "\t"); dprintf(2, "%s %s", program, argstring); - return brief; + return !detail; } -static void help_add(const char *program, int brief, int argc, char **argv) +static void help_add(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n adding devices ('add')" "\n ======================"); - if (help_shortcmdline(brief, program, + if (help_shortcmdline(detail, program, "add {} [ []]")) return; dprintf(2, @@ -256,59 +256,59 @@ static void help_add(const char *program, int brief, int argc, char **argv) "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \tif given, use separate output & capture devices (otherwise they are the same)."); } -static void help_delete(const char *program, int brief, int argc, char **argv) +static void help_delete(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n deleting devices ('delete')" "\n ==========================="); - if (help_shortcmdline(brief, program, "delete ")) + if (help_shortcmdline(detail, program, "delete ")) return; dprintf(2, "\n \tcan be given one more more times (to delete multiple devices at once)." "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')."); } -static void help_query(const char *program, int brief, int argc, char **argv) +static void help_query(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n querying devices ('query')" "\n =========================="); - if (help_shortcmdline(brief, program, "query ")) + if (help_shortcmdline(detail, program, "query ")) return; dprintf(2, "\n \tcan be given one more more times (to query multiple devices at once)." "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')."); } -static void help_setfps(const char *program, int brief, int argc, char **argv) +static void help_setfps(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n setting framerate ('set-fps')" "\n ============================="); - if (help_shortcmdline(brief, program, "set-fps ")) + if (help_shortcmdline(detail, program, "set-fps ")) return; dprintf(2, "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \tframes per second, either as integer ('30') or fraction ('50/2')."); } -static void help_getfps(const char *program, int brief, int argc, char **argv) +static void help_getfps(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n getting framerate ('get-fps')" "\n ============================="); - if (help_shortcmdline(brief, program, "get-fps ")) + if (help_shortcmdline(detail, program, "get-fps ")) return; } -static void help_setcaps(const char *program, int brief, int argc, char **argv) +static void help_setcaps(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n setting capabilities ('set-caps')" "\n ================================="); - if (help_shortcmdline(brief, program, "set-caps ")) + if (help_shortcmdline(detail, program, "set-caps ")) return; dprintf(2, "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \tformat specification, e.g. 'UYVY:3840x2160@60/1 (:x@)" "\n"); - if (!argc) { + if (detail>1) { dprintf(2, "\nknown fourcc-codes" "\n==================" "\nFOURCC\thex \tdec \tdescription" @@ -326,21 +326,21 @@ static void help_setcaps(const char *program, int brief, int argc, char **argv) } } } -static void help_getcaps(const char *program, int brief, int argc, char **argv) +static void help_getcaps(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n getting capabilities ('get-caps')" "\n ================================="); - if (help_shortcmdline(brief, program, "get-caps ")) + if (help_shortcmdline(detail, program, "get-caps ")) return; } -static void help_settimeoutimage(const char *program, int brief, int argc, +static void help_settimeoutimage(const char *program, int detail, int argc, char **argv) { - if (!brief) + if (detail) dprintf(2, "\n setting timeout image ('set-timeout-image')" "\n ==========================================="); - if (help_shortcmdline(brief, program, + if (help_shortcmdline(detail, program, "set-timeout-image {} ")) return; dprintf(2, @@ -350,7 +350,7 @@ static void help_settimeoutimage(const char *program, int brief, int argc, "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \timage file"); } -static void help_none(const char *program, int brief, int argc, char **argv) +static void help_none(const char *program, int detail, int argc, char **argv) { } typedef void (*t_help)(const char *, int, int, char **); @@ -390,12 +390,12 @@ static void help(const char *name, int status) "\n\t-h : print this help and exit"); /* brief helps */ for (cmd = ADD; cmd < _UNKNOWN; cmd++) - get_help(cmd)("", 1, 0, 0); + get_help(cmd)("", 0, 0, 0); dprintf(2, "\n\n"); /* long helps */ for (cmd = ADD; cmd < _UNKNOWN; cmd++) { - get_help(cmd)(name, 0, 0, 0); + get_help(cmd)(name, 1, 0, 0); dprintf(2, "\n\n"); } @@ -411,7 +411,7 @@ static void usage_topic(const char *name, t_command cmd, int argc, char **argv) if (help_none == hlp) usage(name); else - hlp(name, 0, argc, argv); + hlp(name, 2, argc, argv); dprintf(2, "\n"); exit(1); } From 0a3ef216f0356c910cebc8bc554de12a0a8d3b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 11:42:57 +0200 Subject: [PATCH 037/151] help: reverse general form and example --- utils/v4l2loopback-ctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 070efdb7..e8647607 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -306,10 +306,10 @@ static void help_setcaps(const char *program, int detail, int argc, char **argv) return; dprintf(2, "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." - "\n \tformat specification, e.g. 'UYVY:3840x2160@60/1 (:x@)" + "\n \tformat specification as ':x@' (e.g. 'UYVY:3840x2160@60/1')" "\n"); if (detail>1) { - dprintf(2, "\nknown fourcc-codes" + dprintf(2, "\unknown fourcc-codes" "\n==================" "\nFOURCC\thex \tdec \tdescription" "\n------\t----------\t------------\t-----------" From b73a30d1933796344c6759da19c938f516958206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 11:47:42 +0200 Subject: [PATCH 038/151] codespell fixes - 'parm' -> 'param' - escape-sequences that are misinterpreted as words... --- utils/v4l2loopback-ctl.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index e8647607..a6144a29 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -695,7 +695,7 @@ static int read_caps(const char *devicename, t_caps *caps) static int get_fps(const char *devicename) { t_caps caps; - struct v4l2_streamparm parm; + struct v4l2_streamparm param; int fd = -1; int num = -1, denom = -1; int ret = 0; @@ -711,19 +711,19 @@ static int get_fps(const char *devicename) if (fd < 0) goto done; - memset(&parm, 0, sizeof(parm)); - parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - if (ioctl(fd, VIDIOC_G_PARM, &parm) == 0) { - const struct v4l2_fract *tf = &parm.parm.output.timeperframe; + memset(¶m, 0, sizeof(param)); + param.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (ioctl(fd, VIDIOC_G_PARM, ¶m) == 0) { + const struct v4l2_fract *tf = ¶m.parm.output.timeperframe; num = tf->numerator; denom = tf->denominator; goto done; } - memset(&parm, 0, sizeof(parm)); - parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(fd, VIDIOC_G_PARM, &parm) == 0) { - const struct v4l2_fract *tf = &parm.parm.output.timeperframe; + memset(¶m, 0, sizeof(param)); + param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(fd, VIDIOC_G_PARM, ¶m) == 0) { + const struct v4l2_fract *tf = ¶m.parm.output.timeperframe; num = tf->numerator; denom = tf->denominator; goto done; From 493cbf1c3e1b5078ad8e706a72a6ba9653e442ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 15:03:07 +0200 Subject: [PATCH 039/151] fix typo --- utils/v4l2loopback-ctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index a6144a29..216dd219 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -309,11 +309,11 @@ static void help_setcaps(const char *program, int detail, int argc, char **argv) "\n \tformat specification as ':x@' (e.g. 'UYVY:3840x2160@60/1')" "\n"); if (detail>1) { - dprintf(2, "\unknown fourcc-codes" + dprintf(2, "\nknown fourcc-codes" "\n==================" "\nFOURCC\thex \tdec \tdescription" "\n------\t----------\t------------\t-----------" - "\n"); + ""); char fourcc[5]; const size_t num_formats = sizeof(formats) / sizeof(*formats); size_t i = 0; From 131bbaa4ffc2bdcfe72eabd1754e35aad190eb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 15:15:58 +0200 Subject: [PATCH 040/151] add 'install' target for utils --- utils/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/utils/Makefile b/utils/Makefile index f662a4b3..03750757 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -3,6 +3,13 @@ ifneq ($(V4L2LOOPBACK_SNAPSHOT_VERSION),) CPPFLAGS += -DSNAPSHOT_VERSION='"$(V4L2LOOPBACK_SNAPSHOT_VERSION)"' endif +prefix?=/usr +exec_prefix = ${prefix} +bindir = ${exec_prefix}/bin +INSTALL = /usr/bin/install -c +INSTALL_PROGRAM = ${INSTALL} +MKDIR_P = /usr/bin/mkdir -p + .PHONY: default clean programs = v4l2loopback-ctl @@ -11,3 +18,7 @@ default: $(programs) clean: -rm $(programs) + +install: + $(MKDIR_P) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) $(programs) $(DESTDIR)$(bindir) From 69834316f3d86818c9aa38085e7f9c7f45ff778e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 15:19:02 +0200 Subject: [PATCH 041/151] clang-format --- utils/v4l2loopback-ctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 216dd219..3df4e7f6 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -308,7 +308,7 @@ static void help_setcaps(const char *program, int detail, int argc, char **argv) "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \tformat specification as ':x@' (e.g. 'UYVY:3840x2160@60/1')" "\n"); - if (detail>1) { + if (detail > 1) { dprintf(2, "\nknown fourcc-codes" "\n==================" "\nFOURCC\thex \tdec \tdescription" From 58cebf1582e625b20c3bcbe76a4c8ec52529e847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Mon, 24 Apr 2023 13:29:46 +0200 Subject: [PATCH 042/151] more gitignores --- .gitignore | 1 + tests/.gitignore | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 tests/.gitignore diff --git a/.gitignore b/.gitignore index 18ac2c69..66413e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*~ .tmp_versions/ *.cmd *.yuv diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..f5e112fa --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,7 @@ +# ignore binaries (files without a dot) +* +!*.* + +# backup files +*~ +.*.swp From c0c9f418da8463adeba3f5187e4f5b87585fc69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 21 Apr 2023 16:24:13 +0200 Subject: [PATCH 043/151] Bump copyright dates --- README.md | 2 +- utils/v4l2loopback-ctl.c | 12 ++++++++++++ v4l2loopback.c | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 062b3460..58fa5497 100644 --- a/README.md +++ b/README.md @@ -352,7 +352,7 @@ http://github.com/umlaeute/v4l2loopback/. # LICENSE/COPYING -- Copyright (c) 2010-2021 IOhannes m zmoelnig +- Copyright (c) 2010-2023 IOhannes m zmoelnig - Copyright (c) 2016 Gavin Qiu - Copyright (c) 2016 George Chriss - Copyright (c) 2014-2015 Tasos Sahanidis diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 3df4e7f6..61927af6 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -1,3 +1,15 @@ +/* -*- c-file-style: "linux" -*- */ +/* + * v4l2loopback-ctl -- An application to control v4l2loopback devices driver + * + * Copyright (C) 2020-2023 IOhannes m zmoelnig (zmoelnig@iem.at) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ #include #include #include diff --git a/v4l2loopback.c b/v4l2loopback.c index f86b91b6..16f157b7 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -3,7 +3,7 @@ * v4l2loopback.c -- video4linux2 loopback driver * * Copyright (C) 2005-2009 Vasily Levin (vasaka@gmail.com) - * Copyright (C) 2010-2019 IOhannes m zmoelnig (zmoelnig@iem.at) + * Copyright (C) 2010-2023 IOhannes m zmoelnig (zmoelnig@iem.at) * Copyright (C) 2011 Stefan Diewald (stefan.diewald@mytum.de) * Copyright (C) 2012 Anton Novikov (random.plant@gmail.com) * From edca10a595a279242d6dbc2ec2d0654e29c040af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Mon, 24 Apr 2023 11:45:53 +0200 Subject: [PATCH 044/151] simple test application for producing/consuming buffers taken from https://git.linuxtv.org/v4l-utils.git/tree/contrib/test/capture-example.c but enhanced with some debug printout --- tests/Makefile | 4 +- tests/common.h | 133 ++++++++++ tests/consumer.c | 629 +++++++++++++++++++++++++++++++++++++++++++++ tests/producer.c | 652 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1417 insertions(+), 1 deletion(-) create mode 100644 tests/common.h create mode 100644 tests/consumer.c create mode 100644 tests/producer.c diff --git a/tests/Makefile b/tests/Makefile index a1f8b8b2..65c71056 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1 +1,3 @@ -all: test_dqbuf +all: test_dqbuf consumer producer + +consumer producer: common.h diff --git a/tests/common.h b/tests/common.h new file mode 100644 index 00000000..03fd1423 --- /dev/null +++ b/tests/common.h @@ -0,0 +1,133 @@ +/* -*- c-file-style: "linux" -*- */ +/* + * common.h -- some commong functions + * + * Copyright (C) 2023 IOhannes m zmoelnig (zmoelnig@iem.at) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include + +static char *fourcc2str(unsigned int fourcc, char buf[4]) +{ + buf[0] = (fourcc >> 0) & 0xFF; + buf[1] = (fourcc >> 8) & 0xFF; + buf[2] = (fourcc >> 16) & 0xFF; + buf[3] = (fourcc >> 24) & 0xFF; + + return buf; +} +static const char *field2str(unsigned int field) +{ + switch (field) { + case V4L2_FIELD_ANY: + return "any"; + case V4L2_FIELD_NONE: + return "none"; + case V4L2_FIELD_TOP: + return "top"; + case V4L2_FIELD_BOTTOM: + return "bottom"; + case V4L2_FIELD_INTERLACED: + return "interlaced"; + case V4L2_FIELD_SEQ_TB: + return "seq/topbottom"; + case V4L2_FIELD_SEQ_BT: + return "seq/bottomtop"; + case V4L2_FIELD_ALTERNATE: + return "alternate"; + case V4L2_FIELD_INTERLACED_TB: + return "interlaced/topbottom"; + case V4L2_FIELD_INTERLACED_BT: + return "interlaced/bottomtop"; + + default: + break; + } + return "unknown"; +} + +static const char *buftype2str(unsigned int type) +{ + switch (type) { + default: + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return "CAPTURE"; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return "CAPTURE(planar)"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return "OUTPUT"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return "OUTPUT(planar)"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + return "OUTPUT(overlay)"; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return "OVERLAY"; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return "VBI(capture)"; + case V4L2_BUF_TYPE_VBI_OUTPUT: + return "VBI(output)"; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return "SlicedVBI(capture)"; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return "SlicedVBI(output)"; + case V4L2_BUF_TYPE_SDR_CAPTURE: + return "SDR(capture)"; + case V4L2_BUF_TYPE_SDR_OUTPUT: + return "SDR(output)"; + case V4L2_BUF_TYPE_META_CAPTURE: + return "META(capture)"; + case V4L2_BUF_TYPE_META_OUTPUT: + return "META(output)"; + case V4L2_BUF_TYPE_PRIVATE: + return "private"; + } + return "unknown"; +} + +static const char *snprintf_format(char *buf, size_t size, + struct v4l2_format *fmt) +{ + char fourcc[5]; + fourcc[4] = 0; + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + snprintf(buf, size, "%s:%dx%d:%s (%u/%u) field=%s", + buftype2str(fmt->type), fmt->fmt.pix.width, + fmt->fmt.pix.height, + fourcc2str(fmt->fmt.pix.pixelformat, fourcc), + fmt->fmt.pix.bytesperline, fmt->fmt.pix.sizeimage, + field2str(fmt->fmt.pix.field)); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + snprintf(buf, size, "%s:%dx%d:%s (%d planes) field=%s", + buftype2str(fmt->type), fmt->fmt.pix_mp.width, + fmt->fmt.pix_mp.height, + fourcc2str(fmt->fmt.pix_mp.pixelformat, fourcc), + fmt->fmt.pix_mp.num_planes, + field2str(fmt->fmt.pix_mp.field)); + default: + snprintf(buf, size, "TODO: %s(type=%d)", __FUNCTION__, + (fmt->type)); + } + return buf; +} + +static const char *snprintf_buffer(char *strbuf, size_t size, + struct v4l2_buffer *buf) +{ + snprintf(strbuf, size, "#%d:%s (bytes=%d) field=%s @%ld.%06ld", + buf->index, buftype2str(buf->type), buf->bytesused, + field2str(buf->field), buf->timestamp.tv_sec, + buf->timestamp.tv_usec); + return strbuf; +} diff --git a/tests/consumer.c b/tests/consumer.c new file mode 100644 index 00000000..6a7c19f6 --- /dev/null +++ b/tests/consumer.c @@ -0,0 +1,629 @@ +/* + * V4L2 video capture example + * + * This program can be used and distributed without restrictions. + * + * This program is provided with the V4L2 API + * see http://linuxtv.org/docs.php for more information + */ + +#include +#include +#include +#include + +#include /* getopt_long() */ + +#include /* low-level i/o */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" + +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + +enum io_method { + IO_METHOD_READ, + IO_METHOD_MMAP, + IO_METHOD_USERPTR, +}; + +struct buffer { + void *start; + size_t length; +}; + +static char *dev_name; +static enum io_method io = IO_METHOD_MMAP; +static int fd = -1; +struct buffer *buffers; +static unsigned int n_buffers; +static int frame_count = 70; + +static void errno_exit(const char *s) +{ + fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); + exit(EXIT_FAILURE); +} + +static int xioctl(int fh, unsigned long int request, void *arg) +{ + int r; + + do { + r = ioctl(fh, request, arg); + } while (-1 == r && EINTR == errno); + + return r; +} + +static int read_frame(void) +{ + char strbuf[1024]; + struct v4l2_buffer buf; + unsigned int i; + + switch (io) { + case IO_METHOD_READ: + if (-1 == read(fd, buffers[0].start, buffers[0].length)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + errno_exit("read"); + } + } + printf("READ\t%lu@%p\n", buffers[0].length, buffers[0].start); + break; + + case IO_METHOD_MMAP: + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + errno_exit("VIDIOC_DQBUF"); + } + } + + printf("MMAP\t%s\n", + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); + assert(buf.index < n_buffers); + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + break; + + case IO_METHOD_USERPTR: + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + + if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + errno_exit("VIDIOC_DQBUF"); + } + } + + for (i = 0; i < n_buffers; ++i) + if (buf.m.userptr == (unsigned long)buffers[i].start && + buf.length == buffers[i].length) + break; + + printf("USERPTR\t%s\n", + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); + assert(i < n_buffers); + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + break; + } + + return 1; +} + +static void mainloop(void) +{ + unsigned int count; + int keep_running = 1; + + count = frame_count; + + while (1) { + if (count < 1) + break; + if (frame_count >= 0) { + count--; + } + + for (;;) { + fd_set fds; + struct timeval tv; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + /* Timeout. */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + r = select(fd + 1, &fds, NULL, NULL, &tv); + + if (-1 == r) { + if (EINTR == errno) + continue; + errno_exit("select"); + } + + if (0 == r) { + fprintf(stderr, "select timeout\n"); + exit(EXIT_FAILURE); + } + + if (read_frame()) + break; + /* EAGAIN - continue select loop. */ + } + } +} + +static void stop_capturing(void) +{ + enum v4l2_buf_type type; + + switch (io) { + case IO_METHOD_READ: + /* Nothing to do. */ + break; + + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) + errno_exit("VIDIOC_STREAMOFF"); + break; + } +} + +static void start_capturing(void) +{ + unsigned int i; + enum v4l2_buf_type type; + + switch (io) { + case IO_METHOD_READ: + /* Nothing to do. */ + break; + + case IO_METHOD_MMAP: + for (i = 0; i < n_buffers; ++i) { + struct v4l2_buffer buf; + + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); + break; + + case IO_METHOD_USERPTR: + for (i = 0; i < n_buffers; ++i) { + struct v4l2_buffer buf; + + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.index = i; + buf.m.userptr = (unsigned long)buffers[i].start; + buf.length = buffers[i].length; + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); + break; + } +} + +static void uninit_device(void) +{ + unsigned int i; + + switch (io) { + case IO_METHOD_READ: + free(buffers[0].start); + break; + + case IO_METHOD_MMAP: + for (i = 0; i < n_buffers; ++i) + if (-1 == munmap(buffers[i].start, buffers[i].length)) + errno_exit("munmap"); + break; + + case IO_METHOD_USERPTR: + for (i = 0; i < n_buffers; ++i) + free(buffers[i].start); + break; + } + + free(buffers); +} + +static void init_read(unsigned int buffer_size) +{ + buffers = calloc(1, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + buffers[0].length = buffer_size; + buffers[0].start = malloc(buffer_size); + + if (!buffers[0].start) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } +} + +static void init_mmap(void) +{ + char strbuf[1024]; + const int count = 4; + struct v4l2_requestbuffers req; + + CLEAR(req); + + req.count = count; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + fprintf(stderr, + "%s does not support " + "memory mapping\n", + dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_REQBUFS"); + } + } + printf("requested %d buffers, got %d\n", count, req.count); + + if (req.count < 2) { + fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name); + exit(EXIT_FAILURE); + } + + buffers = calloc(req.count, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { + struct v4l2_buffer buf; + + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + + if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) + errno_exit("VIDIOC_QUERYBUF"); + printf("requested buffer %d/%d: %s\n", n_buffers, count, + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); + + buffers[n_buffers].length = buf.length; + buffers[n_buffers].start = + mmap(NULL /* start anywhere */, buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, fd, buf.m.offset); + + if (MAP_FAILED == buffers[n_buffers].start) + errno_exit("mmap"); + } +} + +static void init_userp(unsigned int buffer_size) +{ + struct v4l2_requestbuffers req; + + CLEAR(req); + + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; + + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + fprintf(stderr, + "%s does not support " + "user pointer i/o\n", + dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_REQBUFS"); + } + } + + buffers = calloc(4, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + for (n_buffers = 0; n_buffers < 4; ++n_buffers) { + buffers[n_buffers].length = buffer_size; + buffers[n_buffers].start = malloc(buffer_size); + + if (!buffers[n_buffers].start) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + } +} + +static void init_device(void) +{ + char strbuf[1024]; + struct v4l2_capability cap; + struct v4l2_cropcap cropcap; + struct v4l2_crop crop; + struct v4l2_format fmt; + + if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { + if (EINVAL == errno) { + fprintf(stderr, "%s is no V4L2 device\n", dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_QUERYCAP"); + } + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + fprintf(stderr, "%s is no video capture device\n", dev_name); + exit(EXIT_FAILURE); + } + + switch (io) { + case IO_METHOD_READ: + if (!(cap.capabilities & V4L2_CAP_READWRITE)) { + fprintf(stderr, "%s does not support read i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + fprintf(stderr, "%s does not support streaming i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + } + + /* Select video input, video standard and tune here. */ + + CLEAR(cropcap); + + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = cropcap.defrect; /* reset to default */ + + if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { + switch (errno) { + case EINVAL: + /* Cropping not supported. */ + break; + default: + /* Errors ignored. */ + break; + } + } + } else { + /* Errors ignored. */ + } + + CLEAR(fmt); + + /* get the current format */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) + errno_exit("VIDIOC_G_FMT"); + printf("got format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + + /* try to se tthe current format (no-change should always succeed) */ + if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) + errno_exit("VIDIOC_S_FMT"); + printf("set format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + + switch (io) { + case IO_METHOD_READ: + init_read(fmt.fmt.pix.sizeimage); + break; + + case IO_METHOD_MMAP: + init_mmap(); + break; + + case IO_METHOD_USERPTR: + init_userp(fmt.fmt.pix.sizeimage); + break; + } +} + +static void close_device(void) +{ + if (-1 == close(fd)) + errno_exit("close"); + + fd = -1; +} + +static void open_device(void) +{ + struct stat st; + + if (-1 == stat(dev_name, &st)) { + fprintf(stderr, "Cannot identify '%s': %d, %s\n", dev_name, + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (!S_ISCHR(st.st_mode)) { + fprintf(stderr, "%s is no device\n", dev_name); + exit(EXIT_FAILURE); + } + + fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); + + if (-1 == fd) { + fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_name, errno, + strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static void usage(FILE *fp, int argc, char **argv) +{ + fprintf(fp, + "Usage: %s [options]\n\n" + "Version 1.3\n" + "Options:\n" + "-d | --device name Video device name [%s]\n" + "-h | --help Print this message\n" + "-m | --mmap Use memory mapped buffers [default]\n" + "-r | --read Use read() calls\n" + "-u | --userp Use application allocated buffers\n" + "-c | --count Number of frames to grab [%i] (negative numbers: no limit)\n" + "", + argv[0], dev_name, frame_count); +} + +static const char short_options[] = "d:hmruofc:"; + +static const struct option long_options[] = { + { "device", required_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "mmap", no_argument, NULL, 'm' }, + { "read", no_argument, NULL, 'r' }, + { "userp", no_argument, NULL, 'u' }, + { "count", required_argument, NULL, 'c' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + dev_name = "/dev/video0"; + + for (;;) { + int idx; + int c; + + c = getopt_long(argc, argv, short_options, long_options, &idx); + + if (-1 == c) + break; + + switch (c) { + case 0: /* getopt_long() flag */ + break; + + case 'd': + dev_name = optarg; + break; + + case 'h': + usage(stdout, argc, argv); + exit(EXIT_SUCCESS); + + case 'm': + io = IO_METHOD_MMAP; + break; + + case 'r': + io = IO_METHOD_READ; + break; + + case 'u': + io = IO_METHOD_USERPTR; + break; + + case 'c': + errno = 0; + frame_count = strtol(optarg, NULL, 0); + if (errno) + errno_exit(optarg); + break; + + default: + usage(stderr, argc, argv); + exit(EXIT_FAILURE); + } + } + + open_device(); + init_device(); + start_capturing(); + mainloop(); + stop_capturing(); + uninit_device(); + close_device(); + fprintf(stderr, "\n"); + return 0; +} diff --git a/tests/producer.c b/tests/producer.c new file mode 100644 index 00000000..44116c63 --- /dev/null +++ b/tests/producer.c @@ -0,0 +1,652 @@ +/* + * V4L2 video output example + * + * This program can be used and distributed without restrictions. + * + * This program is provided with the V4L2 API + * see http://linuxtv.org/docs.php for more information + */ + +#include +#include +#include +#include + +#include /* getopt_long() */ + +#include /* low-level i/o */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" + +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + +enum io_method { + IO_METHOD_WRITE, + IO_METHOD_MMAP, + IO_METHOD_USERPTR, +}; + +struct buffer { + void *start; + size_t length; +}; + +static char *dev_name; +static enum io_method io = IO_METHOD_MMAP; +static int fd = -1; +struct buffer *buffers; +static unsigned int n_buffers; +static int frame_count = 70; +static unsigned int width = 640; +static unsigned int height = 480; +static unsigned int fourcc = V4L2_PIX_FMT_YUYV; + +static void errno_exit(const char *s) +{ + fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); + exit(EXIT_FAILURE); +} +static unsigned int str2fourcc(char buf[4]) +{ + return (buf[0]) + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); +} + +static int xioctl(int fh, unsigned long int request, void *arg) +{ + int r; + + do { + r = ioctl(fh, request, arg); + } while (-1 == r && EINTR == errno); + + return r; +} + +static unsigned char randombyte(void) +{ + static unsigned int random_nextseed = 1489853723; + random_nextseed = random_nextseed * 472940017 + 832416023; + return random_nextseed & 0xFF; +} +static void process_image(unsigned char *data, size_t length) +{ + printf("fill %d bytes at %p\n", length, data); + while (length--) { + *data++ = randombyte(); + } +} + +static int write_frame(void) +{ + char strbuf[1024]; + struct v4l2_buffer buf; + unsigned int i; + + switch (io) { + case IO_METHOD_WRITE: + process_image(buffers[0].start, buffers[0].length); + if (-1 == write(fd, buffers[0].start, buffers[0].length)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + errno_exit("write"); + } + } + printf("WRITE\t%lu@%p\n", buffers[0].length, buffers[0].start); + break; + + case IO_METHOD_MMAP: + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + errno_exit("VIDIOC_DQBUF"); + } + } + + printf("MMAP\t%s\n", + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); + assert(buf.index < n_buffers); + process_image(buffers[buf.index].start, buf.bytesused); + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + break; + + case IO_METHOD_USERPTR: + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_USERPTR; + + if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + errno_exit("VIDIOC_DQBUF"); + } + } + + for (i = 0; i < n_buffers; ++i) + if (buf.m.userptr == (unsigned long)buffers[i].start && + buf.length == buffers[i].length) + break; + + assert(i < n_buffers); + printf("USERPTR\t%s\n", + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); + process_image(buffers[buf.index].start, buf.bytesused); + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + break; + } + + return 1; +} + +static void mainloop(void) +{ + unsigned int count; + int keep_running = 1; + + count = frame_count; + + while (1) { + if (count < 1) + break; + if (frame_count >= 0) { + count--; + } + + for (;;) { + if (write_frame()) + break; + /* EAGAIN - continue select loop. */ + } + usleep(33000); + } +} + +static void stop_capturing(void) +{ + enum v4l2_buf_type type; + + switch (io) { + case IO_METHOD_WRITE: + /* Nothing to do. */ + break; + + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) + errno_exit("VIDIOC_STREAMOFF"); + break; + } +} + +static void start_capturing(void) +{ + unsigned int i; + enum v4l2_buf_type type; + + switch (io) { + case IO_METHOD_WRITE: + /* Nothing to do. */ + break; + + case IO_METHOD_MMAP: + for (i = 0; i < n_buffers; ++i) { + struct v4l2_buffer buf; + + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + } + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); + break; + + case IO_METHOD_USERPTR: + for (i = 0; i < n_buffers; ++i) { + struct v4l2_buffer buf; + + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_USERPTR; + buf.index = i; + buf.m.userptr = (unsigned long)buffers[i].start; + buf.length = buffers[i].length; + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + } + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); + break; + } +} + +static void uninit_device(void) +{ + unsigned int i; + + switch (io) { + case IO_METHOD_WRITE: + free(buffers[0].start); + break; + + case IO_METHOD_MMAP: + for (i = 0; i < n_buffers; ++i) + if (-1 == munmap(buffers[i].start, buffers[i].length)) + errno_exit("munmap"); + break; + + case IO_METHOD_USERPTR: + for (i = 0; i < n_buffers; ++i) + free(buffers[i].start); + break; + } + + free(buffers); +} + +static void init_write(unsigned int buffer_size) +{ + buffers = calloc(1, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + buffers[0].length = buffer_size; + buffers[0].start = malloc(buffer_size); + + if (!buffers[0].start) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } +} + +static void init_mmap(void) +{ + char strbuf[1024]; + const int count = 4; + struct v4l2_requestbuffers req; + + CLEAR(req); + + req.count = count; + req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + req.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + fprintf(stderr, + "%s does not support " + "memory mapping\n", + dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_REQBUFS"); + } + } + printf("requested %d buffers, got %d\n", count, req.count); + + if (req.count < 2) { + fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name); + exit(EXIT_FAILURE); + } + + buffers = calloc(req.count, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { + struct v4l2_buffer buf; + + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + + if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) + errno_exit("VIDIOC_QUERYBUF"); + printf("requested buffer %d/%d: %s\n", n_buffers, count, + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); + + buffers[n_buffers].length = buf.length; + buffers[n_buffers].start = + mmap(NULL /* start anywhere */, buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, fd, buf.m.offset); + + if (MAP_FAILED == buffers[n_buffers].start) + errno_exit("mmap"); + //printf("buffer @%p of %d bytes\n", buffers[n_buffers].start, buffers[n_buffers].length); + } +} + +static void init_userp(unsigned int buffer_size) +{ + struct v4l2_requestbuffers req; + + CLEAR(req); + + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + req.memory = V4L2_MEMORY_USERPTR; + + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + fprintf(stderr, + "%s does not support " + "user pointer i/o\n", + dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_REQBUFS"); + } + } + + buffers = calloc(4, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + for (n_buffers = 0; n_buffers < 4; ++n_buffers) { + buffers[n_buffers].length = buffer_size; + buffers[n_buffers].start = malloc(buffer_size); + + if (!buffers[n_buffers].start) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + } +} + +static void init_device(void) +{ + char strbuf[1024]; + struct v4l2_capability cap; + struct v4l2_cropcap cropcap; + struct v4l2_crop crop; + struct v4l2_format fmt; + + if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { + if (EINVAL == errno) { + fprintf(stderr, "%s is no V4L2 device\n", dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_QUERYCAP"); + } + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) { + fprintf(stderr, "%s is no video output device\n", dev_name); + exit(EXIT_FAILURE); + } + + switch (io) { + case IO_METHOD_WRITE: + if (!(cap.capabilities & V4L2_CAP_READWRITE)) { + fprintf(stderr, "%s does not support write i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + fprintf(stderr, "%s does not support streaming i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + } + + /* Select video input, video standard and tune here. */ + + CLEAR(cropcap); + + cropcap.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { + crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + crop.c = cropcap.defrect; /* reset to default */ + + if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { + switch (errno) { + case EINVAL: + /* Cropping not supported. */ + break; + default: + /* Errors ignored. */ + break; + } + } + } else { + /* Errors ignored. */ + } + + CLEAR(fmt); + + /* get the current format */ + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) + errno_exit("VIDIOC_G_FMT"); + printf("got format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + + /* try to se tthe current format (no-change should always succeed) */ + if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) + errno_exit("VIDIOC_S_FMT"); + printf("set format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + + switch (io) { + case IO_METHOD_WRITE: + init_write(fmt.fmt.pix.sizeimage); + break; + + case IO_METHOD_MMAP: + init_mmap(); + break; + + case IO_METHOD_USERPTR: + init_userp(fmt.fmt.pix.sizeimage); + break; + } +} + +static void close_device(void) +{ + if (-1 == close(fd)) + errno_exit("close"); + + fd = -1; +} + +static void open_device(void) +{ + struct stat st; + + if (-1 == stat(dev_name, &st)) { + fprintf(stderr, "Cannot identify '%s': %d, %s\n", dev_name, + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (!S_ISCHR(st.st_mode)) { + fprintf(stderr, "%s is no device\n", dev_name); + exit(EXIT_FAILURE); + } + + fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); + + if (-1 == fd) { + fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_name, errno, + strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static void usage(FILE *fp, int argc, char **argv) +{ + char fourccstr[5]; + fourccstr[4] = 0; + fprintf(fp, + "Usage: %s [options]\n\n" + "Version 1.3\n" + "Options:\n" + "-d | --device name Video device name [%s]\n" + "-h | --help Print this message\n" + "-m | --mmap Use memory mapped buffers [default]\n" + "-w | --write Use write() calls\n" + "-u | --userp Use application allocated buffers\n" + "-c | --count Number of frames to grab [%i] (negative numbers: no limit)\n" + "-f | --format Use format [%dx%d@%s]\n" + "", + argv[0], dev_name, frame_count, width, height, + fourcc2str(fourcc, fourccstr)); +} + +static const char short_options[] = "d:hmwuf:c:"; + +static const struct option long_options[] = { + { "device", required_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "mmap", no_argument, NULL, 'm' }, + { "write", no_argument, NULL, 'w' }, + { "userp", no_argument, NULL, 'u' }, + { "count", required_argument, NULL, 'c' }, + { "format", required_argument, NULL, 'f' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + dev_name = "/dev/video0"; + + for (;;) { + int idx; + int c; + + c = getopt_long(argc, argv, short_options, long_options, &idx); + + if (-1 == c) + break; + + switch (c) { + case 0: /* getopt_long() flag */ + break; + + case 'd': + dev_name = optarg; + break; + + case 'h': + usage(stdout, argc, argv); + exit(EXIT_SUCCESS); + + case 'm': + io = IO_METHOD_MMAP; + break; + + case 'w': + io = IO_METHOD_WRITE; + break; + + case 'u': + io = IO_METHOD_USERPTR; + break; + + case 'c': + errno = 0; + frame_count = strtol(optarg, NULL, 0); + if (errno) + errno_exit(optarg); + break; + + case 'f': { + int n; + int w = 0, h = 0; + char col[5]; + n = sscanf(optarg, "%dx%d@%4c", &w, &h, col); + if (n == 3) { + width = (w > 0) ? w : 0; + height = (h > 0) ? h : 0; + fourcc = str2fourcc(col); + col[4] = 0; + } else { + errno_exit(optarg); + } + break; + } + + default: + usage(stderr, argc, argv); + exit(EXIT_FAILURE); + } + } + + open_device(); + init_device(); + start_capturing(); + mainloop(); + stop_capturing(); + uninit_device(); + close_device(); + fprintf(stderr, "\n"); + return 0; +} From bc79fa3fee16a300eec1dc1b5fb07d7e13c21375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 10:13:15 +0200 Subject: [PATCH 045/151] more debugging and a global buffer --- tests/common.h | 3 ++- tests/producer.c | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/common.h b/tests/common.h index 03fd1423..c625fd97 100644 --- a/tests/common.h +++ b/tests/common.h @@ -125,7 +125,8 @@ static const char *snprintf_format(char *buf, size_t size, static const char *snprintf_buffer(char *strbuf, size_t size, struct v4l2_buffer *buf) { - snprintf(strbuf, size, "#%d:%s (bytes=%d) field=%s @%ld.%06ld", + snprintf(strbuf, size, "@%p #%d:%s (bytes=%d) field=%s @%ld.%06ld", + buf, buf->index, buftype2str(buf->type), buf->bytesused, field2str(buf->field), buf->timestamp.tv_sec, buf->timestamp.tv_usec); diff --git a/tests/producer.c b/tests/producer.c index 44116c63..7b91017b 100644 --- a/tests/producer.c +++ b/tests/producer.c @@ -49,6 +49,7 @@ static int frame_count = 70; static unsigned int width = 640; static unsigned int height = 480; static unsigned int fourcc = V4L2_PIX_FMT_YUYV; +static char strbuf[1024]; static void errno_exit(const char *s) { @@ -87,7 +88,6 @@ static void process_image(unsigned char *data, size_t length) static int write_frame(void) { - char strbuf[1024]; struct v4l2_buffer buf; unsigned int i; @@ -240,6 +240,8 @@ static void start_capturing(void) buf.memory = V4L2_MEMORY_MMAP; buf.index = i; + printf("MMAP init qbuf %d/%d: %s\n", i, n_buffers, + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); } @@ -259,6 +261,8 @@ static void start_capturing(void) buf.m.userptr = (unsigned long)buffers[i].start; buf.length = buffers[i].length; + printf("USERPTR init qbuf %d/%d: %s\n", i, n_buffers, + snprintf_buffer(strbuf, sizeof(strbuf), &buf)); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); } @@ -313,7 +317,6 @@ static void init_write(unsigned int buffer_size) static void init_mmap(void) { - char strbuf[1024]; const int count = 4; struct v4l2_requestbuffers req; @@ -370,7 +373,7 @@ static void init_mmap(void) if (MAP_FAILED == buffers[n_buffers].start) errno_exit("mmap"); - //printf("buffer @%p of %d bytes\n", buffers[n_buffers].start, buffers[n_buffers].length); + printf("buffer#%d @%p of %d bytes\n", n_buffers, buffers[n_buffers].start, buffers[n_buffers].length); } } @@ -416,7 +419,6 @@ static void init_userp(unsigned int buffer_size) static void init_device(void) { - char strbuf[1024]; struct v4l2_capability cap; struct v4l2_cropcap cropcap; struct v4l2_crop crop; @@ -488,7 +490,7 @@ static void init_device(void) printf("got format: %s\n", snprintf_format(strbuf, sizeof(strbuf), &fmt)); - /* try to se tthe current format (no-change should always succeed) */ + /* try to se the current format (no-change should always succeed) */ if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) errno_exit("VIDIOC_S_FMT"); printf("set format: %s\n", From 9e26b24913ef5e1ad97a4a8001680b3e132b95ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 11:18:56 +0200 Subject: [PATCH 046/151] set bufsize/bytesused when initializing buffers for MMAP --- tests/common.h | 6 +++--- tests/producer.c | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/tests/common.h b/tests/common.h index c625fd97..de68f857 100644 --- a/tests/common.h +++ b/tests/common.h @@ -100,7 +100,7 @@ static const char *snprintf_format(char *buf, size_t size, switch (fmt->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_CAPTURE: - snprintf(buf, size, "%s:%dx%d:%s (%u/%u) field=%s", + snprintf(buf, size, "%s:%dx%d:%s bytes/line=%u sizeimage=%u field=%s", buftype2str(fmt->type), fmt->fmt.pix.width, fmt->fmt.pix.height, fourcc2str(fmt->fmt.pix.pixelformat, fourcc), @@ -125,9 +125,9 @@ static const char *snprintf_format(char *buf, size_t size, static const char *snprintf_buffer(char *strbuf, size_t size, struct v4l2_buffer *buf) { - snprintf(strbuf, size, "@%p #%d:%s (bytes=%d) field=%s @%ld.%06ld", + snprintf(strbuf, size, "@%p #%d:%s length=%d used=%d field=%s @%ld.%06ld", buf, - buf->index, buftype2str(buf->type), buf->bytesused, + buf->index, buftype2str(buf->type), buf->length, buf->bytesused, field2str(buf->field), buf->timestamp.tv_sec, buf->timestamp.tv_usec); return strbuf; diff --git a/tests/producer.c b/tests/producer.c index 7b91017b..eaca5e33 100644 --- a/tests/producer.c +++ b/tests/producer.c @@ -38,6 +38,7 @@ enum io_method { struct buffer { void *start; size_t length; + size_t bytesused; }; static char *dev_name; @@ -48,7 +49,7 @@ static unsigned int n_buffers; static int frame_count = 70; static unsigned int width = 640; static unsigned int height = 480; -static unsigned int fourcc = V4L2_PIX_FMT_YUYV; +static unsigned int pixelformat = V4L2_PIX_FMT_YUYV; static char strbuf[1024]; static void errno_exit(const char *s) @@ -93,7 +94,7 @@ static int write_frame(void) switch (io) { case IO_METHOD_WRITE: - process_image(buffers[0].start, buffers[0].length); + process_image(buffers[0].start, buffers[0].bytesused); if (-1 == write(fd, buffers[0].start, buffers[0].length)) { switch (errno) { case EAGAIN: @@ -108,7 +109,7 @@ static int write_frame(void) errno_exit("write"); } } - printf("WRITE\t%lu@%p\n", buffers[0].length, buffers[0].start); + printf("WRITE\t%lu/%lu@%p\n", buffers[0].bytesused, buffers[0].length, buffers[0].start); break; case IO_METHOD_MMAP: @@ -164,7 +165,7 @@ static int write_frame(void) for (i = 0; i < n_buffers; ++i) if (buf.m.userptr == (unsigned long)buffers[i].start && - buf.length == buffers[i].length) + buf.bytesused == buffers[i].bytesused) break; assert(i < n_buffers); @@ -239,8 +240,10 @@ static void start_capturing(void) buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; + buf.length = buffers[i].length; + buf.bytesused = buffers[i].bytesused; - printf("MMAP init qbuf %d/%d: %s\n", i, n_buffers, + printf("MMAP init qbuf %d/%d (length=%d): %s\n", i, n_buffers, buffers[i].length, snprintf_buffer(strbuf, sizeof(strbuf), &buf)); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); @@ -259,6 +262,7 @@ static void start_capturing(void) buf.memory = V4L2_MEMORY_USERPTR; buf.index = i; buf.m.userptr = (unsigned long)buffers[i].start; + buf.bytesused = buffers[i].bytesused; buf.length = buffers[i].length; printf("USERPTR init qbuf %d/%d: %s\n", i, n_buffers, @@ -307,6 +311,7 @@ static void init_write(unsigned int buffer_size) } buffers[0].length = buffer_size; + buffers[0].bytesused = buffer_size; buffers[0].start = malloc(buffer_size); if (!buffers[0].start) { @@ -366,6 +371,7 @@ static void init_mmap(void) snprintf_buffer(strbuf, sizeof(strbuf), &buf)); buffers[n_buffers].length = buf.length; + buffers[n_buffers].bytesused = buf.bytesused; buffers[n_buffers].start = mmap(NULL /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */, @@ -409,6 +415,7 @@ static void init_userp(unsigned int buffer_size) for (n_buffers = 0; n_buffers < 4; ++n_buffers) { buffers[n_buffers].length = buffer_size; buffers[n_buffers].start = malloc(buffer_size); + buffers[n_buffers].bytesused = buffer_size; if (!buffers[n_buffers].start) { fprintf(stderr, "Out of memory\n"); @@ -496,6 +503,25 @@ static void init_device(void) printf("set format: %s\n", snprintf_format(strbuf, sizeof(strbuf), &fmt)); + switch (fmt.type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + fmt.fmt.pix.width = width; + fmt.fmt.pix.height = height; + fmt.fmt.pix.pixelformat = pixelformat; + strbuf[4] = 0; printf("pixelformat=%d (%s)\n", pixelformat, fourcc2str(pixelformat, strbuf)); + break; + default: + printf("unable to set format for anything but output/single-plane\n"); + break; + } + printf("finalizing format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { + fprintf(stderr, "VIDIOC_S_FMT error %d, %s\n", errno, strerror(errno)); + } + printf("final format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + switch (io) { case IO_METHOD_WRITE: init_write(fmt.fmt.pix.sizeimage); @@ -560,7 +586,7 @@ static void usage(FILE *fp, int argc, char **argv) "-f | --format Use format [%dx%d@%s]\n" "", argv[0], dev_name, frame_count, width, height, - fourcc2str(fourcc, fourccstr)); + fourcc2str(pixelformat, fourccstr)); } static const char short_options[] = "d:hmwuf:c:"; @@ -628,7 +654,7 @@ int main(int argc, char **argv) if (n == 3) { width = (w > 0) ? w : 0; height = (h > 0) ? h : 0; - fourcc = str2fourcc(col); + pixelformat = str2fourcc(col); col[4] = 0; } else { errno_exit(optarg); From 49d837aea53e52866479fcf40f3371aa751d28ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 12:22:15 +0200 Subject: [PATCH 047/151] fix random; optionally set timestamp --- tests/producer.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/tests/producer.c b/tests/producer.c index eaca5e33..93a6173e 100644 --- a/tests/producer.c +++ b/tests/producer.c @@ -12,6 +12,7 @@ #include #include +#include /* clock_gettime() */ #include /* getopt_long() */ #include /* low-level i/o */ @@ -50,6 +51,7 @@ static int frame_count = 70; static unsigned int width = 640; static unsigned int height = 480; static unsigned int pixelformat = V4L2_PIX_FMT_YUYV; +static int set_timestamp = 0; static char strbuf[1024]; static void errno_exit(const char *s) @@ -73,20 +75,22 @@ static int xioctl(int fh, unsigned long int request, void *arg) return r; } + +static unsigned int random_nextseed = 148985372; static unsigned char randombyte(void) { - static unsigned int random_nextseed = 1489853723; - random_nextseed = random_nextseed * 472940017 + 832416023; - return random_nextseed & 0xFF; + random_nextseed = (random_nextseed * 472940017) + 832416023; + return ((random_nextseed>>16) & 0xFF); } static void process_image(unsigned char *data, size_t length) { - printf("fill %d bytes at %p\n", length, data); - while (length--) { - *data++ = randombyte(); + size_t i; + for(i=0; i Date: Tue, 25 Apr 2023 13:44:24 +0200 Subject: [PATCH 048/151] try more... --- tests/producer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/producer.c b/tests/producer.c index 93a6173e..25c4c416 100644 --- a/tests/producer.c +++ b/tests/producer.c @@ -504,12 +504,23 @@ static void init_device(void) /* get the current format */ fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) + errno_exit("VIDIOC_G_FMT"); + printf("get format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + + /* try to set the current format (no-change should always succeed) */ + if (xioctl(fd, VIDIOC_TRY_FMT, &fmt) < 0) + errno_exit("VIDIOC_TRY_FMT"); + printf("tried format: %s\n", + snprintf_format(strbuf, sizeof(strbuf), &fmt)); + /* and get the format again */ if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) errno_exit("VIDIOC_G_FMT"); printf("got format: %s\n", snprintf_format(strbuf, sizeof(strbuf), &fmt)); - /* try to se the current format (no-change should always succeed) */ + /* try to set the current format (no-change should always succeed) */ if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) errno_exit("VIDIOC_S_FMT"); printf("set format: %s\n", @@ -520,7 +531,6 @@ static void init_device(void) fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; fmt.fmt.pix.pixelformat = pixelformat; - strbuf[4] = 0; printf("pixelformat=%d (%s)\n", pixelformat, fourcc2str(pixelformat, strbuf)); break; default: printf("unable to set format for anything but output/single-plane\n"); From 047c3159334ca143d4f225c3d552104cd6e51d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 15:06:03 +0200 Subject: [PATCH 049/151] stuff... --- tests/common.h | 31 ++++++++++++++++++++++++++----- tests/producer.c | 11 ++++++++++- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/tests/common.h b/tests/common.h index de68f857..7a86b820 100644 --- a/tests/common.h +++ b/tests/common.h @@ -92,6 +92,24 @@ static const char *buftype2str(unsigned int type) return "unknown"; } +static const char *bufmemory2str(unsigned int mem) +{ + switch(mem) { + case V4L2_MEMORY_MMAP: + return "MMAP"; + case V4L2_MEMORY_USERPTR: + return "USERPTR"; + case V4L2_MEMORY_OVERLAY: + return "OVERLAY"; + case V4L2_MEMORY_DMABUF: + return "DMABUF"; + default: + break; + } + return "unknown"; +} + + static const char *snprintf_format(char *buf, size_t size, struct v4l2_format *fmt) { @@ -125,10 +143,13 @@ static const char *snprintf_format(char *buf, size_t size, static const char *snprintf_buffer(char *strbuf, size_t size, struct v4l2_buffer *buf) { - snprintf(strbuf, size, "@%p #%d:%s length=%d used=%d field=%s @%ld.%06ld", - buf, - buf->index, buftype2str(buf->type), buf->length, buf->bytesused, - field2str(buf->field), buf->timestamp.tv_sec, - buf->timestamp.tv_usec); + snprintf(strbuf, size, "buffer#%d @ %p %s bytesused=%d, length=%d flags=0x%08X field=%s timestamp=%ld.%06ld memory=%s (offset=%d)" + , buf->index, buf, buftype2str(buf->type) + , buf->bytesused, buf->length + , buf->flags + , field2str(buf->field) + , buf->timestamp.tv_sec, buf->timestamp.tv_usec + , bufmemory2str(buf->memory), buf->m.offset + ); return strbuf; } diff --git a/tests/producer.c b/tests/producer.c index 25c4c416..a42235d2 100644 --- a/tests/producer.c +++ b/tests/producer.c @@ -30,6 +30,11 @@ #define CLEAR(x) memset(&(x), 0, sizeof(x)) +#define SET_QUEUED(buffer) ((buffer).flags |= V4L2_BUF_FLAG_QUEUED) + +#define IS_QUEUED(buffer) \ + ((buffer).flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) + enum io_method { IO_METHOD_WRITE, IO_METHOD_MMAP, @@ -146,12 +151,16 @@ static int write_frame(void) buf.timestamp.tv_usec = 0; } printf("MMAP\t%s\n", - snprintf_buffer(strbuf, sizeof(strbuf), &buf)); + snprintf_buffer(strbuf, sizeof(strbuf), &buf));fflush(stdout); assert(buf.index < n_buffers); process_image(buffers[buf.index].start, buf.bytesused); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); + if(!IS_QUEUED (buf)) { + printf("driver pretends buffer is not queued even if queue succeeded\n"); + SET_QUEUED(buf); + } break; case IO_METHOD_USERPTR: From c936024187af03046c3079db7f13b8c7cdcef9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 10:14:23 +0200 Subject: [PATCH 050/151] use DEFAULT colorspace --- v4l2loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 16f157b7..049cb700 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2539,7 +2539,7 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) dev->pix_format.height = 0; /* V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; */ dev->pix_format.pixelformat = formats[0].fourcc; dev->pix_format.colorspace = - V4L2_COLORSPACE_SRGB; /* do we need to set this ? */ + V4L2_COLORSPACE_DEFAULT; /* do we need to set this ? */ dev->pix_format.field = V4L2_FIELD_NONE; dev->buffer_size = PAGE_ALIGN(dev->pix_format.sizeimage); From e4b94a32584ae275721197372228d811470a9119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 13:46:41 +0200 Subject: [PATCH 051/151] set V4L2_BUF_FLAG_TIMESTAMP_COPY flag when copying the buffer timestamp and allow forcing of timestamp copy --- v4l2loopback.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 049cb700..cfb2b2f1 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1527,10 +1527,13 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) case V4L2_BUF_TYPE_VIDEO_OUTPUT: dprintkrw("output QBUF pos: %lld index: %d\n", (long long)dev->write_position, index); - if (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0) + if ((!(b->buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_COPY)) + && (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0)) v4l2l_get_timestamp(&b->buffer); - else + else { b->buffer.timestamp = buf->timestamp; + b->buffer.flags |= V4L2_BUF_FLAG_TIMESTAMP_COPY; + } b->buffer.bytesused = buf->bytesused; set_done(b); buffer_written(dev, b); From b54500f18fea3ec8e3c5d6240cf1da63508d16c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 15:28:27 +0200 Subject: [PATCH 052/151] more diagnostic output --- v4l2loopback.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index cfb2b2f1..a4e41aeb 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1521,12 +1521,25 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) switch (buf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - dprintkrw("capture QBUF index: %d\n", index); + dprintkrw("qbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" + , index + , buf->index, buf + , buf->type, buf->bytesused, buf->length + , buf->flags, buf->field + , buf->timestamp.tv_sec, buf->timestamp.tv_usec + , buf->sequence + ); set_queued(b); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - dprintkrw("output QBUF pos: %lld index: %d\n", - (long long)dev->write_position, index); + dprintkrw("qbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" + , index + , buf->index, buf + , buf->type, buf->bytesused, buf->length + , buf->flags, buf->field + , buf->timestamp.tv_sec, buf->timestamp.tv_usec + , buf->sequence + ); if ((!(b->buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_COPY)) && (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0)) v4l2l_get_timestamp(&b->buffer); @@ -1642,17 +1655,35 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) } unset_flags(&dev->buffers[index]); *buf = dev->buffers[index].buffer; + dprintkrw("dqbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" + , index + , buf->index, buf + , buf->type, buf->bytesused, buf->length + , buf->flags, buf->field + , buf->timestamp.tv_sec, buf->timestamp.tv_usec + , buf->sequence + ); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: spin_lock_bh(&dev->list_lock); + b = list_entry(dev->outbufs_list.prev, struct v4l2l_buffer, list_head); list_move_tail(&b->list_head, &dev->outbufs_list); + spin_unlock_bh(&dev->list_lock); dprintkrw("output DQBUF index: %d\n", b->buffer.index); unset_flags(b); *buf = b->buffer; buf->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + dprintkrw("dqbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" + , index + , buf->index, buf + , buf->type, buf->bytesused, buf->length + , buf->flags, buf->field + , buf->timestamp.tv_sec, buf->timestamp.tv_usec + , buf->sequence + ); return 0; default: return -EINVAL; @@ -2216,7 +2247,6 @@ static void init_buffers(struct v4l2_loopback_device *dev) buffer_size = dev->buffer_size; bytesused = dev->pix_format.sizeimage; - for (i = 0; i < dev->buffers_number; ++i) { struct v4l2_buffer *b = &dev->buffers[i].buffer; b->index = i; From 6e3d0a77736d7a035fa11639be49933f2a849a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 15:28:48 +0200 Subject: [PATCH 053/151] refactor the TRY/S_FMT code --- v4l2loopback.c | 331 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 216 insertions(+), 115 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index a4e41aeb..fade586d 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -439,6 +439,155 @@ static void pix_format_set_size(struct v4l2_pix_format *f, } } +static int v4l2l_fill_format(struct v4l2_format *fmt, int capture, + const u32 maxwidth, const u32 maxheight) +{ + u32 width = fmt->fmt.pix.width, height = fmt->fmt.pix.height; + u32 pixelformat = fmt->fmt.pix.pixelformat; + struct v4l2_format fmt0 = *fmt; + u32 bytesperline = 0, sizeimage = 0; + if (width < 1) + width = V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; + if (width > maxwidth) + width = maxwidth; + if (height < 1) + height = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; + if (height > maxheight) + height = maxheight; + + /* sets: width,height,pixelformat,bytesperline,sizeimage */ + if (!(V4L2_TYPE_IS_MULTIPLANAR(fmt0.type))) { + fmt0.fmt.pix.bytesperline = 0; + fmt0.fmt.pix.sizeimage = 0; + } + + if (0) { + ; + } else if (!v4l2_fill_pixfmt(&fmt0.fmt.pix, pixelformat, width, + height)) { + ; + } else if (!v4l2_fill_pixfmt_mp(&fmt0.fmt.pix_mp, pixelformat, width, + height)) { + ; + } else { + const struct v4l2l_format *format = + format_by_fourcc(pixelformat); + if (!format) + return -EINVAL; + pix_format_set_size(&fmt0.fmt.pix, format, width, height); + fmt0.fmt.pix.pixelformat = format->fourcc; + } + + if (V4L2_TYPE_IS_MULTIPLANAR(fmt0.type)) { + *fmt = fmt0; + + if ((fmt->fmt.pix_mp.colorspace == V4L2_COLORSPACE_DEFAULT) || + (fmt->fmt.pix_mp.colorspace > V4L2_COLORSPACE_DCI_P3)) + fmt->fmt.pix_mp.colorspace = V4L2_COLORSPACE_SRGB; + if (V4L2_FIELD_ANY == fmt->fmt.pix_mp.field) + fmt->fmt.pix_mp.field = V4L2_FIELD_NONE; + if (capture) + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + } else { + bytesperline = fmt->fmt.pix.bytesperline; + sizeimage = fmt->fmt.pix.sizeimage; + + *fmt = fmt0; + + if (!fmt->fmt.pix.bytesperline) + fmt->fmt.pix.bytesperline = bytesperline; + if (!fmt->fmt.pix.sizeimage) + fmt->fmt.pix.sizeimage = sizeimage; + + if ((fmt->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) || + (fmt->fmt.pix.colorspace > V4L2_COLORSPACE_DCI_P3)) + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + if (V4L2_FIELD_ANY == fmt->fmt.pix.field) + fmt->fmt.pix.field = V4L2_FIELD_NONE; + if (capture) + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + else + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + } + + return 0; +} + +static int pix_format_eq(const struct v4l2_pix_format *ref, + const struct v4l2_pix_format *tgt, int strict) +{ + /* check if the two formats are equivalent. + * ANY fields are handled gracefully + */ +#define _pix_format_eq0(x) \ + if (ref->x != tgt->x) \ + result = 0 +#define _pix_format_eq1(x, def) \ + do { \ + if ((def != tgt->x) && (ref->x != tgt->x)) { \ + printk(KERN_INFO #x " failed"); \ + result = 0; \ + } \ + } while (0) + int result = 1; + _pix_format_eq0(width); + _pix_format_eq0(height); + _pix_format_eq0(pixelformat); + if (!strict) + return result; + _pix_format_eq1(field, V4L2_FIELD_ANY); + _pix_format_eq0(bytesperline); + _pix_format_eq0(sizeimage); + _pix_format_eq1(colorspace, V4L2_COLORSPACE_DEFAULT); + return result; +} + +static struct v4l2_loopback_device *v4l2loopback_getdevice(struct file *f); +static int inner_try_setfmt(struct file *file, struct v4l2_format *fmt) +{ + int capture = V4L2_TYPE_IS_CAPTURE(fmt->type); + struct v4l2_loopback_device *dev; + int needschange = 0; + char buf[5]; + buf[4] = 0; + + dev = v4l2loopback_getdevice(file); + + needschange = !(pix_format_eq(&dev->pix_format, &fmt->fmt.pix, 0)); + if (dev->ready_for_capture > 0 || dev->active_readers > 0) { + /* the format is fixated if we + - have readers (active_readers>0) + - and/or have writers (ready_for_capture>0) + */ + + fmt->fmt.pix = dev->pix_format; + if (needschange) { + if (dev->active_readers > 0 && capture) { + /* cannot call fmt_cap while there are readers */ + return -EBUSY; + } + if (dev->ready_for_capture > 0 && !capture) { + /* cannot call fmt_out while there are writers */ + return -EBUSY; + } + } + } + if (v4l2l_fill_format(fmt, capture, dev->max_width, dev->max_height) != + 0) { + return -EINVAL; + } + + if (1) { + char buf[5]; + buf[4] = 0; + dprintk("capFOURCC=%s\n", + fourcc2str(dev->pix_format.pixelformat, buf)); + } + return 0; +} + static int set_timeperframe(struct v4l2_loopback_device *dev, struct v4l2_fract *tpf) { @@ -866,7 +1015,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, } /* checks if it is OK to change to format fmt; - * actual check is done by inner_try_fmt_cap + * actual check is done by inner_try_setfmt * just checking that pixelformat is OK and set other parameters, app should * obey this decision * called on VIDIOC_TRY_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE @@ -874,24 +1023,13 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, static int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct v4l2_loopback_device *dev; - char buf[5]; - - dev = v4l2loopback_getdevice(file); - - if (0 == dev->ready_for_capture) { - dprintk("setting fmt_cap not possible yet\n"); - return -EBUSY; - } - - if (fmt->fmt.pix.pixelformat != dev->pix_format.pixelformat) + int ret = 0; + if (!V4L2_TYPE_IS_CAPTURE(fmt->type)) return -EINVAL; - - fmt->fmt.pix = dev->pix_format; - - buf[4] = 0; - dprintk("capFOURCC=%s\n", fourcc2str(dev->pix_format.pixelformat, buf)); - return 0; + ret = inner_try_setfmt(file, fmt); + if (-EBUSY == ret) + return 0; + return ret; } /* sets new output format, if possible @@ -902,7 +1040,15 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - return vidioc_try_fmt_cap(file, priv, fmt); + int ret; + struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); + if (!V4L2_TYPE_IS_CAPTURE(fmt->type)) + return -EINVAL; + ret = inner_try_setfmt(file, fmt); + if (!ret) { + dev->pix_format = fmt->fmt.pix; + } + return ret; } /* ------------------ OUTPUT ----------------------- */ @@ -985,52 +1131,13 @@ static int vidioc_g_fmt_out(struct file *file, void *priv, static int vidioc_try_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct v4l2_loopback_device *dev; - MARK(); - - dev = v4l2loopback_getdevice(file); - - /* TODO(vasaka) loopback does not care about formats writer want to set, - * maybe it is a good idea to restrict format somehow */ - if (dev->ready_for_capture) { - fmt->fmt.pix = dev->pix_format; - } else { - __u32 w = fmt->fmt.pix.width; - __u32 h = fmt->fmt.pix.height; - __u32 pixfmt = fmt->fmt.pix.pixelformat; - const struct v4l2l_format *format = format_by_fourcc(pixfmt); - - if (w > dev->max_width) - w = dev->max_width; - if (h > dev->max_height) - h = dev->max_height; - - dprintk("trying image %dx%d\n", w, h); - - if (w < 1) - w = V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; - - if (h < 1) - h = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; - - if (NULL == format) - format = &formats[0]; - - pix_format_set_size(&fmt->fmt.pix, format, w, h); - - fmt->fmt.pix.pixelformat = format->fourcc; - - if ((fmt->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) || - (fmt->fmt.pix.colorspace > V4L2_COLORSPACE_DCI_P3)) - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - - if (V4L2_FIELD_ANY == fmt->fmt.pix.field) - fmt->fmt.pix.field = V4L2_FIELD_NONE; - - /* FIXXME: try_fmt should never modify the device-state */ - dev->pix_format = fmt->fmt.pix; - } - return 0; + int ret = 0; + if (!V4L2_TYPE_IS_OUTPUT(fmt->type)) + return -EINVAL; + ret = inner_try_setfmt(file, fmt); + if (-EBUSY == ret) + return 0; + return ret; } /* sets new output format, if possible; @@ -1042,26 +1149,28 @@ static int vidioc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt) { struct v4l2_loopback_device *dev; - char buf[5]; int ret; - MARK(); - - dev = v4l2loopback_getdevice(file); - ret = vidioc_try_fmt_out(file, priv, fmt); - - dprintk("s_fmt_out(%d) %d...%d\n", ret, dev->ready_for_capture, - dev->pix_format.sizeimage); - + char buf[5]; buf[4] = 0; - dprintk("outFOURCC=%s\n", fourcc2str(dev->pix_format.pixelformat, buf)); + if (!V4L2_TYPE_IS_OUTPUT(fmt->type)) + return -EINVAL; + dev = v4l2loopback_getdevice(file); - if (ret < 0) - return ret; + ret = inner_try_setfmt(file, fmt); + if (!ret) { + dev->pix_format = fmt->fmt.pix; + dprintk("s_fmt_out(%d) %d...%d\n", ret, dev->ready_for_capture, + dev->pix_format.sizeimage); + dprintk("outFOURCC=%s\n", + fourcc2str(dev->pix_format.pixelformat, buf)); - if (!dev->ready_for_capture) { - dev->buffer_size = PAGE_ALIGN(dev->pix_format.sizeimage); - fmt->fmt.pix.sizeimage = dev->buffer_size; - ret = allocate_buffers(dev); + if (!dev->ready_for_capture) { + dev->buffer_size = + PAGE_ALIGN(dev->pix_format.sizeimage); + // JMZ: TODO get rid of the next line + fmt->fmt.pix.sizeimage = dev->buffer_size; + ret = allocate_buffers(dev); + } } return ret; } @@ -1521,27 +1630,23 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) switch (buf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - dprintkrw("qbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" - , index - , buf->index, buf - , buf->type, buf->bytesused, buf->length - , buf->flags, buf->field - , buf->timestamp.tv_sec, buf->timestamp.tv_usec - , buf->sequence - ); + dprintkrw( + "qbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + index, buf->index, buf, buf->type, buf->bytesused, + buf->length, buf->flags, buf->field, + buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->sequence); set_queued(b); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - dprintkrw("qbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" - , index - , buf->index, buf - , buf->type, buf->bytesused, buf->length - , buf->flags, buf->field - , buf->timestamp.tv_sec, buf->timestamp.tv_usec - , buf->sequence - ); - if ((!(b->buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_COPY)) - && (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0)) + dprintkrw( + "qbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + index, buf->index, buf, buf->type, buf->bytesused, + buf->length, buf->flags, buf->field, + buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->sequence); + if ((!(b->buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_COPY)) && + (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0)) v4l2l_get_timestamp(&b->buffer); else { b->buffer.timestamp = buf->timestamp; @@ -1655,14 +1760,12 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) } unset_flags(&dev->buffers[index]); *buf = dev->buffers[index].buffer; - dprintkrw("dqbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" - , index - , buf->index, buf - , buf->type, buf->bytesused, buf->length - , buf->flags, buf->field - , buf->timestamp.tv_sec, buf->timestamp.tv_usec - , buf->sequence - ); + dprintkrw( + "dqbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + index, buf->index, buf, buf->type, buf->bytesused, + buf->length, buf->flags, buf->field, + buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->sequence); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: spin_lock_bh(&dev->list_lock); @@ -1676,14 +1779,12 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) unset_flags(b); *buf = b->buffer; buf->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - dprintkrw("dqbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n" - , index - , buf->index, buf - , buf->type, buf->bytesused, buf->length - , buf->flags, buf->field - , buf->timestamp.tv_sec, buf->timestamp.tv_usec - , buf->sequence - ); + dprintkrw( + "dqbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + index, buf->index, buf, buf->type, buf->bytesused, + buf->length, buf->flags, buf->field, + buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->sequence); return 0; default: return -EINVAL; From 1bc2ac0a4ea6694d1613af0a2425a08fd53912c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 17:45:38 +0200 Subject: [PATCH 054/151] fix formatting warnings when printing timestamps --- v4l2loopback.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index fade586d..21274877 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1631,19 +1631,19 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) switch (buf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: dprintkrw( - "qbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + "qbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, buf->sequence); set_queued(b); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: dprintkrw( - "qbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + "qbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, buf->sequence); if ((!(b->buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_COPY)) && (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0)) @@ -1761,10 +1761,10 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) unset_flags(&dev->buffers[index]); *buf = dev->buffers[index].buffer; dprintkrw( - "dqbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + "dqbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, buf->sequence); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -1780,10 +1780,10 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) *buf = b->buffer; buf->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; dprintkrw( - "dqbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06lld sequence=%d\n", + "dqbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, buf->timestamp.tv_usec, + buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, buf->sequence); return 0; default: From 674d9e4e285ae713b09228f1a154fc803c2ed155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 17:57:16 +0200 Subject: [PATCH 055/151] compat for older kernels --- v4l2loopback.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index 21274877..a082cbe0 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -392,6 +392,17 @@ struct v4l2l_format { #include "v4l2loopback_formats.h" +#ifndef V4L2_TYPE_IS_CAPTURE +#define V4L2_TYPE_IS_CAPTURE(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE \ + || (type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE +#endif /* V4L2_TYPE_IS_CAPTURE */ +#ifndef V4L2_TYPE_IS_OUTPUT +#define V4L2_TYPE_IS_OUTPUT(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \ + || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE +#endif /* V4L2_TYPE_IS_OUTPUT */ + static const unsigned int FORMATS = ARRAY_SIZE(formats); static char *fourcc2str(unsigned int fourcc, char buf[4]) @@ -463,12 +474,14 @@ static int v4l2l_fill_format(struct v4l2_format *fmt, int capture, if (0) { ; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) } else if (!v4l2_fill_pixfmt(&fmt0.fmt.pix, pixelformat, width, height)) { ; } else if (!v4l2_fill_pixfmt_mp(&fmt0.fmt.pix_mp, pixelformat, width, height)) { ; +#endif } else { const struct v4l2l_format *format = format_by_fourcc(pixelformat); From 3d25b9b3a7e3e21fc84ae0e849f620e4776a672d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 17:58:04 +0200 Subject: [PATCH 056/151] more clang-format --- tests/common.h | 21 +++---- tests/producer.c | 26 ++++---- tests/test_dqbuf.c | 153 +++++++++++++++++++++++---------------------- 3 files changed, 103 insertions(+), 97 deletions(-) diff --git a/tests/common.h b/tests/common.h index 7a86b820..3878deab 100644 --- a/tests/common.h +++ b/tests/common.h @@ -94,7 +94,7 @@ static const char *buftype2str(unsigned int type) static const char *bufmemory2str(unsigned int mem) { - switch(mem) { + switch (mem) { case V4L2_MEMORY_MMAP: return "MMAP"; case V4L2_MEMORY_USERPTR: @@ -109,7 +109,6 @@ static const char *bufmemory2str(unsigned int mem) return "unknown"; } - static const char *snprintf_format(char *buf, size_t size, struct v4l2_format *fmt) { @@ -118,7 +117,8 @@ static const char *snprintf_format(char *buf, size_t size, switch (fmt->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_CAPTURE: - snprintf(buf, size, "%s:%dx%d:%s bytes/line=%u sizeimage=%u field=%s", + snprintf(buf, size, + "%s:%dx%d:%s bytes/line=%u sizeimage=%u field=%s", buftype2str(fmt->type), fmt->fmt.pix.width, fmt->fmt.pix.height, fourcc2str(fmt->fmt.pix.pixelformat, fourcc), @@ -143,13 +143,12 @@ static const char *snprintf_format(char *buf, size_t size, static const char *snprintf_buffer(char *strbuf, size_t size, struct v4l2_buffer *buf) { - snprintf(strbuf, size, "buffer#%d @ %p %s bytesused=%d, length=%d flags=0x%08X field=%s timestamp=%ld.%06ld memory=%s (offset=%d)" - , buf->index, buf, buftype2str(buf->type) - , buf->bytesused, buf->length - , buf->flags - , field2str(buf->field) - , buf->timestamp.tv_sec, buf->timestamp.tv_usec - , bufmemory2str(buf->memory), buf->m.offset - ); + snprintf( + strbuf, size, + "buffer#%d @ %p %s bytesused=%d, length=%d flags=0x%08X field=%s timestamp=%ld.%06ld memory=%s (offset=%d)", + buf->index, buf, buftype2str(buf->type), buf->bytesused, + buf->length, buf->flags, field2str(buf->field), + buf->timestamp.tv_sec, buf->timestamp.tv_usec, + bufmemory2str(buf->memory), buf->m.offset); return strbuf; } diff --git a/tests/producer.c b/tests/producer.c index a42235d2..0be36d63 100644 --- a/tests/producer.c +++ b/tests/producer.c @@ -33,7 +33,7 @@ #define SET_QUEUED(buffer) ((buffer).flags |= V4L2_BUF_FLAG_QUEUED) #define IS_QUEUED(buffer) \ - ((buffer).flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) + ((buffer).flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) enum io_method { IO_METHOD_WRITE, @@ -80,17 +80,16 @@ static int xioctl(int fh, unsigned long int request, void *arg) return r; } - static unsigned int random_nextseed = 148985372; static unsigned char randombyte(void) { random_nextseed = (random_nextseed * 472940017) + 832416023; - return ((random_nextseed>>16) & 0xFF); + return ((random_nextseed >> 16) & 0xFF); } static void process_image(unsigned char *data, size_t length) { size_t i; - for(i=0; i #define COUNT 4 -#define sysfail(msg) { printf ("%s failed: %s\n", (msg), strerror (errno)); return -1; } +#define sysfail(msg) \ + { \ + printf("%s failed: %s\n", (msg), strerror(errno)); \ + return -1; \ + } -void -usage(const char*progname) +void usage(const char *progname) { - printf("usage: %s \n", progname); - exit(1); + printf("usage: %s \n", progname); + exit(1); } -int -main (int argc, char **argv) +int main(int argc, char **argv) { - struct v4l2_format fmt = { 0 }; - struct v4l2_requestbuffers breq = { 0 }; - struct v4l2_buffer bufs[COUNT]; - void *data[COUNT] = { 0 }; - int fd; - int i; + struct v4l2_format fmt = { 0 }; + struct v4l2_requestbuffers breq = { 0 }; + struct v4l2_buffer bufs[COUNT]; + void *data[COUNT] = { 0 }; + int fd; + int i; - if(argc<2) usage(argv[0]); + if (argc < 2) + usage(argv[0]); - fd = open (argv[1], O_RDWR); - if (fd < 0) - sysfail("open"); + fd = open(argv[1], O_RDWR); + if (fd < 0) + sysfail("open"); - fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - fmt.fmt.pix.width = 320; - fmt.fmt.pix.height = 240; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fmt.fmt.pix.width = 320; + fmt.fmt.pix.height = 240; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; - if (ioctl (fd, VIDIOC_S_FMT, &fmt) < 0) - sysfail ("S_FMT"); + if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) + sysfail("S_FMT"); - breq.count = COUNT; - breq.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - breq.memory = V4L2_MEMORY_MMAP; + breq.count = COUNT; + breq.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + breq.memory = V4L2_MEMORY_MMAP; - if (ioctl (fd, VIDIOC_REQBUFS, &breq) < 0) - sysfail ("REQBUFS"); + if (ioctl(fd, VIDIOC_REQBUFS, &breq) < 0) + sysfail("REQBUFS"); - assert (breq.count == COUNT); + assert(breq.count == COUNT); - memset (bufs, 0, sizeof (bufs)); + memset(bufs, 0, sizeof(bufs)); - for (i = 0; i < COUNT; i++) { - int p; + for (i = 0; i < COUNT; i++) { + int p; - bufs[i].index = i; - bufs[i].type = breq.type; - bufs[i].memory = breq.memory; + bufs[i].index = i; + bufs[i].type = breq.type; + bufs[i].memory = breq.memory; - if (ioctl (fd, VIDIOC_QUERYBUF, &bufs[i]) < 0) - sysfail ("QUERYBUF"); + if (ioctl(fd, VIDIOC_QUERYBUF, &bufs[i]) < 0) + sysfail("QUERYBUF"); - data[i] = mmap (NULL, bufs[i].length, PROT_WRITE, MAP_SHARED, fd, bufs[i].m.offset); - if (data[i] == MAP_FAILED) - sysfail ("mmap"); + data[i] = mmap(NULL, bufs[i].length, PROT_WRITE, MAP_SHARED, fd, + bufs[i].m.offset); + if (data[i] == MAP_FAILED) + sysfail("mmap"); - for (p = 0; p < (bufs[i].bytesused >> 2); p++) - ((unsigned int*)data[i])[p] = 0xFF00FF00; - } + for (p = 0; p < (bufs[i].bytesused >> 2); p++) + ((unsigned int *)data[i])[p] = 0xFF00FF00; + } - if (ioctl (fd, VIDIOC_QBUF, &bufs[0]) < 0) - sysfail ("QBUF"); + if (ioctl(fd, VIDIOC_QBUF, &bufs[0]) < 0) + sysfail("QBUF"); - if ((bufs[0].flags & V4L2_BUF_FLAG_QUEUED) == 0) { - printf ("BUG #1: Driver should set the QUEUED flag before returning from QBUF\n"); - bufs[0].flags |= V4L2_BUF_FLAG_QUEUED; - } + if ((bufs[0].flags & V4L2_BUF_FLAG_QUEUED) == 0) { + printf("BUG #1: Driver should set the QUEUED flag before returning from QBUF\n"); + bufs[0].flags |= V4L2_BUF_FLAG_QUEUED; + } - if (ioctl (fd, VIDIOC_STREAMON, &fmt.type) < 0) - sysfail ("STREAMON"); + if (ioctl(fd, VIDIOC_STREAMON, &fmt.type) < 0) + sysfail("STREAMON"); - i = 1; - while (1) { - struct v4l2_buffer buf = { 0 }; + i = 1; + while (1) { + struct v4l2_buffer buf = { 0 }; - if (ioctl (fd, VIDIOC_QBUF, &bufs[i]) < 0) - sysfail ("QBUF"); + if (ioctl(fd, VIDIOC_QBUF, &bufs[i]) < 0) + sysfail("QBUF"); + printf("\tQUEUED=%d\tDONE=%d\n", + bufs[i].flags & V4L2_BUF_FLAG_QUEUED, + bufs[i].flags & V4L2_BUF_FLAG_DONE); - printf ("\tQUEUED=%d\tDONE=%d\n", - bufs[i].flags & V4L2_BUF_FLAG_QUEUED, - bufs[i].flags & V4L2_BUF_FLAG_DONE); + if ((bufs[i].flags & V4L2_BUF_FLAG_QUEUED) == 0) { + printf("BUG #1: Driver should set the QUEUED flag before returning from QBUF\n"); + bufs[i].flags |= V4L2_BUF_FLAG_QUEUED; + } - if ((bufs[i].flags & V4L2_BUF_FLAG_QUEUED) == 0) { - printf ("BUG #1: Driver should set the QUEUED flag before returning from QBUF\n"); - bufs[i].flags |= V4L2_BUF_FLAG_QUEUED; - } + buf.type = breq.type; + buf.memory = breq.memory; - buf.type = breq.type; - buf.memory = breq.memory; + if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) + sysfail("DBBUF"); - if (ioctl (fd, VIDIOC_DQBUF, &buf) < 0) - sysfail ("DBBUF"); + i = buf.index; - i = buf.index; - - if ((bufs[i].flags & V4L2_BUF_FLAG_QUEUED) == 0) { - printf ("BUG #2: Driver should not dequeue a buffer that was not initially queued\n"); - } + if ((bufs[i].flags & V4L2_BUF_FLAG_QUEUED) == 0) { + printf("BUG #2: Driver should not dequeue a buffer that was not initially queued\n"); + } #if 0 assert (bufs[i].flags & V4L2_BUF_FLAG_QUEUED); assert (!(buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))); #endif - bufs[i] = buf; - } + bufs[i] = buf; + } - return 0; + return 0; } From 12506ff1d0dc973757b4f108110912f2432df60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 18:05:52 +0200 Subject: [PATCH 057/151] reversed compat logic --- v4l2loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index a082cbe0..a41ac40a 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -474,7 +474,7 @@ static int v4l2l_fill_format(struct v4l2_format *fmt, int capture, if (0) { ; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) } else if (!v4l2_fill_pixfmt(&fmt0.fmt.pix, pixelformat, width, height)) { ; From 3575f9075cbbbdeb0feabb35fe0ab8b523a089dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 21:43:58 +0200 Subject: [PATCH 058/151] yikes, yet another typo --- v4l2loopback.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index a41ac40a..676a20a9 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -393,14 +393,14 @@ struct v4l2l_format { #include "v4l2loopback_formats.h" #ifndef V4L2_TYPE_IS_CAPTURE -#define V4L2_TYPE_IS_CAPTURE(type) \ - ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE \ - || (type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE +#define V4L2_TYPE_IS_CAPTURE(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE || \ + (type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) #endif /* V4L2_TYPE_IS_CAPTURE */ #ifndef V4L2_TYPE_IS_OUTPUT -#define V4L2_TYPE_IS_OUTPUT(type) \ - ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \ - || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE +#define V4L2_TYPE_IS_OUTPUT(type) \ + ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT || \ + (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) #endif /* V4L2_TYPE_IS_OUTPUT */ static const unsigned int FORMATS = ARRAY_SIZE(formats); From 19752a76b23305b0e8fd884d0e3172105460ee6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 22:26:59 +0200 Subject: [PATCH 059/151] tests/consumer: make S_FMT errors non-fatal --- tests/consumer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/consumer.c b/tests/consumer.c index 6a7c19f6..a5170c70 100644 --- a/tests/consumer.c +++ b/tests/consumer.c @@ -487,8 +487,11 @@ static void init_device(void) snprintf_format(strbuf, sizeof(strbuf), &fmt)); /* try to se tthe current format (no-change should always succeed) */ - if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) - errno_exit("VIDIOC_S_FMT"); + if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { + const char*s = "VIDIOC_S_FMT"; + fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); + //errno_exit("VIDIOC_S_FMT"); + } printf("set format: %s\n", snprintf_format(strbuf, sizeof(strbuf), &fmt)); From d78f95bb2915ddedaccc87aa2e2fbdd1e0f502d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 22:29:08 +0200 Subject: [PATCH 060/151] prevent multiple output streams --- v4l2loopback.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index 676a20a9..d9d5ee35 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1506,6 +1506,10 @@ static int vidioc_reqbufs(struct file *file, void *fh, return 0; } + if (V4L2_TYPE_IS_OUTPUT(b->type) && (!dev->ready_for_output)) { + return -EBUSY; + } + init_buffers(dev); switch (b->memory) { case V4L2_MEMORY_MMAP: From 845cea0c88c48d5ea4f1cf78104f8002535fe0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Apr 2023 23:15:23 +0200 Subject: [PATCH 061/151] set TIMESTAMP flags --- v4l2loopback.c | 1 + 1 file changed, 1 insertion(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index d9d5ee35..d7a8256c 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -105,6 +105,7 @@ static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) b->timestamp.tv_sec = ts.tv_sec; b->timestamp.tv_usec = (ts.tv_nsec / NSEC_PER_USEC); + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; } #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) From e618c9c06886dee64543106fe8848c76d1202982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 26 Apr 2023 15:43:34 +0200 Subject: [PATCH 062/151] G_FMT_CAP: only report failure if the format has not been fixated that is: we *either* have a producer, or a consumer attached (not just a producer) --- v4l2loopback.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d7a8256c..622d320d 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1019,8 +1019,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, MARK(); dev = v4l2loopback_getdevice(file); - - if (!dev->ready_for_capture) + if (!dev->ready_for_capture && !dev->ready_for_output) return -EINVAL; fmt->fmt.pix = dev->pix_format; From 1a5098e7bfc9066c7bebad04e5b7ce8589939457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 10:12:06 +0200 Subject: [PATCH 063/151] script to check the output/capture formats of a device --- tests/checkformat.sh | 84 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 tests/checkformat.sh diff --git a/tests/checkformat.sh b/tests/checkformat.sh new file mode 100755 index 00000000..03ab5dce --- /dev/null +++ b/tests/checkformat.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +device=${1:-/dev/video0} + +if [ ! -e "${device}" ]; then + echo "Unable to open device ${device}" 1>&2 + exit 1 +fi + + +parse_output() { + # EAGAIN EBUSY EEXIST EFAULT EINVAL EIO ENODEV ENOMEM ENOSPC ENOSYS ENOTTY + while read line; do + if echo "${line}" | grep -q "Invalid argument"; then + echo "EINVAL" + return + fi + if echo "${line}" | grep -q "Device or resource busy"; then + echo "EBUSY" + return + fi + if echo "${line}" | grep -q "Width/Height"; then + echo "${line}" | awk '{print $3}' + return + fi + done +} + +parse_enum_fmt() { + local format + local count=0 + while read line; do + if echo "${line}" | grep -q "\[[0-9]*\]"; then + format=$(printf "%-4s" $(echo "${line}" | awk '{print $2}' | sed -e "s|'||g")) + fi + if echo "${line}" | grep -q "Size: "; then + echo "- ${format}:$(echo $line | awk '{$1=""; $2=""; print $0}' | sed -e 's|[[:space:]]||g')" + count=$((count+1)) + fi + done + [ ${count} -gt 0 ] || echo "- none" +} + +output() { + echo "| ioctl | capture | output |" + echo "| ----- | ------- | ------ |" + echo "| GET | ${G_FMT_CAP} | ${G_FMT_OUT} |" + echo "| TRY0 | ${TRY_FMT_CAP0} | ${TRY_FMT_OUT0} |" + echo "| TRY1 | ${TRY_FMT_CAP1:--} | ${TRY_FMT_OUT1:--} |" + echo "| SET0 | ${S_FMT_CAP0:--} | ${S_FMT_OUT0:--} |" + echo "| SET1 | ${S_FMT_CAP1:--} | ${S_FMT_OUT1:--} |" +} + + +G_FMT_CAP=$(v4l2-ctl -d "${device}" -V | parse_output) +G_FMT_OUT=$(v4l2-ctl -d "${device}" -X | parse_output) + + +TRY_FMT_CAP0=$(v4l2-ctl -d "${device}" --try-fmt-video "width=640,height=480" | parse_output) +TRY_FMT_CAP1=$(v4l2-ctl -d "${device}" --try-fmt-video "width=640,height=4800" | parse_output) +TRY_FMT_OUT0=$(v4l2-ctl -d "${device}" --try-fmt-video-out "width=640,height=480" | parse_output) +TRY_FMT_OUT1=$(v4l2-ctl -d "${device}" --try-fmt-video-out "width=640,height=4800" | parse_output) + +S_FMT_CAP0=$(v4l2-ctl -d "${device}" -v "width=640,height=480" | parse_output) +[ -n "${S_FMT_CAP0}" ] || S_FMT_CAP0=$(v4l2-ctl -d "${device}" -V | parse_output) +S_FMT_CAP1=$(v4l2-ctl -d "${device}" -v "width=640,height=4800" | parse_output) +[ -n "${S_FMT_CAP1}" ] || S_FMT_CAP1=$(v4l2-ctl -d "${device}" -V | parse_output) + +S_FMT_OUT0=$(v4l2-ctl -d "${device}" -x "width=640,height=480" | parse_output) +[ -n "${S_FMT_OUT0}" ] || S_FMT_OUT0=$(v4l2-ctl -d "${device}" -X | parse_output) +S_FMT_OUT1=$(v4l2-ctl -d "${device}" -x "width=640,height=4800" | parse_output) +[ -n "${S_FMT_OUT1}" ] || S_FMT_OUT1=$(v4l2-ctl -d "${device}" -X | parse_output) + + + +output | column -t + +echo +echo "OUTPUT formats" +v4l2-ctl -d "${device}" --list-formats-out-ext | parse_enum_fmt + +echo +echo "CAPTURE formats" +v4l2-ctl -d "${device}" --list-formats-ext | parse_enum_fmt From d12e7d7a861807ff2bb224f89c41e1922a3d2a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 10:53:03 +0200 Subject: [PATCH 064/151] V4L2LOOBACK_IS_FIXED_FMT() to check if the format is changeable --- v4l2loopback.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 622d320d..d41a926f 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -404,6 +404,14 @@ struct v4l2l_format { (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) #endif /* V4L2_TYPE_IS_OUTPUT */ +/* whether the format can be changed */ +/* the format is fixated if we + - have writers (ready_for_capture>0) + - and/or have readers (active_readers>0) +*/ +#define V4L2LOOPBACK_IS_FIXED_FMT(device) \ + (device->ready_for_capture > 0 || device->active_readers > 0) + static const unsigned int FORMATS = ARRAY_SIZE(formats); static char *fourcc2str(unsigned int fourcc, char buf[4]) @@ -570,12 +578,7 @@ static int inner_try_setfmt(struct file *file, struct v4l2_format *fmt) dev = v4l2loopback_getdevice(file); needschange = !(pix_format_eq(&dev->pix_format, &fmt->fmt.pix, 0)); - if (dev->ready_for_capture > 0 || dev->active_readers > 0) { - /* the format is fixated if we - - have readers (active_readers>0) - - and/or have writers (ready_for_capture>0) - */ - + if (V4L2LOOPBACK_IS_FIXED_FMT(dev)) { fmt->fmt.pix = dev->pix_format; if (needschange) { if (dev->active_readers > 0 && capture) { @@ -907,7 +910,7 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, return -EINVAL; dev = v4l2loopback_getdevice(file); - if (dev->ready_for_capture) { + if (V4L2LOOPBACK_IS_FIXED_FMT(dev)) { /* format has already been negotiated * cannot change during runtime */ @@ -950,7 +953,7 @@ static int vidioc_enum_frameintervals(struct file *file, void *fh, if (argp->index) return -EINVAL; - if (dev->ready_for_capture) { + if (V4L2LOOPBACK_IS_FIXED_FMT(dev)) { if (argp->width != dev->pix_format.width || argp->height != dev->pix_format.height || argp->pixel_format != dev->pix_format.pixelformat) @@ -993,7 +996,9 @@ static int vidioc_enum_fmt_cap(struct file *file, void *fh, if (f->index) return -EINVAL; - if (dev->ready_for_capture) { + + if (V4L2LOOPBACK_IS_FIXED_FMT(dev)) { + /* format has been fixed, so only one single format is supported */ const __u32 format = dev->pix_format.pixelformat; snprintf(f->description, sizeof(f->description), "[%c%c%c%c]", @@ -1078,10 +1083,10 @@ static int vidioc_enum_fmt_out(struct file *file, void *fh, dev = v4l2loopback_getdevice(file); - if (dev->ready_for_capture) { + if (V4L2LOOPBACK_IS_FIXED_FMT(dev)) { + /* format has been fixed, so only one single format is supported */ const __u32 format = dev->pix_format.pixelformat; - /* format has been fixed by the writer, so only one single format is supported */ if (f->index) return -EINVAL; From 97ae50f856c1593838a1db3cc57b08e9271726c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 10:53:33 +0200 Subject: [PATCH 065/151] unify the output of vidioc_enum_fmt_* --- v4l2loopback.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index d41a926f..30e26db2 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -990,6 +990,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) { struct v4l2_loopback_device *dev; + const struct v4l2l_format *fmt; MARK(); dev = v4l2loopback_getdevice(file); @@ -1001,9 +1002,15 @@ static int vidioc_enum_fmt_cap(struct file *file, void *fh, /* format has been fixed, so only one single format is supported */ const __u32 format = dev->pix_format.pixelformat; - snprintf(f->description, sizeof(f->description), "[%c%c%c%c]", - (format >> 0) & 0xFF, (format >> 8) & 0xFF, - (format >> 16) & 0xFF, (format >> 24) & 0xFF); + if ((fmt = format_by_fourcc(format))) { + snprintf(f->description, sizeof(f->description), "%s", + fmt->name); + } else { + snprintf(f->description, sizeof(f->description), + "[%c%c%c%c]", (format >> 0) & 0xFF, + (format >> 8) & 0xFF, (format >> 16) & 0xFF, + (format >> 24) & 0xFF); + } f->pixelformat = dev->pix_format.pixelformat; } else { @@ -1090,13 +1097,15 @@ static int vidioc_enum_fmt_out(struct file *file, void *fh, if (f->index) return -EINVAL; - fmt = format_by_fourcc(format); - if (NULL == fmt) - return -EINVAL; - - /* f->flags = ??; */ - snprintf(f->description, sizeof(f->description), "%s", - fmt->name); + if ((fmt = format_by_fourcc(format))) { + snprintf(f->description, sizeof(f->description), "%s", + fmt->name); + } else { + snprintf(f->description, sizeof(f->description), + "[%c%c%c%c]", (format >> 0) & 0xFF, + (format >> 8) & 0xFF, (format >> 16) & 0xFF, + (format >> 24) & 0xFF); + } f->pixelformat = dev->pix_format.pixelformat; } else { From 93c17d2e2f95780228b297500bd9d8e729ff7288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 11:06:02 +0200 Subject: [PATCH 066/151] set default framesize --- v4l2loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 30e26db2..e39c2c69 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2700,8 +2700,8 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) /* FIXME set buffers to 0 */ /* Set initial format */ - dev->pix_format.width = 0; /* V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; */ - dev->pix_format.height = 0; /* V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; */ + dev->pix_format.width = V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; + dev->pix_format.height = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; dev->pix_format.pixelformat = formats[0].fourcc; dev->pix_format.colorspace = V4L2_COLORSPACE_DEFAULT; /* do we need to set this ? */ From 3fc7df2168098b155989320b70c2718d53f5d3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 11:29:56 +0200 Subject: [PATCH 067/151] allow setting of minimum width/height as well --- v4l2loopback.c | 93 +++++++++++++++++++++++++++++++++++--------------- v4l2loopback.h | 8 +++-- 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index e39c2c69..2216dd63 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -347,8 +347,8 @@ struct v4l2_loopback_device { * should only be announced if the resp. "ready" * flag is set; default=TRUE */ - int max_width; - int max_height; + int min_width, max_width; + int min_height, max_height; char card_label[32]; @@ -460,18 +460,23 @@ static void pix_format_set_size(struct v4l2_pix_format *f, } static int v4l2l_fill_format(struct v4l2_format *fmt, int capture, - const u32 maxwidth, const u32 maxheight) + const u32 minwidth, const u32 maxwidth, + const u32 minheight, const u32 maxheight) { u32 width = fmt->fmt.pix.width, height = fmt->fmt.pix.height; u32 pixelformat = fmt->fmt.pix.pixelformat; struct v4l2_format fmt0 = *fmt; u32 bytesperline = 0, sizeimage = 0; - if (width < 1) + if (!width) width = V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; + if (!height) + height = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; + if (width < minwidth) + width = minwidth; if (width > maxwidth) width = maxwidth; - if (height < 1) - height = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; + if (height < minheight) + height = minheight; if (height > maxheight) height = maxheight; @@ -591,8 +596,8 @@ static int inner_try_setfmt(struct file *file, struct v4l2_format *fmt) } } } - if (v4l2l_fill_format(fmt, capture, dev->max_width, dev->max_height) != - 0) { + if (v4l2l_fill_format(fmt, capture, dev->min_width, dev->max_width, + dev->min_height, dev->max_height) != 0) { return -EINVAL; } @@ -927,16 +932,24 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, if (NULL == format_by_fourcc(argp->pixel_format)) return -EINVAL; - argp->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + if (dev->min_width == dev->max_width && + dev->min_height == dev->max_height) { + argp->type = V4L2_FRMSIZE_TYPE_DISCRETE; - argp->stepwise.min_width = V4L2LOOPBACK_SIZE_MIN_WIDTH; - argp->stepwise.min_height = V4L2LOOPBACK_SIZE_MIN_HEIGHT; + argp->discrete.width = dev->min_width; + argp->discrete.height = dev->min_height; + } else { + argp->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - argp->stepwise.max_width = dev->max_width; - argp->stepwise.max_height = dev->max_height; + argp->stepwise.min_width = dev->min_width; + argp->stepwise.min_height = dev->min_height; - argp->stepwise.step_width = 1; - argp->stepwise.step_height = 1; + argp->stepwise.max_width = dev->max_width; + argp->stepwise.max_height = dev->max_height; + + argp->stepwise.step_width = 1; + argp->stepwise.step_height = 1; + } } return 0; } @@ -962,10 +975,10 @@ static int vidioc_enum_frameintervals(struct file *file, void *fh, argp->type = V4L2_FRMIVAL_TYPE_DISCRETE; argp->discrete = dev->capture_param.timeperframe; } else { - if (argp->width < V4L2LOOPBACK_SIZE_MIN_WIDTH || - argp->width > max_width || - argp->height < V4L2LOOPBACK_SIZE_MIN_HEIGHT || - argp->height > max_height || + if (argp->width < dev->min_width || + argp->width > dev->max_width || + argp->height < dev->min_height || + argp->height > dev->max_height || NULL == format_by_fourcc(argp->pixel_format)) return -EINVAL; @@ -2536,10 +2549,18 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) int err = -ENOMEM; - int _max_width = DEFAULT_FROM_CONF( - max_width, < V4L2LOOPBACK_SIZE_MIN_WIDTH, max_width); - int _max_height = DEFAULT_FROM_CONF( - max_height, < V4L2LOOPBACK_SIZE_MIN_HEIGHT, max_height); + u32 _width = V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; + u32 _height = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; + + u32 _min_width = DEFAULT_FROM_CONF(min_width, + < V4L2LOOPBACK_SIZE_MIN_WIDTH, + V4L2LOOPBACK_SIZE_MIN_WIDTH); + u32 _min_height = DEFAULT_FROM_CONF(min_height, + < V4L2LOOPBACK_SIZE_MIN_HEIGHT, + V4L2LOOPBACK_SIZE_MIN_HEIGHT); + u32 _max_width = DEFAULT_FROM_CONF(max_width, < _min_width, max_width); + u32 _max_height = + DEFAULT_FROM_CONF(max_height, < _min_height, max_height); bool _announce_all_caps = (conf && conf->announce_all_caps >= 0) ? (conf->announce_all_caps) : V4L2LOOPBACK_DEFAULT_EXCLUSIVECAPS; @@ -2641,6 +2662,8 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) dev->sustain_framerate = 0; dev->announce_all_caps = _announce_all_caps; + dev->min_width = _min_width; + dev->min_height = _min_height; dev->max_width = _max_width; dev->max_height = _max_height; dev->max_openers = _max_openers; @@ -2700,8 +2723,17 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) /* FIXME set buffers to 0 */ /* Set initial format */ - dev->pix_format.width = V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; - dev->pix_format.height = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; + if (_width < _min_width) + _width = _min_width; + if (_width > _max_width) + _width = _max_width; + if (_height < _min_height) + _height = _min_height; + if (_height > _max_height) + _height = _max_height; + + dev->pix_format.width = _width; + dev->pix_format.height = _height; dev->pix_format.pixelformat = formats[0].fourcc; dev->pix_format.colorspace = V4L2_COLORSPACE_DEFAULT; /* do we need to set this ? */ @@ -2836,6 +2868,8 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, dev->card_label); MARK(); conf.output_nr = conf.capture_nr = dev->vdev->num; + conf.min_width = dev->min_width; + conf.min_height = dev->min_height; conf.max_width = dev->max_width; conf.max_height = dev->max_height; conf.announce_all_caps = dev->announce_all_caps; @@ -2961,6 +2995,8 @@ static void free_devices(void) static int __init v4l2loopback_init_module(void) { + const u32 min_width = V4L2LOOPBACK_SIZE_MIN_WIDTH; + const u32 min_height = V4L2LOOPBACK_SIZE_MIN_HEIGHT; int err; int i; MARK(); @@ -3002,23 +3038,24 @@ static int __init v4l2loopback_init_module(void) max_openers = 2; } - if (max_width < V4L2LOOPBACK_SIZE_MIN_WIDTH) { + if (max_width < min_width) { max_width = V4L2LOOPBACK_SIZE_DEFAULT_MAX_WIDTH; printk(KERN_INFO "v4l2loopback: using max_width %d\n", max_width); } - if (max_height < V4L2LOOPBACK_SIZE_MIN_HEIGHT) { + if (max_height < min_height) { max_height = V4L2LOOPBACK_SIZE_DEFAULT_MAX_HEIGHT; printk(KERN_INFO "v4l2loopback: using max_height %d\n", max_height); } - /* kfree on module release */ for (i = 0; i < devices; i++) { struct v4l2_loopback_config cfg = { // clang-format off .output_nr = video_nr[i], .capture_nr = video_nr[i], + .min_width = min_width, + .min_height = min_height, .max_width = max_width, .max_height = max_height, .announce_all_caps = (!exclusive_caps[i]), diff --git a/v4l2loopback.h b/v4l2loopback.h index 10f8e662..606efd06 100644 --- a/v4l2loopback.h +++ b/v4l2loopback.h @@ -42,11 +42,13 @@ struct v4l2_loopback_config { char card_label[32]; /** - * maximum allowed frame size + * allowed frame size * if too low, default values are used */ - int max_width; - int max_height; + unsigned int min_width; + unsigned int max_width; + unsigned int min_height; + unsigned int max_height; /** * number of buffers to allocate for the queue From 1dc5b3340570ac7c31c050176dd2af3efce8eaa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 11:43:02 +0200 Subject: [PATCH 068/151] v4l2loopback-ctl: allow setting of minimum framesize this breaks compat with the flags, as now lowercase ("-w") is the minimum and uppercase ("-W") is the maximm, while previously "-w" was the max... --- utils/v4l2loopback-ctl.c | 62 +++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 61927af6..75094902 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -257,8 +257,10 @@ static void help_add(const char *program, int detail, int argc, char **argv) dprintf(2, "\n \tany of the following flags may be present" "\n\t -n : pretty name for the device" - "\n\t -w : maximum allowed frame width" - "\n\t -h : maximum allowed frame height" + "\n\t -w : minimum allowed frame width" + "\n\t -h : minimum allowed frame height" + "\n\t -W : maximum allowed frame width" + "\n\t -H : maximum allowed frame height" "\n\t -x : whether to announce OUTPUT/CAPTURE capabilities exclusively" "\n\t -b : buffers to queue" "\n\t -o : maximum allowed concurrent openers" @@ -458,37 +460,43 @@ static void print_conf(struct v4l2_loopback_config *cfg) printf("\tcapture_device# : %d" "\n\toutput_device# : %d" "\n\tcard_label : %s" + "\n\tmin_width : %d" "\n\tmax_width : %d" + "\n\tmin_height : %d" "\n\tmax_height : %d" "\n\tannounce_all_caps: %d" "\n\tmax_buffers : %d" "\n\tmax_openers : %d" "\n\tdebug : %d" "\n", - cfg->capture_nr, cfg->output_nr, cfg->card_label, cfg->max_width, - cfg->max_height, cfg->announce_all_caps, cfg->max_buffers, - cfg->max_openers, cfg->debug); + cfg->capture_nr, cfg->output_nr, cfg->card_label, cfg->min_width, + cfg->max_width, cfg->min_height, cfg->max_height, + cfg->announce_all_caps, cfg->max_buffers, cfg->max_openers, + cfg->debug); MARK(); } static struct v4l2_loopback_config * -make_conf(struct v4l2_loopback_config *cfg, const char *label, int max_width, - int max_height, int exclusive_caps, int buffers, int openers, - int capture_device, int output_device) +make_conf(struct v4l2_loopback_config *cfg, const char *label, int min_width, + int max_width, int min_height, int max_height, int exclusive_caps, + int buffers, int openers, int capture_device, int output_device) { if (!cfg) return 0; - if (!label && max_width <= 0 && max_height <= 0 && exclusive_caps < 0 && - buffers <= 0 && openers <= 0 && capture_device < 0 && - output_device < 0) + /* check if at least one of the args are non-default */ + if (!label && min_width <= 0 && max_width <= 0 && min_height <= 0 && + max_height <= 0 && exclusive_caps < 0 && buffers <= 0 && + openers <= 0 && capture_device < 0 && output_device < 0) return 0; cfg->capture_nr = capture_device; cfg->output_nr = output_device; cfg->card_label[0] = 0; if (label) snprintf(cfg->card_label, 32, "%s", label); - cfg->max_height = max_height; - cfg->max_width = max_width; + cfg->min_width = (min_width < 0) ? 0 : min_width; + cfg->max_width = (max_width < 0) ? 0 : max_width; + cfg->min_height = (min_height < 0) ? 0 : min_height; + cfg->max_height = (max_height < 0) ? 0 : max_height; cfg->announce_all_caps = (exclusive_caps < 0) ? -1 : !exclusive_caps; cfg->max_buffers = buffers; cfg->max_openers = openers; @@ -996,7 +1004,9 @@ int main(int argc, char **argv) t_command cmd; char *label = 0; + int min_width = -1; int max_width = -1; + int min_height = -1; int max_height = -1; int exclusive_caps = -1; int buffers = -1; @@ -1018,7 +1028,8 @@ int main(int argc, char **argv) help(argv[0], 0); break; case ADD: - while ((c = getopt(argc - 1, argv + 1, "vn:w:h:x:b:o:")) != -1) + while ((c = getopt(argc - 1, argv + 1, "vn:w:W:h:H:x:b:o:")) != + -1) switch (c) { case 'v': verbose++; @@ -1027,9 +1038,15 @@ int main(int argc, char **argv) label = optarg; break; case 'w': - max_width = my_atoi("max_width", optarg); + min_width = my_atoi("min_width", optarg); break; case 'h': + min_height = my_atoi("min_height", optarg); + break; + case 'W': + max_width = my_atoi("max_width", optarg); + break; + case 'H': max_height = my_atoi("max_height", optarg); break; case 'x': @@ -1047,6 +1064,18 @@ int main(int argc, char **argv) return 1; } fd = open_controldevice(); + if (min_width > max_width && max_width > 0) { + dprintf(2, + "min_width (%d) must not be greater than max_width (%d)\n", + min_width, max_width); + return 1; + } + if (min_height > max_height && max_height > 0) { + dprintf(2, + "min_height (%d) must not be greater than max_height (%d)\n", + min_height, max_height); + return 1; + } do { struct v4l2_loopback_config cfg; int capture_nr = -1, output_nr = -1; @@ -1064,7 +1093,8 @@ int main(int argc, char **argv) usage_topic(argv[0], cmd, argc - 2, argv + 2); } ret = add_device(fd, - make_conf(&cfg, label, max_width, + make_conf(&cfg, label, min_width, + max_width, min_height, max_height, exclusive_caps, buffers, openers, capture_nr, output_nr), From 66124d3b362b6206a34df384ca8e7db13a679bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 14:28:14 +0200 Subject: [PATCH 069/151] clang-format --- tests/consumer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/consumer.c b/tests/consumer.c index a5170c70..14533e25 100644 --- a/tests/consumer.c +++ b/tests/consumer.c @@ -488,7 +488,7 @@ static void init_device(void) /* try to se tthe current format (no-change should always succeed) */ if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { - const char*s = "VIDIOC_S_FMT"; + const char *s = "VIDIOC_S_FMT"; fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); //errno_exit("VIDIOC_S_FMT"); } From 0b3ea01b1a0b2e88c3169934294c5ee6d27021a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 14:28:36 +0200 Subject: [PATCH 070/151] tests/producer: fix description of "-c" flag and linefeed --- tests/producer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/producer.c b/tests/producer.c index 0be36d63..03c73e4a 100644 --- a/tests/producer.c +++ b/tests/producer.c @@ -617,9 +617,9 @@ static void usage(FILE *fp, int argc, char **argv) "-m | --mmap Use memory mapped buffers [default]\n" "-w | --write Use write() calls\n" "-u | --userp Use application allocated buffers\n" - "-c | --count Number of frames to grab [%i] (negative numbers: no limit)\n" + "-c | --count Number of frames to create [%i] (negative numbers: no limit)\n" "-f | --format Use format [%dx%d@%s]\n" - "-t | --timestamp Set timestamp" + "-t | --timestamp Set timestamp\n" "", argv[0], dev_name, frame_count, width, height, fourcc2str(pixelformat, fourccstr)); From cbc32d047e87b0345054e93e0f379a82f53258b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 16:55:50 +0200 Subject: [PATCH 071/151] long-options for v4l2loopback-ctl this restore sthe flag-compatibility with older versions of this utility: - "-w" is (now) an alias with "--max-width" - the minimum width can (only) be set with "--min-width" --- utils/v4l2loopback-ctl.c | 264 +++++++++++++++++++++++++++------------ 1 file changed, 182 insertions(+), 82 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 75094902..fc4292ac 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -234,6 +235,7 @@ typedef enum { SET_CAPS, GET_CAPS, SET_TIMEOUTIMAGE, + MOO, _UNKNOWN } t_command; @@ -256,15 +258,16 @@ static void help_add(const char *program, int detail, int argc, char **argv) return; dprintf(2, "\n \tany of the following flags may be present" - "\n\t -n : pretty name for the device" - "\n\t -w : minimum allowed frame width" - "\n\t -h : minimum allowed frame height" - "\n\t -W : maximum allowed frame width" - "\n\t -H : maximum allowed frame height" - "\n\t -x : whether to announce OUTPUT/CAPTURE capabilities exclusively" - "\n\t -b : buffers to queue" - "\n\t -o : maximum allowed concurrent openers" - "\n\t -v : verbose mode (print properties of device after successfully creating it)" + "\n\t -n/--name : pretty name for the device" + "\n\t --min-width : minimum allowed frame width" + "\n\t -w/--max-width : maximum allowed frame width" + "\n\t --min-height : minimum allowed frame height" + "\n\t -h/--max-height : maximum allowed frame height" + "\n\t -x/--exclusive-caps : whether to announce OUTPUT/CAPTURE capabilities exclusively" + "\n\t -b/--buffers : buffers to queue" + "\n\t -o/--max-openers : maximum allowed concurrent openers" + "\n\t -v/--verbose : verbose mode (print properties of device after successfully creating it)" + "\n\t -?/--help : print this help" "\n" "\n \tif given, create a specific device (otherwise just create a free one)." "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." @@ -359,7 +362,7 @@ static void help_settimeoutimage(const char *program, int detail, int argc, return; dprintf(2, "\n \tany of the following flags may be present" - "\n\t -t : timeout (in ms)" + "\n\t -t/--timeout : timeout (in ms)" "\n" "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \timage file"); @@ -400,8 +403,8 @@ static void help(const char *name, int status) dprintf(2, "\n\n"); dprintf(2, "\n general commands" "\n ================" - "\n\t-v : print version and exit" - "\n\t-h : print this help and exit"); + "\n\t-v/--version : print version and exit" + "\n\t-h/-?/--help : print this help and exit"); /* brief helps */ for (cmd = ADD; cmd < _UNKNOWN; cmd++) get_help(cmd)("", 0, 0, 0); @@ -605,7 +608,7 @@ static int open_sysfs_file(const char *devicename, const char *filename, sysdev[sizeof(sysdev) - 1] = 0; fd = open(sysdev, flags); if (fd < 0) { - perror("unable to open /sys-device"); + perror(sysdev); return -1; } //dprintf(2, "%s\n", sysdev); @@ -892,6 +895,10 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, "show-preroll-frame=false", 0, 0 }; +#if 0 + printf("set-timeout-image '%s' for '%s' with %dms timeout\n", imagefile, + devicename, timeout); +#endif snprintf(imagearg, 4096, "uri=file://%s", realpath(imagefile, imagefile2)); snprintf(devicearg, 4096, "device=%s", devicename); @@ -937,30 +944,34 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, static t_command get_command(const char *command) { - if (!strncmp(command, "-h", 2)) + if (!strncmp(command, "-h", 3)) + return HELP; + if (!strncmp(command, "-?", 3)) return HELP; - if (!strncmp(command, "-?", 2)) + if (!strncmp(command, "--help", 7)) return HELP; - if (!strncmp(command, "-v", 2)) + if (!strncmp(command, "-v", 3)) return VERSION; - if (!strncmp(command, "--version", 9)) + if (!strncmp(command, "--version", 10)) return VERSION; if (!strncmp(command, "add", 4)) return ADD; - if (!strncmp(command, "del", 3)) + if (!strncmp(command, "del", 3)) /* also allow delete */ return DELETE; - if (!strncmp(command, "query", 5)) + if (!strncmp(command, "query", 6)) return QUERY; - if (!strncmp(command, "set-fps", 7)) + if (!strncmp(command, "set-fps", 8)) return SET_FPS; - if (!strncmp(command, "get-fps", 7)) + if (!strncmp(command, "get-fps", 8)) return GET_FPS; - if (!strncmp(command, "set-caps", 8)) + if (!strncmp(command, "set-caps", 9)) return SET_CAPS; - if (!strncmp(command, "get-caps", 8)) + if (!strncmp(command, "get-caps", 9)) return GET_CAPS; - if (!strncmp(command, "set-timeout-image", 17)) + if (!strncmp(command, "set-timeout-image", 18)) return SET_TIMEOUTIMAGE; + if (!strncmp(command, "moo", 10)) + return MOO; return _UNKNOWN; } @@ -996,8 +1007,36 @@ static int called_deprecated(const char *device, const char *argument, return 0; } +static int do_defaultargs(const char *progname, t_command cmd, int argc, + char **argv) +{ + static const char options_short[] = "?h"; + static const struct option options_long[] = { + { "help", no_argument, NULL, 'h' }, { 0, 0, 0, 0 } + }; + for (;;) { + int c; + int idx; + c = getopt_long(argc - 1, argv + 1, options_short, options_long, + &idx); + if (-1 == c) + break; + switch (c) { + case 'h': + usage_topic(argv[0], cmd, argc - 1, argv + 1); + exit(0); + default: + usage_topic(argv[0], cmd, argc - 1, argv + 1); + exit(1); + } + } + return optind; +} + int main(int argc, char **argv) { + const char *progname = argv[0]; + const char *cmdname; int i; int fd = -1; int verbose = 0; @@ -1014,22 +1053,49 @@ int main(int argc, char **argv) int ret = 0; - int c; + static const char add_options_short[] = "?vn:w:h:x:b:o:"; + static const struct option add_options_long[] = { + { "help", no_argument, NULL, '?' }, + { "verbose", no_argument, NULL, 'v' }, + { "name", required_argument, NULL, 'n' }, + { "min-width", required_argument, NULL, 'w' + 0xFFFF }, + { "max-width", required_argument, NULL, 'w' }, + { "min-height", required_argument, NULL, 'h' + 0xFFFF }, + { "max-height", required_argument, NULL, 'h' }, + { "exclusive-caps", required_argument, NULL, 'x' }, + { "buffers", required_argument, NULL, 'b' }, + { "max-openers", required_argument, NULL, 'o' }, + { 0, 0, 0, 0 } + }; + static const char timeoutimg_options_short[] = "?ht:"; + static const struct option timeoutimg_options_long[] = { + { "help", no_argument, NULL, 'h' }, + { "timeout", required_argument, NULL, 't' }, + { 0, 0, 0, 0 } + }; if (argc < 2) - usage(argv[0]); + usage(progname); cmd = get_command(argv[1]); - switch (cmd) { - case _UNKNOWN: + if (_UNKNOWN == cmd) { dprintf(2, "unknown command '%s'\n\n", argv[1]); - usage(argv[0]); - break; + usage(progname); + return 1; + } + argc--; + argv++; + switch (cmd) { case HELP: - help(argv[0], 0); + help(progname, 0); break; case ADD: - while ((c = getopt(argc - 1, argv + 1, "vn:w:W:h:H:x:b:o:")) != - -1) + for (;;) { + int c; + int idx; + c = getopt_long(argc, argv, add_options_short, + add_options_long, &idx); + if (-1 == c) + break; switch (c) { case 'v': verbose++; @@ -1037,16 +1103,16 @@ int main(int argc, char **argv) case 'n': label = optarg; break; - case 'w': + case 'w' + 0xFFFF: min_width = my_atoi("min_width", optarg); break; - case 'h': + case 'h' + 0xFFFF: min_height = my_atoi("min_height", optarg); break; - case 'W': + case 'w': max_width = my_atoi("max_width", optarg); break; - case 'H': + case 'h': max_height = my_atoi("max_height", optarg); break; case 'x': @@ -1060,9 +1126,12 @@ int main(int argc, char **argv) openers = my_atoi("openers", optarg); break; default: - usage_topic(argv[0], cmd, argc - 2, argv + 2); + usage_topic(progname, cmd, argc - 1, argv + 1); return 1; } + } + argc -= optind; + argv += optind; fd = open_controldevice(); if (min_width > max_width && max_width > 0) { dprintf(2, @@ -1079,18 +1148,22 @@ int main(int argc, char **argv) do { struct v4l2_loopback_config cfg; int capture_nr = -1, output_nr = -1; - if ((optind + 1) == argc) { + switch (argc) { + case 0: /* no device given: pick some */ - } else if ((optind + 2) == argc) { + break; + case 1: /* single device given: use it for both input and output */ - capture_nr = output_nr = - parse_device(argv[optind + 1]); - } else if ((optind + 3) == argc) { + capture_nr = output_nr = parse_device(argv[0]); + break; + case 2: /* two devices given: capture_device and output_device */ - capture_nr = parse_device(argv[optind + 1]); - output_nr = parse_device(argv[optind + 2]); - } else { - usage_topic(argv[0], cmd, argc - 2, argv + 2); + capture_nr = parse_device(argv[0]); + output_nr = parse_device(argv[1]); + break; + default: + usage_topic(progname, cmd, argc, argv); + return 1; } ret = add_device(fd, make_conf(&cfg, label, min_width, @@ -1102,79 +1175,106 @@ int main(int argc, char **argv) } while (0); break; case DELETE: - if (argc == 2) - usage_topic(argv[0], cmd, argc - 2, argv + 2); + optind = do_defaultargs(progname, cmd, argc, argv); + argc -= optind; + argv += optind; + + if (!argc) + usage_topic(progname, cmd, argc, argv); fd = open_controldevice(); - for (i = 2; i < argc; i++) { + for (i = 0; i < argc; i++) { ret += (delete_device(fd, argv[i]) != 0); } ret = (ret > 0); break; case QUERY: - if (argc == 2) - usage_topic(argv[0], cmd, argc - 2, argv + 2); + optind = do_defaultargs(progname, cmd, argc, argv); + argc -= optind; + argv += optind; + if (!argc) + usage_topic(progname, cmd, argc, argv); fd = open_controldevice(); - for (i = 2; i < argc; i++) { + for (i = 0; i < argc; i++) { ret += query_device(fd, argv[i]); } ret = (ret > 0); break; case SET_FPS: - if (argc != 4) - usage_topic(argv[0], cmd, argc - 2, argv + 2); - if (called_deprecated(argv[2], argv[3], argv[0], "set-fps", + optind = do_defaultargs(progname, cmd, argc, argv); + argc -= optind; + argv += optind; + if (argc != 2) + usage_topic(progname, cmd, argc, argv); + if (called_deprecated(argv[0], argv[1], progname, "set-fps", "fps", is_fps)) { - ret = set_fps(argv[3], argv[2]); + ret = set_fps(argv[1], argv[0]); } else - ret = set_fps(argv[2], argv[3]); + ret = set_fps(argv[0], argv[1]); break; case GET_FPS: - if (argc != 3) - usage_topic(argv[0], cmd, argc - 2, argv + 2); - ret = get_fps(argv[2]); + optind = do_defaultargs(progname, cmd, argc, argv); + argc -= optind; + argv += optind; + if (argc != 1) + usage_topic(progname, cmd, argc, argv); + ret = get_fps(argv[0]); break; case SET_CAPS: - if (argc != 4) - usage_topic(argv[0], cmd, argc - 2, argv + 2); - if (called_deprecated(argv[2], argv[3], argv[0], "set-caps", + optind = do_defaultargs(progname, cmd, argc, argv); + argc -= optind; + argv += optind; + if (argc != 2) + usage_topic(progname, cmd, argc, argv); + if (called_deprecated(argv[0], argv[1], progname, "set-caps", "caps", 0)) { - ret = set_caps(argv[3], argv[2]); + ret = set_caps(argv[1], argv[0]); } else { - ret = set_caps(argv[2], argv[3]); + ret = set_caps(argv[0], argv[1]); } break; case GET_CAPS: - if (argc != 3) - usage_topic(argv[0], cmd, argc - 2, argv + 2); - ret = get_caps(argv[2]); + optind = do_defaultargs(progname, cmd, argc, argv); + argc -= optind; + argv += optind; + if (argc != 1) + usage_topic(progname, cmd, argc, argv); + ret = get_caps(argv[0]); break; case SET_TIMEOUTIMAGE: - if ((4 == argc) && (strncmp("-t", argv[2], 4)) && - (called_deprecated(argv[2], argv[3], argv[0], + if ((3 == argc) && (strncmp("-t", argv[1], 3)) && + (strncmp("--timeout", argv[1], 10)) && + (called_deprecated(argv[1], argv[2], progname, "set-timeout-image", "image", 0))) { - ret = set_timeoutimage(argv[3], argv[2], -1); + ret = set_timeoutimage(argv[2], argv[1], -1); } else { int timeout = -1; - while ((c = getopt(argc - 1, argv + 1, "t:")) != -1) + for (;;) { + int c, idx; + c = getopt_long(argc, argv, + timeoutimg_options_short, + timeoutimg_options_long, &idx); + if (-1 == c) + break; switch (c) { case 't': timeout = my_atoi("timeout", optarg); break; default: - usage_topic(argv[0], cmd, argc - 2, - argv + 2); + usage_topic(progname, cmd, argc, argv); } - if (optind + 3 != argc) - usage_topic(argv[0], cmd, argc - 2, argv + 2); - ret = set_timeoutimage(argv[1 + optind], - argv[2 + optind], timeout); + } + argc -= optind; + argv += optind; + if (argc != 2) + usage_topic(progname, cmd, argc, argv); + ret = set_timeoutimage(argv[0], argv[1], timeout); } break; case VERSION: #ifdef SNAPSHOT_VERSION - printf("%s v%s\n", argv[0], SNAPSHOT_VERSION); + printf("%s v%s\n", progname, SNAPSHOT_VERSION); #else - printf("%s v%d.%d.%d\n", argv[0], V4L2LOOPBACK_VERSION_MAJOR, + printf("%s v%d.%d.%d\n", progname, V4L2LOOPBACK_VERSION_MAJOR, V4L2LOOPBACK_VERSION_MINOR, V4L2LOOPBACK_VERSION_BUGFIX); #endif fd = open("/sys/module/v4l2loopback/version", O_RDONLY); @@ -1189,7 +1289,7 @@ int main(int argc, char **argv) } break; default: - dprintf(2, "not implemented '%s'\n", argv[1]); + dprintf(2, "not implemented: '%s'\n", argv[0]); break; } From df09bff935c4fc8853b995450f00abb1b638e345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 17:02:56 +0200 Subject: [PATCH 072/151] indentation --- utils/v4l2loopback-ctl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index fc4292ac..67b2537f 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -981,10 +981,10 @@ static int called_deprecated(const char *device, const char *argument, const char *argname, t_argcheck argcheck) { /* check if does not look like a device, but does - * if so, assume that the user swapped the two */ + * if so, assume that the user swapped the two */ /* if the looks about right, optionally do some extra - * -check, to see if it can be used - */ + * -check, to see if it can be used + */ int deviceswapped = 0; int argswapped = 0; From a2ccb15a39c8f4f6bacdfb990fdece180cb0bda2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 27 Apr 2023 22:17:19 +0200 Subject: [PATCH 073/151] unset the timeout_image_io flag if allocating the timeout-image fails --- v4l2loopback.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 2216dd63..6f1a8a76 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2419,13 +2419,17 @@ static void init_buffers(struct v4l2_loopback_device *dev) static int allocate_timeout_image(struct v4l2_loopback_device *dev) { MARK(); - if (dev->buffer_size <= 0) + if (dev->buffer_size <= 0) { + dev->timeout_image_io = 0; return -EINVAL; + } if (dev->timeout_image == NULL) { dev->timeout_image = vzalloc(dev->buffer_size); - if (dev->timeout_image == NULL) + if (dev->timeout_image == NULL) { + dev->timeout_image_io = 0; return -ENOMEM; + } } return 0; } From 7ab4cad7025f8e04673daa43b90ee97c8f6e7115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 16:34:23 +0200 Subject: [PATCH 074/151] only unset the timeout_image_io flag when requesting buffers for the timeout image GStreamer's v4l2sink does multiple calls to open() (for querying format and the like) and we don't want that to clear the flag... --- v4l2loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 6f1a8a76..5e5fa477 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1527,9 +1527,10 @@ static int vidioc_reqbufs(struct file *file, void *fh, dprintk("reqbufs: %d\t%d=%d\n", b->memory, b->count, dev->buffers_number); if (opener->timeout_image_io) { + dev->timeout_image_io = 0; if (b->memory != V4L2_MEMORY_MMAP) return -EINVAL; - b->count = 1; + b->count = 2; return 0; } @@ -2161,7 +2162,6 @@ static int v4l2_loopback_open(struct file *file) } } - dev->timeout_image_io = 0; v4l2_fh_init(&opener->fh, video_devdata(file)); file->private_data = &opener->fh; From de7bfdb722100db7d2510a2cf06520d0f9dca5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 17:48:01 +0200 Subject: [PATCH 075/151] turn the "timeout_image_io" ctrl into a button --- v4l2loopback.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 5e5fa477..8175a7f2 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -268,7 +268,7 @@ static const struct v4l2_ctrl_config v4l2loopback_ctrl_timeoutimageio = { .ops = &v4l2loopback_ctrl_ops, .id = CID_TIMEOUT_IMAGE_IO, .name = "timeout_image_io", - .type = V4L2_CTRL_TYPE_BOOLEAN, + .type = V4L2_CTRL_TYPE_BUTTON, .min = 0, .max = 1, .step = 1, @@ -1363,9 +1363,7 @@ static int v4l2loopback_set_ctrl(struct v4l2_loopback_device *dev, u32 id, allocate_timeout_image(dev); break; case CID_TIMEOUT_IMAGE_IO: - if (val < 0 || val > 1) - return -EINVAL; - dev->timeout_image_io = val; + dev->timeout_image_io = 1; break; default: return -EINVAL; @@ -1526,6 +1524,7 @@ static int vidioc_reqbufs(struct file *file, void *fh, dprintk("reqbufs: %d\t%d=%d\n", b->memory, b->count, dev->buffers_number); + if (opener->timeout_image_io) { dev->timeout_image_io = 0; if (b->memory != V4L2_MEMORY_MMAP) From 400fdd556d393c5237175be5282832951d834424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 17:53:17 +0200 Subject: [PATCH 076/151] whitespace --- v4l2loopback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 8175a7f2..9bd0fb55 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2161,7 +2161,6 @@ static int v4l2_loopback_open(struct file *file) } } - v4l2_fh_init(&opener->fh, video_devdata(file)); file->private_data = &opener->fh; From a846f1e372833c74ff644801de3e9d0ecbd5658e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 17:53:37 +0200 Subject: [PATCH 077/151] fixate format with "keep_format" --- v4l2loopback.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 9bd0fb55..7ee314b9 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -409,8 +409,9 @@ struct v4l2l_format { - have writers (ready_for_capture>0) - and/or have readers (active_readers>0) */ -#define V4L2LOOPBACK_IS_FIXED_FMT(device) \ - (device->ready_for_capture > 0 || device->active_readers > 0) +#define V4L2LOOPBACK_IS_FIXED_FMT(device) \ + (device->ready_for_capture > 0 || device->active_readers > 0 || \ + device->keep_format) static const unsigned int FORMATS = ARRAY_SIZE(formats); From 85207e13c3029297cc561ee181c33f089b2558cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 17:55:05 +0200 Subject: [PATCH 078/151] report format via /sys if it is somehow FIXED --- v4l2loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 7ee314b9..0922b202 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -636,7 +636,7 @@ static ssize_t attr_show_format(struct device *cd, const struct v4l2_fract *tpf; char buf4cc[5], buf_fps[32]; - if (!dev || !dev->ready_for_capture) + if (!dev || !V4L2LOOPBACK_IS_FIXED_FMT(dev)) return 0; tpf = &dev->capture_param.timeperframe; From 6c9d8fe7312a62b29e2bfa35526d43f32d5e4e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 18:22:31 +0200 Subject: [PATCH 079/151] run timeout-image gst-pipeline through "tee" this appeats to "magically" make it work... --- utils/v4l2loopback-ctl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 67b2537f..5c836b6b 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -889,7 +889,9 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, "imagefreeze", "!", "identity", - "error-after=3", + "eos-after=3", + "!", + "tee", "!", "v4l2sink", "show-preroll-frame=false", @@ -904,7 +906,7 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, snprintf(devicearg, 4096, "device=%s", devicename); imagearg[4095] = devicearg[4095] = 0; args[2] = imagearg; - args[15] = devicearg; + args[17] = devicearg; fd = open_videodevice(devicename, O_RDWR); if (fd >= 0) { From 68c74f5cc9e036458f6744227624dc0f2a0849ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 18:23:27 +0200 Subject: [PATCH 080/151] add "--verbose" flag to "set-timeout-image" --- utils/v4l2loopback-ctl.c | 42 ++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 5c836b6b..72e7c3c7 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -363,6 +363,7 @@ static void help_settimeoutimage(const char *program, int detail, int argc, dprintf(2, "\n \tany of the following flags may be present" "\n\t -t/--timeout : timeout (in ms)" + "\n\t -v/--verbose : raise verbosity (print what is being done)" "\n" "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \timage file"); @@ -874,7 +875,7 @@ static int get_caps(const char *devicename) return 0; } static int set_timeoutimage(const char *devicename, const char *imagefile, - int timeout) + int timeout, int verbose) { int fd = -1; char imagearg[4096], imagefile2[4096], devicearg[4096]; @@ -897,10 +898,10 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, "show-preroll-frame=false", 0, 0 }; -#if 0 - printf("set-timeout-image '%s' for '%s' with %dms timeout\n", imagefile, - devicename, timeout); -#endif + if (verbose) + printf("set-timeout-image '%s' for '%s' with %dms timeout\n", + imagefile, devicename, timeout); + snprintf(imagearg, 4096, "uri=file://%s", realpath(imagefile, imagefile2)); snprintf(devicearg, 4096, "device=%s", devicename); @@ -910,17 +911,27 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, fd = open_videodevice(devicename, O_RDWR); if (fd >= 0) { + dprintf(2, "v4l2-ctl -d %s -c timeout_image_io=1\n", + devicename); set_control_i(fd, "timeout_image_io", 1); close(fd); } + if (verbose > 1) { + char **ap = args; + while (*ap) { + dprintf(2, "%s", *ap); + if (*ap++) + dprintf(2, " "); + else + dprintf(2, "\n"); + } + } + dprintf(2, "v======================================================================v\n"); if (my_execv(args)) { - /* - dprintf(2, "ERROR: setting time-out image failed\n"); - return 1; - */ + dprintf(2, "ERROR: setting time-out image failed\n"); } dprintf(2, "^======================================================================^\n"); @@ -931,6 +942,8 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, if (timeout < 0) { timeout = get_control_i(fd, "timeout"); } else { + dprintf(2, "v4l2-ctl -d %s -c timeout=%d\n", devicename, + timeout); timeout = set_control_i(fd, "timeout", timeout); } if (timeout <= 0) { @@ -1069,10 +1082,11 @@ int main(int argc, char **argv) { "max-openers", required_argument, NULL, 'o' }, { 0, 0, 0, 0 } }; - static const char timeoutimg_options_short[] = "?ht:"; + static const char timeoutimg_options_short[] = "?ht:v"; static const struct option timeoutimg_options_long[] = { { "help", no_argument, NULL, 'h' }, { "timeout", required_argument, NULL, 't' }, + { "verbose", no_argument, NULL, 'v' }, { 0, 0, 0, 0 } }; @@ -1247,7 +1261,7 @@ int main(int argc, char **argv) (strncmp("--timeout", argv[1], 10)) && (called_deprecated(argv[1], argv[2], progname, "set-timeout-image", "image", 0))) { - ret = set_timeoutimage(argv[2], argv[1], -1); + ret = set_timeoutimage(argv[2], argv[1], -1, verbose); } else { int timeout = -1; for (;;) { @@ -1261,6 +1275,9 @@ int main(int argc, char **argv) case 't': timeout = my_atoi("timeout", optarg); break; + case 'v': + verbose++; + break; default: usage_topic(progname, cmd, argc, argv); } @@ -1269,7 +1286,8 @@ int main(int argc, char **argv) argv += optind; if (argc != 2) usage_topic(progname, cmd, argc, argv); - ret = set_timeoutimage(argv[0], argv[1], timeout); + ret = set_timeoutimage(argv[0], argv[1], timeout, + verbose); } break; case VERSION: From dfe65ebd2de2dd34ee7d8d81154405745be6d200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 18:35:38 +0200 Subject: [PATCH 081/151] prevent multiple readers to start streaming Closes: https://github.com/umlaeute/v4l2loopback/issues/457 --- v4l2loopback.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index 0922b202..939aef92 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1864,6 +1864,8 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type type) case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (!dev->ready_for_capture) return -EIO; + if (dev->active_readers > 0) + return -EBUSY; opener->type = READER; dev->active_readers++; client_usage_queue_event(dev->vdev); From f4bc73268fe428e09022d1a766cf6e67ec853983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 18:47:52 +0200 Subject: [PATCH 082/151] indentation... --- v4l2loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 939aef92..7a553ff3 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1864,8 +1864,8 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type type) case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (!dev->ready_for_capture) return -EIO; - if (dev->active_readers > 0) - return -EBUSY; + if (dev->active_readers > 0) + return -EBUSY; opener->type = READER; dev->active_readers++; client_usage_queue_event(dev->vdev); From dab962d0eaaa15547052c92f33ac57597174deea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 2 May 2023 22:57:12 +0200 Subject: [PATCH 083/151] add /sys/devices/virtual/video4linux/video*/type interface to check whether the device is an "output" device or a "capture" device --- v4l2loopback.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index 7a553ff3..6def964c 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -730,6 +730,24 @@ static ssize_t attr_store_maxopeners(struct device *cd, static DEVICE_ATTR(max_openers, S_IRUGO | S_IWUSR, attr_show_maxopeners, attr_store_maxopeners); +static ssize_t attr_show_type(struct device *cd, struct device_attribute *attr, + char *buf) +{ + struct v4l2_loopback_device *dev = v4l2loopback_cd2dev(cd); + + if (!dev) + return -ENODEV; + + if (dev->ready_for_capture) + return sprintf(buf, "capture\n"); + if (dev->ready_for_output) + return sprintf(buf, "output\n"); + + return -EAGAIN; +} + +static DEVICE_ATTR(type, S_IRUGO, attr_show_type, NULL); + static void v4l2loopback_remove_sysfs(struct video_device *vdev) { #define V4L2_SYSFS_DESTROY(x) device_remove_file(&vdev->dev, &dev_attr_##x) @@ -738,6 +756,7 @@ static void v4l2loopback_remove_sysfs(struct video_device *vdev) V4L2_SYSFS_DESTROY(format); V4L2_SYSFS_DESTROY(buffers); V4L2_SYSFS_DESTROY(max_openers); + V4L2_SYSFS_DESTROY(type); /* ... */ } } @@ -756,6 +775,7 @@ static void v4l2loopback_create_sysfs(struct video_device *vdev) V4L2_SYSFS_CREATE(format); V4L2_SYSFS_CREATE(buffers); V4L2_SYSFS_CREATE(max_openers); + V4L2_SYSFS_CREATE(type); /* ... */ } while (0); From 339fa7dbbd08df5fcffb3d09e94e8cc3e1ea0fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 11:56:58 +0200 Subject: [PATCH 084/151] fix device_nr checks in V4L2LOOPBACK_CTL_QUERY --- v4l2loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 6def964c..7858e313 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2877,14 +2877,14 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, * make sure that both refer to the same device (or bail out) */ if ((device_nr != conf.capture_nr) && (conf.capture_nr >= 0) && - (ret != v4l2loopback_lookup(conf.capture_nr, 0))) + ((ret = v4l2loopback_lookup(conf.capture_nr, 0)) < 0)) break; MARK(); /* if otoh, we got the device from capture_nr and there is a valid output_nr, * make sure that both refer to the same device (or bail out) */ if ((device_nr != conf.output_nr) && (conf.output_nr >= 0) && - (ret != v4l2loopback_lookup(conf.output_nr, 0))) + ((ret = v4l2loopback_lookup(conf.output_nr, 0)) < 0)) break; MARK(); From 856f196924264208a755ec8290f75cc7af36ce49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 11:57:23 +0200 Subject: [PATCH 085/151] v4l2loopback-ctl: add "list" verb --- utils/v4l2loopback-ctl.c | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 72e7c3c7..26b6e29f 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -229,6 +230,7 @@ typedef enum { HELP, ADD, DELETE, + LIST, QUERY, SET_FPS, GET_FPS, @@ -248,6 +250,17 @@ static int help_shortcmdline(int detail, const char *program, dprintf(2, "%s %s", program, argstring); return !detail; } +static void help_list(const char *program, int detail, int argc, char **argv) +{ + if (detail) + dprintf(2, "\n listing devices ('list')" + "\n ========================"); + if (help_shortcmdline(detail, program, "list")) + return; + dprintf(2, + "\n \tlist all available loopback-devices (both OUTPUT and CAPTURE devices)" + ""); +} static void help_add(const char *program, int detail, int argc, char **argv) { if (detail) @@ -381,6 +394,8 @@ static t_help get_help(t_command cmd) return help_add; case DELETE: return help_delete; + case LIST: + return help_list; case QUERY: return help_query; case SET_FPS: @@ -571,6 +586,83 @@ static int query_device(int fd, const char *devicename) } return err; } +static int list_devices(int fd) +{ + struct devnode_ { + int output, capture; + char name[32]; + } *devices = 0; + size_t numdevices = 0, i; + glob_t globbuf = { 0 }; + glob("/sys/devices/virtual/video4linux/video*", GLOB_ONLYDIR, 0, + &globbuf); + if (globbuf.gl_pathc) { + devices = malloc(globbuf.gl_pathc * sizeof(*devices)); + } + for (i = 0; i < globbuf.gl_pathc; i++) { + size_t j; + struct v4l2_loopback_config config = { 0 }; + int dev = -1; + char *endptr; + struct stat sb; + const char *path = globbuf.gl_pathv[i]; + if (lstat(path, &sb)) { + //perror("stat"); + continue; + } + if (!S_ISDIR(sb.st_mode)) { + //dprintf(2, "not a directory\n"); + continue; + } + dev = strtol(path + 38, &endptr, 10); + if (*endptr) { + //dprintf(2, "unable to parse device-name\n"); + continue; + } + /* check if this is a loopback device */ + config.output_nr = dev; + config.capture_nr = -1; + if (ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config)) { + memset(&config, 0, sizeof(config)); + config.output_nr = -1; + config.capture_nr = dev; + if (ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config)) { + //dprintf(2, "not a loopback device\n"); + continue; + } + } + /* check if we already have this device */ + for (j = 0; j < numdevices; j++) { + if ((devices[j].output == config.output_nr) && + (devices[j].capture == config.capture_nr)) { + //dprintf(2, "duplicate device\n"); + config.output_nr = config.capture_nr = -1; + break; + } + } + if ((config.output_nr < 0) || (config.capture_nr < 0)) + continue; + + devices[numdevices].output = config.output_nr; + devices[numdevices].capture = config.capture_nr; + snprintf(devices[numdevices].name, + sizeof(devices[numdevices].name), "%s", + config.card_label); + numdevices++; + } + if (numdevices) { + dprintf(2, "OUTPUT \tCAPTURE \tNAME\n"); + } else { + dprintf(2, "no loopback devices found\n"); + } + for (i = 0; i < numdevices; i++) { + printf("/dev/video%-3d\t/dev/video%-3d\t%s\n", + devices[i].output, devices[i].capture, devices[i].name); + } + globfree(&globbuf); + free(devices); + return 0; +} static int open_videodevice(const char *devicename, int mode) { int fd = open(devicename, mode); @@ -969,6 +1061,8 @@ static t_command get_command(const char *command) return VERSION; if (!strncmp(command, "--version", 10)) return VERSION; + if (!strncmp(command, "list", 5)) + return LIST; if (!strncmp(command, "add", 4)) return ADD; if (!strncmp(command, "del", 3)) /* also allow delete */ @@ -1104,6 +1198,18 @@ int main(int argc, char **argv) case HELP: help(progname, 0); break; + case LIST: + if (1 != argc) { + dprintf(2, "'list' does not take any arguments\n", + argc); + return 1; + } + fd = open_controldevice(); + if (fd >= 0) + list_devices(fd); + else + return 1; + break; case ADD: for (;;) { int c; From 300dfe3792a60e4bec75e3dd27a3d91e11410362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 12:24:40 +0200 Subject: [PATCH 086/151] v4l2loopback-ctl: escape special chars in device-names --- utils/v4l2loopback-ctl.c | 92 ++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 26b6e29f..f637b6ef 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -258,7 +259,9 @@ static void help_list(const char *program, int detail, int argc, char **argv) if (help_shortcmdline(detail, program, "list")) return; dprintf(2, - "\n \tlist all available loopback-devices (both OUTPUT and CAPTURE devices)" + "\n \tlist all available loopback-devices" + "\n\t -e/--escape : escape control-characters in the device-name" + "\n\t -h/--help : print this help and exit" ""); } static void help_add(const char *program, int detail, int argc, char **argv) @@ -280,7 +283,7 @@ static void help_add(const char *program, int detail, int argc, char **argv) "\n\t -b/--buffers : buffers to queue" "\n\t -o/--max-openers : maximum allowed concurrent openers" "\n\t -v/--verbose : verbose mode (print properties of device after successfully creating it)" - "\n\t -?/--help : print this help" + "\n\t -?/--help : print this help and exit" "\n" "\n \tif given, create a specific device (otherwise just create a free one)." "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." @@ -336,7 +339,7 @@ static void help_setcaps(const char *program, int detail, int argc, char **argv) return; dprintf(2, "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." - "\n \tformat specification as ':x@' (e.g. 'UYVY:3840x2160@60/1')" + "\n \tformat specification as ':x@' (e.g. 'UYVY:1024x768@60/1')" "\n"); if (detail > 1) { dprintf(2, "\nknown fourcc-codes" @@ -375,8 +378,9 @@ static void help_settimeoutimage(const char *program, int detail, int argc, return; dprintf(2, "\n \tany of the following flags may be present" - "\n\t -t/--timeout : timeout (in ms)" - "\n\t -v/--verbose : raise verbosity (print what is being done)" + "\n\t -t/--timeout : timeout (in ms)" + "\n\t -v/--verbose : raise verbosity (print what is being done)" + "\n\t -h/--help : print this help and exit" "\n" "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \timage file"); @@ -586,7 +590,7 @@ static int query_device(int fd, const char *devicename) } return err; } -static int list_devices(int fd) +static int list_devices(int fd, int escape) { struct devnode_ { int output, capture; @@ -656,8 +660,47 @@ static int list_devices(int fd) dprintf(2, "no loopback devices found\n"); } for (i = 0; i < numdevices; i++) { - printf("/dev/video%-3d\t/dev/video%-3d\t%s\n", - devices[i].output, devices[i].capture, devices[i].name); + const char *str = devices[i].name; + const char *backslash = (escape > 1) ? "\\\\" : "\\"; + printf("/dev/video%-3d\t/dev/video%-3d\t", devices[i].output, + devices[i].capture); + if (escape) { + while (*str) { + char c = *str++; + switch (c) { + case '\"': + printf("%s\"", backslash); + break; + case '\'': + printf("%s\'", backslash); + break; + case '\\': + printf("%s\\", backslash); + break; + case '\a': + printf("%sa", backslash); + break; + case '\b': + printf("%sb", backslash); + break; + case '\n': + printf("%sn", backslash); + break; + case '\t': + printf("%st", backslash); + break; + // and so on + default: + if (iscntrl(c)) + printf("%s%03o", backslash, c); + else + printf("%c", c); + } + } + } else { + printf("%s", str); + } + printf("\n"); } globfree(&globbuf); free(devices); @@ -1159,6 +1202,7 @@ int main(int argc, char **argv) int exclusive_caps = -1; int buffers = -1; int openers = -1; + int escape_strings = 0; int ret = 0; @@ -1176,6 +1220,12 @@ int main(int argc, char **argv) { "max-openers", required_argument, NULL, 'o' }, { 0, 0, 0, 0 } }; + static const char list_options_short[] = "?he"; + static const struct option list_options_long[] = { + { "help", no_argument, NULL, 'h' }, + { "escape", no_argument, NULL, 'e' }, + { 0, 0, 0, 0 } + }; static const char timeoutimg_options_short[] = "?ht:v"; static const struct option timeoutimg_options_long[] = { { "help", no_argument, NULL, 'h' }, @@ -1199,14 +1249,32 @@ int main(int argc, char **argv) help(progname, 0); break; case LIST: - if (1 != argc) { - dprintf(2, "'list' does not take any arguments\n", - argc); + for (;;) { + int c; + int idx; + c = getopt_long(argc, argv, list_options_short, + list_options_long, &idx); + if (-1 == c) + break; + switch (c) { + case 'e': + escape_strings++; + break; + default: + usage_topic(progname, cmd, argc - 1, argv + 1); + return 1; + } + } + argc -= optind; + argv += optind; + + if (argc) { + dprintf(2, "'list' does not take any arguments\n"); return 1; } fd = open_controldevice(); if (fd >= 0) - list_devices(fd); + list_devices(fd, escape_strings); else return 1; break; From 65755ae68b7787bf140f2078c2515a008fd51b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 12:31:30 +0200 Subject: [PATCH 087/151] refactored raw-string printout into helper function --- utils/v4l2loopback-ctl.c | 79 +++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index f637b6ef..d42410fb 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -152,6 +152,47 @@ static int my_atoi(const char *name, const char *s) } return n; } + +static void printf_raw(const char *str, int escape_level) +{ + const char *backslash = (escape_level > 1) ? "\\\\" : "\\"; + if (escape_level > 0) + while (*str) { + char c = *str++; + switch (c) { + case '\"': + printf("%s\"", backslash); + break; + case '\'': + printf("%s\'", backslash); + break; + case '\\': + printf("%s\\", backslash); + break; + case '\a': + printf("%sa", backslash); + break; + case '\b': + printf("%sb", backslash); + break; + case '\n': + printf("%sn", backslash); + break; + case '\t': + printf("%st", backslash); + break; + // and so on + default: + if (iscntrl(c)) + printf("%s%03o", backslash, c); + else + printf("%c", c); + } + } + else + printf("%s", str); +} + static char *fourcc2str(unsigned int fourcc, char buf[4]) { buf[0] = (fourcc >> 0) & 0xFF; @@ -661,45 +702,9 @@ static int list_devices(int fd, int escape) } for (i = 0; i < numdevices; i++) { const char *str = devices[i].name; - const char *backslash = (escape > 1) ? "\\\\" : "\\"; printf("/dev/video%-3d\t/dev/video%-3d\t", devices[i].output, devices[i].capture); - if (escape) { - while (*str) { - char c = *str++; - switch (c) { - case '\"': - printf("%s\"", backslash); - break; - case '\'': - printf("%s\'", backslash); - break; - case '\\': - printf("%s\\", backslash); - break; - case '\a': - printf("%sa", backslash); - break; - case '\b': - printf("%sb", backslash); - break; - case '\n': - printf("%sn", backslash); - break; - case '\t': - printf("%st", backslash); - break; - // and so on - default: - if (iscntrl(c)) - printf("%s%03o", backslash, c); - else - printf("%c", c); - } - } - } else { - printf("%s", str); - } + printf_raw(str, escape); printf("\n"); } globfree(&globbuf); From fca530bd1de145ff7689517a9ab78e0107975069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 12:55:50 +0200 Subject: [PATCH 088/151] v4l2loopback-ctl: align help --- utils/v4l2loopback-ctl.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index d42410fb..7c4c2968 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -314,7 +314,7 @@ static void help_add(const char *program, int detail, int argc, char **argv) "add {} [ []]")) return; dprintf(2, - "\n \tany of the following flags may be present" + "\n \tany of the following flags may be present" "\n\t -n/--name : pretty name for the device" "\n\t --min-width : minimum allowed frame width" "\n\t -w/--max-width : maximum allowed frame width" @@ -326,9 +326,9 @@ static void help_add(const char *program, int detail, int argc, char **argv) "\n\t -v/--verbose : verbose mode (print properties of device after successfully creating it)" "\n\t -?/--help : print this help and exit" "\n" - "\n \tif given, create a specific device (otherwise just create a free one)." - "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." - "\n \tif given, use separate output & capture devices (otherwise they are the same)."); + "\n \tif given, create a specific device (otherwise just create a free one)." + "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." + "\n \tif given, use separate output & capture devices (otherwise they are the same)."); } static void help_delete(const char *program, int detail, int argc, char **argv) { @@ -338,8 +338,8 @@ static void help_delete(const char *program, int detail, int argc, char **argv) if (help_shortcmdline(detail, program, "delete ")) return; dprintf(2, - "\n \tcan be given one more more times (to delete multiple devices at once)." - "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')."); + "\n \tcan be given one more more times (to delete multiple devices at once)." + "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')."); } static void help_query(const char *program, int detail, int argc, char **argv) { @@ -349,7 +349,7 @@ static void help_query(const char *program, int detail, int argc, char **argv) if (help_shortcmdline(detail, program, "query ")) return; dprintf(2, - "\n \tcan be given one more more times (to query multiple devices at once)." + "\n \tcan be given one more more times (to query multiple devices at once)." "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')."); } static void help_setfps(const char *program, int detail, int argc, char **argv) @@ -360,8 +360,8 @@ static void help_setfps(const char *program, int detail, int argc, char **argv) if (help_shortcmdline(detail, program, "set-fps ")) return; dprintf(2, - "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." - "\n \tframes per second, either as integer ('30') or fraction ('50/2')."); + "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." + "\n \tframes per second, either as integer ('30') or fraction ('50/2')."); } static void help_getfps(const char *program, int detail, int argc, char **argv) { @@ -379,8 +379,8 @@ static void help_setcaps(const char *program, int detail, int argc, char **argv) if (help_shortcmdline(detail, program, "set-caps ")) return; dprintf(2, - "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." - "\n \tformat specification as ':x@' (e.g. 'UYVY:1024x768@60/1')" + "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." + "\n \tformat specification as ':x@' (e.g. 'UYVY:1024x768@60/1')" "\n"); if (detail > 1) { dprintf(2, "\nknown fourcc-codes" @@ -418,13 +418,13 @@ static void help_settimeoutimage(const char *program, int detail, int argc, "set-timeout-image {} ")) return; dprintf(2, - "\n \tany of the following flags may be present" + "\n \tany of the following flags may be present" "\n\t -t/--timeout : timeout (in ms)" "\n\t -v/--verbose : raise verbosity (print what is being done)" "\n\t -h/--help : print this help and exit" "\n" - "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." - "\n \timage file"); + "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." + "\n \timage file"); } static void help_none(const char *program, int detail, int argc, char **argv) { From 588defa72449c015433c303374d262290d98a90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 12:56:44 +0200 Subject: [PATCH 089/151] v4l2loopback-ctl: more escaping for device-name --- utils/v4l2loopback-ctl.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 7c4c2968..788f7042 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -513,7 +513,7 @@ static int parse_device(const char *devicename_) return -1; } -static void print_conf(struct v4l2_loopback_config *cfg) +static void print_conf(struct v4l2_loopback_config *cfg, int escape_level) { MARK(); if (!cfg) { @@ -523,8 +523,10 @@ static void print_conf(struct v4l2_loopback_config *cfg) MARK(); printf("\tcapture_device# : %d" "\n\toutput_device# : %d" - "\n\tcard_label : %s" - "\n\tmin_width : %d" + "\n\tcard_label : ", + cfg->capture_nr, cfg->output_nr); + printf_raw(cfg->card_label, escape_level); + printf("\n\tmin_width : %d" "\n\tmax_width : %d" "\n\tmin_height : %d" "\n\tmax_height : %d" @@ -533,8 +535,7 @@ static void print_conf(struct v4l2_loopback_config *cfg) "\n\tmax_openers : %d" "\n\tdebug : %d" "\n", - cfg->capture_nr, cfg->output_nr, cfg->card_label, cfg->min_width, - cfg->max_width, cfg->min_height, cfg->max_height, + cfg->min_width, cfg->max_width, cfg->min_height, cfg->max_height, cfg->announce_all_caps, cfg->max_buffers, cfg->max_openers, cfg->debug); MARK(); @@ -590,7 +591,7 @@ static int add_device(int fd, struct v4l2_loopback_config *cfg, int verbose) if (!ret) perror("failed querying newly added device"); MARK(); - print_conf(&config); + print_conf(&config, 0); MARK(); } return (!ret); @@ -609,7 +610,7 @@ static int delete_device(int fd, const char *devicename) return 0; } -static int query_device(int fd, const char *devicename) +static int query_device(int fd, const char *devicename, int escape) { int err; struct v4l2_loopback_config config; @@ -626,7 +627,7 @@ static int query_device(int fd, const char *devicename) perror("query failed"); else { printf("%s\n", devicename); - print_conf(&config); + print_conf(&config, escape); return 0; } return err; @@ -1390,7 +1391,7 @@ int main(int argc, char **argv) usage_topic(progname, cmd, argc, argv); fd = open_controldevice(); for (i = 0; i < argc; i++) { - ret += query_device(fd, argv[i]); + ret += query_device(fd, argv[i], escape_strings); } ret = (ret > 0); break; From cd5932aaf46efa8d051d8b0456f8c277dfba7831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 12:57:01 +0200 Subject: [PATCH 090/151] v4l2loopback-ctl: add flags to "query" verb --- utils/v4l2loopback-ctl.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 788f7042..7b3d3a07 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -346,9 +346,13 @@ static void help_query(const char *program, int detail, int argc, char **argv) if (detail) dprintf(2, "\n querying devices ('query')" "\n =========================="); - if (help_shortcmdline(detail, program, "query ")) + if (help_shortcmdline(detail, program, "query {} ")) return; dprintf(2, + "\n \tany of the following flags may be present" + "\n\t -e/--escape : escape control-characters in the device-name" + "\n\t -h/--help : print this help and exit" + "\n" "\n \tcan be given one more more times (to query multiple devices at once)." "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')."); } @@ -1384,9 +1388,25 @@ int main(int argc, char **argv) ret = (ret > 0); break; case QUERY: - optind = do_defaultargs(progname, cmd, argc, argv); + for (;;) { + int c; + int idx; + c = getopt_long(argc, argv, list_options_short, + list_options_long, &idx); + if (-1 == c) + break; + switch (c) { + case 'e': + escape_strings++; + break; + default: + usage_topic(progname, cmd, argc - 1, argv + 1); + return 1; + } + } argc -= optind; argv += optind; + if (!argc) usage_topic(progname, cmd, argc, argv); fd = open_controldevice(); From 2c9b67072b15d903fecde67c7f269abeafee4c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 12:59:39 +0200 Subject: [PATCH 091/151] v4l2loopback-ctl: streamline help --- utils/v4l2loopback-ctl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 7b3d3a07..0bba97f5 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -289,7 +289,7 @@ static int help_shortcmdline(int detail, const char *program, dprintf(2, "\n"); //if(detail)dprintf(2, " -->"); dprintf(2, "\t"); - dprintf(2, "%s %s", program, argstring); + dprintf(2, "%s %s\n", program, argstring); return !detail; } static void help_list(const char *program, int detail, int argc, char **argv) @@ -297,12 +297,14 @@ static void help_list(const char *program, int detail, int argc, char **argv) if (detail) dprintf(2, "\n listing devices ('list')" "\n ========================"); - if (help_shortcmdline(detail, program, "list")) + if (help_shortcmdline(detail, program, "list {}")) return; dprintf(2, - "\n \tlist all available loopback-devices" - "\n\t -e/--escape : escape control-characters in the device-name" + "\n \tany of the following flags may be present" + "\n\t -e/--escape : escape control-characters in (device) names" "\n\t -h/--help : print this help and exit" + "\n" + "\n \tlist all available loopback-devices" ""); } static void help_add(const char *program, int detail, int argc, char **argv) @@ -350,7 +352,7 @@ static void help_query(const char *program, int detail, int argc, char **argv) return; dprintf(2, "\n \tany of the following flags may be present" - "\n\t -e/--escape : escape control-characters in the device-name" + "\n\t -e/--escape : escape control-characters in (device) names" "\n\t -h/--help : print this help and exit" "\n" "\n \tcan be given one more more times (to query multiple devices at once)." From 175adaf96d6d3784b922d3e0c77e9fd3e9ad8f53 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 8 May 2023 12:08:12 +0200 Subject: [PATCH 092/151] v4l2loopback: Fixup bytesused field when writer sends a too large value Gstreamer's v4l2sink is known to submit buffers with bytesused set to the length of the buffer instead of the size of the actual image-data within the buffer which is typically smaller due to buffer sizes being rounded op to a page-size: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2532 Despite this being a long standing issue and their being 2 merge-reqs: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3713 https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4527 to try and fix this it looks like this is not going to get fixed in gst's v4l2sink anytime soon and even if once it is fixed many users will likely be using an older v4l2sink which still has this bug. These buffers with a too large bytes used value are causing issues with various apps which reject these buffers when reading from the v4l2loopback device, such as e.g. ffmpeg and firefox. Add a pix_format_has_valid_sizeimage flag which gets set from vidioc_s_fmt_out() if dev->pix_format.sizeimage is known to have just been set to a valid, fixed size (so this e.g. won't be set for MJPG). And then fix this issue by making vidioc_qbuf() truncate V4L2_BUF_TYPE_VIDEO_OUTPUT buffer's bytes_used value to dev->pix_format.sizeimage when this flag is set. Closes #190 Closes #448 Obsoletes #435 --- v4l2loopback.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 7858e313..21682399 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -297,6 +297,7 @@ struct v4l2_loopback_device { struct video_device *vdev; /* pixel and stream format */ struct v4l2_pix_format pix_format; + bool pix_format_has_valid_sizeimage; struct v4l2_captureparm capture_param; unsigned long frame_jiffies; @@ -543,6 +544,20 @@ static int v4l2l_fill_format(struct v4l2_format *fmt, int capture, return 0; } +/* Checks if v4l2l_fill_format() has set a valid, fixed sizeimage val. */ +static bool v4l2l_pix_format_has_valid_sizeimage(struct v4l2_format *fmt) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) + const struct v4l2_format_info *info; + + info = v4l2_format_info(fmt->fmt.pix.pixelformat); + if (info && info->mem_planes == 1) + return true; +#endif + + return false; +} + static int pix_format_eq(const struct v4l2_pix_format *ref, const struct v4l2_pix_format *tgt, int strict) { @@ -1220,6 +1235,8 @@ static int vidioc_s_fmt_out(struct file *file, void *priv, ret = inner_try_setfmt(file, fmt); if (!ret) { dev->pix_format = fmt->fmt.pix; + dev->pix_format_has_valid_sizeimage = + v4l2l_pix_format_has_valid_sizeimage(fmt); dprintk("s_fmt_out(%d) %d...%d\n", ret, dev->ready_for_capture, dev->pix_format.sizeimage); dprintk("outFOURCC=%s\n", @@ -1717,7 +1734,18 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) b->buffer.timestamp = buf->timestamp; b->buffer.flags |= V4L2_BUF_FLAG_TIMESTAMP_COPY; } - b->buffer.bytesused = buf->bytesused; + if (dev->pix_format_has_valid_sizeimage) { + if (buf->bytesused >= dev->pix_format.sizeimage) { + b->buffer.bytesused = dev->pix_format.sizeimage; + } else { + dev_warn_ratelimited(&dev->vdev->dev, "warning queued output buffer bytesused too small %d < %d\n", + buf->bytesused, dev->pix_format.sizeimage); + b->buffer.bytesused = buf->bytesused; + } + } else { + b->buffer.bytesused = buf->bytesused; + } + set_done(b); buffer_written(dev, b); From 8cb5270d20484bde26eabb085011a8ea27285446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 10 May 2023 16:30:28 +0200 Subject: [PATCH 093/151] fallback to dprintkrw() if dev_warn_ratelimited() is not available the branch is really dead-code on such old kernels, since dev->pix_format_has_valid_sizeimage can only ever be true on linux>5.2.0 --- v4l2loopback.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 21682399..4b5a6e1a 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1738,8 +1738,15 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) if (buf->bytesused >= dev->pix_format.sizeimage) { b->buffer.bytesused = dev->pix_format.sizeimage; } else { - dev_warn_ratelimited(&dev->vdev->dev, "warning queued output buffer bytesused too small %d < %d\n", - buf->bytesused, dev->pix_format.sizeimage); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + dev_warn_ratelimited( + &dev->vdev->dev, +#else + dprintkrw( +#endif + "warning queued output buffer bytesused too small %d < %d\n", + buf->bytesused, + dev->pix_format.sizeimage); b->buffer.bytesused = buf->bytesused; } } else { From 5b6fdb85865f4cd1d8660ac59082fdce295d1906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 May 2023 16:43:49 +0200 Subject: [PATCH 094/151] rename sysfs-attribute "type" to "state" --- v4l2loopback.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 4b5a6e1a..7af83954 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -745,7 +745,7 @@ static ssize_t attr_store_maxopeners(struct device *cd, static DEVICE_ATTR(max_openers, S_IRUGO | S_IWUSR, attr_show_maxopeners, attr_store_maxopeners); -static ssize_t attr_show_type(struct device *cd, struct device_attribute *attr, +static ssize_t attr_show_state(struct device *cd, struct device_attribute *attr, char *buf) { struct v4l2_loopback_device *dev = v4l2loopback_cd2dev(cd); @@ -761,7 +761,7 @@ static ssize_t attr_show_type(struct device *cd, struct device_attribute *attr, return -EAGAIN; } -static DEVICE_ATTR(type, S_IRUGO, attr_show_type, NULL); +static DEVICE_ATTR(state, S_IRUGO, attr_show_state, NULL); static void v4l2loopback_remove_sysfs(struct video_device *vdev) { @@ -771,7 +771,7 @@ static void v4l2loopback_remove_sysfs(struct video_device *vdev) V4L2_SYSFS_DESTROY(format); V4L2_SYSFS_DESTROY(buffers); V4L2_SYSFS_DESTROY(max_openers); - V4L2_SYSFS_DESTROY(type); + V4L2_SYSFS_DESTROY(state); /* ... */ } } @@ -790,7 +790,7 @@ static void v4l2loopback_create_sysfs(struct video_device *vdev) V4L2_SYSFS_CREATE(format); V4L2_SYSFS_CREATE(buffers); V4L2_SYSFS_CREATE(max_openers); - V4L2_SYSFS_CREATE(type); + V4L2_SYSFS_CREATE(state); /* ... */ } while (0); From d42d07ec3734ee48392987f0b5c06d0c52aecdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 12 May 2023 22:47:44 +0200 Subject: [PATCH 095/151] make the code less-dependant on the "capture_nr" member of the config-struct so we can (temporarily) remove it for the releases --- utils/v4l2loopback-ctl.c | 26 +++++++++++++++++--------- v4l2loopback.c | 39 +++++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 0bba97f5..56dee5bf 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -521,16 +521,19 @@ static int parse_device(const char *devicename_) static void print_conf(struct v4l2_loopback_config *cfg, int escape_level) { + int output_nr, capture_nr; MARK(); if (!cfg) { printf("configuration: %p\n", cfg); return; } + output_nr = capture_nr = cfg->output_nr; + capture_nr = cfg->capture_nr; MARK(); printf("\tcapture_device# : %d" "\n\toutput_device# : %d" "\n\tcard_label : ", - cfg->capture_nr, cfg->output_nr); + capture_nr, output_nr); printf_raw(cfg->card_label, escape_level); printf("\n\tmin_width : %d" "\n\tmax_width : %d" @@ -592,7 +595,8 @@ static int add_device(int fd, struct v4l2_loopback_config *cfg, int verbose) MARK(); struct v4l2_loopback_config config; memset(&config, 0, sizeof(config)); - config.output_nr = config.capture_nr = ret; + config.output_nr = ret; + config.capture_nr = ret; ret = ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config); if (!ret) perror("failed querying newly added device"); @@ -627,7 +631,8 @@ static int query_device(int fd, const char *devicename, int escape) } memset(&config, 0, sizeof(config)); - config.output_nr = config.capture_nr = dev; + config.output_nr = dev; + config.capture_nr = dev; err = ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config); if (err) perror("query failed"); @@ -646,6 +651,7 @@ static int list_devices(int fd, int escape) } *devices = 0; size_t numdevices = 0, i; glob_t globbuf = { 0 }; + int output_nr, capture_nr; glob("/sys/devices/virtual/video4linux/video*", GLOB_ONLYDIR, 0, &globbuf); if (globbuf.gl_pathc) { @@ -683,20 +689,22 @@ static int list_devices(int fd, int escape) continue; } } + capture_nr = config.capture_nr; + output_nr = config.output_nr; /* check if we already have this device */ for (j = 0; j < numdevices; j++) { - if ((devices[j].output == config.output_nr) && - (devices[j].capture == config.capture_nr)) { + if ((devices[j].output == output_nr) && + (devices[j].capture == capture_nr)) { //dprintf(2, "duplicate device\n"); - config.output_nr = config.capture_nr = -1; + output_nr = capture_nr = -1; break; } } - if ((config.output_nr < 0) || (config.capture_nr < 0)) + if ((output_nr < 0) || (capture_nr < 0)) continue; - devices[numdevices].output = config.output_nr; - devices[numdevices].capture = config.capture_nr; + devices[numdevices].output = output_nr; + devices[numdevices].capture = capture_nr; snprintf(devices[numdevices].name, sizeof(devices[numdevices].name), "%s", config.card_label); diff --git a/v4l2loopback.c b/v4l2loopback.c index 7af83954..68e5cd4f 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -746,7 +746,7 @@ static DEVICE_ATTR(max_openers, S_IRUGO | S_IWUSR, attr_show_maxopeners, attr_store_maxopeners); static ssize_t attr_show_state(struct device *cd, struct device_attribute *attr, - char *buf) + char *buf) { struct v4l2_loopback_device *dev = v4l2loopback_cd2dev(cd); @@ -2632,21 +2632,22 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) _announce_all_caps = (!!_announce_all_caps); if (conf) { - if (conf->capture_nr >= 0 && - conf->output_nr == conf->capture_nr) { - nr = conf->capture_nr; - } else if (conf->capture_nr < 0 && conf->output_nr < 0) { + const int output_nr = conf->output_nr; + const int capture_nr = conf->capture_nr; + if (capture_nr >= 0 && output_nr == capture_nr) { + nr = output_nr; + } else if (capture_nr < 0 && output_nr < 0) { nr = -1; - } else if (conf->capture_nr < 0) { - nr = conf->output_nr; - } else if (conf->output_nr < 0) { - nr = conf->capture_nr; + } else if (capture_nr < 0) { + nr = output_nr; + } else if (output_nr < 0) { + nr = capture_nr; } else { printk(KERN_ERR "split OUTPUT and CAPTURE devices not yet supported."); printk(KERN_INFO "both devices must have the same number (%d != %d).", - conf->output_nr, conf->capture_nr); + output_nr, capture_nr); return -EINVAL; } } @@ -2855,7 +2856,7 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, struct v4l2_loopback_device *dev; struct v4l2_loopback_config conf; struct v4l2_loopback_config *confptr = &conf; - int device_nr; + int device_nr, capture_nr, output_nr; int ret; ret = mutex_lock_killable(&v4l2loopback_ctl_mutex); @@ -2901,8 +2902,9 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, if ((ret = copy_from_user(&conf, (void *)parm, sizeof(conf))) < 0) break; - device_nr = (conf.output_nr < 0) ? conf.capture_nr : - conf.output_nr; + capture_nr = output_nr = conf.output_nr; + capture_nr = conf.capture_nr; + device_nr = (output_nr < 0) ? capture_nr : output_nr; MARK(); /* get the device from either capture_nr or output_nr (whatever is valid) */ if ((ret = v4l2loopback_lookup(device_nr, &dev)) < 0) @@ -2911,15 +2913,15 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, /* if we got the device from output_nr and there is a valid capture_nr, * make sure that both refer to the same device (or bail out) */ - if ((device_nr != conf.capture_nr) && (conf.capture_nr >= 0) && - ((ret = v4l2loopback_lookup(conf.capture_nr, 0)) < 0)) + if ((device_nr != capture_nr) && (capture_nr >= 0) && + ((ret = v4l2loopback_lookup(capture_nr, 0)) < 0)) break; MARK(); /* if otoh, we got the device from capture_nr and there is a valid output_nr, * make sure that both refer to the same device (or bail out) */ - if ((device_nr != conf.output_nr) && (conf.output_nr >= 0) && - ((ret = v4l2loopback_lookup(conf.output_nr, 0)) < 0)) + if ((device_nr != output_nr) && (output_nr >= 0) && + ((ret = v4l2loopback_lookup(output_nr, 0)) < 0)) break; MARK(); @@ -2927,7 +2929,8 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, snprintf(conf.card_label, sizeof(conf.card_label), "%s", dev->card_label); MARK(); - conf.output_nr = conf.capture_nr = dev->vdev->num; + conf.output_nr = dev->vdev->num; + conf.capture_nr = dev->vdev->num; conf.min_width = dev->min_width; conf.min_height = dev->min_height; conf.max_width = dev->max_width; From 2689e9ec3b40b6d327c06fca632317a22d5ab08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 12 May 2023 23:26:10 +0200 Subject: [PATCH 096/151] swap output/capture device when adding new devices the 2nd arg is currently dysfunctional anyhow, so there's no actual harm. --- utils/v4l2loopback-ctl.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 56dee5bf..37223efe 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -312,8 +312,9 @@ static void help_add(const char *program, int detail, int argc, char **argv) if (detail) dprintf(2, "\n adding devices ('add')" "\n ======================"); - if (help_shortcmdline(detail, program, - "add {} [ []]")) + if (help_shortcmdline( + detail, program, + "add {} [ []]")) return; dprintf(2, "\n \tany of the following flags may be present" @@ -328,9 +329,9 @@ static void help_add(const char *program, int detail, int argc, char **argv) "\n\t -v/--verbose : verbose mode (print properties of device after successfully creating it)" "\n\t -?/--help : print this help and exit" "\n" - "\n \tif given, create a specific device (otherwise just create a free one)." + "\n \tif given, create a specific device (otherwise just create a free one)." "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." - "\n \tif given, use separate output & capture devices (otherwise they are the same)."); + "\n \tif given, use separate output & capture devices (otherwise they are the same)."); } static void help_delete(const char *program, int detail, int argc, char **argv) { @@ -1362,15 +1363,15 @@ int main(int argc, char **argv) case 0: /* no device given: pick some */ break; + case 2: + /* two devices given: capture_device and output_device */ + output_nr = parse_device(argv[0]); + capture_nr = parse_device(argv[1]); + break; case 1: /* single device given: use it for both input and output */ capture_nr = output_nr = parse_device(argv[0]); break; - case 2: - /* two devices given: capture_device and output_device */ - capture_nr = parse_device(argv[0]); - output_nr = parse_device(argv[1]); - break; default: usage_topic(progname, cmd, argc, argv); return 1; From 73213814a7b31f331c7dfb16ad2ae364b8ead03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 12 May 2023 23:27:24 +0200 Subject: [PATCH 097/151] Remove the 'capture_nr' member from the v4l2_looback_config struct and protect the remaining uses of this member with SPLIT_DEVICES --- utils/v4l2loopback-ctl.c | 22 ++++++++++++++++++++++ v4l2loopback.c | 10 ++++++++++ v4l2loopback.h | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 37223efe..3b7af39e 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -529,7 +529,9 @@ static void print_conf(struct v4l2_loopback_config *cfg, int escape_level) return; } output_nr = capture_nr = cfg->output_nr; +#ifdef SPLIT_DEVICES capture_nr = cfg->capture_nr; +#endif MARK(); printf("\tcapture_device# : %d" "\n\toutput_device# : %d" @@ -563,7 +565,9 @@ make_conf(struct v4l2_loopback_config *cfg, const char *label, int min_width, max_height <= 0 && exclusive_caps < 0 && buffers <= 0 && openers <= 0 && capture_device < 0 && output_device < 0) return 0; +#ifdef SPLIT_DEVICES cfg->capture_nr = capture_device; +#endif cfg->output_nr = output_device; cfg->card_label[0] = 0; if (label) @@ -597,7 +601,9 @@ static int add_device(int fd, struct v4l2_loopback_config *cfg, int verbose) struct v4l2_loopback_config config; memset(&config, 0, sizeof(config)); config.output_nr = ret; +#ifdef SPLIT_DEVICES config.capture_nr = ret; +#endif ret = ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config); if (!ret) perror("failed querying newly added device"); @@ -633,7 +639,9 @@ static int query_device(int fd, const char *devicename, int escape) memset(&config, 0, sizeof(config)); config.output_nr = dev; +#ifdef SPLIT_DEVICES config.capture_nr = dev; +#endif err = ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config); if (err) perror("query failed"); @@ -680,6 +688,7 @@ static int list_devices(int fd, int escape) } /* check if this is a loopback device */ config.output_nr = dev; +#ifdef SPLIT_DEVICES config.capture_nr = -1; if (ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config)) { memset(&config, 0, sizeof(config)); @@ -691,6 +700,13 @@ static int list_devices(int fd, int escape) } } capture_nr = config.capture_nr; +#else + if (ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config)) { + //dprintf(2, "not a loopback device\n"); + continue; + } + capture_nr = config.output_nr; +#endif output_nr = config.output_nr; /* check if we already have this device */ for (j = 0; j < numdevices; j++) { @@ -1367,6 +1383,12 @@ int main(int argc, char **argv) /* two devices given: capture_device and output_device */ output_nr = parse_device(argv[0]); capture_nr = parse_device(argv[1]); +#ifndef SPLIT_DEVICES + if (capture_nr != output_nr) + dprintf(2, + "split output/capture devices currently not supported...ignoring capture device\n"); + capture_nr = output_nr; +#endif break; case 1: /* single device given: use it for both input and output */ diff --git a/v4l2loopback.c b/v4l2loopback.c index 68e5cd4f..79f0d8e1 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2633,7 +2633,11 @@ static int v4l2_loopback_add(struct v4l2_loopback_config *conf, int *ret_nr) if (conf) { const int output_nr = conf->output_nr; +#ifdef SPLIT_DEVICES const int capture_nr = conf->capture_nr; +#else + const int capture_nr = output_nr; +#endif if (capture_nr >= 0 && output_nr == capture_nr) { nr = output_nr; } else if (capture_nr < 0 && output_nr < 0) { @@ -2903,7 +2907,9 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, 0) break; capture_nr = output_nr = conf.output_nr; +#ifdef SPLIT_DEVICES capture_nr = conf.capture_nr; +#endif device_nr = (output_nr < 0) ? capture_nr : output_nr; MARK(); /* get the device from either capture_nr or output_nr (whatever is valid) */ @@ -2930,7 +2936,9 @@ static long v4l2loopback_control_ioctl(struct file *file, unsigned int cmd, dev->card_label); MARK(); conf.output_nr = dev->vdev->num; +#ifdef SPLIT_DEVICES conf.capture_nr = dev->vdev->num; +#endif conf.min_width = dev->min_width; conf.min_height = dev->min_height; conf.max_width = dev->max_width; @@ -3116,7 +3124,9 @@ static int __init v4l2loopback_init_module(void) struct v4l2_loopback_config cfg = { // clang-format off .output_nr = video_nr[i], +#ifdef SPLIT_DEVICES .capture_nr = video_nr[i], +#endif .min_width = min_width, .min_height = min_height, .max_width = max_width, diff --git a/v4l2loopback.h b/v4l2loopback.h index 606efd06..18f2f376 100644 --- a/v4l2loopback.h +++ b/v4l2loopback.h @@ -33,7 +33,7 @@ struct v4l2_loopback_config { * */ int output_nr; - int capture_nr; + int unused; /*capture_nr;*/ /** * a nice name for your device From c4dc81009ba1a96bfcd83ac8b2ddf87b70a80d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 12 May 2023 22:35:08 +0200 Subject: [PATCH 098/151] build-fixes: install and utils - install header files - re-build utils if header changes --- Makefile | 12 +++++++++--- utils/Makefile | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index dd76a18b..385c24d9 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ PWD := $(shell pwd) PREFIX ?= /usr/local BINDIR = $(PREFIX)/bin +INCLUDEDIR = $(PREFIX)/include MANDIR = $(PREFIX)/share/man MAN1DIR = $(MANDIR)/man1 INSTALL = install @@ -37,7 +38,7 @@ MODULE_OPTIONS = devices=2 .PHONY: all install clean distclean -.PHONY: install-all install-utils install-man +.PHONY: install-all install-extra install-utils install-man install-headers .PHONY: modprobe v4l2loopback # we don't control the .ko file dependencies, as it is done by kernel @@ -51,13 +52,14 @@ v4l2loopback.ko: @echo "Building v4l2-loopback driver..." $(MAKE) -C $(KERNEL_DIR) M=$(PWD) KCPPFLAGS="$(KCPPFLAGS)" modules -install-all: install install-utils install-man +install-all: install install-extra install: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install @echo "" @echo "SUCCESS (if you got 'SSL errors' above, you can safely ignore them)" @echo "" +install-extra: install-utils install-man install-headers install-utils: utils/v4l2loopback-ctl $(INSTALL_DIR) "$(DESTDIR)$(BINDIR)" $(INSTALL_PROGRAM) $< "$(DESTDIR)$(BINDIR)" @@ -66,6 +68,10 @@ install-man: man/v4l2loopback-ctl.1 $(INSTALL_DIR) "$(DESTDIR)$(MAN1DIR)" $(INSTALL_DATA) $< "$(DESTDIR)$(MAN1DIR)" +install-headers: v4l2loopback.h + $(INSTALL_DIR) "$(DESTDIR)$(INCLUDEDIR)/linux" + $(INSTALL_DATA) $< "$(DESTDIR)$(INCLUDEDIR)/linux" + clean: rm -f *~ rm -f Module.symvers Module.markers modules.order @@ -87,7 +93,7 @@ man/v4l2loopback-ctl.1: utils/v4l2loopback-ctl $^ > $@ utils: utils/v4l2loopback-ctl -utils/v4l2loopback-ctl: utils/v4l2loopback-ctl.c +utils/v4l2loopback-ctl: utils/v4l2loopback-ctl.c v4l2loopback.h $(MAKE) -C utils V4L2LOOPBACK_SNAPSHOT_VERSION=$(V4L2LOOPBACK_SNAPSHOT_VERSION) .clang-format: diff --git a/utils/Makefile b/utils/Makefile index 03750757..8261cd38 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -22,3 +22,6 @@ clean: install: $(MKDIR_P) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) $(programs) $(DESTDIR)$(bindir) + +v4l2loopback-ctl.o: v4l2loopback-ctl.c ../v4l2loopback.h +v4l2loopback-ctl: v4l2loopback-ctl.o From 5eaff75d98e4695ce87db756e3fa4d81a657d785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 12 May 2023 23:32:59 +0200 Subject: [PATCH 099/151] force timestamp.tv_sec to (long long int) so it also compiles on 32bit platforms Closes: https://github.com/umlaeute/v4l2loopback/pull/542 --- v4l2loopback.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 79f0d8e1..ebcb11e4 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -1716,8 +1716,8 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) "qbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, - buf->sequence); + (long long)buf->timestamp.tv_sec, + (long int)buf->timestamp.tv_usec, buf->sequence); set_queued(b); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -1725,8 +1725,8 @@ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) "qbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, - buf->sequence); + (long long)buf->timestamp.tv_sec, + (long int)buf->timestamp.tv_usec, buf->sequence); if ((!(b->buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_COPY)) && (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0)) v4l2l_get_timestamp(&b->buffer); @@ -1864,8 +1864,8 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) "dqbuf(CAPTURE)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, - buf->sequence); + (long long)buf->timestamp.tv_sec, + (long int)buf->timestamp.tv_usec, buf->sequence); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: spin_lock_bh(&dev->list_lock); @@ -1883,8 +1883,8 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) "dqbuf(OUTPUT)#%d: buffer#%d @ %p type=%d bytesused=%d length=%d flags=%x field=%d timestamp=%lld.%06ld sequence=%d\n", index, buf->index, buf, buf->type, buf->bytesused, buf->length, buf->flags, buf->field, - buf->timestamp.tv_sec, (long int)buf->timestamp.tv_usec, - buf->sequence); + (long long)buf->timestamp.tv_sec, + (long int)buf->timestamp.tv_usec, buf->sequence); return 0; default: return -EINVAL; From f5192e626cc942f4b3ab6601f8e6a54761f4875d Mon Sep 17 00:00:00 2001 From: Wren Turkal Date: Wed, 24 Feb 2021 18:32:57 -0800 Subject: [PATCH 100/151] Remove support for pre-3.6.1 Linux kernels. Signed-off-by: Wren Turkal --- v4l2loopback.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index ebcb11e4..1c9d218c 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -34,7 +34,7 @@ #include "v4l2loopback.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 1) -#define kstrtoul strict_strtoul +#error This module is not supported on kernels before 3.6.1. #endif #if defined(timer_setup) && defined(from_timer) @@ -912,11 +912,6 @@ static int vidioc_querycap(struct file *file, void *priv, snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:v4l2loopback-%03d", device_nr); -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - /* since 3.1.0, the v4l2-core system is supposed to set the version */ - cap->version = V4L2LOOPBACK_VERSION_CODE; -#endif - if (dev->announce_all_caps) { capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; } else { @@ -933,9 +928,7 @@ static int vidioc_querycap(struct file *file, void *priv, #endif /* >=linux-4.7.0 */ cap->device_caps = cap->capabilities = capabilities; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) cap->capabilities |= V4L2_CAP_DEVICE_CAPS; -#endif memset(cap->reserved, 0, sizeof(cap->reserved)); return 0; @@ -2455,9 +2448,6 @@ static void init_buffers(struct v4l2_loopback_device *dev) b->length = buffer_size; b->field = V4L2_FIELD_NONE; b->flags = 0; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 1) - b->input = 0; -#endif b->m.offset = i * buffer_size; b->memory = V4L2_MEMORY_MMAP; b->sequence = 0; From 9ba7e294ea6f45410a15f192381d4918e8df4f02 Mon Sep 17 00:00:00 2001 From: Wren Turkal Date: Mon, 1 Mar 2021 11:47:23 -0800 Subject: [PATCH 101/151] Remove support for pre-4.0.0 kernels. Signed-off-by: Wren Turkal --- v4l2loopback.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 1c9d218c..4659bdf7 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -33,8 +33,8 @@ #include #include "v4l2loopback.h" -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 1) -#error This module is not supported on kernels before 3.6.1. +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) +#error This module is not supported on kernels before 4.0.0. #endif #if defined(timer_setup) && defined(from_timer) @@ -94,14 +94,8 @@ MODULE_LICENSE("GPL"); static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) { - /* ktime_get_ts is considered deprecated, so use ktime_get_ts64 if possible */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) - struct timespec ts; - ktime_get_ts(&ts); -#else struct timespec64 ts; ktime_get_ts64(&ts); -#endif b->timestamp.tv_sec = ts.tv_sec; b->timestamp.tv_usec = (ts.tv_nsec / NSEC_PER_USEC); @@ -2501,18 +2495,10 @@ static void init_vdev(struct video_device *vdev, int nr) #endif if (debug > 1) -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 20, 0) - vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; -#else vdev->dev_debug = V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG; -#endif - /* since kernel-3.7, there is a new field 'vfl_dir' that has to be - * set to VFL_DIR_M2M for bidirectional devices */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) vdev->vfl_dir = VFL_DIR_M2M; -#endif MARK(); } From 5bb9bed4e2a34761dc5dc188faec7315ae3930fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 13 Sep 2023 08:17:45 +0200 Subject: [PATCH 102/151] [github] Mention "discussions" in the issue landing-page --- .github/ISSUE_TEMPLATE/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4939691c..c22b746d 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -9,3 +9,6 @@ contact_links: - name: Look for more Documentation url: https://github.com/umlaeute/v4l2loopback/wiki about: Check our WIKI to see whether your question has already been answered. + - name: Start a discussion + url: https://github.com/umlaeute/v4l2loopback/discussions + about: Ask questions and share ideas on our "discussion" page. From 9f271e88e6e2268fc15d87900df9c4efbca543f6 Mon Sep 17 00:00:00 2001 From: rdbo Date: Wed, 15 Nov 2023 23:34:09 -0300 Subject: [PATCH 103/151] fixed utils build for musl (missing GLOB_ONLYDIR) --- utils/v4l2loopback-ctl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 3b7af39e..d33c2885 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -29,6 +29,10 @@ #include "v4l2loopback.h" +#ifndef GLOB_ONLYDIR +#define GLOB_ONLYDIR 0 /* Fix for musl libc and other libcs missing GLOB_ONLYDIR at glob.h */ +#endif + #define CONTROLDEVICE "/dev/v4l2loopback" #if 0 From 636779741884364226830e5ee5f585a5b28eaa3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 16 Nov 2023 10:34:26 +0100 Subject: [PATCH 104/151] note that GLOB_ONLYDIR is indeed not requried by POSIX --- utils/v4l2loopback-ctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index d33c2885..49c64305 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -30,6 +30,7 @@ #include "v4l2loopback.h" #ifndef GLOB_ONLYDIR +/* not required by POSIX */ #define GLOB_ONLYDIR 0 /* Fix for musl libc and other libcs missing GLOB_ONLYDIR at glob.h */ #endif From 2b29020e57903b46b1b9c2c631d941a508b0953a Mon Sep 17 00:00:00 2001 From: rdbo Date: Wed, 15 Nov 2023 23:59:06 -0300 Subject: [PATCH 105/151] added v4l2loopback-ctl.o to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 66413e9d..d285cc4a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.yuv Module.symvers modules.order +utils/v4l2loopback-ctl.o utils/v4l2loopback-ctl v4l2loopback.ko v4l2loopback.mod From 719af83ce1d7ce2cabe7fede4556e54362787c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 16 Nov 2023 10:38:29 +0100 Subject: [PATCH 106/151] Fix formatting --- utils/v4l2loopback-ctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 49c64305..618f2a56 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -30,8 +30,9 @@ #include "v4l2loopback.h" #ifndef GLOB_ONLYDIR -/* not required by POSIX */ -#define GLOB_ONLYDIR 0 /* Fix for musl libc and other libcs missing GLOB_ONLYDIR at glob.h */ +/* Fix for musl libc and other libcs missing GLOB_ONLYDIR at glob.h */ +/* (GLOB_ONLYDIR is not required by POSIX) */ +#define GLOB_ONLYDIR 0 #endif #define CONTROLDEVICE "/dev/v4l2loopback" From 29d8abfd5e2aded1dec2702d15e0d7e037311172 Mon Sep 17 00:00:00 2001 From: Fufu Fang Date: Mon, 10 Jul 2023 18:06:14 +0100 Subject: [PATCH 107/151] Update README.md Multiple module parameters are allowed in a single line in modern kernel. --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 58fa5497..d146f3b9 100644 --- a/README.md +++ b/README.md @@ -331,14 +331,13 @@ If needed, one can specify default module options by creating `/etc/modprobe.d/v4l2loopback.conf` in the following form: ~~~ -options v4l2loopback video_nr=3,4,7 -options v4l2loopback card_label="device number 3,the number four,the last one" +options v4l2loopback video_nr=3,4,7 card_label="device number 3,the number four,the last one" ~~~ -One can only add one option per line. These options also become the defaults when -manually calling `modprobe v4l2loopback`. Note that the double quotes can only -be used at the beginning and the end of the option's value, as opposed to when -they are specified on the command line. +These options also become the defaults when manually calling +`modprobe v4l2loopback`. Note that the double quotes can only be used at the +beginning and the end of the option's value, as opposed to when they are +specified on the command line. If your system boots with an initial ramdisk, which is the case for most modern distributions, you need to update this ramdisk with the settings above, From 91c20dc270462abeb38b30902d41f12fb9717e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 13:12:27 +0100 Subject: [PATCH 108/151] [ci] skip failures on kernels without v4l2 support --- .github/workflows/kmod-compatibility-checks.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 03aaaba1..8e21920e 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -73,8 +73,12 @@ jobs: echo "#### Skipped known failure ${kver}"; ;; *) - echo "#### Skipped unexpected failure ${kver}"; - failed="${failed} ${kver}"; + if grep -qE "^(CONFIG_VIDEO_DEV|CONFIG_VIDEO_V4L2)=" "${kver}/.config"; then + echo "#### Unexpected failure ${kver}"; + failed="${failed} ${kver}"; + else + echo "#### Skipped failure ${kver} (no v4l2 support)"; + fi; ;; esac; fi; From 4290e1786237abaed5b3144591d9206206d37b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 13:16:45 +0100 Subject: [PATCH 109/151] [ci] install all available kernel headers not just the most recent one... as of today, the most recent headers on Debian are the "cloud" variants that do not have v4l2loopback support. --- .github/workflows/kmod-compatibility-checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 8e21920e..b7157950 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -50,10 +50,10 @@ jobs: apt-get update --quiet; apt-get install --yes --no-install-recommends kmod for kver in $(apt-cache search 'linux-headers-.*-generic' | cut -d- -f3 | sort -u -V); do - apt-cache search 'linux-headers-'"${kver}"'-.*-generic' | sort -V | tail -n 1 | awk '{print $1}'; + apt-cache search 'linux-headers-'"${kver}"'-.*-generic' | sort -V | awk '{print $1}'; done | xargs apt-get install --yes --no-install-recommends || true for kver in $(apt-cache search "linux-headers-.*-${arch}" | cut -d- -f3 | sort -u -V); do - apt-cache search 'linux-headers-'"${kver}"'-.*-'"${arch}" | sort -V | tail -n 1 | awk '{print $1}'; + apt-cache search 'linux-headers-'"${kver}"'-.*-'"${arch}" | sort -V | awk '{print $1}'; done | xargs apt-get install --yes --no-install-recommends || true failed="" From fdc753fd190cbe41bb7593e6b24f9e97e8e1c987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 13:23:19 +0100 Subject: [PATCH 110/151] [ci] show skipped builds --- .github/workflows/kmod-compatibility-checks.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index b7157950..911120c4 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -58,6 +58,7 @@ jobs: failed="" succeeded="" + skipped="" for kver in /lib/modules/*/build; do test -d $kver || continue kver=${kver%/build} @@ -71,6 +72,7 @@ jobs: case " ${JOB_KNOWN_FAILURES} " in *" ${kver%.*} "*) echo "#### Skipped known failure ${kver}"; + skipped="${skipped} ${kver}"; ;; *) if grep -qE "^(CONFIG_VIDEO_DEV|CONFIG_VIDEO_V4L2)=" "${kver}/.config"; then @@ -78,6 +80,7 @@ jobs: failed="${failed} ${kver}"; else echo "#### Skipped failure ${kver} (no v4l2 support)"; + skipped="${skipped} ${kver}"; fi; ;; esac; @@ -88,4 +91,7 @@ jobs: echo "#### Failed kernels: ${failed}"; exit 1 fi + if [ "x${skipped}" != "x" ]; then + echo "#### Skipped kernels: ${skipped}"; + fi echo "#### Successful builds for kernels: ${succeeded}"; From 425f2648f02c577a1ed36b3641d0cbc2d1a4ff51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 13:27:11 +0100 Subject: [PATCH 111/151] [ci] Fix testing for v4l2 capabilities --- .github/workflows/kmod-compatibility-checks.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 911120c4..4116b7eb 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -59,9 +59,9 @@ jobs: failed="" succeeded="" skipped="" - for kver in /lib/modules/*/build; do - test -d $kver || continue - kver=${kver%/build} + for kbuild in /lib/modules/*/build; do + test -d $kbuild || continue + kver=${kbuild%/build} kver=${kver##*/} echo "=== Testing ${kver} ==="; ret=$(make KERNELRELEASE="${kver}" >&2; echo $?); @@ -75,12 +75,12 @@ jobs: skipped="${skipped} ${kver}"; ;; *) - if grep -qE "^(CONFIG_VIDEO_DEV|CONFIG_VIDEO_V4L2)=" "${kver}/.config"; then - echo "#### Unexpected failure ${kver}"; - failed="${failed} ${kver}"; - else + if test -e "${kbuild}/.config" && ! grep -qE "^(CONFIG_VIDEO_DEV|CONFIG_VIDEO_V4L2)=" "${kbuild}/.config"; then echo "#### Skipped failure ${kver} (no v4l2 support)"; skipped="${skipped} ${kver}"; + else + echo "#### Unexpected failure ${kver}"; + failed="${failed} ${kver}"; fi; ;; esac; From 9fe9741dc39f48b60c5cd15705981b7a3e35ba35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 13:32:01 +0100 Subject: [PATCH 112/151] [ci] bump actions/checkout to v4 --- .github/workflows/kmod-compatibility-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 4116b7eb..7cbbc508 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -41,7 +41,7 @@ jobs: env: JOB_KNOWN_FAILURES: "3.13 3.16 3.19 4.2" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Compile against all available kernel header versions shell: bash From 3bb27e6638c85ebf929c590a851d18a58b16c555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 13:34:18 +0100 Subject: [PATCH 113/151] [ci] lower actions/checkout to v3 v4 doesn't work on trusty, xenial, bionic --- .github/workflows/kmod-compatibility-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 7cbbc508..958cb877 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -41,7 +41,7 @@ jobs: env: JOB_KNOWN_FAILURES: "3.13 3.16 3.19 4.2" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - name: Compile against all available kernel header versions shell: bash From fbe6cd2ae63bd28306f15a03f3a8e1a4ce1e01d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 15:02:51 +0100 Subject: [PATCH 114/151] [ci] only install latest packageversion of each kernel-flavour --- .github/workflows/kmod-compatibility-checks.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 958cb877..458a5d02 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -46,16 +46,11 @@ jobs: - name: Compile against all available kernel header versions shell: bash run: | - arch=$(dpkg --print-architecture) apt-get update --quiet; apt-get install --yes --no-install-recommends kmod - for kver in $(apt-cache search 'linux-headers-.*-generic' | cut -d- -f3 | sort -u -V); do - apt-cache search 'linux-headers-'"${kver}"'-.*-generic' | sort -V | awk '{print $1}'; - done | xargs apt-get install --yes --no-install-recommends || true - for kver in $(apt-cache search "linux-headers-.*-${arch}" | cut -d- -f3 | sort -u -V); do - apt-cache search 'linux-headers-'"${kver}"'-.*-'"${arch}" | sort -V | awk '{print $1}'; - done | xargs apt-get install --yes --no-install-recommends || true - + apt-cache search "linux-headers-[0-9.]*-[0-9]*-" | cut -d " " -f1 \ + | sed -e 's|linux-headers-\([0-9.]*\)-[0-9]*-\(.*\)|\0 \1 \2|' | sort -t- -k3,4 -V -r | sort -u -k2,3 -V | cut -d " " -f1 \ + | xargs apt-get install --yes --no-install-recommends failed="" succeeded="" skipped="" From f9d79a82facc9ffc3722d050f3587b172607bc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 15:37:49 +0100 Subject: [PATCH 115/151] [ci] install 'dkms' to get some more building prerequisites --- .github/workflows/kmod-compatibility-checks.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 458a5d02..37feb9a1 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -48,6 +48,7 @@ jobs: run: | apt-get update --quiet; apt-get install --yes --no-install-recommends kmod + apt-get install --yes --no-install-recommends dkms || true apt-cache search "linux-headers-[0-9.]*-[0-9]*-" | cut -d " " -f1 \ | sed -e 's|linux-headers-\([0-9.]*\)-[0-9]*-\(.*\)|\0 \1 \2|' | sort -t- -k3,4 -V -r | sort -u -k2,3 -V | cut -d " " -f1 \ | xargs apt-get install --yes --no-install-recommends From 850a2e36849f6ad3c9bf74f2ae3f603452bd8a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 23 Nov 2023 16:07:30 +0100 Subject: [PATCH 116/151] some minor typos --- .github/workflows/kmod-compatibility-checks.yml | 2 +- tests/consumer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/kmod-compatibility-checks.yml b/.github/workflows/kmod-compatibility-checks.yml index 37feb9a1..bb016e00 100644 --- a/.github/workflows/kmod-compatibility-checks.yml +++ b/.github/workflows/kmod-compatibility-checks.yml @@ -21,7 +21,7 @@ jobs: # the following does: # - fetch all available tags for the "buildpack-deps" Docker image # - fetch all available Ubuntu and Debian releases (that are still hosted on the distros' archive servers) - # - get the intersection of the tags and the releaess + # - get the intersection of the tags and the releases # - drop some releases (e.g. 'oldstable') # - format as a JSON array run: | diff --git a/tests/consumer.c b/tests/consumer.c index 14533e25..434b1d27 100644 --- a/tests/consumer.c +++ b/tests/consumer.c @@ -486,7 +486,7 @@ static void init_device(void) printf("got format: %s\n", snprintf_format(strbuf, sizeof(strbuf), &fmt)); - /* try to se tthe current format (no-change should always succeed) */ + /* try to set the current format (no-change should always succeed) */ if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { const char *s = "VIDIOC_S_FMT"; fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); From a0b9df930701ea349ec61df0ed9df65b2bf0cd74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 9 Jan 2024 17:10:05 +0100 Subject: [PATCH 117/151] use 'sudo' to change the permissions of the module --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 385c24d9..d97adc78 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ distclean: clean rm -f man/v4l2loopback-ctl.1 modprobe: v4l2loopback.ko - chmod a+r v4l2loopback.ko + -sudo chmod a+r v4l2loopback.ko sudo modprobe videodev -sudo rmmod v4l2loopback sudo insmod ./v4l2loopback.ko $(MODULE_OPTIONS) From 1ddd627e25ac9f75f883140e6630058905febd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 9 Jan 2024 17:10:24 +0100 Subject: [PATCH 118/151] use $< instead of hardcoding the module name --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d97adc78..65971ffe 100644 --- a/Makefile +++ b/Makefile @@ -82,10 +82,10 @@ distclean: clean rm -f man/v4l2loopback-ctl.1 modprobe: v4l2loopback.ko - -sudo chmod a+r v4l2loopback.ko - sudo modprobe videodev - -sudo rmmod v4l2loopback - sudo insmod ./v4l2loopback.ko $(MODULE_OPTIONS) + -sudo chmod a+r $< + -sudo modprobe videodev + -sudo rmmod $< + sudo insmod ./$< $(MODULE_OPTIONS) man/v4l2loopback-ctl.1: utils/v4l2loopback-ctl help2man -N --name "control v4l2 loopback devices" \ From aa8ec0ca7d954d76613cb8920eb8c3cfbc95eb85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 11 Jan 2024 12:18:29 +0100 Subject: [PATCH 119/151] 'sign' target to sign the generated module for use with secure boot --- Makefile | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Makefile b/Makefile index 65971ffe..b76846d1 100644 --- a/Makefile +++ b/Makefile @@ -103,4 +103,38 @@ utils/v4l2loopback-ctl: utils/v4l2loopback-ctl.c v4l2loopback.h clang-format: .clang-format clang-format -i *.c *.h utils/*.c +.PHONY: sign +# try to read the default certificate/key from the dkms config +dkms_framework=/etc/dkms/framework.conf +-include $(dkms_framework) +KBUILD_SIGN_KEY=$(mok_signing_key) +KBUILD_SIGN_CERT=$(mok_certificate) + +ifeq ($(KBUILD_SIGN_PIN),) +define usage_kbuildsignpin +$(info ) +$(info ++++++ If your certificate requires a password, pass it via the KBUILD_SIGN_PIN env-var!) +$(info ++++++ E.g. using 'export KBUILD_SIGN_PIN; read -s -p "Passphrase for signing key $(KBUILD_SIGN_KEY): " KBUILD_SIGN_PIN; sudo --preserve-env=KBUILD_SIGN_PIN make sign') +$(info ) +endef +endif + +define usage_kbuildsign +sign: v4l2loopback.ko + $(info ) + $(info ++++++ To sign the $< module, you must set KBUILD_SIGN_KEY/KBUILD_SIGN_CERT to point to the signing key/certificate!) + $(info ++++++ For your convenience, we try to read these variables as 'mok_signing_key' resp. 'mok_certificate' from $(dkms_framework)) + $(call usage_kbuildsignpin) +endef + +ifeq ($(wildcard $(KBUILD_SIGN_KEY)),) +$(call usage_kbuildsign) +else ifeq ($(wildcard $(KBUILD_SIGN_CERT)),) +$(call usage_kbuildsign) +else +sign: v4l2loopback.ko + $(call usage_kbuildsignpin) + "$(KERNEL_DIR)"/scripts/sign-file sha256 $(KBUILD_SIGN_KEY) $(KBUILD_SIGN_CERT) $< +endif + endif # !kbuild From ee177b680f68f7862e349c2d97230c785620e94f Mon Sep 17 00:00:00 2001 From: Joaquim Monteiro Date: Mon, 22 Jan 2024 09:29:40 +0000 Subject: [PATCH 120/151] Use strscpy instead of strlcpy if available Linux 6.8 removed strlcpy, so using strscpy is necessary on newer kernels. --- v4l2loopback.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/v4l2loopback.c b/v4l2loopback.c index 4659bdf7..8c8f8361 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -37,6 +37,10 @@ #error This module is not supported on kernels before 4.0.0. #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) +#define HAVE_STRSCPY +#endif + #if defined(timer_setup) && defined(from_timer) #define HAVE_TIMER_SETUP #endif @@ -901,7 +905,11 @@ static int vidioc_querycap(struct file *file, void *priv, ->device_nr; __u32 capabilities = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; +#ifdef HAVE_STRSCPY + strscpy(cap->driver, "v4l2 loopback", sizeof(cap->driver)); +#else strlcpy(cap->driver, "v4l2 loopback", sizeof(cap->driver)); +#endif snprintf(cap->card, sizeof(cap->card), "%s", dev->card_label); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:v4l2loopback-%03d", device_nr); @@ -1423,7 +1431,11 @@ static int vidioc_enum_output(struct file *file, void *fh, memset(outp, 0, sizeof(*outp)); outp->index = index; +#ifdef HAVE_STRSCPY + strscpy(outp->name, "loopback in", sizeof(outp->name)); +#else strlcpy(outp->name, "loopback in", sizeof(outp->name)); +#endif outp->type = V4L2_OUTPUT_TYPE_ANALOG; outp->audioset = 0; outp->modulator = 0; @@ -1483,7 +1495,11 @@ static int vidioc_enum_input(struct file *file, void *fh, memset(inp, 0, sizeof(*inp)); inp->index = index; +#ifdef HAVE_STRSCPY + strscpy(inp->name, "loopback", sizeof(inp->name)); +#else strlcpy(inp->name, "loopback", sizeof(inp->name)); +#endif inp->type = V4L2_INPUT_TYPE_CAMERA; inp->audioset = 0; inp->tuner = 0; From b261f19bc74862abb3ad1999bd8e8ba69052b105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 24 Jan 2024 07:45:08 +0100 Subject: [PATCH 121/151] redefine strscpy() as strlcpy() if needed cleaner code that littering with HAVE_STRSCPY --- v4l2loopback.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 8c8f8361..8529007a 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -37,8 +37,8 @@ #error This module is not supported on kernels before 4.0.0. #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) -#define HAVE_STRSCPY +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) +#define strscpy strlcpy #endif #if defined(timer_setup) && defined(from_timer) @@ -905,11 +905,7 @@ static int vidioc_querycap(struct file *file, void *priv, ->device_nr; __u32 capabilities = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; -#ifdef HAVE_STRSCPY strscpy(cap->driver, "v4l2 loopback", sizeof(cap->driver)); -#else - strlcpy(cap->driver, "v4l2 loopback", sizeof(cap->driver)); -#endif snprintf(cap->card, sizeof(cap->card), "%s", dev->card_label); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:v4l2loopback-%03d", device_nr); @@ -1431,11 +1427,7 @@ static int vidioc_enum_output(struct file *file, void *fh, memset(outp, 0, sizeof(*outp)); outp->index = index; -#ifdef HAVE_STRSCPY strscpy(outp->name, "loopback in", sizeof(outp->name)); -#else - strlcpy(outp->name, "loopback in", sizeof(outp->name)); -#endif outp->type = V4L2_OUTPUT_TYPE_ANALOG; outp->audioset = 0; outp->modulator = 0; @@ -1495,11 +1487,7 @@ static int vidioc_enum_input(struct file *file, void *fh, memset(inp, 0, sizeof(*inp)); inp->index = index; -#ifdef HAVE_STRSCPY strscpy(inp->name, "loopback", sizeof(inp->name)); -#else - strlcpy(inp->name, "loopback", sizeof(inp->name)); -#endif inp->type = V4L2_INPUT_TYPE_CAMERA; inp->audioset = 0; inp->tuner = 0; From 5d72c17f92ee0e38efbb7eb85e34443ecbf1a80c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Mon, 12 Feb 2024 14:19:39 +0100 Subject: [PATCH 122/151] Lower minimum width/height to extreme values Closes: https://github.com/umlaeute/v4l2loopback/issues/573 --- v4l2loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 8529007a..5861995e 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -188,8 +188,8 @@ MODULE_PARM_DESC( V4L2LOOPBACK_DEFAULT_EXCLUSIVECAPS) "]"); /* format specifications */ -#define V4L2LOOPBACK_SIZE_MIN_WIDTH 48 -#define V4L2LOOPBACK_SIZE_MIN_HEIGHT 32 +#define V4L2LOOPBACK_SIZE_MIN_WIDTH 2 +#define V4L2LOOPBACK_SIZE_MIN_HEIGHT 1 #define V4L2LOOPBACK_SIZE_DEFAULT_MAX_WIDTH 8192 #define V4L2LOOPBACK_SIZE_DEFAULT_MAX_HEIGHT 8192 From b9a340e6d0d71388303fad9a73e4edc83b563ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 16:45:40 +0100 Subject: [PATCH 123/151] allocate_buffers: fix check whether we can re-allocate Closes: https://github.com/umlaeute/v4l2loopback/issues/553 --- v4l2loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 5861995e..61663d57 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -2393,8 +2393,8 @@ static int allocate_buffers(struct v4l2_loopback_device *dev) if (dev->buffer_size * dev->buffers_number == dev->imagesize) return 0; - /* if there is only one writer, no problem should occur */ - if (dev->open_count.counter == 1) + /* check whether the total number of readers/writers is <=1 */ + if ((dev->ready_for_capture + dev->active_readers) <= 1) free_buffers(dev); else return -EINVAL; From 2f3460045deb77427e09a9efbdfddf3d96fa6ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 17:24:07 +0100 Subject: [PATCH 124/151] Release v0.13.0 --- AUTHORS | 8 + ChangeLog | 557 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 6 + dkms.conf | 2 +- 4 files changed, 572 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 143deef5..6dd2ec13 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,15 +1,18 @@ Aidan Thornton Alex Hu +Alienmaster Anatolij Gutschin Andrii Danyleiko Angus McInnes Anton Novikov Attila Tőkés +Benny Baumann Dmitry Eremin Gavin Qiu George Chriss Gorinich Zmey IOhannes m zmoelnig +Jan-Ralf Meier Javier Infante Jon Morley Joan Bruguera @@ -17,6 +20,7 @@ Kai Kang Kurt Kiefer Michel Promonet Nick Sarnie +Oleksandr Natalenko Paul Brook Ricardo Ribalda Delgado Scott Maines @@ -25,10 +29,14 @@ Tasos Sahanidis Ted Mielczarek Theodore Cipicchio Thomas Hutterer +Tim Gardner +Tobias Stoeckmann Todor Minchev tongdaxu tz Vasily Levin WaleedTageldeen +Wren Turkal wuweixin +You-Sheng Yang Yusuke Ohshima diff --git a/ChangeLog b/ChangeLog index f16ed6c3..35bafe1e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,560 @@ +v4l2loopback (0.13.0) unstable; urgency=medium + + [ IOhannes m zmölnig ] + * Set device_caps in init_vdev() for all kernel-versions (not just >4.7.0) + * Don't set the V4L2_CAP_DEVICE_CAPS on device_caps + * only set (struct video_device).device_caps on linux>=4.7.0 + * pre-processor directives should be left-aligned + * tabify + * Drop obsolete ISSUE_TEMPLATE + * Mention that people should do research before creating a new ticket + * document that we expect people to do some research before creating a ticket + * document that people must have a proper bulid-environment + + [ mpromonet ] + * reset ready_for_capture flag processing VIDIOC_STREAMOFF + + [ IOhannes m zmölnig ] + * redirect people for StackOverflow for general help. + * rule to get the latest&greatest .clang-format for the linux-kernel + * clang-format -i *.c *.h + * fixed whitespace in comments and strings + * use $() rather than `` + * use more frames for setting caps. + * document that GStreamer-1.0 is required (rather than GStreamer-0.10) + * rename _SIZE_MAX_WIDTH to _SIZE_DEFAULT_MAX_WIDTH (same for HEIGHT) + * Add clang-format target + * comment formatting + * switched v4l2_loopback_init() for a more complete v4l2_loopback_add() + * per-device max_width/max_height + * store card-label per device + * copy newly created video-device back + * indentation + * use per-device name rather than the global ones + * switching free_devices() to idr-iterator + * getting rid of static number of devices... + * limit card_label to 32 chars (like v4l2_capability->card) + * make sure to neither exceed size of cap->card nor dev->card_label + * prevent out-of-bounds access to exclusive_caps[] + * less ambiguous wording when MAX_DEVICES is exceeded + * allow device_nr > MAX_DEVICES + * less verbose output + * use video_nr for initial devices + * macro for the default value of EXLCUSIVE_CAPS + * reordered card_label calculation + * fill default-values + * return the used device number + * move v4l2_loopback_config to a separate header + * move IDR declaration to a better place. + * drop devptr from v4l2_loopback_add + * use "int" instead of "bool" for the public interface + * now that exclusive_caps are int and can be negative, use that to ask for the default + * use "err" instead of "ret" + * implement /dev/v4l2loopback to dynamically create/remove devices + * use a mutex to prevent concurrent access to the /dev/v4l2loopback device + * check whether the device is currently open before closing it + * v4l2_loopback_config.card_label now has a fixed size (32 bytes) + * only return the device-number if device_add was successfull + * added QUERY ioctl + * sample application to add/delete/query devices on the fly + * indentation + * moved DEFINE_IDR/MUTEX *again* + * "every" -> "each" + * show default values in "modinfo" + * Makefile for utils/ + * don't assume that IDR matches the device-number + * less verbose + * print perror() if query fails + * explicitely return ENOSYS for invalid ioctls + * use "break" instead of "goto" + * return EINVAL by default for valid ioctls + * copy data from userspace instead of casting away + * note that we need two device numbers + * Add example udev rules. + Thanks to Norman Rasmussen + * formatting + * perror() if query-after-add fails + * only set device is the pointer is valid + * (struct v4l2_loopback_config) now has both output_nr and capture_nr + * adjust utility to (currently defunct) split CAPTURE/OUTPUT devices + * document how to call /dev/v4l2loopback ioctls + * gitignore .clang-format + * don't export repo-configuration + * mention that v4l2_loopback_config.announce_all_caps is to be going away + * more specific rule for creating persistent names + * rule to allow members of 'video' to change device-properties via sysfs + * restructed usage() a bit + * implement "set-fps" + * also format code in utils/ + * moved v4l2loopback version to header file + * capitalize command enums + * added "-h" and "-v" commands + * SETFPS -> SET_FPS + * dropped unused "fd" from set_fps call + + [ You-Sheng Yang ] + * utils: respect CPPFLAGS from env vars + + [ Sakari Tanskanen ] + * Allow reading buffered frames + + [ You-Sheng Yang ] + * dev: initialize per opener v4l2_fh + * event: install event (un)subscribe hook + * event: support polling for events + + [ IOhannes m zmölnig ] + * control more thingies + * whitespace + * Add documentation about using dkms. + Thanks to Toon Verstraelen + + [ Toon Verstraelen ] + * Document how to load the module at boot + + [ IOhannes m zmölnig ] + * lower-case readme + * Usde "If" rather than "When" + + [ IOhannes m zmoelnig ] + * note that you cannot just load an old module into a new kernel (even if you built the module yourself). + + [ ilsi ] + * Fix DKMS typos in README + + [ IOhannes m zmölnig ] + * use a helper for showing short descriptions of commands + * announce 'set-caps' and 'set-timeout-image' + * re-formatted + * allow some commands without a /dev/v4l2loopback device + * "not implemented" is not the same as "unknown command" + * announce the not-yet-implemented "get-fps" and "get-caps" commands + * make commands static when possible + * helper-function to get sysfs files + * specify mode when opening a sysfs-file + * close fd on error + * implement get_fps + * basic get_caps implementation + * store fourcc as uint32_t + * fixed help-display + * rename get_ to open_ + * properly display FOURCC when printing caps + * re-factored caps-parsing into separate function + * implement set_caps + * less verbose + * query default values before setting those we want to + * clang-format + * reverse arguments for set-* commands + * draft implementation of set-timeout-image + * proper ordering of arguments in help + * remove printout + * search for executable in PATH + * comments + * clang-format + * v4l2loopback-ctl: support for adding split-devices + * fixed spelling errors + * v4l2loopback_private.devicenr -> .device_nr + * remove debugging output + * function to check for deprecated calls + * wrap launching of external programs into separate function + * TERM the child-process on receiving SIGINT; wait for child-process to cleanup + * move helpers to top of file + * TODO: we want to be able to set the timeout directly + * helpers for getting/setting controls by name + * have "set-timeout-image" set the 'timeout' and 'timeout_image_io' controls + * allow setting of the timeout (TODO: parse args for timeout) + * fix(?) GStreamer-pipeline for setting the timeout image + * added 'default' clause to switch-statement + * clang-format + * build utils from master-makefile + * factored device-opening into helper-function + * handle "any" caps and set "keep_format" and "sustain_framerate" + * resolve timeout-image to full path (and allow 4096 chars in paths) + * track how many producers have opened the device + * reformatting + * close video-device before trying to set timeout-image + + [ You-Sheng Yang ] + * event: fix backward compatibility to 3.16 + * add GitHub Actions yaml + * utils: use VIDIOC_QUERYCTRL for control name resolution + * utils: fix error: 'for' loop initial declarations are only allowed in C99 mode + + [ IOhannes m zmölnig ] + * improve parse-device to deal with /dev/v4l/by-*/* symlinks + * keep alignment when running clang-format + * moved fallback defines for V4L2_PIX_FMT_* to v4l2loopback_formats.h + * modular help + * set-timeout-image subcommand now takes a "-t " flag + * less verbose + * optional argument-check for called_deprecated() + * check framerate-argument for "set_fps" + * return success of subcommands + * catch empty /sysfs/.../format (but don't complain loudly) + * simplified caps-parsing + * get-fps now falls back to querying the video-device's PARMs + * use C-style comments for documentation + * print error when GStreamer-style caps are detected + * renamed "utils/v4l2loopback" to "utils/v4l2loopback-ctl" + * replaced placeholder file with .gitignore + * fixed help2man invocation + * clean-target for utils + * call utils' "clean" target when running "make clean" + * use /dev/video0 throughough + * update to new v4l2loopback-ctl syntax + * Fixed upper-casing to standard English + * note on changing module options + * note on dynamic device management + * make "changing options" a sub-item in the "options" chapter + * grouping into chapters + * more upper-casing + + [ You-Sheng Yang ] + * ci: match known failures to kernel versions + + [ IOhannes m zmölnig ] + * comment fixes + * ctrl_handler_init/setup + * note on why try_free_buffers() doesn't do harm if keep_format is true + * dropped double "sudo" + + [ You-Sheng Yang ] + * gitignore: add utils/v4l2loopback-ctl and v4l2loopback.mod + + [ IOhannes m zmölnig ] + * automatically assign new-bugreports a default label/assignee + * disable blank issues and redirect people to U&L resp the wiki + * stackoverflow -> unix.stackexchange + * also mention stackoverflow + * use an imperative rather than a noun as the bug-report name. + * pass number of args to help-functions + * print known fourcc flags when calling 'set-caps' without arguments + * clang-format + * editorconfig + + [ Luigi Baldoni ] + * Include header outside of struct definition + + [ Oleksandr Natalenko ] + * confine v4l2loopback_cleanup_module + + [ IOhannes m zmölnig ] + * fixed building of v4l2loopback-ctl in the aftermath of #389 + * doc on how to develop with vagrant + * slight re-ordering of struct-init + * use module_init()/module_exit() rather than '#ifdef MODULE' + * note on what to do if the VM freezes + * drop MODULE_ALIAS("devname") + * add args to vbox-restart + * [ci] don't bail out on the first kernel that unexpectedly fails + * [ci] if a build failed, accept that 'make clean' might fail as well... + * [ci] mark all trusty kernels as known-bad + + [ Piotr Orzechowski ] + * Fix the dkms install instructions in README.md + + [ IOhannes m zmölnig ] + * [ci] renamed 'master' branch to 'main' + * embrace Kbuild + * fixed typo + * use KBUILD_MODULES to detect Kbuild + * [gh] fixes errors in the issue template + * bumped copyright-dates in README + * mention "SSL" in the troubleshooting section + * fixed typos + * more english style fixes + * Fix scanf/printf formatting + + [ Tadayuki Okada ] + * try to set requested colorspace + + [ You-Sheng Yang ] + * coverity: fix unchecked return value + * coverity: fix null pointer dereference + + [ IOhannes m zmölnig ] + * don't fail if allocating 0-sized buffers + + [ a1346054 ] + * use apt-get instead of aptitude + * master branch was renamed to main + * fix shellcheck warnings + * unify codestyle + * fix spelling + * add missing final newline + * trim excess whitespace + * trim trailing whitespace + + [ Emil Velikov ] + * dkms.conf: remove REMAKE_INITRD option + + [ bakedpotato191 ] + * Add module_version macro + + [ Bechir Mghirbi ] + * Adding support for RGBA32 (AB24) + + [ Jim Scarborough ] + * Fix currentversion.sh to return the value + + [ IOhannes m zmölnig ] + * made "currentversion.sh" script POSIX conformant + * [ci] drop 'groovy'; add 'hirsuite' and 'impish' + * [ci] dynamically generate Debian/Ubuntu releases to test + * on Debian kernel-header packages have a different suffix... + * guard V4L2_PIX_FMT_RGBA32 definition... + * [ci] better check for installed kernel headers + * clang format + * [ci] show successfull builds + + [ Anatoli Babenia ] + * README.md Add info how to check the device number + + [ IOhannes m zmölnig ] + * inject snapshot-version in banner + + [ Benny Baumann ] + * Limit v4l2_loopback_write calls to (streaming) writers + * Simplify code by using upstream video_drvdata helper + * Free module data on error + * Initialize max_width/max_height relative to defaults + * Off-by-one condition when retrieving defaults + + [ IOhannes m zmölnig ] + * Make sure that only the boolean state of announce_all_caps is used. + + [ He1nMueck ] + * Make free_buffers() return void. + + [ Benny Baumann ] + * Reintroduce previous behavior for write call + + [ Jan-Ralf Meier ] + * Check for negative index before proceeding + + [ Benny Baumann ] + * Free module data on error + + [ Robert Geislinger ] + * fixed possible bufferoverflow + + [ Alienmaster ] + * simplify bufferoverflow checks + + [ IOhannes m zmölnig ] + * indentation + + [ Alienmaster ] + * added explicit cleanup on error + + [ IOhannes m zmölnig ] + * removed superfluous check + * only allocate_buffers() if buffer_size>0 + + [ Benny Baumann ] + * Release memory on error in v4l2_loopback_open + * Only reset timeout_image_io when open succeeds + * Avoid redundant/conflicting casts + + [ Tobias Stoeckmann ] + * attr_store_maxopener: Handle integer truncation + + [ Benny Baumann ] + * Properly snprintf the device card label string into the capability query buffer + * Avoid format string issue + + [ umläute ] + * Create SECURITY.md + + [ IOhannes m zmölnig ] + * add explicit format specifier to printf() invocations + + [ Sludge ] + * Support `V4L2_PIX_FMT_ABGR32` format + + [ Tobias Stoeckmann ] + * Fix warning with gcc 12 + + [ Jan-Ralf Meier ] + * Fix return condition, due to EINVAL memory offset. + + [ IOhannes m zmölnig ] + * Update release-script to work with any branch + * Exclude repository config in source-tarballs + * Backported dkms-patch from Ubuntu + + [ Oleksandr Natalenko ] + * v4l2loopback: un-confine v4l2loopback_cleanup_module() + + [ IOhannes m zmölnig ] + * quote FOURCC-codes to show the space + + [ Jianhui Dai ] + * Reset V4L2_BUF_FLAG_MAPPED if use_count <= 0 + * log pid for debug + + [ tudor ] + * DKMS section clarification + * too many empty lines + + [ You-Sheng Yang ] + * Track active readers + * Support V4L2_EVENT_PRI_CLIENT_USAGE + + [ Tobias Stoeckmann ] + * Fix signed integer overflows in increments. + + [ Kate Hsuan ] + * v4l2loopback: a new offset for V4L2_EVENT_PRI_CLIENT_USAGE + + [ IOhannes m zmölnig ] + * Allow a minimum of 0 fps + * clang-format + * Propagate snapshot version to v4l2loopback-ctl + * strip leading 'v' from git snapshot version + * v4l2loopback-ctl: compat flag "--version" + * calculate MODULE_VERSION from V4L2LOOPBACK_VERSION + * use linux' __stringify + * re-ordered 'struct v4l2_loopback_config' so "announce_all_caps" comes last + * logging: move "pid(%d)" after the v4l2-loopback identifier + * [ci] use Environment-File rather than set-output + * Drop support for Linux<2.6.37 + * v4l2loopback-ctl: print module version as well + * [ci] Job for code-linting + * [ci] Fixup lint-job + * Re-formatted code with updated .clang-format + * [ci] hardcode Debian's supported releases to their rolling names + + [ sanbrother ] + * Fix compilation issues under AOSP + + [ mzihlmann ] + * mutex lock access to outbufs_list in vidioc_dqbuf + + [ Asahi Lina ] + * Revert "Fix signed integer overflows in increments." + * Change the type of read_position and write_position to 64-bit + + [ IOhannes m zmölnig ] + * Use separate spinlocks for protecting list access + * Make VIDIOC_ENUMINPUT return V4L2_IN_ST_NO_SIGNAL if there's no producer + * code formatting + * Always protect access to dev->outbufs_list with the list_lock mutex + * v4l2loopback-ctl help: use 'detail' level rather than 'brief' flag + * help: reverse general form and example + * codespell fixes + * fix typo + * add 'install' target for utils + * clang-format + * more gitignores + * Bump copyright dates + * simple test application for producing/consuming buffers + * more debugging and a global buffer + * set bufsize/bytesused when initializing buffers for MMAP + * fix random; optionally set timestamp + * try more... + * stuff... + * use DEFAULT colorspace + * set V4L2_BUF_FLAG_TIMESTAMP_COPY flag when copying the buffer timestamp + * more diagnostic output + * refactor the TRY/S_FMT code + * fix formatting warnings when printing timestamps + * compat for older kernels + * more clang-format + * reversed compat logic + * yikes, yet another typo + * tests/consumer: make S_FMT errors non-fatal + * prevent multiple output streams + * set TIMESTAMP flags + * G_FMT_CAP: only report failure if the format has not been fixated + * script to check the output/capture formats of a device + * V4L2LOOBACK_IS_FIXED_FMT() to check if the format is changeable + * unify the output of vidioc_enum_fmt_* + * set default framesize + * allow setting of minimum width/height as well + * v4l2loopback-ctl: allow setting of minimum framesize + * clang-format + * tests/producer: fix description of "-c" flag and linefeed + * long-options for v4l2loopback-ctl + * indentation + * unset the timeout_image_io flag if allocating the timeout-image fails + * only unset the timeout_image_io flag when requesting buffers for the timeout image + * turn the "timeout_image_io" ctrl into a button + * whitespace + * fixate format with "keep_format" + * report format via /sys if it is somehow FIXED + * run timeout-image gst-pipeline through "tee" + * add "--verbose" flag to "set-timeout-image" + * prevent multiple readers to start streaming + * indentation... + * add /sys/devices/virtual/video4linux/video*/type interface + * fix device_nr checks in V4L2LOOPBACK_CTL_QUERY + * v4l2loopback-ctl: add "list" verb + * v4l2loopback-ctl: escape special chars in device-names + * refactored raw-string printout into helper function + * v4l2loopback-ctl: align help + * v4l2loopback-ctl: more escaping for device-name + * v4l2loopback-ctl: add flags to "query" verb + * v4l2loopback-ctl: streamline help + + [ Hans de Goede ] + * v4l2loopback: Fixup bytesused field when writer sends a too large value + + [ IOhannes m zmölnig ] + * fallback to dprintkrw() if dev_warn_ratelimited() is not available + * rename sysfs-attribute "type" to "state" + * make the code less-dependant on the "capture_nr" member of the config-struct + * swap output/capture device when adding new devices + * Remove the 'capture_nr' member from the v4l2_looback_config struct + * build-fixes: install and utils + * force timestamp.tv_sec to (long long int) + + [ Wren Turkal ] + * Remove support for pre-3.6.1 Linux kernels. + * Remove support for pre-4.0.0 kernels. + + [ IOhannes m zmölnig ] + * [github] Mention "discussions" in the issue landing-page + + [ rdbo ] + * fixed utils build for musl (missing GLOB_ONLYDIR) + + [ IOhannes m zmölnig ] + * note that GLOB_ONLYDIR is indeed not requried by POSIX + + [ rdbo ] + * added v4l2loopback-ctl.o to gitignore + + [ IOhannes m zmölnig ] + * Fix formatting + + [ Fufu Fang ] + * Update README.md + + [ IOhannes m zmölnig ] + * [ci] skip failures on kernels without v4l2 support + * [ci] install all available kernel headers + * [ci] show skipped builds + * [ci] Fix testing for v4l2 capabilities + * [ci] bump actions/checkout to v4 + * [ci] lower actions/checkout to v3 + * [ci] only install latest packageversion of each kernel-flavour + * [ci] install 'dkms' to get some more building prerequisites + * some minor typos + * use 'sudo' to change the permissions of the module + * use $< instead of hardcoding the module name + * 'sign' target to sign the generated module for use with secure boot + + [ Joaquim Monteiro ] + * Use strscpy instead of strlcpy if available + + [ IOhannes m zmölnig ] + * redefine strscpy() as strlcpy() if needed + * Lower minimum width/height to extreme values + * allocate_buffers: fix check whether we can re-allocate + + -- IOhannes m zmölnig (Debian/GNU) Tue, 19 Mar 2024 17:11:08 +0100 + v4l2loopback (0.12.7) unstable; urgency=medium [ IOhannes m zmölnig ] diff --git a/NEWS b/NEWS index 8e38a73d..4add1c14 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +v4l2loopback-0.13.0 + + * Dynamic device management + + -- IOhannes m zmölnig (Debian/GNU) Tue, 19 Mar 2024 17:11:08 +0100 + v4l2loopback-0.12.3 * Fixed compat with kernel 5.4 diff --git a/dkms.conf b/dkms.conf index 7d5ed75c..1cbc6db0 100644 --- a/dkms.conf +++ b/dkms.conf @@ -1,5 +1,5 @@ PACKAGE_NAME="v4l2loopback" -PACKAGE_VERSION="0.12.7" +PACKAGE_VERSION="0.13.0" if [ -f $kernel_source_dir/.config ]; then . $kernel_source_dir/.config From 269f623483f0df533bda44de6082de057160e014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 21:46:29 +0100 Subject: [PATCH 125/151] shellchecked 'release.sh' script --- release.sh | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/release.sh b/release.sh index a6e29676..70bb7145 100755 --- a/release.sh +++ b/release.sh @@ -13,7 +13,7 @@ CHANGELOG=ChangeLog AUTHORS=AUTHORS NEWS=NEWS -: ${mainbranch:=main} +: "${mainbranch:=main}" error() { echo "$@" 1>&2 @@ -43,7 +43,7 @@ if [ "$(getgitbranch)" != "${mainbranch}" ]; then fatal "current branch '$(getgitbranch)' is not '${mainbranch}'" fi -if [ "x$2" = "x" ]; then +if [ -z "$2" ]; then ## guess current version NEWVERSION=$1 OLDVERSION=$(getoldversion) @@ -52,28 +52,28 @@ else NEWVERSION=$2 fi -if [ "x${NEWVERSION}" = "x" ]; then - NEWVERSION=$(getmoduleversion) +if [ -z "${NEWVERSION}" ]; then + NEWVERSION="$(getmoduleversion)" fi -if git tag -l v${OLDVERSION} | grep . >/dev/null +if git tag -l "v${OLDVERSION}" | grep . >/dev/null then : else fatal "it seems like there is no tag 'v${OLDVERSION}'" fi -if [ "x${OLDVERSION}" = "x" ]; then +if [ -z "${OLDVERSION}" ]; then usage fi echo "updating from ${OLDVERSION}" -if [ "x${NEWVERSION}" = "x" ]; then +if [ -z "${NEWVERSION}" ]; then usage fi -if dpkg --compare-versions ${OLDVERSION} ge ${NEWVERSION} +if dpkg --compare-versions "${OLDVERSION}" ge "${NEWVERSION}" then fatal "version mismatch: ${NEWVERSION} is not newer than ${OLDVERSION}" fi @@ -86,11 +86,14 @@ cp "${CHANGELOG}" debian/changelog gbp dch -R --since "v${OLDVERSION}" -N "${NEWVERSION}" --debian-branch="${mainbranch}" && cat debian/changelog > "${CHANGELOG}" && OK=true rm -rf debian -if [ "x${OK}" = "xtrue" ]; then +if [ "${OK}" = "true" ]; then sed -e "s|^PACKAGE_VERSION=\".*\"$|PACKAGE_VERSION=\"${NEWVERSION}\"|" -i dkms.conf fi -if [ "x${OK}" = "xtrue" ]; then + + + +if [ "${OK}" = "true" ]; then echo "all went well" echo "" echo "- please check your ${CHANGELOG}" From fa7b9eff53f145e9c853d4e917ed3a5e116cc7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 22:02:59 +0100 Subject: [PATCH 126/151] release-script: fix retrieving of module-version the version is now in v4l2loopback.h --- release.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/release.sh b/release.sh index 70bb7145..85f8b490 100755 --- a/release.sh +++ b/release.sh @@ -35,6 +35,12 @@ getmoduleversion() { -e 's|^[^0-9]*||' -e 's|[^0-9]*$||' \ -e 's|[^0-9][^0-9]*|.|g' } +getmoduleversion_() { + grep "^[[:space:]]*#[[:space:]]*define[[:space:]]*V4L2LOOPBACK_VERSION_$1[[:space:]]" v4l2loopback.h | awk '{print $NF}' +} +getmoduleversion() { + echo "$(getmoduleversion_ MAJOR).$(getmoduleversion_ MINOR).$(getmoduleversion_ BUGFIX)" +} getgitbranch() { git rev-parse --abbrev-ref HEAD } @@ -43,6 +49,8 @@ if [ "$(getgitbranch)" != "${mainbranch}" ]; then fatal "current branch '$(getgitbranch)' is not '${mainbranch}'" fi +moduleversion=$(getmoduleversion) + if [ -z "$2" ]; then ## guess current version NEWVERSION=$1 @@ -53,7 +61,7 @@ else fi if [ -z "${NEWVERSION}" ]; then - NEWVERSION="$(getmoduleversion)" + NEWVERSION="${moduleversion}" fi if git tag -l "v${OLDVERSION}" | grep . >/dev/null From a0af17b70ff164ef0cac40f070b189e0a7d60ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 22:03:19 +0100 Subject: [PATCH 127/151] release-script: write back new version to v4l2loopback.h (if necessary) --- release.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/release.sh b/release.sh index 85f8b490..8de80cdf 100755 --- a/release.sh +++ b/release.sh @@ -88,6 +88,18 @@ fi echo "updating to ${NEWVERSION}" +if [ "${NEWVERSION}" != "${moduleversion}" ]; then + echo "${NEWVERSION}" | sed -e 's|\.| |g' | while read major minor bugfix; do + major=$((major+0)) + minor=$((minor+0)) + bugfix=$((bugfix+0)) + sed -e "s|^\([[:space:]]*#[[:space:]]*define[[:space:]]*V4L2LOOPBACK_VERSION_MAJOR[[:space:]]\).*|\1${major}|" -i v4l2loopback.h + sed -e "s|^\([[:space:]]*#[[:space:]]*define[[:space:]]*V4L2LOOPBACK_VERSION_MINOR[[:space:]]\).*|\1${minor}|" -i v4l2loopback.h + sed -e "s|^\([[:space:]]*#[[:space:]]*define[[:space:]]*V4L2LOOPBACK_VERSION_BUGFIX[[:space:]]\).*|\1${bugfix}|" -i v4l2loopback.h + break + done +fi + OK=false mkdir debian cp "${CHANGELOG}" debian/changelog From 017b0890db38a87f5c3bab20028b4887d9c2597f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 22:09:33 +0100 Subject: [PATCH 128/151] improve printout in release script --- release.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/release.sh b/release.sh index 8de80cdf..cae108ca 100755 --- a/release.sh +++ b/release.sh @@ -64,6 +64,9 @@ if [ -z "${NEWVERSION}" ]; then NEWVERSION="${moduleversion}" fi +echo "module version: ${moduleversion}" + +echo "updating from: ${OLDVERSION}" if git tag -l "v${OLDVERSION}" | grep . >/dev/null then : @@ -75,18 +78,17 @@ if [ -z "${OLDVERSION}" ]; then usage fi -echo "updating from ${OLDVERSION}" if [ -z "${NEWVERSION}" ]; then usage fi +echo "updating to: ${NEWVERSION}" if dpkg --compare-versions "${OLDVERSION}" ge "${NEWVERSION}" then fatal "version mismatch: ${NEWVERSION} is not newer than ${OLDVERSION}" fi -echo "updating to ${NEWVERSION}" if [ "${NEWVERSION}" != "${moduleversion}" ]; then echo "${NEWVERSION}" | sed -e 's|\.| |g' | while read major minor bugfix; do From 3a1b8f578514d04d6f78b308c4d3dfffb6c0e3ca Mon Sep 17 00:00:00 2001 From: Max Harmathy Date: Tue, 19 Mar 2024 19:35:58 +0100 Subject: [PATCH 129/151] Avoid building utils with dkms This changes the dkms configuration to only build the kernel module, not the utils, i.e. v4l2loopback-ctl. --- dkms.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dkms.conf b/dkms.conf index 1cbc6db0..6230d8cf 100644 --- a/dkms.conf +++ b/dkms.conf @@ -16,7 +16,7 @@ if [ -f $kernel_source_dir/.config ]; then fi # Items below here should not have to change with each driver version -MAKE[0]="make KERNEL_DIR=${kernel_source_dir} all" +MAKE[0]="make KERNEL_DIR=${kernel_source_dir} v4l2loopback" CLEAN="make clean" BUILT_MODULE_NAME[0]="$PACKAGE_NAME" From 6e9a90d492ceaaeceb83690588203c90af1d2b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 22:15:20 +0100 Subject: [PATCH 130/151] more shellcheck --- release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.sh b/release.sh index cae108ca..cf378b27 100755 --- a/release.sh +++ b/release.sh @@ -29,7 +29,7 @@ usage() { getoldversion() { dpkg-parsechangelog --count 1 -l${CHANGELOG} | grep -E "^Version:" | head -1 | cut -f2 -d' ' } -getmoduleversion() { +getmoduleversion0() { grep "^#define V4L2LOOPBACK_VERSION_CODE KERNEL_VERSION" v4l2loopback.c \ | sed -e 's|^#define V4L2LOOPBACK_VERSION_CODE KERNEL_VERSION||' \ -e 's|^[^0-9]*||' -e 's|[^0-9]*$||' \ @@ -91,7 +91,7 @@ fi if [ "${NEWVERSION}" != "${moduleversion}" ]; then - echo "${NEWVERSION}" | sed -e 's|\.| |g' | while read major minor bugfix; do + echo "${NEWVERSION}" | sed -e 's|\.| |g' | while read -r major minor bugfix; do major=$((major+0)) minor=$((minor+0)) bugfix=$((bugfix+0)) From 4f99388d491cd5c1155c0df88316bd22aae40d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 22:17:09 +0100 Subject: [PATCH 131/151] Release v0.13.1 --- ChangeLog | 12 ++++++++++++ NEWS | 7 +++++++ dkms.conf | 2 +- v4l2loopback.h | 4 ++-- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 35bafe1e..72d4ff03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +v4l2loopback (0.13.1) unstable; urgency=medium + + [ Max Harmathy ] + * Avoid building utils with dkms + + [ IOhannes m zmölnig ] + * 'release.sh' script + + shellchecked + + write back new version to v4l2loopback.h (if necessary) + + -- IOhannes m zmölnig (Debian/GNU) Tue, 19 Mar 2024 22:15:34 +0100 + v4l2loopback (0.13.0) unstable; urgency=medium [ IOhannes m zmölnig ] diff --git a/NEWS b/NEWS index 4add1c14..dca121f8 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,10 @@ +v4l2loopback-0.13.1 + + * Avoid building utils with dkms + * Fix version number in module + + -- IOhannes m zmölnig (Debian/GNU) Tue, 19 Mar 2024 22:15:34 +0100 + v4l2loopback-0.13.0 * Dynamic device management diff --git a/dkms.conf b/dkms.conf index 6230d8cf..b6405054 100644 --- a/dkms.conf +++ b/dkms.conf @@ -1,5 +1,5 @@ PACKAGE_NAME="v4l2loopback" -PACKAGE_VERSION="0.13.0" +PACKAGE_VERSION="0.13.1" if [ -f $kernel_source_dir/.config ]; then . $kernel_source_dir/.config diff --git a/v4l2loopback.h b/v4l2loopback.h index 18f2f376..1bc7e6b7 100644 --- a/v4l2loopback.h +++ b/v4l2loopback.h @@ -11,8 +11,8 @@ #define _V4L2LOOPBACK_H #define V4L2LOOPBACK_VERSION_MAJOR 0 -#define V4L2LOOPBACK_VERSION_MINOR 12 -#define V4L2LOOPBACK_VERSION_BUGFIX 7 +#define V4L2LOOPBACK_VERSION_MINOR 13 +#define V4L2LOOPBACK_VERSION_BUGFIX 1 /* /dev/v4l2loopback interface */ From cb9e676b46623bfb37f08944a60b1ecb7f20db76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 19 Mar 2024 22:30:35 +0100 Subject: [PATCH 132/151] Don't fail 'clean' target if 'make -C utils clean' fails --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b76846d1..89ea12fb 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ clean: rm -f *~ rm -f Module.symvers Module.markers modules.order $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean - $(MAKE) -C utils clean + -$(MAKE) -C utils clean distclean: clean rm -f man/v4l2loopback-ctl.1 From f72315ce266a80207a81dc31465e91e7a427ba57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 12:14:16 +0100 Subject: [PATCH 133/151] explicitly return the fd from open_controldevice() why is the compiler not emitting a warning? --- utils/v4l2loopback-ctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 618f2a56..7f6934aa 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -770,6 +770,7 @@ static int open_controldevice() perror("unable to open control device '" CONTROLDEVICE "'"); exit(1); } + return fd; } static int open_sysfs_file(const char *devicename, const char *filename, From 89e87edff7c177c87e2c7262bdceb29b8fed66a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 12:16:39 +0100 Subject: [PATCH 134/151] utils: clean objectfiles --- utils/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/Makefile b/utils/Makefile index 8261cd38..99c68620 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -18,6 +18,7 @@ default: $(programs) clean: -rm $(programs) + -rm $(programs:%=%.o) install: $(MKDIR_P) $(DESTDIR)$(bindir) From 1adad61a9982802a8ee53431ef2768ec000b15b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 12:29:59 +0100 Subject: [PATCH 135/151] v4l2loopback-ctl: return errcodes where appropriate --- utils/v4l2loopback-ctl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 7f6934aa..c1886097 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -1056,6 +1056,7 @@ static int get_caps(const char *devicename) static int set_timeoutimage(const char *devicename, const char *imagefile, int timeout, int verbose) { + int err=0; int fd = -1; char imagearg[4096], imagefile2[4096], devicearg[4096]; char *args[] = { "gst-launch-1.0", @@ -1094,6 +1095,8 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, devicename); set_control_i(fd, "timeout_image_io", 1); close(fd); + } else { + err = errno; } if (verbose > 1) { @@ -1111,6 +1114,7 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, "v======================================================================v\n"); if (my_execv(args)) { dprintf(2, "ERROR: setting time-out image failed\n"); + err = 1; } dprintf(2, "^======================================================================^\n"); @@ -1133,7 +1137,10 @@ static int set_timeoutimage(const char *devicename, const char *imagefile, } close(fd); + } else { + err = errno; } + return err; } static t_command get_command(const char *command) From 7b835d0738e34701ba07d851cf3480a831f27344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 12:45:03 +0100 Subject: [PATCH 136/151] avoid unused variable warning --- utils/v4l2loopback-ctl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index c1886097..e8d6a168 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -102,19 +102,20 @@ void exec_cleanup(int signal) static int my_execv(char *const *cmdline) { char exe[1024]; - //pid_t pid; int res = 0; - char *const *argp = cmdline; if (!which(exe, 1024, cmdline[0])) { dprintf(2, "cannot find %s - is it installed???\n", cmdline[0]); return 1; } #if 0 - dprintf(2, "%s:", exe); - while (*argp) { - dprintf(2, " %s", *argp++); - }; - dprintf(2, "\n"); + do { + char *const *argp = cmdline; + dprintf(2, "%s:", exe); + while (*argp) { + dprintf(2, " %s", *argp++); + }; + dprintf(2, "\n"); + } while(0); #endif pid = fork(); @@ -1239,7 +1240,6 @@ static int do_defaultargs(const char *progname, t_command cmd, int argc, int main(int argc, char **argv) { const char *progname = argv[0]; - const char *cmdname; int i; int fd = -1; int verbose = 0; From b05a125e81984b9e4f9fc05acb61069e0f57b80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 12:45:44 +0100 Subject: [PATCH 137/151] improve error-reporting --- utils/v4l2loopback-ctl.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index e8d6a168..97e67ab0 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -592,12 +592,14 @@ make_conf(struct v4l2_loopback_config *cfg, const char *label, int min_width, static int add_device(int fd, struct v4l2_loopback_config *cfg, int verbose) { + int err = 0; MARK(); int ret = ioctl(fd, V4L2LOOPBACK_CTL_ADD, cfg); MARK(); if (ret < 0) { + err = errno; perror("failed to create device"); - return 1; + return err; } MARK(); @@ -612,26 +614,31 @@ static int add_device(int fd, struct v4l2_loopback_config *cfg, int verbose) config.capture_nr = ret; #endif ret = ioctl(fd, V4L2LOOPBACK_CTL_QUERY, &config); - if (!ret) + if (ret < 0) { + err = errno; perror("failed querying newly added device"); + } MARK(); print_conf(&config, 0); MARK(); } - return (!ret); + return err; } static int delete_device(int fd, const char *devicename) { + int err = 0; int dev = parse_device(devicename); if (dev < 0) { dprintf(2, "ignoring illegal devicename '%s'\n", devicename); return 1; } - if (ioctl(fd, V4L2LOOPBACK_CTL_REMOVE, dev) < 0) + if (ioctl(fd, V4L2LOOPBACK_CTL_REMOVE, dev) < 0) { + err = errno; perror(devicename); + } - return 0; + return err; } static int query_device(int fd, const char *devicename, int escape) @@ -912,8 +919,10 @@ static int get_fps(const char *devicename) /* get the framerate via ctls */ fd = open_videodevice(devicename, O_RDWR); - if (fd < 0) + if (fd < 0) { + ret = 1; goto done; + } memset(¶m, 0, sizeof(param)); param.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; @@ -1430,9 +1439,10 @@ int main(int argc, char **argv) usage_topic(progname, cmd, argc, argv); fd = open_controldevice(); for (i = 0; i < argc; i++) { - ret += (delete_device(fd, argv[i]) != 0); + int err = delete_device(fd, argv[i]); + if(err) + ret = err; } - ret = (ret > 0); break; case QUERY: for (;;) { From 8f264d74518f2b5eff515ecba343586b1dcc5f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 12:57:54 +0100 Subject: [PATCH 138/151] mention the special caps 'any' --- utils/v4l2loopback-ctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 97e67ab0..5bd13e30 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -395,6 +395,7 @@ static void help_setcaps(const char *program, int detail, int argc, char **argv) dprintf(2, "\n \teither specify a device name (e.g. '/dev/video1') or a device number ('1')." "\n \tformat specification as ':x@' (e.g. 'UYVY:1024x768@60/1')" + "\n \tunset the current caps with the special value 'any'" "\n"); if (detail > 1) { dprintf(2, "\nknown fourcc-codes" From c94a1e899bf5a5b952130395018686cd780848d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 13:04:09 +0100 Subject: [PATCH 139/151] clang-format --- utils/v4l2loopback-ctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/v4l2loopback-ctl.c b/utils/v4l2loopback-ctl.c index 5bd13e30..7fe8cd9f 100644 --- a/utils/v4l2loopback-ctl.c +++ b/utils/v4l2loopback-ctl.c @@ -1067,7 +1067,7 @@ static int get_caps(const char *devicename) static int set_timeoutimage(const char *devicename, const char *imagefile, int timeout, int verbose) { - int err=0; + int err = 0; int fd = -1; char imagearg[4096], imagefile2[4096], devicearg[4096]; char *args[] = { "gst-launch-1.0", @@ -1441,7 +1441,7 @@ int main(int argc, char **argv) fd = open_controldevice(); for (i = 0; i < argc; i++) { int err = delete_device(fd, argv[i]); - if(err) + if (err) ret = err; } break; From 60be7c3f9734ab2ac87938eb15d551af18ca7870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 14:08:07 +0100 Subject: [PATCH 140/151] Replace (s64 % u32) with our own v4l2l_mod64() wrapper Closes: https://github.com/umlaeute/v4l2loopback/issues/550 Closes: https://github.com/umlaeute/v4l2loopback/issues/567 --- v4l2loopback.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 61663d57..8e545d06 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -106,6 +106,25 @@ static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; } +#if BITS_PER_LONG == 32 +#include /* do_div() for 64bit division */ +static inline int v4l2l_mod64(const s64 A, const u32 B) +{ + s64 a = A; + u32 b = B; + + if (a > 0) + return do_div(a, b); + a = -a; + return -do_div(a, b); +} +#else +static inline int v4l2l_mod64(const s64 A, const u32 B) +{ + return A % B; +} +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) typedef unsigned __poll_t; #endif @@ -1598,7 +1617,7 @@ static int vidioc_reqbufs(struct file *file, void *fh, /* after we update dev->used_buffers, buffers in outbufs_list will * correspond to dev->write_position + [0;b->count-1] range */ - i = dev->write_position % b->count; + i = v4l2l_mod64(dev->write_position, b->count); list_for_each_entry(pos, &dev->outbufs_list, list_head) { dev->bufpos2index[i % b->count] = @@ -1671,7 +1690,7 @@ static void buffer_written(struct v4l2_loopback_device *dev, spin_unlock_bh(&dev->list_lock); spin_lock_bh(&dev->lock); - dev->bufpos2index[dev->write_position % dev->used_buffers] = + dev->bufpos2index[v4l2l_mod64(dev->write_position, dev->used_buffers)] = buf->buffer.index; ++dev->write_position; dev->reread_count = 0; @@ -1791,14 +1810,14 @@ static int get_capture_buffer(struct file *file) if (dev->reread_count > opener->reread_count + 2) opener->reread_count = dev->reread_count - 1; ++opener->reread_count; - pos = (opener->read_position + dev->used_buffers - 1) % - dev->used_buffers; + pos = v4l2l_mod64(opener->read_position + dev->used_buffers - 1, + dev->used_buffers); } else { opener->reread_count = 0; if (dev->write_position > opener->read_position + dev->used_buffers) opener->read_position = dev->write_position - 1; - pos = opener->read_position % dev->used_buffers; + pos = v4l2l_mod64(opener->read_position, dev->used_buffers); ++opener->read_position; } timeout_happened = dev->timeout_happened; @@ -2324,7 +2343,7 @@ static ssize_t v4l2_loopback_write(struct file *file, const char __user *buf, if (count > dev->buffer_size) count = dev->buffer_size; - write_index = dev->write_position % dev->used_buffers; + write_index = v4l2l_mod64(dev->write_position, dev->used_buffers); b = &dev->buffers[write_index].buffer; if (copy_from_user((void *)(dev->image + b->m.offset), (void *)buf, From 201462733ec4dbfd30f31e173e09152ada306329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 21 Mar 2024 15:14:50 +0100 Subject: [PATCH 141/151] fix mod64 warnings on arm --- v4l2loopback.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v4l2loopback.c b/v4l2loopback.c index 8e545d06..25cb1beb 100644 --- a/v4l2loopback.c +++ b/v4l2loopback.c @@ -110,12 +110,12 @@ static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) #include /* do_div() for 64bit division */ static inline int v4l2l_mod64(const s64 A, const u32 B) { - s64 a = A; + u64 a = (u64)A; u32 b = B; - if (a > 0) + if (A > 0) return do_div(a, b); - a = -a; + a = -A; return -do_div(a, b); } #else From a2e60b507b0943e4989a5a3c40536e16411bd666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Mon, 8 Apr 2024 10:36:44 +0200 Subject: [PATCH 142/151] [github] YML header --- .github/ISSUE_TEMPLATE/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c22b746d..b5ea6336 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,3 +1,4 @@ +--- blank_issues_enabled: false contact_links: - name: Ask a Question From 61848a0eecc761984adf362deea8319251e069e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Mon, 8 Apr 2024 10:37:39 +0200 Subject: [PATCH 143/151] [github] replace bug tempate with form --- .github/ISSUE_TEMPLATE/bug.yml | 91 ++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 101 --------------------------- 2 files changed, 91 insertions(+), 101 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 00000000..c2639571 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,91 @@ +--- +name: Report a bug +about: Create a report to help us improve (but make sure to read all the documentation first) +title: '' +labels: 'needs triage' +assignees: 'umlaeute' +body: + - type: markdown + attributes: + value: | + Thanks for your feedback. It is invaluable for making `v4l2loopback` a better software. + + To help us making the most of your feedback (so we can e.g. fix bugs more quickly), please make sure to provide the information requested in this template. + Also make sure to remove any non-relevant parts (so we can focus on the essential problem). + + Please keep in mind that the development of `v4l2loopback` is done by volunteers. + They are spending their spare time to give you a hopefully nice product and to help you if you have troubles - for free. + + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an (open or closed) issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true + + - id: details + type: textarea + attributes: + label: Detail + description: What went wrong? + placeholder: | + What did you do (step by step)? what did you expect? what happened? + render: markdown + validations: + required: true + + - id: version + type: input + attributes: + label: v4l2loopback version + description: | + What is the exact version of v4l2loopback you are using? (e.g. as reported by `sudo dmesg | grep -i "v4l2loopback driver version"` or `git describe`) + placeholder: | + e.g. v0.13.1-10-g2014627 + validations: + required: true + + - id: kernelversion + type: input + attributes: + label: kernel version + description: | + What is the exact version of the linux kernel you are using? (reported via `uname -a`) + placeholder: | + e.g. Linux umlautT 6.7.9-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.7.9-2 (2024-03-13) x86_64 GNU/Linux + validations: + required: true + + + # - type: dropdown + # id: OS + # attributes: + # label: Operating System + # description: Which OS are you using? + # multiple: true + # options: + # - Linux + # - macOS + # - Windows + # - other + - id: osversion + type: input + attributes: + label: OS Version + description: | + Which version of the Operating System are you using? + placeholder: | + e.g. Ubuntu/4.10 "Warty" + + - id: arch + type: dropdown + attributes: + label: Which CPU are you using? + multiple: true + options: + - amd64/x86_64 ("64bit Intel") + - i386 ("32bit Intel") + - arm64 ("64bit ARM"; e.g. Apple Silicon,...) + - arm ("32bit ARM"; e.g. Raspberry Pi,...) + - other diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index a77e3c51..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -name: Report a bug -about: Create a report to help us improve (but make sure to read all the documentation first) -title: '' -labels: 'needs triage' -assignees: 'umlaeute' - ---- - -### Step 1: Read this - -Thanks for your feedback. It is invaluable for making `v4l2loopback` a better software. - -To help us making the most of your feedback (so we can e.g. fix bugs more quickly), please make sure to provide the information requested in this template. -Also make sure to remove any non-relevant parts (so we can focus on the essential problem). - -Please keep in mind that the development of `v4l2loopback` is done by volunteers. -They are spending their spare time to give you a hopefully nice product and to help you if you have troubles - for free. - -Please look through the list of issues (*open* and **closed** ones alike), to see whether you problem has been reported before. Probably you can find a solution to your problem without having to create a new ticket. - -#### Seeking Help? Looking for Support? -This issue tracker is meant to track specific bugs in the code (and new features). -However, it is ill-suited as a user support forum. - -If you have general questions or problems, please use the `v4l2loopback` tag on Stack Overflow instead: -https://stackoverflow.com/questions/tagged/v4l2loopback - - -#### Remove Cruft - -Please *remove* these instructions (and other non-relevant information) from your report. -If your report looks like a copy of the template, it might get closed immediately. - -#### Title -Please chose an appropriate title: "*does not work*" or "*found a bug*" are way too generic. -Try to find a one-liner that says what is not working (e.g. "*module fails to load*"). - -Also try not to use relative terms. -E.g. "*fails to build with latest kernel*" is bad, because the latest kernel at the time you create the bug report might not be the latest kernel when the problem is being worked on. - -#### Accessibility -Sometimes pictures say more. -However, mostly they prevent the use of advanced tools (like "search" or "copy&paste"). -And always they prevent people who don't use graphical browser to access the tracker from reading your content. -So, to make the web a better place, we ask you to post *text* rather than *screenshots of text* whenever feasible (pretty much always). - - -#### Do some research first - -Please read the documentation carefully: while it is not very long, it does -contain some information that might help fixing your problem. -If you have trouble compiling the kernel module, double-read the 'DEPENDENCIES' -section in the README.md. - -Also make sure to check the issue tracker. -Maybe somebody already reported your problem? Maybe they found a workaround that -could help you. -If you found that somebody already reported your problem, it's *much* better to -add additional information to that report than opening a new one. -If your entire problem has been described and there's nothing to add for you, -consider voting the issue up (using the :+1: emoji; please avoid posts that only -say "me too"). - - - -### Step 2: Describe your environment - - * `v4l2loopback` version: _____ - - sudo dmesg | grep -i v4l2loopback - - * kernel version: _____ - - uname -a - - * Distribution (+version): _____ - - lsb_release -a - -### Step 3: Describe the problem: - -#### Steps to reproduce: - - 1. _____ - 2. _____ - 3. _____ - -#### Observed Results: - - * What happened? This could be a description, log output, etc. - -#### Expected Results: - - * What did you expect to happen? - -#### Relevant Code: - - ``` - // TODO(you): code here to reproduce the problem - ``` From a8e46c61d2281fb330af5bcfa3f91ef745854fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Mon, 8 Apr 2024 10:39:29 +0200 Subject: [PATCH 144/151] [github] add 'title' to form template --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index c2639571..1ffdd26c 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,7 +1,7 @@ --- name: Report a bug about: Create a report to help us improve (but make sure to read all the documentation first) -title: '' +title: "[Bug]: " labels: 'needs triage' assignees: 'umlaeute' body: From 881a9553c95707bda52d3acb4546a83dde91e5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= <zmoelnig@umlautT.umlaeute.mur.at> Date: Mon, 8 Apr 2024 10:40:21 +0200 Subject: [PATCH 145/151] [github] about->description --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 1ffdd26c..1071fb86 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,6 +1,6 @@ --- name: Report a bug -about: Create a report to help us improve (but make sure to read all the documentation first) +description: Create a report to help us improve (but make sure to read all the documentation first) title: "[Bug]: <title>" labels: 'needs triage' assignees: 'umlaeute' From 4ade9c01470d9fbff012daf623d4032d0788d394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= <zmoelnig@umlautT.umlaeute.mur.at> Date: Mon, 8 Apr 2024 10:53:49 +0200 Subject: [PATCH 146/151] [gitub] improve bug-description --- .github/ISSUE_TEMPLATE/bug.yml | 41 +++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 1071fb86..1cf591e4 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -8,18 +8,29 @@ body: - type: markdown attributes: value: | - Thanks for your feedback. It is invaluable for making `v4l2loopback` a better software. + Thanks for your feedback. It is invaluable for making `v4l2loopback` a + better software. - To help us making the most of your feedback (so we can e.g. fix bugs more quickly), please make sure to provide the information requested in this template. - Also make sure to remove any non-relevant parts (so we can focus on the essential problem). + To help us making the most of your feedback (so we can e.g. fix bugs + more quickly), please make sure to provide the information requested + in this template. - Please keep in mind that the development of `v4l2loopback` is done by volunteers. - They are spending their spare time to give you a hopefully nice product and to help you if you have troubles - for free. + Please keep in mind that the development of `v4l2loopback` is done by + volunteers. + They are spending their spare time to give you a hopefully nice product + and to help you if you have troubles - for free. + + If you are experiencing interoperability problems with a commercial + product, contact *their* support first. - type: checkboxes attributes: label: Is there an existing issue for this? - description: Please search to see if an (open or closed) issue already exists for the bug you encountered. + description: | + Please search to see if an (open or closed) issue already exists for + the bug you encountered. + If so, consider adding more information to the existing issue, rather + than creating a new one. options: - label: I have searched the existing issues required: true @@ -28,9 +39,13 @@ body: type: textarea attributes: label: Detail - description: What went wrong? + description: | + What went wrong? + If you are using additional software (e.g. `obs-studio`, `ffmpeg`, ...) + please make sure to include full version information (and - if it is + not a very common product - a link to their webpage). placeholder: | - What did you do (step by step)? what did you expect? what happened? + What did you do (step by step)? What did you expect? What happened? render: markdown validations: required: true @@ -40,7 +55,9 @@ body: attributes: label: v4l2loopback version description: | - What is the exact version of v4l2loopback you are using? (e.g. as reported by `sudo dmesg | grep -i "v4l2loopback driver version"` or `git describe`) + What is the exact version of v4l2loopback you are using? (e.g. as + reported by `sudo dmesg | grep -i "v4l2loopback driver version"` + or `git describe`) placeholder: | e.g. v0.13.1-10-g2014627 validations: @@ -51,7 +68,8 @@ body: attributes: label: kernel version description: | - What is the exact version of the linux kernel you are using? (reported via `uname -a`) + What is the exact version of the linux kernel you are using? (reported + via `uname -a`) placeholder: | e.g. Linux umlautT 6.7.9-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.7.9-2 (2024-03-13) x86_64 GNU/Linux validations: @@ -74,7 +92,8 @@ body: attributes: label: OS Version description: | - Which version of the Operating System are you using? + Which distribution/version are you using? + (numeric versions are much preferred over codenames) placeholder: | e.g. Ubuntu/4.10 "Warty" From ee0347c01a31850a9e49f1f82826dcd995afea3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= <zmoelnig@umlautT.umlaeute.mur.at> Date: Mon, 8 Apr 2024 10:55:57 +0200 Subject: [PATCH 147/151] [github] unbreak lines --- .github/ISSUE_TEMPLATE/bug.yml | 36 +++++++++++----------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 1cf591e4..87307058 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -8,29 +8,21 @@ body: - type: markdown attributes: value: | - Thanks for your feedback. It is invaluable for making `v4l2loopback` a - better software. + Thanks for your feedback. It is invaluable for making `v4l2loopback` a better software. - To help us making the most of your feedback (so we can e.g. fix bugs - more quickly), please make sure to provide the information requested - in this template. + To help us making the most of your feedback (so we can e.g. fix bugs more quickly), please make sure to provide the information requested in this template. - Please keep in mind that the development of `v4l2loopback` is done by - volunteers. - They are spending their spare time to give you a hopefully nice product - and to help you if you have troubles - for free. + Please keep in mind that the development of `v4l2loopback` is done by volunteers. + They are spending their spare time to give you a hopefully nice product and to help you if you have troubles - for free. - If you are experiencing interoperability problems with a commercial - product, contact *their* support first. + If you are experiencing interoperability problems with a commercial product, contact *their* support first. - type: checkboxes attributes: label: Is there an existing issue for this? description: | - Please search to see if an (open or closed) issue already exists for - the bug you encountered. - If so, consider adding more information to the existing issue, rather - than creating a new one. + Please search to see if an (open or closed) issue already exists for the bug you encountered. + If so, consider adding more information to the existing issue, rather than creating a new one. options: - label: I have searched the existing issues required: true @@ -41,9 +33,7 @@ body: label: Detail description: | What went wrong? - If you are using additional software (e.g. `obs-studio`, `ffmpeg`, ...) - please make sure to include full version information (and - if it is - not a very common product - a link to their webpage). + If you are using additional software (e.g. `obs-studio`, `ffmpeg`, ...) please make sure to include full version information (and - if it is not a very common product - a link to their webpage). placeholder: | What did you do (step by step)? What did you expect? What happened? render: markdown @@ -55,9 +45,7 @@ body: attributes: label: v4l2loopback version description: | - What is the exact version of v4l2loopback you are using? (e.g. as - reported by `sudo dmesg | grep -i "v4l2loopback driver version"` - or `git describe`) + What is the exact version of v4l2loopback you are using? (e.g. as reported by `sudo dmesg | grep -i "v4l2loopback driver version"` or `git describe`) placeholder: | e.g. v0.13.1-10-g2014627 validations: @@ -68,8 +56,7 @@ body: attributes: label: kernel version description: | - What is the exact version of the linux kernel you are using? (reported - via `uname -a`) + What is the exact version of the linux kernel you are using? (reported via `uname -a`) placeholder: | e.g. Linux umlautT 6.7.9-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.7.9-2 (2024-03-13) x86_64 GNU/Linux validations: @@ -92,8 +79,7 @@ body: attributes: label: OS Version description: | - Which distribution/version are you using? - (numeric versions are much preferred over codenames) + Which distribution/version are you using? (numeric versions are much preferred over codenames) placeholder: | e.g. Ubuntu/4.10 "Warty" From 5f24d9d25e0a6dad1d639eb05905b7da84b55072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= <zmoelnig@umlautT.umlaeute.mur.at> Date: Mon, 8 Apr 2024 11:00:38 +0200 Subject: [PATCH 148/151] [github] add 'bug' label --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 87307058..8f5b061e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -2,7 +2,7 @@ name: Report a bug description: Create a report to help us improve (but make sure to read all the documentation first) title: "[Bug]: <title>" -labels: 'needs triage' +labels: ['needs triage','kind:bug'] assignees: 'umlaeute' body: - type: markdown From d8e0c4d2090805e1e5ff7222cdaadeffff84ac38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= <zmoelnig@umlautT.umlaeute.mur.at> Date: Mon, 8 Apr 2024 11:03:57 +0200 Subject: [PATCH 149/151] [github] add feature-request template --- .github/ISSUE_TEMPLATE/feature_request.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..5b207002 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,8 @@ +--- +name: Request a new feature +about: Suggest some new great functionality (but make sure to read all the documentation first). +title: '' +labels: ['needs triage', 'kind: feature request'] +assignees: 'umlaeute' + +--- From 48245383f12e3c9e8ac0b28bc39e2255a257a049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= <zmoelnig@umlautT.umlaeute.mur.at> Date: Mon, 8 Apr 2024 11:06:24 +0200 Subject: [PATCH 150/151] [github] trailing dot --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 8f5b061e..927dc02e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,6 +1,6 @@ --- name: Report a bug -description: Create a report to help us improve (but make sure to read all the documentation first) +description: Create a report to help us improve (but make sure to read all the documentation first). title: "[Bug]: <title>" labels: ['needs triage','kind:bug'] assignees: 'umlaeute' From 2d44c2f3a33844dfd9928dc536288283289bbc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= <zmoelnig@umlautT.umlaeute.mur.at> Date: Fri, 24 May 2024 11:35:43 +0200 Subject: [PATCH 151/151] Released 0.13.2 --- ChangeLog | 16 ++++++++++++++++ NEWS | 6 ++++++ dkms.conf | 2 +- v4l2loopback.h | 2 +- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 72d4ff03..3fb6dcec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +v4l2loopback (0.13.2) unstable; urgency=medium + + * Fix compilation on 32bit architectures (i386, armhf,...) + * Replace (s64 % u32) with our own v4l2l_mod64() wrapper + * Fix mod64 warnings on arm + * utils buildsystem + + Don't fail 'clean' target if 'make -C utils clean' fails + + clean objectfiles + * v4l2loopback-ctl + + mention the special caps 'any' in help + + improve error-reporting and return errcodes where appropriate + + avoid unused variable warning + + explicitly return the fd from open_controldevice() + + -- IOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org> Fri, 24 May 2024 11:30:51 +0200 + v4l2loopback (0.13.1) unstable; urgency=medium [ Max Harmathy ] diff --git a/NEWS b/NEWS index dca121f8..1071fef0 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +v4l2loopback-0.13.2 + + * Fix compilation on 32bit architectures (i386, armhf,...) + + -- IOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org> Fri, 24 May 2024 11:30:51 +0200 + v4l2loopback-0.13.1 * Avoid building utils with dkms diff --git a/dkms.conf b/dkms.conf index b6405054..8deacf98 100644 --- a/dkms.conf +++ b/dkms.conf @@ -1,5 +1,5 @@ PACKAGE_NAME="v4l2loopback" -PACKAGE_VERSION="0.13.1" +PACKAGE_VERSION="0.13.2" if [ -f $kernel_source_dir/.config ]; then . $kernel_source_dir/.config diff --git a/v4l2loopback.h b/v4l2loopback.h index 1bc7e6b7..48b9d988 100644 --- a/v4l2loopback.h +++ b/v4l2loopback.h @@ -12,7 +12,7 @@ #define V4L2LOOPBACK_VERSION_MAJOR 0 #define V4L2LOOPBACK_VERSION_MINOR 13 -#define V4L2LOOPBACK_VERSION_BUGFIX 1 +#define V4L2LOOPBACK_VERSION_BUGFIX 2 /* /dev/v4l2loopback interface */