From b000aff8c91a75c7be5c79439119203d5fdfe891 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Jul 2014 15:30:59 +0200 Subject: [PATCH] qv4l2: implement cropping and composing for video output. To simplify this work g/s_selection helper functions are added to the helper headers that will fallback to S/G_CROP if necessary. This patch also fixes a bug where the buffer field was never set when generating a FIELD_ALTERNATE video output stream. Signed-off-by: Hans Verkuil --- utils/qv4l2/general-tab.cpp | 115 +++++++++++++++++++++++------------ utils/qv4l2/general-tab.h | 6 +- utils/qv4l2/qv4l2.cpp | 14 ++++- utils/v4l2-compliance/cv4l-helpers.h | 45 +++++++------- utils/v4l2-compliance/v4l-helpers.h | 81 +++++++++++++++++++++++- 5 files changed, 196 insertions(+), 65 deletions(-) diff --git a/utils/qv4l2/general-tab.cpp b/utils/qv4l2/general-tab.cpp index 8a716b7..df926b7 100644 --- a/utils/qv4l2/general-tab.cpp +++ b/utils/qv4l2/general-tab.cpp @@ -257,7 +257,7 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *paren m_recordPrio = new QCheckBox(parentWidget()); addWidget(m_recordPrio); - if (!isRadio() && !isVbi() && !m_isOutput && (has_crop() || has_compose())) { + if (!isRadio() && !isVbi() && (has_crop() || has_compose())) { addTitle("Cropping & Compose Settings"); cropSection(); } @@ -1285,6 +1285,10 @@ void GeneralTab::inputChanged(int input) void GeneralTab::outputChanged(int output) { s_output((__u32)output); + + if (m_audioOutput) + updateAudioOutput(); + updateVideoOutput(); updateVidOutFormat(); updateGUIOutput(output); @@ -1565,35 +1569,36 @@ void GeneralTab::vbiMethodsChanged(int idx) void GeneralTab::cropChanged() { - v4l2_crop crop; + v4l2_selection sel; - if (!m_cropWidth->isEnabled()) + if (!m_cropWidth->isEnabled() || !cur_io_has_crop()) return; - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c.width = m_cropWidth->value(); - crop.c.left = m_cropLeft->value(); - crop.c.height = m_cropHeight->value(); - crop.c.top = m_cropTop->value(); - cv4l_ioctl(VIDIOC_S_CROP, &crop); - updateVidCapFormat(); + sel.type = g_selection_type(); + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = m_cropWidth->value(); + sel.r.left = m_cropLeft->value(); + sel.r.height = m_cropHeight->value(); + sel.r.top = m_cropTop->value(); + s_selection(sel); + updateVidFormat(); } void GeneralTab::composeChanged() { v4l2_selection sel; - if (!m_composeWidth->isEnabled() || !input_has_compose()) + if (!m_composeWidth->isEnabled() || !cur_io_has_compose()) return; - sel.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sel.type = g_selection_type(); sel.target = V4L2_SEL_TGT_COMPOSE; sel.r.width = m_composeWidth->value(); sel.r.left = m_composeLeft->value(); sel.r.height = m_composeHeight->value(); sel.r.top = m_composeTop->value(); - cv4l_ioctl(VIDIOC_S_SELECTION, &sel); - updateVidCapFormat(); + s_selection(sel); + updateVidFormat(); } void GeneralTab::updateVideoInput() @@ -1638,7 +1643,7 @@ void GeneralTab::updateVideoInput() if (m_audioInput) m_audioInput->setEnabled(in.audioset); if (m_cropWidth) { - bool has_crop = input_has_crop(); + bool has_crop = cur_io_has_crop(); m_cropWidth->setEnabled(has_crop); m_cropLeft->setEnabled(has_crop); @@ -1646,7 +1651,7 @@ void GeneralTab::updateVideoInput() m_cropTop->setEnabled(has_crop); } if (m_composeWidth) { - bool has_compose = input_has_compose(); + bool has_compose = cur_io_has_compose(); m_composeWidth->setEnabled(has_compose); m_composeLeft->setEnabled(has_compose); @@ -1676,6 +1681,24 @@ void GeneralTab::updateVideoOutput() updateTimings(); m_videoTimings->setEnabled(out.capabilities & V4L2_OUT_CAP_DV_TIMINGS); } + if (m_audioOutput) + m_audioOutput->setEnabled(out.audioset); + if (m_cropWidth) { + bool has_crop = cur_io_has_crop(); + + m_cropWidth->setEnabled(has_crop); + m_cropLeft->setEnabled(has_crop); + m_cropHeight->setEnabled(has_crop); + m_cropTop->setEnabled(has_crop); + } + if (m_composeWidth) { + bool has_compose = cur_io_has_compose(); + + m_composeWidth->setEnabled(has_compose); + m_composeLeft->setEnabled(has_compose); + m_composeHeight->setEnabled(has_compose); + m_composeTop->setEnabled(has_compose); + } g_mw->updateLimRGBRange(); } @@ -1935,6 +1958,8 @@ void GeneralTab::updateVidOutFormat() return; m_vidOutFormats->setCurrentIndex(desc.index); updateVidFields(); + updateCrop(); + updateCompose(); } void GeneralTab::updateVidFields() @@ -1965,14 +1990,19 @@ void GeneralTab::updateCrop() if (m_cropWidth == NULL || !m_cropWidth->isEnabled()) return; - v4l2_cropcap cropcap; - v4l2_rect &b = cropcap.bounds; - v4l2_crop crop; - v4l2_rect &c = crop.c; + v4l2_selection sel; + v4l2_rect &r = sel.r; + v4l2_rect b = { 0, 0, m_width, m_height }; - cropcap.type = crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (cv4l_ioctl(VIDIOC_CROPCAP, &cropcap) || - cv4l_ioctl(VIDIOC_G_CROP, &crop)) + sel.type = g_selection_type(); + if (sel.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + sel.target = V4L2_SEL_TGT_CROP_BOUNDS; + if (g_selection(sel)) + return; + b = sel.r; + } + sel.target = V4L2_SEL_TGT_CROP; + if (g_selection(sel)) return; m_cropWidth->blockSignals(true); @@ -1981,16 +2011,16 @@ void GeneralTab::updateCrop() m_cropTop->blockSignals(true); m_cropWidth->setRange(8, b.width); - m_cropWidth->setSliderPosition(c.width); - if (b.width != c.width) { - m_cropLeft->setRange(b.left, b.left + b.width - c.width); - m_cropLeft->setSliderPosition(c.left); + m_cropWidth->setSliderPosition(r.width); + if (b.width != r.width) { + m_cropLeft->setRange(b.left, b.left + b.width - r.width); + m_cropLeft->setSliderPosition(r.left); } m_cropHeight->setRange(8, b.height); - m_cropHeight->setSliderPosition(c.height); - if (b.height != c.height) { - m_cropTop->setRange(b.top, b.top + b.height - c.height); - m_cropTop->setSliderPosition(c.top); + m_cropHeight->setSliderPosition(r.height); + if (b.height != r.height) { + m_cropTop->setRange(b.top, b.top + b.height - r.height); + m_cropTop->setSliderPosition(r.top); } m_cropWidth->blockSignals(false); @@ -2006,10 +2036,17 @@ void GeneralTab::updateCompose() v4l2_selection sel; v4l2_rect &r = sel.r; + v4l2_rect b = { 0, 0, m_width, m_height }; - sel.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sel.type = g_selection_type(); + if (sel.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + sel.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; + if (g_selection(sel)) + return; + b = sel.r; + } sel.target = V4L2_SEL_TGT_COMPOSE; - if (cv4l_ioctl(VIDIOC_G_SELECTION, &sel)) + if (g_selection(sel)) return; m_composeWidth->blockSignals(true); @@ -2017,16 +2054,16 @@ void GeneralTab::updateCompose() m_composeHeight->blockSignals(true); m_composeTop->blockSignals(true); - m_composeWidth->setRange(8, m_width); + m_composeWidth->setRange(8, b.width); m_composeWidth->setSliderPosition(r.width); - if (m_width != r.width) { - m_composeLeft->setRange(0, m_width - r.width); + if (b.width != r.width) { + m_composeLeft->setRange(b.left, b.left + b.width - r.width); m_composeLeft->setSliderPosition(r.left); } - m_composeHeight->setRange(8, m_height); + m_composeHeight->setRange(8, b.height); m_composeHeight->setSliderPosition(r.height); - if (m_height != r.height) { - m_composeTop->setRange(0, m_height - r.height); + if (b.height != r.height) { + m_composeTop->setRange(b.top, b.top + b.height - r.height); m_composeTop->setSliderPosition(r.top); } diff --git a/utils/qv4l2/general-tab.h b/utils/qv4l2/general-tab.h index e39589c..a31d386 100644 --- a/utils/qv4l2/general-tab.h +++ b/utils/qv4l2/general-tab.h @@ -223,6 +223,8 @@ private: int g_fmt(v4l2_format &fmt, unsigned type = 0) { return m_fd->g_fmt(fmt); } int try_fmt(v4l2_format &fmt) { return m_fd->try_fmt(fmt); } int s_fmt(v4l2_format &fmt) { return m_fd->s_fmt(fmt); } + int g_selection(v4l2_selection &sel) { return m_fd->g_selection(sel); } + int s_selection(v4l2_selection &sel) { return m_fd->s_selection(sel); } int g_tuner(v4l2_tuner &tuner, unsigned index = 0) { return m_fd->g_tuner(tuner, index); } int s_tuner(v4l2_tuner &tuner) { return m_fd->g_tuner(tuner); } int g_modulator(v4l2_modulator &modulator) { return m_fd->g_modulator(modulator); } @@ -269,8 +271,8 @@ private: } bool has_crop() { return m_fd->has_crop(); } bool has_compose() { return m_fd->has_compose(); } - bool input_has_crop() { return m_fd->input_has_crop(); } - bool input_has_compose() { return m_fd->input_has_compose(); } + bool cur_io_has_crop() { return m_fd->cur_io_has_crop(); } + bool cur_io_has_compose() { return m_fd->cur_io_has_compose(); } bool ioctl_exists(int ret) { return ret == 0 || errno != ENOTTY; diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp index 3147d68..3c11ddc 100644 --- a/utils/qv4l2/qv4l2.cpp +++ b/utils/qv4l2/qv4l2.cpp @@ -485,6 +485,12 @@ bool ApplicationWindow::startStreaming() cv4l_buffer buf; m_queue.buffer_init(buf, i); + buf.s_field(m_tpgField); + tpg_s_field(&m_tpg, m_tpgField); + if (m_tpgField == V4L2_FIELD_TOP) + m_tpgField = V4L2_FIELD_BOTTOM; + else if (m_tpgField == V4L2_FIELD_BOTTOM) + m_tpgField = V4L2_FIELD_TOP; for (unsigned p = 0; p < m_queue.g_num_planes(); p++) tpg_fillbuffer(&m_tpg, m_tpgStd, p, (u8 *)m_queue.g_dataptr(i, p)); qbuf(buf); @@ -740,6 +746,12 @@ void ApplicationWindow::outFrame() return; } m_queue.buffer_init(buf, buf.g_index()); + buf.s_field(m_tpgField); + tpg_s_field(&m_tpg, m_tpgField); + if (m_tpgField == V4L2_FIELD_TOP) + m_tpgField = V4L2_FIELD_BOTTOM; + else if (m_tpgField == V4L2_FIELD_BOTTOM) + m_tpgField = V4L2_FIELD_TOP; for (unsigned p = 0; p < m_queue.g_num_planes(); p++) tpg_fillbuffer(&m_tpg, m_tpgStd, p, (u8 *)m_queue.g_dataptr(buf.g_index(), p)); tpg_update_mv_count(&m_tpg, V4L2_FIELD_HAS_T_OR_B(m_tpgField)); @@ -1078,7 +1090,7 @@ void ApplicationWindow::outStart(bool start) g_std(m_tpgStd); else m_tpgStd = 0; - m_tpgField = fmt.g_field(); + m_tpgField = fmt.g_first_field(m_tpgStd); m_tpgSizeImage = fmt.g_sizeimage(0); tpg_alloc(&m_tpg, fmt.g_width()); m_useTpg = tpg_s_fourcc(&m_tpg, fmt.g_pixelformat()); diff --git a/utils/v4l2-compliance/cv4l-helpers.h b/utils/v4l2-compliance/cv4l-helpers.h index b18c2ac..d050686 100644 --- a/utils/v4l2-compliance/cv4l-helpers.h +++ b/utils/v4l2-compliance/cv4l-helpers.h @@ -114,6 +114,16 @@ public: return v4l_s_fmt(this, &fmt); } + int g_selection(v4l2_selection &sel) + { + return v4l_g_selection(this, &sel); + } + + int s_selection(v4l2_selection &sel) + { + return v4l_s_selection(this, &sel); + } + int g_tuner(v4l2_tuner &tuner, unsigned index = 0) { memset(&tuner, 0, sizeof(tuner)); @@ -191,14 +201,12 @@ public: bool has_crop() { - v4l2_crop crop; - v4l2_cropcap cropcap; + v4l2_selection sel; - crop.type = g_selection_type(); - cropcap.type = crop.type; - return ioctl_exists(cv4l_ioctl(VIDIOC_G_CROP, &crop)) && - ioctl_exists(cv4l_ioctl(VIDIOC_S_CROP, &crop)) && - ioctl_exists(cv4l_ioctl(VIDIOC_CROPCAP, &cropcap)); + memset(&sel, 0, sizeof(sel)); + sel.type = g_selection_type(); + sel.target = V4L2_SEL_TGT_CROP; + return ioctl_exists(g_selection(sel)); } bool has_compose() @@ -208,32 +216,27 @@ public: memset(&sel, 0, sizeof(sel)); sel.type = g_selection_type(); sel.target = V4L2_SEL_TGT_COMPOSE; - return ioctl_exists(cv4l_ioctl(VIDIOC_G_SELECTION, &sel)) && - ioctl_exists(cv4l_ioctl(VIDIOC_S_SELECTION, &sel)); + return ioctl_exists(g_selection(sel)); } - bool input_has_crop() + bool cur_io_has_crop() { - v4l2_crop crop; - v4l2_cropcap cropcap; + v4l2_selection sel; - crop.type = g_selection_type(); - cropcap.type = crop.type; - return cv4l_ioctl(VIDIOC_G_CROP, &crop) == 0 && - cv4l_ioctl(VIDIOC_S_CROP, &crop) == 0 && - cv4l_ioctl(VIDIOC_CROPCAP, &cropcap) == 0 && - cropcap.bounds.width && cropcap.bounds.height; + memset(&sel, 0, sizeof(sel)); + sel.type = g_selection_type(); + sel.target = V4L2_SEL_TGT_CROP; + return g_selection(sel) == 0; } - bool input_has_compose() + bool cur_io_has_compose() { v4l2_selection sel; memset(&sel, 0, sizeof(sel)); sel.type = g_selection_type(); sel.target = V4L2_SEL_TGT_COMPOSE; - return cv4l_ioctl(VIDIOC_G_SELECTION, &sel) == 0 && - cv4l_ioctl(VIDIOC_S_SELECTION, &sel) == 0; + return g_selection(sel) == 0; } int subscribe_event(v4l2_event_subscription &sub) diff --git a/utils/v4l2-compliance/v4l-helpers.h b/utils/v4l2-compliance/v4l-helpers.h index a3721bc..e9f4176 100644 --- a/utils/v4l2-compliance/v4l-helpers.h +++ b/utils/v4l2-compliance/v4l-helpers.h @@ -28,6 +28,7 @@ struct v4l_fd { bool have_query_ext_ctrl; bool have_ext_ctrls; bool have_next_ctrl; + bool have_selection; int (*open)(struct v4l_fd *f, const char *file, int oflag, ...); int (*close)(struct v4l_fd *f); @@ -392,6 +393,7 @@ static inline int v4l_open(struct v4l_fd *f, const char *devname, bool non_block struct v4l2_query_ext_ctrl qec = { V4L2_CTRL_FLAG_NEXT_CTRL }; struct v4l2_ext_controls ec = { 0, 0 }; struct v4l2_queryctrl qc = { V4L2_CTRL_FLAG_NEXT_CTRL }; + struct v4l2_selection sel = { 0 }; f->fd = f->open(f, devname, O_RDWR | (non_blocking ? O_NONBLOCK : 0)); @@ -405,12 +407,17 @@ static inline int v4l_open(struct v4l_fd *f, const char *devname, bool non_block v4l_close(f); return -1; } + f->caps = v4l_capability_g_caps(&f->cap); + f->type = v4l_determine_type(f); + f->have_query_ext_ctrl = v4l_ioctl(f, VIDIOC_QUERY_EXT_CTRL, &qec) == 0; f->have_ext_ctrls = v4l_ioctl(f, VIDIOC_TRY_EXT_CTRLS, &ec) == 0; f->have_next_ctrl = v4l_ioctl(f, VIDIOC_QUERYCTRL, &qc) == 0; + sel.type = v4l_g_selection_type(f); + sel.target = sel.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? + V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE; + f->have_selection = v4l_ioctl(f, VIDIOC_G_SELECTION, &sel) == 0; - f->caps = v4l_capability_g_caps(&f->cap); - f->type = v4l_determine_type(f); return f->fd; } @@ -1507,6 +1514,76 @@ 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) +{ + struct v4l2_cropcap cc; + struct v4l2_crop crop; + int ret; + + if (f->have_selection) + return v4l_ioctl(f, VIDIOC_G_SELECTION, sel); + crop.type = sel->type; + cc.type = sel->type; + ret = v4l_ioctl(f, VIDIOC_CROPCAP, &cc); + if (ret) + return ret; + ret = v4l_ioctl(f, VIDIOC_G_CROP, &crop); + if (ret) + return ret; + if (sel->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = crop.c; + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = cc.defrect; + return 0; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = cc.bounds; + return 0; + default: + return EINVAL; + } + } + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE: + sel->r = crop.c; + return 0; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + sel->r = cc.defrect; + return 0; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r = cc.bounds; + return 0; + default: + return EINVAL; + } +} + +static inline int v4l_s_selection(v4l_fd *f, struct v4l2_selection *sel) +{ + struct v4l2_crop crop; + int ret; + + if (f->have_selection) + return v4l_ioctl(f, VIDIOC_S_SELECTION, sel); + crop.type = sel->type; + ret = v4l_ioctl(f, VIDIOC_G_CROP, &crop); + if (ret) + return ret; + if (sel->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + sel->target == V4L2_SEL_TGT_CROP) { + crop.c = sel->r; + return v4l_ioctl(f, VIDIOC_S_CROP, &crop); + } + if (sel->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + sel->target == V4L2_SEL_TGT_COMPOSE) { + crop.c = sel->r; + return v4l_ioctl(f, VIDIOC_S_CROP, &crop); + } + return EINVAL; +} + #ifdef __cplusplus } #endif /* __cplusplus */ -- 2.7.4