qv4l2: correctly resubscribe events after a reopen.
authorHans Verkuil <hans.verkuil@cisco.com>
Sun, 20 Jul 2014 15:48:15 +0000 (17:48 +0200)
committerHans Verkuil <hans.verkuil@cisco.com>
Sun, 20 Jul 2014 15:48:15 +0000 (17:48 +0200)
The reopen() call closes the original filehandle and at the same time any
subscribed events. So resubscribe the events.

In addition both the ApplicationWindow and the GeneralTab had a cv4l_fd
instance with the same fd. With reopen that means that those fd's are
no longer in sync.

GeneralTab now has a pointer to the cv4l_fd of the ApplicationWindow, so
they remain synchronized.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
utils/qv4l2/ctrl-tab.cpp
utils/qv4l2/general-tab.cpp
utils/qv4l2/general-tab.h
utils/qv4l2/qv4l2.cpp
utils/qv4l2/qv4l2.h

index a1d34e9..c264394 100644 (file)
@@ -404,11 +404,6 @@ void ApplicationWindow::addCtrl(QGridLayout *grid, const v4l2_queryctrl &qctrl)
        default:
                return;
        }
-       struct v4l2_event_subscription sub;
-       memset(&sub, 0, sizeof(sub));
-       sub.type = V4L2_EVENT_CTRL;
-       sub.id = qctrl.id;
-       subscribe_event(sub);
 
        m_sigMapper->setMapping(m_widgetMap[qctrl.id], qctrl.id);
        if (qctrl.flags & CTRL_FLAG_DISABLED) {
@@ -673,6 +668,21 @@ void ApplicationWindow::updateCtrlRange(unsigned id, __s32 new_val)
        }
 }
 
+void ApplicationWindow::subscribeCtrlEvents()
+{
+       for (ClassMap::iterator iter = m_classMap.begin(); iter != m_classMap.end(); ++iter) {
+               for (unsigned i = 0; i < m_classMap[iter->first].size(); i++) {
+                       unsigned id = m_classMap[iter->first][i];
+                       struct v4l2_event_subscription sub;
+
+                       memset(&sub, 0, sizeof(sub));
+                       sub.type = V4L2_EVENT_CTRL;
+                       sub.id = id;
+                       subscribe_event(sub);
+               }
+       }
+}
+
 void ApplicationWindow::refresh(unsigned ctrl_class)
 {
        if (!m_haveExtendedUserCtrls && ctrl_class == V4L2_CTRL_CLASS_USER) {
index 84eabf8..8b865ee 100644 (file)
@@ -55,9 +55,9 @@ static QString pixfmt2s(unsigned id)
        return pixfmt;
 }
 
-GeneralTab::GeneralTab(const QString &device, cv4l_fd *_fd, int n, QWidget *parent) :
+GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *parent) :
        QGridLayout(parent),
-       cv4l_fd(_fd),
+       m_fd(fd),
        m_row(0),
        m_col(0),
        m_cols(n),
@@ -128,7 +128,7 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *_fd, int n, QWidget *pare
        addTitle("General Information");
 
        addLabel("Device");
-       addLabel(device + (g_direct() ? "" : " (wrapped)"));
+       addLabel(device + (m_fd->g_direct() ? "" : " (wrapped)"));
 
        addLabel("Driver");
        addLabel((char *)m_querycap.driver);
@@ -225,7 +225,7 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *_fd, int n, QWidget *pare
 capture_method:
        addLabel("Capture Method");
        m_capMethods = new QComboBox(parent);
