diff -Nru v4l2loopback-0.12.5/debian/changelog v4l2loopback-0.12.5/debian/changelog --- v4l2loopback-0.12.5/debian/changelog 2020-04-22 22:36:06.000000000 +0800 +++ v4l2loopback-0.12.5/debian/changelog 2021-03-29 17:02:42.000000000 +0800 @@ -1,3 +1,19 @@ +v4l2loopback (0.12.5-1ubuntu1) hirsute; urgency=medium + + * Support client usage notification via V4l2 Event API (LP: #1921474) + - dev: initialize per opener v4l2_fh + - event: install event (un)subscribe hook + - event: support polling for events + - event: fix backward compatibility to 3.16 + - UBUNTU: SAUCE: track active readers + - UBUNTU: SAUCE: event: support V4L2_EVENT_PRI_CLIENT_USAGE + - compliance: stop declaring V4L2_CAP_VIDEO_M2M capability + - compliance: fix vidioc_enum_frameintervals in output side + - compliance: fix enum frame sizes/intervals errors + - compliance: fix "fmtdesc.type was modified" error + + -- You-Sheng Yang Mon, 29 Mar 2021 17:02:42 +0800 + v4l2loopback (0.12.5-1) unstable; urgency=medium * New upstream version 0.12.5 diff -Nru v4l2loopback-0.12.5/debian/control v4l2loopback-0.12.5/debian/control --- v4l2loopback-0.12.5/debian/control 2020-04-22 22:36:06.000000000 +0800 +++ v4l2loopback-0.12.5/debian/control 2021-03-29 17:02:42.000000000 +0800 @@ -1,7 +1,8 @@ Source: v4l2loopback Section: kernel Priority: optional -Maintainer: IOhannes m zmölnig (Debian/GNU) +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: IOhannes m zmölnig (Debian/GNU) Build-Depends: debhelper-compat (= 12), licensecheck, help2man, diff -Nru v4l2loopback-0.12.5/debian/patches/0001-dev-initialize-per-opener-v4l2_fh.patch v4l2loopback-0.12.5/debian/patches/0001-dev-initialize-per-opener-v4l2_fh.patch --- v4l2loopback-0.12.5/debian/patches/0001-dev-initialize-per-opener-v4l2_fh.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0001-dev-initialize-per-opener-v4l2_fh.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,216 @@ +From: You-Sheng Yang +Date: Thu, 13 Aug 2020 23:19:39 +0800 +Subject: dev: initialize per opener v4l2_fh + +When v4l2_fh_init() is called, kernel V4L2 subsystem would assume +file->private_data is a pointer to a v4l2_fh instance. Follow this and +update corresponding references with a `fh_to_opener` macro. + +Signed-off-by: You-Sheng Yang +(cherry picked from commit b39a091f473c3bc2aea275e2b3a43713a591bce7) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 49 +++++++++++++++++++++++++++++++------------------ + 1 file changed, 31 insertions(+), 18 deletions(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index bcf7667..16ecd09 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -381,8 +381,12 @@ struct v4l2_loopback_opener { + struct v4l2_buffer *buffers; + int buffers_number; /* should not be big, 4 is a good choice */ + int timeout_image_io; ++ ++ struct v4l2_fh fh; + }; + ++#define fh_to_opener(ptr) container_of((ptr), struct v4l2_loopback_opener, fh) ++ + /* this is heavily inspired by the bttv driver found in the linux kernel */ + struct v4l2l_format { + char *name; +@@ -753,7 +757,7 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsi + static int vidioc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *argp) + { + struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); +- struct v4l2_loopback_opener *opener = file->private_data; ++ struct v4l2_loopback_opener *opener = fh_to_opener(fh); + + if (dev->ready_for_capture) { + if (opener->vidioc_enum_frameintervals_calls > 0) +@@ -1089,7 +1093,7 @@ static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm * + * added to support effecttv + * called on VIDIOC_S_STD + */ +-static int vidioc_s_std(struct file *file, void *private_data, v4l2_std_id *_std) ++static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *_std) + { + v4l2_std_id req_std = 0, supported_std = 0; + const v4l2_std_id all_std = V4L2_STD_ALL, no_std = 0; +@@ -1111,7 +1115,7 @@ static int vidioc_s_std(struct file *file, void *private_data, v4l2_std_id *_std + /* gets a fake video standard + * called on VIDIOC_G_STD + */ +-static int vidioc_g_std(struct file *file, void *private_data, v4l2_std_id *norm) ++static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) + { + if (norm) + *norm = V4L2_STD_ALL; +@@ -1120,7 +1124,7 @@ static int vidioc_g_std(struct file *file, void *private_data, v4l2_std_id *norm + /* gets a fake video standard + * called on VIDIOC_QUERYSTD + */ +-static int vidioc_querystd(struct file *file, void *private_data, v4l2_std_id *norm) ++static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) + { + if (norm) + *norm = V4L2_STD_ALL; +@@ -1374,7 +1378,7 @@ static int vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffer + MARK(); + + dev = v4l2loopback_getdevice(file); +- opener = file->private_data; ++ opener = fh_to_opener(fh); + + dprintk("reqbufs: %d\t%d=%d\n", b->memory, b->count, dev->buffers_number); + if (opener->timeout_image_io) { +@@ -1447,7 +1451,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) + type = b->type; + index = b->index; + dev = v4l2loopback_getdevice(file); +- opener = file->private_data; ++ opener = fh_to_opener(fh); + + if ((b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (b->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) { +@@ -1491,7 +1495,7 @@ static void buffer_written(struct v4l2_loopback_device *dev, struct v4l2l_buffer + /* put buffer to queue + * called on VIDIOC_QBUF + */ +-static int vidioc_qbuf(struct file *file, void *private_data, struct v4l2_buffer *buf) ++static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) + { + struct v4l2_loopback_device *dev; + struct v4l2_loopback_opener *opener; +@@ -1499,7 +1503,7 @@ static int vidioc_qbuf(struct file *file, void *private_data, struct v4l2_buffer + int index; + + dev = v4l2loopback_getdevice(file); +- opener = file->private_data; ++ opener = fh_to_opener(fh); + + if (buf->index > max_buffers) + return -EINVAL; +@@ -1552,7 +1556,7 @@ static int can_read(struct v4l2_loopback_device *dev, struct v4l2_loopback_opene + static int get_capture_buffer(struct file *file) + { + struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); +- struct v4l2_loopback_opener *opener = file->private_data; ++ struct v4l2_loopback_opener *opener = fh_to_opener(file->private_data); + int pos, ret; + int timeout_happened; + +@@ -1593,7 +1597,7 @@ static int get_capture_buffer(struct file *file) + /* put buffer to dequeue + * called on VIDIOC_DQBUF + */ +-static int vidioc_dqbuf(struct file *file, void *private_data, struct v4l2_buffer *buf) ++static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) + { + struct v4l2_loopback_device *dev; + struct v4l2_loopback_opener *opener; +@@ -1601,7 +1605,7 @@ static int vidioc_dqbuf(struct file *file, void *private_data, struct v4l2_buffe + struct v4l2l_buffer *b; + + dev = v4l2loopback_getdevice(file); +- opener = file->private_data; ++ opener = fh_to_opener(fh); + if (opener->timeout_image_io) { + *buf = dev->timeout_image_buffer.buffer; + return 0; +@@ -1638,14 +1642,14 @@ static int vidioc_dqbuf(struct file *file, void *private_data, struct v4l2_buffe + /* start streaming + * called on VIDIOC_STREAMON + */ +-static int vidioc_streamon(struct file *file, void *private_data, enum v4l2_buf_type type) ++static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type type) + { + struct v4l2_loopback_device *dev; + struct v4l2_loopback_opener *opener; + MARK(); + + dev = v4l2loopback_getdevice(file); +- opener = file->private_data; ++ opener = fh_to_opener(fh); + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: +@@ -1672,7 +1676,8 @@ static int vidioc_streamon(struct file *file, void *private_data, enum v4l2_buf_ + /* stop streaming + * called on VIDIOC_STREAMOFF + */ +-static int vidioc_streamoff(struct file *file, void *private_data, enum v4l2_buf_type type) ++static int vidioc_streamoff(struct file *file, void *fh, ++ enum v4l2_buf_type type) + { + MARK(); + dprintk("%d\n", type); +@@ -1732,7 +1737,7 @@ static int v4l2_loopback_mmap(struct file *file, struct vm_area_struct *vma) + size = (unsigned long) (vma->vm_end - vma->vm_start); + + dev = v4l2loopback_getdevice(file); +- opener = file->private_data; ++ opener = fh_to_opener(file->private_data); + + if (size > dev->buffer_size) { + dprintk("userspace tries to mmap too much, fail\n"); +@@ -1802,7 +1807,7 @@ static unsigned int v4l2_loopback_poll(struct file *file, struct poll_table_stru + int ret_mask = 0; + MARK(); + +- opener = file->private_data; ++ opener = fh_to_opener(file->private_data); + dev = v4l2loopback_getdevice(file); + + switch (opener->type) { +@@ -1838,7 +1843,9 @@ static int v4l2_loopback_open(struct file *file) + opener = kzalloc(sizeof(*opener), GFP_KERNEL); + if (opener == NULL) + return -ENOMEM; +- file->private_data = opener; ++ ++ v4l2_fh_init(&opener->fh, video_devdata(file)); ++ file->private_data = &opener->fh; + atomic_inc(&dev->open_count); + + opener->timeout_image_io = dev->timeout_image_io; +@@ -1852,6 +1859,8 @@ static int v4l2_loopback_open(struct file *file) + return r; + } + } ++ ++ v4l2_fh_add(&opener->fh); + dprintk("opened dev:%p with image:%p\n", dev, dev ? dev->image : NULL); + MARK(); + return 0; +@@ -1865,7 +1874,7 @@ static int v4l2_loopback_close(struct file *file) + MARK(); + + +- opener = file->private_data; ++ opener = fh_to_opener(file->private_data); + dev = v4l2loopback_getdevice(file); + + if(WRITER == opener->type) +@@ -1877,6 +1886,10 @@ static int v4l2_loopback_close(struct file *file) + del_timer_sync(&dev->timeout_timer); + } + try_free_buffers(dev); ++ ++ v4l2_fh_del(&opener->fh); ++ v4l2_fh_exit(&opener->fh); ++ + kfree(opener); + if(iswriter) { + dev->ready_for_output = 1; diff -Nru v4l2loopback-0.12.5/debian/patches/0002-event-install-event-un-subscribe-hook.patch v4l2loopback-0.12.5/debian/patches/0002-event-install-event-un-subscribe-hook.patch --- v4l2loopback-0.12.5/debian/patches/0002-event-install-event-un-subscribe-hook.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0002-event-install-event-un-subscribe-hook.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,53 @@ +From: You-Sheng Yang +Date: Tue, 15 Sep 2020 19:12:54 +0800 +Subject: event: install event (un)subscribe hook + +Signed-off-by: You-Sheng Yang +(cherry picked from commit 5987e8dd9981ec2aa3ed71a7534af2d189e5ed0a) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 16ecd09..8ae18d1 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -31,6 +31,7 @@ + # define HAVE__V4L2_CTRLS + # include + #endif ++#include + + #if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,1) + # define kstrtoul strict_strtoul +@@ -1699,6 +1700,18 @@ static int vidiocgmbuf(struct file *file, void *fh, struct video_mbuf *p) + } + #endif + ++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); ++ } ++ ++ return -EINVAL; ++} ++ + /* file operations */ + static void vm_open(struct vm_area_struct *vma) + { +@@ -2355,6 +2368,10 @@ static const struct v4l2_ioctl_ops v4l2_loopback_ioctl_ops = { + #ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = &vidiocgmbuf, + #endif ++ ++ .vidioc_subscribe_event = &vidioc_subscribe_event, ++ .vidioc_unsubscribe_event = &v4l2_event_unsubscribe, ++ + }; + + static void zero_devices(void) diff -Nru v4l2loopback-0.12.5/debian/patches/0003-event-support-polling-for-events.patch v4l2loopback-0.12.5/debian/patches/0003-event-support-polling-for-events.patch --- v4l2loopback-0.12.5/debian/patches/0003-event-support-polling-for-events.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0003-event-support-polling-for-events.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,63 @@ +From: You-Sheng Yang +Date: Wed, 16 Sep 2020 18:35:09 +0800 +Subject: event: support polling for events + +Signed-off-by: You-Sheng Yang +(cherry picked from commit 64795fd1cfa7d6fc15154c6184d8789f8573e2b4) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 27 +++++++++++++++++++++------ + 1 file changed, 21 insertions(+), 6 deletions(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 8ae18d1..9d39311 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -1817,26 +1817,41 @@ static unsigned int v4l2_loopback_poll(struct file *file, struct poll_table_stru + { + struct v4l2_loopback_opener *opener; + struct v4l2_loopback_device *dev; ++ __poll_t req_events = poll_requested_events(pts); + int ret_mask = 0; + MARK(); + + opener = fh_to_opener(file->private_data); + dev = v4l2loopback_getdevice(file); + ++ if (req_events & EPOLLPRI) { ++ if (!v4l2_event_pending(&opener->fh)) ++ poll_wait(file, &opener->fh.wait, pts); ++ if (v4l2_event_pending(&opener->fh)) { ++ ret_mask |= EPOLLPRI; ++ if (!(req_events & DEFAULT_POLLMASK)) ++ return ret_mask; ++ } ++ } ++ + switch (opener->type) { + case WRITER: +- ret_mask = POLLOUT | POLLWRNORM; ++ ret_mask |= EPOLLOUT | EPOLLWRNORM; + break; + case READER: +- poll_wait(file, &dev->read_event, pts); ++ if (!can_read(dev, opener)) { ++ if (ret_mask) ++ return ret_mask; ++ poll_wait(file, &dev->read_event, pts); ++ } + if (can_read(dev, opener)) +- ret_mask = POLLIN | POLLRDNORM; ++ ret_mask |= EPOLLIN | EPOLLRDNORM; ++ if (v4l2_event_pending(&opener->fh)) ++ ret_mask |= EPOLLPRI; + break; +- default: +- ret_mask = -POLLERR; + } +- MARK(); + ++ MARK(); + return ret_mask; + } + diff -Nru v4l2loopback-0.12.5/debian/patches/0004-event-fix-backward-compatibility-to-3.16.patch v4l2loopback-0.12.5/debian/patches/0004-event-fix-backward-compatibility-to-3.16.patch --- v4l2loopback-0.12.5/debian/patches/0004-event-fix-backward-compatibility-to-3.16.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0004-event-fix-backward-compatibility-to-3.16.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,73 @@ +From: You-Sheng Yang +Date: Wed, 16 Sep 2020 18:35:09 +0800 +Subject: event: fix backward compatibility to 3.16 + +3.16 is the oldest kernel known working, and it fails on 3.13 due to +incompatibility introduced by IDR. + +Signed-off-by: You-Sheng Yang +(cherry picked from commit afd4348f287a3db23957d820b128a5b010fff576) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 9d39311..4bd1252 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -21,6 +21,9 @@ + #include + #include + #include ++#include ++#include ++#include + #include + #include + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +@@ -153,6 +156,9 @@ static inline void v4l2l_get_timestamp(struct v4l2_buffer *b) { + b->timestamp.tv_usec = (ts.tv_nsec / NSEC_PER_USEC); + } + ++#if !defined(__poll_t) ++typedef unsigned __poll_t; ++#endif + + /* module constants + * can be overridden during he build process using something like +@@ -1824,11 +1830,11 @@ static unsigned int v4l2_loopback_poll(struct file *file, struct poll_table_stru + opener = fh_to_opener(file->private_data); + dev = v4l2loopback_getdevice(file); + +- if (req_events & EPOLLPRI) { ++ if (req_events & POLLPRI) { + if (!v4l2_event_pending(&opener->fh)) + poll_wait(file, &opener->fh.wait, pts); + if (v4l2_event_pending(&opener->fh)) { +- ret_mask |= EPOLLPRI; ++ ret_mask |= POLLPRI; + if (!(req_events & DEFAULT_POLLMASK)) + return ret_mask; + } +@@ -1836,7 +1842,7 @@ static unsigned int v4l2_loopback_poll(struct file *file, struct poll_table_stru + + switch (opener->type) { + case WRITER: +- ret_mask |= EPOLLOUT | EPOLLWRNORM; ++ ret_mask |= POLLOUT | POLLWRNORM; + break; + case READER: + if (!can_read(dev, opener)) { +@@ -1845,9 +1851,9 @@ static unsigned int v4l2_loopback_poll(struct file *file, struct poll_table_stru + poll_wait(file, &dev->read_event, pts); + } + if (can_read(dev, opener)) +- ret_mask |= EPOLLIN | EPOLLRDNORM; ++ ret_mask |= POLLIN | POLLRDNORM; + if (v4l2_event_pending(&opener->fh)) +- ret_mask |= EPOLLPRI; ++ ret_mask |= POLLPRI; + break; + } + diff -Nru v4l2loopback-0.12.5/debian/patches/0005-track-active-readers.patch v4l2loopback-0.12.5/debian/patches/0005-track-active-readers.patch --- v4l2loopback-0.12.5/debian/patches/0005-track-active-readers.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0005-track-active-readers.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,95 @@ +From: You-Sheng Yang +Date: Mon, 19 Oct 2020 23:11:42 +0800 +Subject: UBUNTU: SAUCE: track active readers + +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 36 ++++++++++++++++++++++++++++++------ + 1 file changed, 30 insertions(+), 6 deletions(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 4bd1252..504fb61 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -363,6 +363,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 */ +@@ -1670,9 +1671,10 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type type) + } + return 0; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: +- opener->type = READER; + if (!dev->ready_for_capture) + return -EIO; ++ opener->type = READER; ++ dev->active_readers++; + return 0; + default: + return -EINVAL; +@@ -1686,8 +1688,27 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type type) + 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: ++ break; ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ if (opener->type == READER) { ++ opener->type = 0; ++ dev->active_readers--; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ + return 0; + } + +@@ -1904,15 +1925,17 @@ 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; ++ if (WRITER == opener->type) ++ is_writer = 1; ++ if (READER == opener->type) ++ is_reader = 1; + + atomic_dec(&dev->open_count); + if (dev->open_count.counter == 0) { +@@ -1925,9 +1948,10 @@ static int v4l2_loopback_close(struct file *file) + v4l2_fh_exit(&opener->fh); + + kfree(opener); +- if(iswriter) { ++ if (is_writer) + dev->ready_for_output = 1; +- } ++ if (is_reader) ++ dev->active_readers--; + MARK(); + return 0; + } diff -Nru v4l2loopback-0.12.5/debian/patches/0006-UBUNTU-SAUCE-event-support-V4L2_EVENT_PRI_CLIENT_USA.patch v4l2loopback-0.12.5/debian/patches/0006-UBUNTU-SAUCE-event-support-V4L2_EVENT_PRI_CLIENT_USA.patch --- v4l2loopback-0.12.5/debian/patches/0006-UBUNTU-SAUCE-event-support-V4L2_EVENT_PRI_CLIENT_USA.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0006-UBUNTU-SAUCE-event-support-V4L2_EVENT_PRI_CLIENT_USA.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,127 @@ +From: You-Sheng Yang +Date: Thu, 13 Aug 2020 23:21:05 +0800 +Subject: UBUNTU: SAUCE: event: support + V4L2_EVENT_PRI_CLIENT_USAGE + +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 62 insertions(+), 1 deletion(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 504fb61..3cea842 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -605,6 +605,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 */ + struct v4l2_loopback_device *devs[MAX_DEVICES]; + +@@ -637,6 +645,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 int free_buffers(struct v4l2_loopback_device *dev); +@@ -1675,6 +1684,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; +@@ -1703,6 +1713,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); + } + break; + default: +@@ -1727,6 +1738,52 @@ 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) + { +@@ -1734,6 +1791,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, + { + 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; +@@ -1950,8 +2009,10 @@ static int v4l2_loopback_close(struct file *file) + kfree(opener); + if (is_writer) + dev->ready_for_output = 1; +- if (is_reader) ++ if (is_reader) { + dev->active_readers--; ++ client_usage_queue_event(dev->vdev); ++ } + MARK(); + return 0; + } diff -Nru v4l2loopback-0.12.5/debian/patches/0007-compliance-stop-declaring-V4L2_CAP_VIDEO_M2M-capabil.patch v4l2loopback-0.12.5/debian/patches/0007-compliance-stop-declaring-V4L2_CAP_VIDEO_M2M-capabil.patch --- v4l2loopback-0.12.5/debian/patches/0007-compliance-stop-declaring-V4L2_CAP_VIDEO_M2M-capabil.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0007-compliance-stop-declaring-V4L2_CAP_VIDEO_M2M-capabil.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,52 @@ +From: You-Sheng Yang +Date: Thu, 11 Mar 2021 21:42:28 +0800 +Subject: compliance: stop declaring V4L2_CAP_VIDEO_M2M capability + +v4l2-compliance complains about "Video Capture cap set, but no Video +Capture formats defined" because V4L2_CAP_VIDEO_M2M is set and yet +vidioc_enum_fmt_cap implementation would simply return -EINVAL before +being ready for capturing. + +V4L2 memory-to-memory operatons have never been actually supported. It +was introduced in an attempt to fix umlaeute/v4l2loopback#67, +VIDIOC_G/S_PRIORITY test failures reported by v4l2-compliance, but +VIDIOC_G/S_PRIORITY is an exclusive feature when V4L2 file handle +framework is used, and this was certainly not the case until +umlaeute/v4l2loopback#345 that brought in v4l2_fh for V4L2 Event API +support. + +This change removes all declarations of M2M support. + +Signed-off-by: You-Sheng Yang +(backported from +https://github.com/vicamo/v4l2loopback/commit/9a77eb691f1db37cce268ef58ad763f1e2f5a619) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 3cea842..222e621 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -703,10 +703,6 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability + cap->version = V4L2LOOPBACK_VERSION_CODE; + #endif + +-#ifdef V4L2_CAP_VIDEO_M2M +- capabilities |= V4L2_CAP_VIDEO_M2M; +-#endif /* V4L2_CAP_VIDEO_M2M */ +- + if (dev->announce_all_caps) { + capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; + } else { +@@ -2220,9 +2216,6 @@ static void init_vdev(struct video_device *vdev, int nr) + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) + vdev->device_caps = + V4L2_CAP_DEVICE_CAPS | +-#ifdef V4L2_CAP_VIDEO_M2M +- V4L2_CAP_VIDEO_M2M | +-#endif + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + #endif diff -Nru v4l2loopback-0.12.5/debian/patches/0008-compliance-fix-vidioc_enum_frameintervals-in-output-.patch v4l2loopback-0.12.5/debian/patches/0008-compliance-fix-vidioc_enum_frameintervals-in-output-.patch --- v4l2loopback-0.12.5/debian/patches/0008-compliance-fix-vidioc_enum_frameintervals-in-output-.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0008-compliance-fix-vidioc_enum_frameintervals-in-output-.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,88 @@ +From: You-Sheng Yang +Date: Thu, 31 Dec 2020 16:23:05 +0800 +Subject: compliance: fix vidioc_enum_frameintervals in output side + +Frame intervals is also critical for the output side. Report some +reasonable value here. 1000 fps should be enough for everybody. + +Signed-off-by: You-Sheng Yang +(backported from +https://github.com/vicamo/v4l2loopback/commit/b5272ea59663f35d009a623a2ce4d2137ffcff06) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 37 +++++++++++++++++++++---------------- + 1 file changed, 21 insertions(+), 16 deletions(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 222e621..aa3ad44 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -239,6 +239,9 @@ static int max_height = V4L2LOOPBACK_SIZE_MAX_HEIGHT; + module_param(max_height, int, S_IRUGO); + MODULE_PARM_DESC(max_height, "maximum frame height"); + ++/* frame intervals */ ++#define V4L2LOOPBACK_FPS_MIN 1 ++#define V4L2LOOPBACK_FPS_MAX 1000 + + /* control IDs */ + #ifndef HAVE__V4L2_CTRLS +@@ -382,7 +385,6 @@ enum opener_type { + /* struct keeping state and type of opener */ + struct v4l2_loopback_opener { + enum opener_type type; +- int vidioc_enum_frameintervals_calls; + int read_position; /* number of last processed frame + 1 or + * write_position - 1 if reader went out of sync */ + unsigned int reread_count; +@@ -729,11 +731,6 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsi + { + struct v4l2_loopback_device *dev; + +- /* LATER: what does the index really mean? +- * if it's about enumerating formats, we can safely ignore it +- * (CHECK) +- */ +- + /* there can be only one... */ + if (argp->index) + return -EINVAL; +@@ -770,20 +767,28 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsi + static int vidioc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *argp) + { + struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); +- struct v4l2_loopback_opener *opener = fh_to_opener(fh); ++ ++ /* there can be only one... */ ++ if (argp->index) ++ return -EINVAL; + + if (dev->ready_for_capture) { +- if (opener->vidioc_enum_frameintervals_calls > 0) ++ if (argp->width != dev->pix_format.width || ++ argp->height != dev->pix_format.height) + return -EINVAL; +- if (argp->width == dev->pix_format.width && +- argp->height == dev->pix_format.height) { +- argp->type = V4L2_FRMIVAL_TYPE_DISCRETE; +- argp->discrete = dev->capture_param.timeperframe; +- opener->vidioc_enum_frameintervals_calls++; +- return 0; +- } +- return -EINVAL; ++ ++ argp->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ argp->discrete = dev->capture_param.timeperframe; ++ } else { ++ argp->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ argp->stepwise.min.numerator = 1; ++ argp->stepwise.min.denominator = V4L2LOOPBACK_FPS_MAX; ++ argp->stepwise.max.numerator = 1; ++ argp->stepwise.max.denominator = V4L2LOOPBACK_FPS_MIN; ++ argp->stepwise.step.numerator = 1; ++ argp->stepwise.step.denominator = 1; + } ++ + return 0; + } + diff -Nru v4l2loopback-0.12.5/debian/patches/0009-compliance-fix-enum-frame-sizes-intervals-errors.patch v4l2loopback-0.12.5/debian/patches/0009-compliance-fix-enum-frame-sizes-intervals-errors.patch --- v4l2loopback-0.12.5/debian/patches/0009-compliance-fix-enum-frame-sizes-intervals-errors.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0009-compliance-fix-enum-frame-sizes-intervals-errors.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,60 @@ +From: You-Sheng Yang +Date: Thu, 11 Mar 2021 21:42:43 +0800 +Subject: compliance: fix enum frame sizes/intervals errors + +This change checks frame size/format from user input. + +Signed-off-by: You-Sheng Yang +(cherry picked from +https://github.com/vicamo/v4l2loopback/commit/80639c9fc1ca06ad5b7a1013ccd9ad12866fd807) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index aa3ad44..11fa8a2 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -740,6 +740,9 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsi + /* format has already been negotiated + * cannot change during runtime + */ ++ if (argp->pixel_format != dev->pix_format.pixelformat) ++ return -EINVAL; ++ + argp->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + argp->discrete.width = dev->pix_format.width; +@@ -747,6 +750,9 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsi + } else { + /* if the format has not been negotiated yet, we accept anything + */ ++ if (NULL == format_by_fourcc(argp->pixel_format)) ++ return -EINVAL; ++ + argp->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + + argp->stepwise.min_width = V4L2LOOPBACK_SIZE_MIN_WIDTH; +@@ -774,12 +780,20 @@ static int vidioc_enum_frameintervals(struct file *file, void *fh, struct v4l2_f + + if (dev->ready_for_capture) { + if (argp->width != dev->pix_format.width || +- argp->height != dev->pix_format.height) ++ argp->height != dev->pix_format.height || ++ argp->pixel_format != dev->pix_format.pixelformat) + return -EINVAL; + + 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 || ++ NULL == format_by_fourcc(argp->pixel_format)) ++ return -EINVAL; ++ + argp->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + argp->stepwise.min.numerator = 1; + argp->stepwise.min.denominator = V4L2LOOPBACK_FPS_MAX; diff -Nru v4l2loopback-0.12.5/debian/patches/0010-compliance-fix-fmtdesc.type-was-modified-error.patch v4l2loopback-0.12.5/debian/patches/0010-compliance-fix-fmtdesc.type-was-modified-error.patch --- v4l2loopback-0.12.5/debian/patches/0010-compliance-fix-fmtdesc.type-was-modified-error.patch 1970-01-01 08:00:00.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/0010-compliance-fix-fmtdesc.type-was-modified-error.patch 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,30 @@ +From: You-Sheng Yang +Date: Sat, 13 Mar 2021 21:54:43 +0800 +Subject: compliance: fix "fmtdesc.type was modified" error + +Buffer type in vidioc_enum_fmt_out will always either be +V4L2_BUF_TYPE_VIDEO_OUTPUT or V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, and +while MPLANE capaility is not declared, it cannot be +V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE anyway. Besides, this field is an +input field and should not be modified by kernel driver. + +Signed-off-by: You-Sheng Yang +(cherry picked from +https://github.com/vicamo/v4l2loopback/commit/a4b049bb0c9bf3a49418ff238ad7b74e0d024bd2) +Signed-off-by: You-Sheng Yang +--- + v4l2loopback.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/v4l2loopback.c b/v4l2loopback.c +index 11fa8a2..c9b1445 100644 +--- a/v4l2loopback.c ++++ b/v4l2loopback.c +@@ -920,7 +920,6 @@ static int vidioc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc + if (NULL == fmt) + return -EINVAL; + +- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + /* f->flags = ??; */ + snprintf(f->description, sizeof(f->description), "%s", fmt->name); + diff -Nru v4l2loopback-0.12.5/debian/patches/series v4l2loopback-0.12.5/debian/patches/series --- v4l2loopback-0.12.5/debian/patches/series 2020-04-22 22:36:06.000000000 +0800 +++ v4l2loopback-0.12.5/debian/patches/series 2021-03-29 17:02:42.000000000 +0800 @@ -0,0 +1,10 @@ +0001-dev-initialize-per-opener-v4l2_fh.patch +0002-event-install-event-un-subscribe-hook.patch +0003-event-support-polling-for-events.patch +0004-event-fix-backward-compatibility-to-3.16.patch +0005-track-active-readers.patch +0006-UBUNTU-SAUCE-event-support-V4L2_EVENT_PRI_CLIENT_USA.patch +0007-compliance-stop-declaring-V4L2_CAP_VIDEO_M2M-capabil.patch +0008-compliance-fix-vidioc_enum_frameintervals-in-output-.patch +0009-compliance-fix-enum-frame-sizes-intervals-errors.patch +0010-compliance-fix-fmtdesc.type-was-modified-error.patch