This is an RFC patch only, and will have to be split up in multiple patches before it can be merged. This adds support for read-only requests to v4l2-ctl and v4l2-compliance. It also adds new v4l2-ctl options to report either all controls or only read-only controls contained in a completed request. Signed-off-by: Hans Verkuil <hverkuil-cisco@xxxxxxxxx> --- diff --git a/contrib/freebsd/include/linux/videodev2.h b/contrib/freebsd/include/linux/videodev2.h index 4121ea0c..6d549530 100644 --- a/contrib/freebsd/include/linux/videodev2.h +++ b/contrib/freebsd/include/linux/videodev2.h @@ -974,6 +974,7 @@ struct v4l2_requestbuffers { #define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4) #define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5) #define V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS (1 << 6) +#define V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS (1 << 7) /** * struct v4l2_plane - plane info for multi-planar buffers diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 26b87f6d..50895990 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -940,6 +940,7 @@ struct v4l2_requestbuffers { #define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4) #define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5) #define V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS (1 << 6) +#define V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS (1 << 7) /** * struct v4l2_plane - plane info for multi-planar buffers diff --git a/utils/common/cv4l-helpers.h b/utils/common/cv4l-helpers.h index 6295054a..7e2f2076 100644 --- a/utils/common/cv4l-helpers.h +++ b/utils/common/cv4l-helpers.h @@ -765,6 +765,30 @@ public: { return v4l_queue_create_bufs(fd->g_v4l_fd(), this, count, fmt, flags); } + bool has_requests() const + { + return v4l_queue_has_requests(this); + } + int g_request_fd(unsigned index) const + { + return v4l_queue_g_request_fd(this, index); + } + int create_requests(int media_fd) + { + return v4l_queue_create_requests(this, media_fd); + } + void free_requests() + { + v4l_queue_free_requests(this); + } + int reinit_request(unsigned b) + { + return v4l_queue_reinit_request(this, b); + } + void free_request(unsigned b) + { + v4l_queue_free_request(this, b); + } int mmap_bufs(cv4l_fd *fd, unsigned from = 0) { return v4l_queue_mmap_bufs(fd->g_v4l_fd(), this, from); @@ -813,6 +837,10 @@ public: { v4l_queue_buffer_update(this, &buf, index); } + int buffer_queue_request(cv4l_fd *fd, v4l_buffer &buf) + { + return v4l_queue_buffer_queue_request(fd->g_v4l_fd(), this, &buf); + } int queue_all(cv4l_fd *fd); }; @@ -903,7 +931,7 @@ inline int cv4l_queue::queue_all(cv4l_fd *fd) for (unsigned i = 0; i < g_buffers(); i++) { buf.init(*this, i); - int ret = fd->qbuf(buf); + int ret = buffer_queue_request(fd, buf); if (ret) return ret; } diff --git a/utils/common/v4l-helpers.h b/utils/common/v4l-helpers.h index e093e717..f614c31d 100644 --- a/utils/common/v4l-helpers.h +++ b/utils/common/v4l-helpers.h @@ -9,7 +9,6 @@ #ifndef _V4L_HELPERS_H_ #define _V4L_HELPERS_H_ -#include <linux/videodev2.h> #include <string.h> #include <stdlib.h> #include <stdio.h> @@ -20,6 +19,8 @@ #include <fcntl.h> #include <sys/mman.h> #include <errno.h> +#include <linux/media.h> +#include <linux/videodev2.h> #ifdef __cplusplus extern "C" { @@ -1415,6 +1416,8 @@ struct v4l_queue { void *mmappings[VIDEO_MAX_FRAME][VIDEO_MAX_PLANES]; unsigned long userptrs[VIDEO_MAX_FRAME][VIDEO_MAX_PLANES]; int fds[VIDEO_MAX_FRAME][VIDEO_MAX_PLANES]; + int request_fds[VIDEO_MAX_FRAME]; + bool has_requests; }; static inline void v4l_queue_init(struct v4l_queue *q, @@ -1425,9 +1428,11 @@ static inline void v4l_queue_init(struct v4l_queue *q, memset(q, 0, sizeof(*q)); q->type = type; q->memory = memory; - for (i = 0; i < VIDEO_MAX_FRAME; i++) + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + q->request_fds[i] = -1; for (p = 0; p < VIDEO_MAX_PLANES; p++) q->fds[i][p] = -1; + } } static inline unsigned v4l_queue_g_type(const struct v4l_queue *q) { return q->type; } @@ -1571,6 +1576,60 @@ static inline int v4l_queue_create_bufs(struct v4l_fd *f, return v4l_queue_querybufs(f, q, q->buffers - createbufs.count); } +static inline int v4l_queue_g_request_fd(const struct v4l_queue *q, unsigned index) +{ + return q->request_fds[index]; +} + +static inline bool v4l_queue_has_requests(const struct v4l_queue *q) +{ + return q->has_requests; +} + +static inline void v4l_queue_free_request(struct v4l_queue *q, unsigned index) +{ + if (index < v4l_queue_g_buffers(q) && q->request_fds[index] >= 0) { + close(q->request_fds[index]); + q->request_fds[index] = -1; + } +} + +static inline void v4l_queue_free_requests(struct v4l_queue *q) +{ + unsigned b; + + for (b = 0; b < v4l_queue_g_buffers(q) && q->request_fds[b] >= 0; b++) { + close(q->request_fds[b]); + q->request_fds[b] = -1; + } + q->has_requests = false; +} + +static inline int v4l_queue_create_requests(struct v4l_queue *q, int media_fd) +{ + unsigned b; + int ret = 0; + + for (b = 0; b < v4l_queue_g_buffers(q); b++) { + int ret = ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &q->request_fds[b]); + + if (ret) + break; + } + if (ret) + v4l_queue_free_requests(q); + else + q->has_requests = true; + return ret; +} + +static inline int v4l_queue_reinit_request(struct v4l_queue *q, unsigned b) +{ + if (q->request_fds[b] >= 0) + return ioctl(q->request_fds[b], MEDIA_REQUEST_IOC_REINIT, NULL); + return 0; +} + static inline int v4l_queue_mmap_bufs(struct v4l_fd *f, struct v4l_queue *q, unsigned from) { @@ -1732,6 +1791,7 @@ static inline void v4l_queue_free(struct v4l_fd *f, struct v4l_queue *q) { v4l_ioctl(f, VIDIOC_STREAMOFF, &q->type); v4l_queue_release_bufs(f, q, 0); + v4l_queue_free_requests(q); v4l_queue_close_exported_fds(q); v4l_queue_reqbufs(f, q, 0, 0); } @@ -1767,7 +1827,23 @@ static inline void v4l_queue_buffer_init(const struct v4l_queue *q, struct v4l_b v4l_queue_buffer_update(q, buf, index); } -static inline int v4l_query_ext_ctrl(v4l_fd *f, struct v4l2_query_ext_ctrl *qec, +static inline int v4l_queue_buffer_queue_request(struct v4l_fd *f, struct v4l_queue *q, struct v4l_buffer *buf) +{ + unsigned b = v4l_buffer_g_index(buf); + int req_fd = q->request_fds[b]; + int ret; + + if (req_fd >= 0) { + v4l_buffer_s_request_fd(buf, req_fd); + v4l_buffer_or_flags(buf, V4L2_BUF_FLAG_REQUEST_FD); + } + ret = v4l_buffer_qbuf(f, buf); + if (!ret && req_fd >= 0) + ret = ioctl(req_fd, MEDIA_REQUEST_IOC_QUEUE, NULL); + return ret; +} + +static inline int v4l_query_ext_ctrl(struct v4l_fd *f, struct v4l2_query_ext_ctrl *qec, bool next_ctrl, bool next_compound) { struct v4l2_queryctrl qc; @@ -1839,7 +1915,7 @@ static inline int v4l_query_ext_ctrl(v4l_fd *f, struct v4l2_query_ext_ctrl *qec, return 0; } -static inline int v4l_g_ext_ctrls(v4l_fd *f, struct v4l2_ext_controls *ec) +static inline int v4l_g_ext_ctrls(struct v4l_fd *f, struct v4l2_ext_controls *ec) { unsigned i; @@ -1860,7 +1936,7 @@ static inline int v4l_g_ext_ctrls(v4l_fd *f, struct v4l2_ext_controls *ec) return 0; } -static inline int v4l_s_ext_ctrls(v4l_fd *f, struct v4l2_ext_controls *ec) +static inline int v4l_s_ext_ctrls(struct v4l_fd *f, struct v4l2_ext_controls *ec) { unsigned i; @@ -1880,7 +1956,7 @@ static inline int v4l_s_ext_ctrls(v4l_fd *f, struct v4l2_ext_controls *ec) return 0; } -static inline int v4l_try_ext_ctrls(v4l_fd *f, struct v4l2_ext_controls *ec) +static inline int v4l_try_ext_ctrls(struct v4l_fd *f, struct v4l2_ext_controls *ec) { unsigned i; @@ -1905,7 +1981,7 @@ static inline int v4l_try_ext_ctrls(v4l_fd *f, struct v4l2_ext_controls *ec) return 0; } -static inline int v4l_g_selection(v4l_fd *f, struct v4l2_selection *sel) +static inline int v4l_g_selection(struct v4l_fd *f, struct v4l2_selection *sel) { struct v4l2_cropcap cc; struct v4l2_crop crop; @@ -1951,7 +2027,7 @@ static inline int v4l_g_selection(v4l_fd *f, struct v4l2_selection *sel) } } -static inline int v4l_s_selection(v4l_fd *f, struct v4l2_selection *sel) +static inline int v4l_s_selection(struct v4l_fd *f, struct v4l2_selection *sel) { struct v4l2_crop crop; int ret; @@ -2003,7 +2079,7 @@ static inline void v4l_frame_selection(struct v4l2_selection *sel, bool to_frame } } -static inline int v4l_g_frame_selection(v4l_fd *f, struct v4l2_selection *sel, __u32 field) +static inline int v4l_g_frame_selection(struct v4l_fd *f, struct v4l2_selection *sel, __u32 field) { int ret = v4l_g_selection(f, sel); @@ -2012,7 +2088,7 @@ static inline int v4l_g_frame_selection(v4l_fd *f, struct v4l2_selection *sel, _ return ret; } -static inline int v4l_s_frame_selection(v4l_fd *f, struct v4l2_selection *sel, __u32 field) +static inline int v4l_s_frame_selection(struct v4l_fd *f, struct v4l2_selection *sel, __u32 field) { int ret; diff --git a/utils/common/v4l2-info.cpp b/utils/common/v4l2-info.cpp index 16be409a..dc88c2f1 100644 --- a/utils/common/v4l2-info.cpp +++ b/utils/common/v4l2-info.cpp @@ -207,6 +207,7 @@ static const flag_def bufcap_def[] = { { V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS, "orphaned-bufs" }, { V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF, "m2m-hold-capture-buf" }, { V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS, "mmap-cache-hints" }, + { V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS, "ro-requests" }, { 0, NULL } }; diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp index fa941a14..57ad3068 100644 --- a/utils/v4l2-compliance/v4l2-compliance.cpp +++ b/utils/v4l2-compliance/v4l2-compliance.cpp @@ -1269,7 +1269,8 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_ node.has_enc_cap_frame_interval = false; node.valid_buftypes = 0; node.valid_memorytype = 0; - node.buf_caps = 0; + node.buf_caps[0] = 0; + node.buf_caps[1] = 0; for (auto &buftype_pixfmt : node.buftype_pixfmts) buftype_pixfmt.clear(); diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h index 38a4ded3..4a214b2d 100644 --- a/utils/v4l2-compliance/v4l2-compliance.h +++ b/utils/v4l2-compliance/v4l2-compliance.h @@ -135,7 +135,7 @@ struct base_node { int frame_interval_pad; int enum_frame_interval_pad; __u32 fbuf_caps; - __u32 buf_caps; + __u32 buf_caps[2]; // [0] = capture, [1] = output buffer caps const char *bus_info; pixfmt_map buftype_pixfmts[V4L2_BUF_TYPE_LAST + 1]; frmsizes_set frmsizes; diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp index 5df71b80..ccfe0715 100644 --- a/utils/v4l2-compliance/v4l2-test-buffers.cpp +++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp @@ -580,7 +580,7 @@ int testReqBufs(struct node *node) fail_on_test(ret && ret != EINVAL); mmap_valid = !ret; if (mmap_valid) - node->buf_caps = caps = q.g_capabilities(); + node->buf_caps[V4L2_TYPE_IS_OUTPUT(i)] = caps = q.g_capabilities(); if (caps) { fail_on_test(mmap_valid ^ !!(caps & V4L2_BUF_CAP_SUPPORTS_MMAP)); if (caps & V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS) @@ -1962,8 +1962,10 @@ int testDmaBuf(struct node *expbuf_node, struct node *node, struct node *node_m2 return 0; } -int testRequests(struct node *node, bool test_streaming) +static int testRequestsType(struct node *node, bool test_streaming, int type) { + const unsigned int req_buf_caps = V4L2_BUF_CAP_SUPPORTS_REQUESTS | + V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS; filehandles fhs; int media_fd = fhs.add(mi_get_media_fd(node->g_fd(), node->bus_info)); int req_fd; @@ -1972,10 +1974,19 @@ int testRequests(struct node *node, bool test_streaming) v4l2_ext_controls ctrls; v4l2_ext_control ctrl; bool have_controls; + bool is_output = V4L2_TYPE_IS_OUTPUT(type); + __u32 buf_caps = node->buf_caps[is_output]; + __u32 inv_buf_caps = node->is_m2m ? node->buf_caps[!is_output] : 0; + bool supports_requests = buf_caps & req_buf_caps; + bool supports_ro_requests = buf_caps & V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS; + bool supports_inv_requests = inv_buf_caps & req_buf_caps; int ret; - if (node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS) + if (supports_requests) fail_on_test(media_fd < 0); + fail_on_test((buf_caps & req_buf_caps) == req_buf_caps); + fail_on_test((inv_buf_caps & req_buf_caps) == req_buf_caps); + fail_on_test(buf_caps & inv_buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS); memset(&valid_qctrl, 0, sizeof(valid_qctrl)); memset(&ctrls, 0, sizeof(ctrls)); @@ -1999,8 +2010,7 @@ int testRequests(struct node *node, bool test_streaming) if (ctrl.id == 0) { info("could not test the Request API, no suitable control found\n"); - return (node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS) ? - 0 : ENOTTY; + return supports_requests ? 0 : ENOTTY; } ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL; @@ -2009,7 +2019,7 @@ int testRequests(struct node *node, bool test_streaming) have_controls = ret != ENOTTY; if (media_fd < 0 || ret == EBADR) { - fail_on_test(node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS); + fail_on_test(supports_requests); return ENOTTY; } if (have_controls) { @@ -2018,7 +2028,7 @@ int testRequests(struct node *node, bool test_streaming) } ret = doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd); if (ret == ENOTTY) { - fail_on_test(node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS); + fail_on_test(supports_requests); return ENOTTY; } fail_on_test(ret); @@ -2069,9 +2079,6 @@ int testRequests(struct node *node, bool test_streaming) fhs.del(media_fd); node->reopen(); - int type = node->g_type(); - if (node->is_m2m) - type = v4l_type_invert(type); if (v4l_type_is_vbi(type)) { cv4l_fmt fmt; @@ -2080,10 +2087,9 @@ int testRequests(struct node *node, bool test_streaming) } if (!(node->valid_buftypes & (1 << type))) { - fail_on_test(node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS); + fail_on_test(supports_requests); return ENOTTY; } - bool supports_requests = node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS; buffer_info.clear(); @@ -2103,8 +2109,8 @@ int testRequests(struct node *node, bool test_streaming) for (unsigned i = 0; i < num_requests; i++) { fail_on_test(doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &buf_req_fds[i])); - fhs.add(buf_req_fds[i]); fail_on_test(buf_req_fds[i] < 0); + fhs.add(buf_req_fds[i]); fail_on_test(!doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, 0)); } fhs.del(media_fd); @@ -2134,9 +2140,11 @@ int testRequests(struct node *node, bool test_streaming) buffer buf(m2m_q); fail_on_test(buf.querybuf(node, 0)); - buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD); - buf.s_request_fd(buf_req_fds[0]); - fail_on_test(!buf.qbuf(node)); + if (!supports_inv_requests) { + buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD); + buf.s_request_fd(buf_req_fds[0]); + fail_on_test(!buf.qbuf(node)); + } fail_on_test(node->streamoff(m2m_q.g_type())); fail_on_test(m2m_q.reqbufs(node, 0)); @@ -2177,7 +2185,10 @@ int testRequests(struct node *node, bool test_streaming) fail_on_test(!buf.qbuf(node)); } else { fail_on_test(supports_requests); - fail_on_test(err != EBADR); + if (!is_output && (node->codec_mask & (STATEFUL_DECODER | STATEFUL_ENCODER))) + fail_on_test(err != EBADR && err != EPERM); + else + fail_on_test(err != EBADR); } if (err) { fail_on_test(node->streamoff(q.g_type())); @@ -2240,13 +2251,17 @@ int testRequests(struct node *node, bool test_streaming) test_streaming = false; break; } - fail_on_test(ret); - fail_on_test(buf.querybuf(node, i)); - fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST); - fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD)); - fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_QUEUED)); - fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, 0) != EBUSY); - fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, 0) != EBUSY); + if (supports_ro_requests) { + fail_on_test(ret != EINVAL); + } else { + fail_on_test(ret); + fail_on_test(buf.querybuf(node, i)); + fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD)); + fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_QUEUED)); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, 0) != EBUSY); + fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, 0) != EBUSY); + } if (i >= min_bufs) { close(buf_req_fds[i]); buf_req_fds[i] = -1; @@ -2312,6 +2327,24 @@ int testRequests(struct node *node, bool test_streaming) return 0; } +int testRequests(struct node *node, bool test_streaming) +{ + int type = node->g_type(); + int ret, inv_ret = ENOTTY; + + if (node->is_m2m) + type = v4l_type_invert(type); + ret = testRequestsType(node, test_streaming, type); + fail_on_test(ret && ret != ENOTTY); + if (node->is_m2m) { + node->reopen(); + inv_ret = testRequestsType(node, false, node->g_type()); + fail_on_test(inv_ret && inv_ret != ENOTTY); + if (!inv_ret) + ret = 0; + } + return ret; +} /* * This class wraps a pthread in such a way that it simplifies passing diff --git a/utils/v4l2-ctl/v4l2-ctl-common.cpp b/utils/v4l2-ctl/v4l2-ctl-common.cpp index ed28a568..ed218864 100644 --- a/utils/v4l2-ctl/v4l2-ctl-common.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-common.cpp @@ -977,7 +977,7 @@ void common_set(cv4l_fd &_fd) break; default: fprintf(stderr, "%s: unsupported payload type\n", - qc.name); + qc.name); break; } } else { @@ -1112,8 +1112,7 @@ void common_print_control(v4l2_query_ext_ctrl &qc, v4l2_ext_control &ctrl) ctrl.p_area->height); break; default: - fprintf(stderr, "%s: unsupported payload type\n", - name.c_str()); + printf("%s: unsupported payload type\n", name.c_str()); break; } } else if (qc.type == V4L2_CTRL_TYPE_INTEGER64) { diff --git a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp index c936299f..fb016597 100644 --- a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp @@ -1,6 +1,7 @@ #include <algorithm> #include <cstdlib> #include <cstring> +#include <vector> #include <unistd.h> #include <stdlib.h> @@ -38,6 +39,10 @@ static __u32 memory = V4L2_MEMORY_MMAP; static __u32 out_memory = V4L2_MEMORY_MMAP; static int stream_sleep = -1; static bool stream_no_query; +static bool stream_req_ctrls; +static bool stream_req_ro_ctrls; +static bool stream_out_req_ctrls; +static bool stream_out_req_ro_ctrls; static unsigned stream_pat; static bool stream_loop; static bool stream_out_square; @@ -91,7 +96,6 @@ static __u64 last_fwht_bf_ts; static fwht_cframe_hdr last_fwht_hdr; struct request_fwht { - int fd; __u64 ts; struct v4l2_ctrl_fwht_params params; }; @@ -286,6 +290,8 @@ void streaming_usage() #endif " --stream-poll use non-blocking mode and select() to stream.\n" " --stream-buf-caps show capture buffer capabilities\n" + " --stream-req-ctrls show all controls in a capture request\n" + " --stream-req-ro-ctrls show all read-only controls in a capture request\n" " --stream-mmap <count>\n" " capture video using mmap() [VIDIOC_(D)QBUF]\n" " count: the number of buffers to allocate. The default is 3.\n" @@ -335,6 +341,8 @@ void streaming_usage() " percentage of the frame to actually fill. The default is 100%%.\n" " --stream-out-buf-caps\n" " show output buffer capabilities\n" + " --stream-out-req-ctrls show all controls in an output request\n" + " --stream-out-req-ro-ctrls show all read-only controls in an output request\n" " --stream-out-mmap <count>\n" " output video using mmap() [VIDIOC_(D)QBUF]\n" " count: the number of buffers to allocate. The default is 4.\n" @@ -668,6 +676,18 @@ void streaming_cmd(int ch, char *optarg) case OptStreamLoop: stream_loop = true; break; + case OptStreamReqCtrls: + stream_req_ctrls = true; + break; + case OptStreamReqROCtrls: + stream_req_ro_ctrls = true; + break; + case OptStreamOutReqCtrls: + stream_out_req_ctrls = true; + break; + case OptStreamOutReqROCtrls: + stream_out_req_ro_ctrls = true; + break; case OptStreamOutPattern: stream_pat = strtoul(optarg, 0L, 0); for (i = 0; tpg_pattern_strings[i]; i++) ; @@ -839,20 +859,6 @@ static void set_fwht_stateless_params(struct v4l2_ctrl_fwht_params &fwht_params, fwht_params.flags |= FWHT_FL_I_FRAME; } -static int alloc_fwht_req(int media_fd, unsigned index) -{ - int rc = 0; - - rc = ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &fwht_reqs[index]); - if (rc < 0) { - fprintf(stderr, "Unable to allocate media request: %s\n", - strerror(errno)); - return rc; - } - - return 0; -} - static void set_fwht_req_by_idx(unsigned idx, const struct fwht_cframe_hdr *hdr, __u64 last_bf_ts, __u64 ts) { @@ -873,23 +879,6 @@ static int get_fwht_req_by_ts(__u64 ts) return -1; } -static bool set_fwht_req_by_fd(const struct fwht_cframe_hdr *hdr, - int req_fd, __u64 last_bf_ts, __u64 ts) -{ - struct v4l2_ctrl_fwht_params fwht_params; - - set_fwht_stateless_params(fwht_params, hdr, last_bf_ts); - - for (auto &fwht_req : fwht_reqs) { - if (fwht_req.fd == req_fd) { - fwht_req.ts = ts; - fwht_req.params = fwht_params; - return true; - } - } - return false; -} - static int set_fwht_ext_ctrl(cv4l_fd &fd, const struct fwht_cframe_hdr *hdr, __u64 last_bf_ts, int req_fd) { @@ -1228,6 +1217,21 @@ static int do_setup_out_buffers(cv4l_fd &fd, cv4l_queue &q, FILE *fin, bool qbuf stream_out_refresh = true; } + if (((q.g_capabilities() & V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS) && + (stream_out_req_ctrls || stream_out_req_ro_ctrls)) || + fmt.g_pixelformat() == V4L2_PIX_FMT_FWHT_STATELESS) { + int media_fd = mi_get_media_fd(fd.g_fd()); + + if (media_fd < 0) { + fprintf(stderr, "%s: mi_get_media_fd failed\n", __func__); + return QUEUE_ERROR; + } + int ret = q.create_requests(media_fd); + close(media_fd); + if (ret) + return QUEUE_ERROR; + } + for (unsigned i = 0; i < q.g_buffers(); i++) { cv4l_buffer buf(q); @@ -1259,20 +1263,8 @@ static int do_setup_out_buffers(cv4l_fd &fd, cv4l_queue &q, FILE *fin, bool qbuf return QUEUE_STOPPED; if (fmt.g_pixelformat() == V4L2_PIX_FMT_FWHT_STATELESS) { - int media_fd = mi_get_media_fd(fd.g_fd()); - - if (media_fd < 0) { - fprintf(stderr, "%s: mi_get_media_fd failed\n", __func__); - return media_fd; - } - - if (alloc_fwht_req(media_fd, i)) - return QUEUE_ERROR; - buf.s_request_fd(fwht_reqs[i].fd); - buf.or_flags(V4L2_BUF_FLAG_REQUEST_FD); - if (set_fwht_ext_ctrl(fd, &last_fwht_hdr, last_fwht_bf_ts, - buf.g_request_fd())) { + q.g_request_fd(i))) { fprintf(stderr, "%s: set_fwht_ext_ctrl failed on %dth buf: %s\n", __func__, i, strerror(errno)); return QUEUE_ERROR; @@ -1282,7 +1274,7 @@ static int do_setup_out_buffers(cv4l_fd &fd, cv4l_queue &q, FILE *fin, bool qbuf fps_timestamps fps_ts; set_time_stamp(buf); - if (fd.qbuf(buf)) + if (q.buffer_queue_request(&fd, buf)) return QUEUE_ERROR; tpg_update_mv_count(&tpg, V4L2_FIELD_HAS_T_OR_B(field)); if (!verbose) @@ -1296,11 +1288,6 @@ static int do_setup_out_buffers(cv4l_fd &fd, cv4l_queue &q, FILE *fin, bool qbuf set_fwht_req_by_idx(i, &last_fwht_hdr, last_fwht_bf_ts, buf.g_timestamp_ns()); last_fwht_bf_ts = buf.g_timestamp_ns(); - if (ioctl(buf.g_request_fd(), MEDIA_REQUEST_IOC_QUEUE) < 0) { - fprintf(stderr, "Unable to queue media request: %s\n", - strerror(errno)); - return QUEUE_ERROR; - } } } if (qbuf) @@ -1382,6 +1369,81 @@ static void write_buffer_to_file(cv4l_fd &fd, cv4l_queue &q, cv4l_buffer &buf, #endif } +static void log_ctrls(cv4l_fd &fd, int req_fd, bool read_only, + const char *prefix) +{ + std::vector<v4l2_ext_control> ctrl_vec; + std::vector<v4l2_query_ext_ctrl> qec_vec; + v4l2_ext_controls ctrls = {}; + v4l2_query_ext_ctrl qec = { 0 }; + cv4l_disable_trace dt(fd); + + while (!fd.query_ext_ctrl(qec, true, true)) { + if (read_only && !(qec.flags & V4L2_CTRL_FLAG_READ_ONLY)) + continue; + if (qec.type == V4L2_CTRL_TYPE_CTRL_CLASS || + qec.type == V4L2_CTRL_TYPE_BUTTON) + continue; + v4l2_ext_control ctrl = { qec.id }; + if (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) { + ctrl.size = qec.elems * qec.elem_size; + ctrl.ptr = malloc(ctrl.size); + } + ctrl_vec.push_back(ctrl); + qec_vec.push_back(qec); + } + if (ctrl_vec.empty()) + return; + ctrls.count = ctrl_vec.size(); + ctrls.controls = &ctrl_vec[0]; + ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL; + ctrls.request_fd = req_fd; + ioctl(fd.g_fd(), VIDIOC_G_EXT_CTRLS, &ctrls); + printf("\n"); + for (unsigned i = 0; i < ctrls.count; i++) { + printf("%s", prefix); + common_print_control(qec_vec[i], ctrl_vec[i]); + } + printf("\n"); +} + +static int wait_for_request(cv4l_fd &fd, cv4l_queue &q, unsigned index) +{ + if (!q.has_requests()) + return 0; + + int req_fd = q.g_request_fd(index); + fd_set exception_fds; + struct timeval tv = { 2, 0 }; + int rc; + + FD_ZERO(&exception_fds); + FD_SET(req_fd, &exception_fds); + rc = select(req_fd + 1, NULL, NULL, &exception_fds, &tv); + + if (rc == 0) { + fprintf(stderr, "Timeout when waiting for media request\n"); + return QUEUE_ERROR; + } + if (rc < 0) { + fprintf(stderr, "Unable to select media request: %s\n", + strerror(errno)); + return QUEUE_ERROR; + } + + bool show_ctrls = V4L2_TYPE_IS_CAPTURE(q.g_type()) ? + stream_req_ctrls || stream_req_ro_ctrls : + stream_out_req_ctrls || stream_out_req_ro_ctrls; + bool show_ro_ctrls = V4L2_TYPE_IS_CAPTURE(q.g_type()) ? stream_req_ro_ctrls : + stream_out_req_ro_ctrls; + char prefix[16]; + + sprintf(prefix, "%s[%u]: ", V4L2_TYPE_IS_CAPTURE(q.g_type()) ? "cap" : "out", index); + if (show_ctrls) + log_ctrls(fd, req_fd, show_ro_ctrls, prefix); + return 0; +} + static int do_handle_cap(cv4l_fd &fd, cv4l_queue &q, FILE *fout, int *index, unsigned &count, fps_timestamps &fps_ts, cv4l_fmt &fmt, bool ignore_count_skip) @@ -1400,6 +1462,8 @@ static int do_handle_cap(cv4l_fd &fd, cv4l_queue &q, FILE *fout, int *index, fprintf(stderr, "%s: failed: %s\n", "VIDIOC_DQBUF", strerror(errno)); return QUEUE_ERROR; } + if (q.reinit_request(buf.g_index())) + return QUEUE_ERROR; if (buf.g_flags() & V4L2_BUF_FLAG_LAST) { last_buffer = true; break; @@ -1408,7 +1472,7 @@ static int do_handle_cap(cv4l_fd &fd, cv4l_queue &q, FILE *fout, int *index, break; if (verbose) print_concise_buffer(stderr, buf, fmt, q, fps_ts, -1); - if (fd.qbuf(buf)) + if (q.buffer_queue_request(&fd, buf)) return QUEUE_ERROR; } @@ -1440,7 +1504,7 @@ static int do_handle_cap(cv4l_fd &fd, cv4l_queue &q, FILE *fout, int *index, * has the size that fits the old resolution and might not * fit to the new one. */ - if (fd.qbuf(buf) && errno != EINVAL) { + if (q.buffer_queue_request(&fd, buf) && errno != EINVAL) { fprintf(stderr, "%s: qbuf error\n", __func__); return QUEUE_ERROR; } @@ -1560,13 +1624,10 @@ static int do_handle_out(cv4l_fd &fd, cv4l_queue &q, FILE *fin, cv4l_buffer *cap if (is_meta) meta_fillbuffer(buf, fmt, q); - if (fmt.g_pixelformat() == V4L2_PIX_FMT_FWHT_STATELESS) { - if (ioctl(buf.g_request_fd(), MEDIA_REQUEST_IOC_REINIT, NULL)) { - fprintf(stderr, "Unable to reinit media request: %s\n", - strerror(errno)); - return QUEUE_ERROR; - } + if (q.reinit_request(buf.g_index())) + return QUEUE_ERROR; + if (fmt.g_pixelformat() == V4L2_PIX_FMT_FWHT_STATELESS) { if (set_fwht_ext_ctrl(fd, &last_fwht_hdr, last_fwht_bf_ts, buf.g_request_fd())) { fprintf(stderr, "%s: set_fwht_ext_ctrl failed: %s\n", @@ -1577,24 +1638,15 @@ static int do_handle_out(cv4l_fd &fd, cv4l_queue &q, FILE *fin, cv4l_buffer *cap set_time_stamp(buf); - if (fd.qbuf(buf)) { - fprintf(stderr, "%s: failed: %s\n", "VIDIOC_QBUF", strerror(errno)); - return QUEUE_ERROR; - } if (fmt.g_pixelformat() == V4L2_PIX_FMT_FWHT_STATELESS) { - if (!set_fwht_req_by_fd(&last_fwht_hdr, buf.g_request_fd(), last_fwht_bf_ts, - buf.g_timestamp_ns())) { - fprintf(stderr, "%s: request for fd %d does not exist\n", - __func__, buf.g_request_fd()); - return QUEUE_ERROR; - } - + set_fwht_req_by_idx(buf.g_index(), &last_fwht_hdr, + last_fwht_bf_ts, buf.g_timestamp_ns()); last_fwht_bf_ts = buf.g_timestamp_ns(); - if (ioctl(buf.g_request_fd(), MEDIA_REQUEST_IOC_QUEUE) < 0) { - fprintf(stderr, "Unable to queue media request: %s\n", - strerror(errno)); - return QUEUE_ERROR; - } + } + + if (q.buffer_queue_request(&fd, buf)) { + fprintf(stderr, "%s: failed: %s\n", "VIDIOC_QBUF", strerror(errno)); + return QUEUE_ERROR; } tpg_update_mv_count(&tpg, V4L2_FIELD_HAS_T_OR_B(output_field)); @@ -1775,6 +1827,8 @@ static void streaming_set_cap(cv4l_fd &fd, cv4l_fd &exp_fd) if (use_poll) subscribe_event(fd, V4L2_EVENT_SOURCE_CHANGE); + int media_fd = mi_get_media_fd(fd.g_fd()); + recover: eos = false; source_change = false; @@ -1821,6 +1875,11 @@ recover: if (q.obtain_bufs(&fd)) goto done; + if ((q.g_capabilities() & V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS) && + (stream_req_ctrls || stream_req_ro_ctrls) && + q.create_requests(media_fd)) + goto done; + if (q.queue_all(&fd)) goto done; @@ -1904,8 +1963,10 @@ recover: goto recover; done: - if (options[OptStreamDmaBuf]) - exp_q.close_exported_fds(); + q.free(&fd); + exp_q.free(&exp_fd); + close(media_fd); + if (fout && fout != stdout) { if (host_fd_to >= 0) write_u32(fout, V4L_STREAM_PACKET_END); @@ -2162,12 +2223,11 @@ static void streaming_set_out(cv4l_fd &fd, cv4l_fd &exp_fd) fcntl(fd.g_fd(), F_SETFL, fd_flags); fprintf(stderr, "\n"); - q.free(&fd); tpg_free(&tpg); done: - if (options[OptStreamOutDmaBuf]) - exp_q.close_exported_fds(); + q.free(&fd); + exp_q.free(&fd); if (fin && fin != stdin) fclose(fin); } @@ -2183,6 +2243,7 @@ static int capture_setup(cv4l_fd &fd, cv4l_queue &in, cv4l_fd *exp_fd, cv4l_fmt fprintf(stderr, "%s: fd.streamoff error\n", __func__); return -1; } + in.free_requests(); /* release any buffer allocated */ if (in.reqbufs(&fd)) { @@ -2214,10 +2275,25 @@ static int capture_setup(cv4l_fd &fd, cv4l_queue &in, cv4l_fd *exp_fd, cv4l_fmt if (exp_fd && in.export_bufs(exp_fd, exp_fd->g_type())) return -1; - if (in.obtain_bufs(&fd) || in.queue_all(&fd)) { + if ((in.g_capabilities() & V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS) && + (stream_req_ctrls || stream_req_ro_ctrls)) { + int media_fd = mi_get_media_fd(fd.g_fd()); + + if (media_fd < 0) + return -1; + int ret = in.create_requests(media_fd); + close(media_fd); + if (ret) + return -1; + } + if (in.obtain_bufs(&fd)) { fprintf(stderr, "%s: in.obtain_bufs error\n", __func__); return -1; } + if (in.queue_all(&fd)) { + fprintf(stderr, "%s: in.queue_all error\n", __func__); + return -1; + } if (fd.streamon(in.g_type())) { fprintf(stderr, "%s: fd.streamon error\n", __func__); @@ -2434,6 +2510,7 @@ static void stateless_m2m(cv4l_fd &fd, cv4l_queue &in, cv4l_queue &out, unsigned count[2] = { 0, 0 }; int fd_flags = fcntl(fd.g_fd(), F_GETFL); bool stopped = false; + int media_fd = mi_get_media_fd(fd.g_fd()); if (out.reqbufs(&fd, reqbufs_count_out)) { fprintf(stderr, "%s: out.reqbufs failed\n", __func__); @@ -2453,6 +2530,11 @@ static void stateless_m2m(cv4l_fd &fd, cv4l_queue &in, cv4l_queue &out, return; } + if ((in.g_capabilities() & V4L2_BUF_CAP_SUPPORTS_RO_REQUESTS) && + (stream_req_ctrls || stream_req_ro_ctrls) && + in.create_requests(media_fd)) + return; + if (do_setup_out_buffers(fd, out, fout, true, true) == QUEUE_ERROR) { fprintf(stderr, "%s: do_setup_out_buffers failed\n", __func__); return; @@ -2473,45 +2555,38 @@ static void stateless_m2m(cv4l_fd &fd, cv4l_queue &in, cv4l_queue &out, return; } int index = 0; - bool queue_lst_buf = false; + bool queue_last_buf = false; cv4l_buffer last_in_buf; fcntl(fd.g_fd(), F_SETFL, fd_flags | O_NONBLOCK); while (true) { - fd_set except_fds; - int req_fd = fwht_reqs[index].fd; - struct timeval tv = { 2, 0 }; + int buf_idx = -1; + int rc = 0; - if (req_fd < 0) + if (out.g_request_fd(index) < 0) break; - FD_ZERO(&except_fds); - FD_SET(req_fd, &except_fds); - - int rc = select(req_fd + 1, NULL, NULL, &except_fds, &tv); - - if (rc == 0) { - fprintf(stderr, "Timeout when waiting for media request\n"); + rc = wait_for_request(fd, out, index); + if (rc) return; - } - if (rc < 0) { - fprintf(stderr, "Unable to select media request: %s\n", - strerror(errno)); - return; - } + /* * it is safe to queue back last cap buffer only after * the following request is done so that the buffer * is not needed anymore as a reference frame */ - if (queue_lst_buf) { - if (fd.qbuf(last_in_buf)) { + if (queue_last_buf) { + if (in.buffer_queue_request(&fd, last_in_buf)) { fprintf(stderr, "%s: qbuf failed\n", __func__); return; } } - int buf_idx = -1; + + rc = wait_for_request(fd, in, index); + if (rc) + return; + /* * fin is not sent to do_handle_cap since the capture buf is * written to the file in current function @@ -2522,6 +2597,7 @@ static void stateless_m2m(cv4l_fd &fd, cv4l_queue &in, cv4l_queue &out, fprintf(stderr, "%s: do_handle_cap err\n", __func__); return; } + /* * in case of an error in the frame, set last ts to 0 as a * means to recover so that next request will not use a @@ -2536,7 +2612,7 @@ static void stateless_m2m(cv4l_fd &fd, cv4l_queue &in, cv4l_queue &out, if (fd.querybuf(cap_buf)) return; last_in_buf = cap_buf; - queue_lst_buf = true; + queue_last_buf = true; if (fin && cap_buf.g_bytesused(0) && !(cap_buf.g_flags() & V4L2_BUF_FLAG_ERROR)) { int idx = get_fwht_req_by_ts(cap_buf.g_timestamp_ns()); @@ -2562,8 +2638,7 @@ static void stateless_m2m(cv4l_fd &fd, cv4l_queue &in, cv4l_queue &out, stopped = true; if (rc != QUEUE_STOPPED) fprintf(stderr, "%s: output stream ended\n", __func__); - close(req_fd); - fwht_reqs[index].fd = -1; + out.free_request(index); } } index = (index + 1) % out.g_buffers(); @@ -2625,8 +2700,9 @@ static void streaming_set_m2m(cv4l_fd &fd, cv4l_fd &exp_fd) stateful_m2m(fd, in, out, file[CAP], file[OUT], fmt[CAP], fmt[OUT], exp_fd_p); done: - if (options[OptStreamDmaBuf] || options[OptStreamOutDmaBuf]) - exp_q.close_exported_fds(); + in.free(&fd); + out.free(&fd); + exp_q.free(&exp_fd); if (file[CAP] && file[CAP] != stdout) fclose(file[CAP]); diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp index a31b29f8..edfd2530 100644 --- a/utils/v4l2-ctl/v4l2-ctl.cpp +++ b/utils/v4l2-ctl/v4l2-ctl.cpp @@ -260,6 +260,8 @@ static struct option long_options[] = { {"stream-to-host", required_argument, 0, OptStreamToHost}, #endif {"stream-buf-caps", no_argument, 0, OptStreamBufCaps}, + {"stream-req-ctrls", no_argument, 0, OptStreamReqCtrls}, + {"stream-req-ro-ctrls", no_argument, 0, OptStreamReqROCtrls}, {"stream-mmap", optional_argument, 0, OptStreamMmap}, {"stream-user", optional_argument, 0, OptStreamUser}, {"stream-dmabuf", no_argument, 0, OptStreamDmaBuf}, @@ -280,6 +282,8 @@ static struct option long_options[] = { {"stream-out-vert-speed", required_argument, 0, OptStreamOutVertSpeed}, {"stream-out-perc-fill", required_argument, 0, OptStreamOutPercFill}, {"stream-out-buf-caps", no_argument, 0, OptStreamOutBufCaps}, + {"stream-out-req-ctrls", no_argument, 0, OptStreamOutReqCtrls}, + {"stream-out-req-ro-ctrls", no_argument, 0, OptStreamOutReqROCtrls}, {"stream-out-mmap", optional_argument, 0, OptStreamOutMmap}, {"stream-out-user", optional_argument, 0, OptStreamOutUser}, {"stream-out-dmabuf", no_argument, 0, OptStreamOutDmaBuf}, diff --git a/utils/v4l2-ctl/v4l2-ctl.h b/utils/v4l2-ctl/v4l2-ctl.h index 4acb4d51..0590289c 100644 --- a/utils/v4l2-ctl/v4l2-ctl.h +++ b/utils/v4l2-ctl/v4l2-ctl.h @@ -224,6 +224,8 @@ enum Option { OptStreamToHost, OptStreamLossless, OptStreamBufCaps, + OptStreamReqCtrls, + OptStreamReqROCtrls, OptStreamMmap, OptStreamUser, OptStreamDmaBuf, @@ -244,6 +246,8 @@ enum Option { OptStreamOutPixelAspect, OptStreamOutVideoAspect, OptStreamOutBufCaps, + OptStreamOutReqCtrls, + OptStreamOutReqROCtrls, OptStreamOutMmap, OptStreamOutUser, OptStreamOutDmaBuf,