qv4l2: implement cropping and composing for video output.
authorHans Verkuil <hans.verkuil@cisco.com>
Sun, 27 Jul 2014 13:30:59 +0000 (15:30 +0200)
committerHans Verkuil <hans.verkuil@cisco.com>
Sun, 27 Jul 2014 13:30:59 +0000 (15:30 +0200)
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 <hans.verkuil@cisco.com>
utils/qv4l2/general-tab.cpp
utils/qv4l2/general-tab.h
utils/qv4l2/qv4l2.cpp
utils/v4l2-compliance/cv4l-helpers.h
utils/v4l2-compliance/v4l-helpers.h

index 8a716b7..df926b7 100644 (file)
@@ -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);
        }
 
index e39589c..a31d386 100644 (file)
@@ -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;
index 3147d68..3c11ddc 100644 (file)
@@ -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());
index b18c2ac..d050686 100644 (file)
@@ -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)
index a3721bc..e9f4176 100644 (file)
@@ -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 */