-       if (g_caps() & V4L2_CAP_STREAMING) {
+       if (has_streaming()) {
                cv4l_queue q;
 
                // Yuck. The videobuf framework does not accept a reqbufs count of 0.
@@ -235,23 +235,24 @@ capture_method:
                // buffers allocated. This is the only really portable way as long
                // as there are still drivers around that do not support reqbufs(0).
                q.init(g_type(), V4L2_MEMORY_USERPTR);
-               if (q.reqbufs(this, 1) == 0) {
+               if (q.reqbufs(m_fd, 1) == 0) {
                        m_capMethods->addItem("User pointer I/O", QVariant(methodUser));
-                       reopen(true);
+                       m_fd->reopen(true);
                }
                q.init(g_type(), V4L2_MEMORY_MMAP);
-               if (q.reqbufs(this, 1) == 0) {
+               if (q.reqbufs(m_fd, 1) == 0) {
                        m_capMethods->addItem("Memory mapped I/O", QVariant(methodMmap));
-                       reopen(true);
+                       m_fd->reopen(true);
                }
        }
-       if (g_caps() & V4L2_CAP_READWRITE) {
-               m_capMethods->addItem("read()", QVariant(methodRead));
+       if (has_rw()) {
+               if (v4l_type_is_output(g_type()))
+                       m_capMethods->addItem("write()", QVariant(methodRead));
+               else
+                       m_capMethods->addItem("read()", QVariant(methodRead));
        }
        addWidget(m_capMethods);
 
-
-
        if (!isRadio() && !isVbi() && !m_isOutput && (has_crop() || has_compose())) {
                addTitle("Cropping & Compose Settings");
                cropSection();
@@ -271,6 +272,19 @@ done:
        fixWidth();
 }
 
+void GeneralTab::sourceChangeSubscribe()
+{
+       v4l2_input vin;
+
+       if (!enum_input(vin, true)) {
+               struct v4l2_event_subscription sub = {
+                       V4L2_EVENT_SOURCE_CHANGE, vin.index
+               };
+
+               subscribe_event(sub);
+       } while (!enum_input(vin));
+}
+
 void GeneralTab::inputSection(bool needsStd, bool needsTimings, v4l2_input vin)
 {
        if (!isRadio() && !enum_input(vin, true)) {
@@ -282,12 +296,6 @@ void GeneralTab::inputSection(bool needsStd, bool needsTimings, v4l2_input vin)
                                needsStd = true;
                        if (vin.capabilities & V4L2_IN_CAP_DV_TIMINGS)
                                needsTimings = true;
-
-                       struct v4l2_event_subscription sub = {
-                               V4L2_EVENT_SOURCE_CHANGE, vin.index
-                       };
-
-                       subscribe_event(sub);
                } while (!enum_input(vin));
                addWidget(m_videoInput);
                connect(m_videoInput, SIGNAL(activated(int)), SLOT(inputChanged(int)));
@@ -542,7 +550,7 @@ void GeneralTab::audioSection(v4l2_audio vaudio, v4l2_audioout vaudout)
                                setAudioDeviceBufferSize(75);
                        } else {
                                v4l2_fract fract;
-                               if (cv4l_fd::get_interval(fract)) {
+                               if (m_fd->get_interval(fract)) {
                                        // Default values are for 30 FPS
                                        fract.numerator = 33;
                                        fract.denominator = 1000;
@@ -2070,7 +2078,7 @@ void GeneralTab::updateFrameInterval()
        m_frameInterval->setEnabled(m_has_interval);
        if (m_has_interval) {
                m_interval = frmival.discrete;
-               curr_ok = !cv4l_fd::get_interval(curr);
+               curr_ok = !m_fd->get_interval(curr);
                do {
                        m_frameInterval->addItem(QString("%1 fps")
                                .arg((double)frmival.discrete.denominator / frmival.discrete.numerator));
index 48109f7..2f50d22 100644 (file)
@@ -47,7 +47,7 @@ class QSpinBox;
 class QToolButton;
 class QSlider;
 
-class GeneralTab: public QGridLayout, public cv4l_fd
+class GeneralTab: public QGridLayout
 {
        Q_OBJECT
 
@@ -73,6 +73,7 @@ public:
        bool isPlanar() const { return m_isPlanar; }
        void setHaveBuffers(bool haveBuffers);
        void sourceChange(const v4l2_event &ev);
+       void sourceChangeSubscribe();
        unsigned getDisplayColorspace() const;
        unsigned getColorspace() const;
        int getWidth();
@@ -175,7 +176,96 @@ private:
        {
                g_mw->error(error);
        }
+       v4l_fd *g_v4l_fd() { return m_fd->g_v4l_fd(); }
 
+       __u32 g_type() const { return m_fd->g_type(); }
+       void s_type(__u32 type) { m_fd->s_type(type); }
+       __u32 g_selection_type() const { return m_fd->g_selection_type(); }
+       __u32 g_caps() const { return m_fd->g_caps(); }
+
+       bool has_vid_cap() const { return m_fd->has_vid_cap(); }
+       bool has_vid_out() const { return m_fd->has_vid_out(); }
+       bool has_vid_m2m() const { return m_fd->has_vid_m2m(); }
+       bool has_vid_mplane() const { return m_fd->has_vid_mplane(); }
+       bool has_overlay_cap() const { return m_fd->has_overlay_cap(); }
+       bool has_overlay_out() const { return m_fd->has_overlay_out(); }
+       bool has_raw_vbi_cap() const { return m_fd->has_raw_vbi_cap(); }
+       bool has_sliced_vbi_cap() const { return m_fd->has_sliced_vbi_cap(); }
+       bool has_vbi_cap() const { return m_fd->has_vbi_cap(); }
+       bool has_raw_vbi_out() const { return m_fd->has_raw_vbi_out(); }
+       bool has_sliced_vbi_out() const { return m_fd->has_sliced_vbi_out(); }
+       bool has_vbi_out() const { return m_fd->has_vbi_out(); }
+       bool has_vbi() const { return m_fd->has_vbi(); }
+       bool has_radio_rx() const { return m_fd->has_radio_rx(); }
+       bool has_radio_tx() const { return m_fd->has_radio_tx(); }
+       bool has_rds_cap() const { return m_fd->has_rds_cap(); }
+       bool has_rds_out() const { return m_fd->has_rds_out(); }
+       bool has_sdr_cap() const { return m_fd->has_sdr_cap(); }
+       bool has_hwseek() const { return m_fd->has_hwseek(); }
+       bool has_rw() const { return m_fd->has_rw(); }
+       bool has_streaming() const { return m_fd->has_streaming(); }
+       bool has_ext_pix_format() const { return m_fd->has_ext_pix_format(); }
+
+       bool g_direct() const { return m_fd->g_direct(); }
+       void s_direct(bool direct) { m_fd->s_direct(direct); }
+       int queryctrl(v4l2_queryctrl &qc) { return m_fd->queryctrl(qc); }
+       int querymenu(v4l2_querymenu &qm) { return m_fd->querymenu(qm); }
+       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_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); }
+       int s_modulator(v4l2_modulator &modulator) { return m_fd->s_modulator(modulator); }
+       int enum_input(v4l2_input &in, bool init = false, int index = 0) { return m_fd->enum_input(in, init, index); }
+       int enum_output(v4l2_output &out, bool init = false, int index = 0) { return m_fd->enum_output(out, init, index); }
+       int enum_audio(v4l2_audio &audio, bool init = false, int index = 0) { return m_fd->enum_audio(audio, init, index); }
+       int enum_audout(v4l2_audioout &audout, bool init = false, int index = 0) { return m_fd->enum_audout(audout, init, index); }
+       int subscribe_event(v4l2_event_subscription &sub) { return m_fd->subscribe_event(sub); }
+       int dqevent(v4l2_event &ev) { return m_fd->dqevent(ev); }
+       int g_input(int &input) { return m_fd->g_input(input); }
+       int s_input(int input) { return m_fd->s_input(input); }
+       int g_output(int &output) { return m_fd->g_output(output); }
+       int s_output(int output) { return m_fd->s_output(output); }
+       int g_audio(v4l2_audio &audio) { return m_fd->g_audio(audio); }
+       int s_audio(int input) { return m_fd->s_audio(input); }
+       int g_audout(v4l2_audioout &audout) { return m_fd->g_audout(audout); }
+       int s_audout(int output) { return m_fd->s_audout(output); }
+       int g_std(v4l2_std_id &std) { return m_fd->g_std(std); }
+       int s_std(v4l2_std_id std) { return m_fd->s_std(std); }
+       int query_std(v4l2_std_id &std) { return m_fd->query_std(std); }
+       int g_dv_timings(v4l2_dv_timings &timings) { return m_fd->g_dv_timings(timings); }
+       int s_dv_timings(v4l2_dv_timings &timings) { return m_fd->s_dv_timings(timings); }
+       int query_dv_timings(v4l2_dv_timings &timings) { return m_fd->query_dv_timings(timings); }
+       int g_frequency(v4l2_frequency &freq, unsigned index = 0) { return m_fd->g_frequency(freq, index); }
+       int s_frequency(v4l2_frequency &freq) { return m_fd->s_frequency(freq); }
+       int streamon(__u32 type = 0) { return m_fd->streamon(type); }
+       int streamoff(__u32 type = 0) { return m_fd->streamoff(type); }
+       int querybuf(v4l_buffer &buf, unsigned index) { return m_fd->querybuf(buf, index); }
+       int dqbuf(v4l_buffer &buf) { return m_fd->dqbuf(buf); }
+       int qbuf(v4l_buffer &buf) { return m_fd->qbuf(buf); }
+       int prepare_buf(v4l_buffer &buf) { return m_fd->prepare_buf(buf); }
+       int enum_std(v4l2_standard &std, bool init = false, int index = 0) { return m_fd->enum_std(std, init, index); }
+       int enum_dv_timings(v4l2_enum_dv_timings &timings, bool init = false, int index = 0) { return m_fd->enum_dv_timings(timings, init, index); }
+       int enum_fmt(v4l2_fmtdesc &fmt, bool init = false, int index = 0, unsigned type = 0) { return m_fd->enum_fmt(fmt, init, index, type); }
+       int enum_framesizes(v4l2_frmsizeenum &frm, __u32 init_pixfmt = 0, int index = 0) { return m_fd->enum_framesizes(frm, init_pixfmt, index); }
+       int enum_frameintervals(v4l2_frmivalenum &frm, __u32 init_pixfmt = 0, __u32 w = 0, __u32 h = 0, int index = 0) { return m_fd->enum_frameintervals(frm, init_pixfmt, w, h, index); }
+       int set_interval(v4l2_fract interval, unsigned type = 0) { return m_fd->set_interval(interval, type); }
+       v4l2_fract g_pixel_aspect(unsigned &width, unsigned &height, unsigned type = 0)
+       {
+               return m_fd->g_pixel_aspect(width, height, type);
+       }
+       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 ioctl_exists(int ret)
+       {
+               return ret == 0 || errno != ENOTTY;
+       }
+
+
+       cv4l_fd *m_fd;
        int m_row;
        int m_col;
        int m_cols;
index 7fd5753..b0e5d4f 100644 (file)
@@ -287,6 +287,8 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
 #ifdef HAVE_QTGL
        m_useGLAct->setEnabled(CaptureWinGL::isSupported());
 #endif
+       m_genTab->sourceChangeSubscribe();
+       subscribeCtrlEvents();
        m_ctrlNotifier = new QSocketNotifier(g_fd(), QSocketNotifier::Exception, m_tabs);
        connect(m_ctrlNotifier, SIGNAL(activated(int)), this, SLOT(ctrlEvent()));
 }
@@ -474,6 +476,8 @@ bool ApplicationWindow::startCapture()
 
        m_queue.free(this);
        reopen(true);
+       m_genTab->sourceChangeSubscribe();
+       subscribeCtrlEvents();
        m_capStartAct->setChecked(false);
 #ifdef HAVE_QTGL
        m_useGLAct->setEnabled(CaptureWinGL::isSupported());
@@ -739,6 +743,8 @@ void ApplicationWindow::stopCapture()
                break;
        }
        reopen(true);
+       m_genTab->sourceChangeSubscribe();
+       subscribeCtrlEvents();
        m_genTab->setHaveBuffers(false);
        refresh();
 }
@@ -757,7 +763,6 @@ bool ApplicationWindow::showFrames()
 void ApplicationWindow::traceIoctls(bool enable)
 {
        s_trace(enable);
-       m_genTab->s_trace(enable);
 }
 
 void ApplicationWindow::enableScaling(bool enable)
index dcc7b03..7ebc887 100644 (file)
@@ -174,6 +174,7 @@ private:
        void addCtrl(QGridLayout *grid, const struct v4l2_queryctrl &qctrl);
        void updateCtrl(unsigned id);
        void updateCtrlRange(unsigned id, __s32 val);
+       void subscribeCtrlEvents();
        void refresh(unsigned ctrl_class);
        void refresh();
        void makeSnapshot(unsigned char *buf, unsigned size);