qv4l2: add support for output video devices
authorHans Verkuil <hans.verkuil@cisco.com>
Fri, 25 Jul 2014 14:24:39 +0000 (16:24 +0200)
committerHans Verkuil <hans.verkuil@cisco.com>
Fri, 25 Jul 2014 14:28:30 +0000 (16:28 +0200)
Hook the test pattern generator into qv4l2, allowing it to be used
as a generator for video output devices. Careful attention has been
given to correct colorspace and RGB quantization handling.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
utils/qv4l2/Makefile.am
utils/qv4l2/general-tab.cpp
utils/qv4l2/general-tab.h
utils/qv4l2/qv4l2.cpp
utils/qv4l2/qv4l2.h
utils/qv4l2/tpg-tab.cpp [new file with mode: 0644]
utils/v4l2-compliance/cv4l-helpers.h

index 1e3d081..ab57056 100644 (file)
@@ -1,13 +1,14 @@
 bin_PROGRAMS = qv4l2
 man_MANS = qv4l2.1
 
-qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp capture-win.cpp \
+qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp capture-win.cpp tpg-tab.cpp \
   capture-win-qt.cpp capture-win-qt.h capture-win-gl.cpp capture-win-gl.h alsa_stream.c alsa_stream.h \
-  raw2sliced.cpp qv4l2.h capture-win.h general-tab.h vbi-tab.h raw2sliced.h
+  raw2sliced.cpp qv4l2.h capture-win.h general-tab.h vbi-tab.h raw2sliced.h \
+  ../v4l2-ctl/vivid-tpg.c ../v4l2-ctl/vivid-tpg.h ../v4l2-ctl/vivid-tpg-colors.c ../v4l2-ctl/vivid-tpg-colors.h
 nodist_qv4l2_SOURCES = moc_qv4l2.cpp moc_general-tab.cpp moc_capture-win.cpp moc_vbi-tab.cpp qrc_qv4l2.cpp
 qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la \
   ../libmedia_dev/libmedia_dev.la
-qv4l2_CPPFLAGS = -I../v4l2-compliance
+qv4l2_CPPFLAGS = -I../v4l2-compliance -I../v4l2-ctl
 
 if WITH_QTGL
 qv4l2_CPPFLAGS += $(QTGL_CFLAGS)
index e94d99f..8a716b7 100644 (file)
@@ -1197,7 +1197,7 @@ CapMethod GeneralTab::capMethod()
        return (CapMethod)m_capMethods->itemData(m_capMethods->currentIndex()).toInt();
 }
 
-void GeneralTab::updateGUIInput(int input)
+void GeneralTab::updateGUIInput(__u32 input)
 {
        v4l2_input in;
        enum_input(in, true, input);
@@ -1237,7 +1237,7 @@ void GeneralTab::updateGUIInput(int input)
        }
 }
 
-void GeneralTab::updateGUIOutput(int output)
+void GeneralTab::updateGUIOutput(__u32 output)
 {
        v4l2_output out;
        enum_output(out, true, output);
@@ -1272,7 +1272,7 @@ void GeneralTab::updateGUIOutput(int output)
 
 void GeneralTab::inputChanged(int input)
 {
-       s_input(input);
+       s_input((__u32)input);
 
        if (m_audioInput)
                updateAudioInput();
@@ -1284,7 +1284,7 @@ void GeneralTab::inputChanged(int input)
 
 void GeneralTab::outputChanged(int output)
 {
-       s_output(output);
+       s_output((__u32)output);
        updateVideoOutput();
        updateVidOutFormat();
        updateGUIOutput(output);
@@ -1292,13 +1292,13 @@ void GeneralTab::outputChanged(int output)
 
 void GeneralTab::inputAudioChanged(int input)
 {
-       s_audio(input);
+       s_audio((__u32)input);
        updateAudioInput();
 }
 
 void GeneralTab::outputAudioChanged(int output)
 {
-       s_audout(output);
+       s_audout((__u32)output);
        updateAudioOutput();
 }
 
@@ -1598,7 +1598,7 @@ void GeneralTab::composeChanged()
 
 void GeneralTab::updateVideoInput()
 {
-       int input;
+       __u32 input;
        v4l2_input in;
 
        if (g_input(input))
@@ -1657,7 +1657,7 @@ void GeneralTab::updateVideoInput()
 
 void GeneralTab::updateVideoOutput()
 {
-       int output;
+       __u32 output;
        v4l2_output out;
 
        if (g_output(output))
@@ -1676,6 +1676,7 @@ void GeneralTab::updateVideoOutput()
                updateTimings();
                m_videoTimings->setEnabled(out.capabilities & V4L2_OUT_CAP_DV_TIMINGS);
        }
+       g_mw->updateLimRGBRange();
 }
 
 void GeneralTab::updateAudioInput()
@@ -1815,6 +1816,7 @@ void GeneralTab::updateTimings()
        m_videoTimings->setStatusTip(what);
        m_videoTimings->setWhatsThis(what);
        updateVidFormat();
+       g_mw->updateLimRGBRange();
 }
 
 void GeneralTab::qryTimingsClicked()
index ac9868e..e39589c 100644 (file)
@@ -132,8 +132,8 @@ private:
        void formatSection(v4l2_fmtdesc fmt); 
        void cropSection(); 
        void fixWidth();
-       void updateGUIInput(int);
-       void updateGUIOutput(int);
+       void updateGUIInput(__u32);
+       void updateGUIOutput(__u32);
        void updateVideoInput();
        void updateVideoOutput();
        void updateAudioInput();
@@ -233,14 +233,14 @@ private:
        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_input(__u32 &input) { return m_fd->g_input(input); }
+       int s_input(__u32 input) { return m_fd->s_input(input); }
+       int g_output(__u32 &output) { return m_fd->g_output(output); }
+       int s_output(__u32 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 s_audio(__u32 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 s_audout(__u32 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); }
index c4c071a..3147d68 100644 (file)
@@ -77,11 +77,13 @@ ApplicationWindow::ApplicationWindow() :
        setAttribute(Qt::WA_DeleteOnClose, true);
 
        m_capNotifier = NULL;
+       m_outNotifier = NULL;
        m_ctrlNotifier = NULL;
        m_capImage = NULL;
        m_frameData = NULL;
        m_nbuffers = 0;
        m_makeSnapshot = false;
+       m_tpgLimRGBRange = NULL;
        for (unsigned b = 0; b < sizeof(m_clear); b++)
                m_clear[b] = false;
 
@@ -267,6 +269,14 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
        connect(m_genTab, SIGNAL(displayColorspaceChanged()), this, SLOT(updateDisplayColorspace()));
        connect(m_genTab, SIGNAL(clearBuffers()), this, SLOT(clearBuffers()));
        m_tabs->addTab(w, "General Settings");
+
+       m_tpgLimRGBRange = NULL;
+       if (has_vid_out()) {
+               addTpgTab(m_minWidth);
+               tpg_init(&m_tpg, 640, 360);
+               updateLimRGBRange();
+       }
+
        addTabs(m_winWidth);
        m_vbiTab = NULL;
        if (has_vbi_cap()) {
@@ -281,7 +291,7 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
        m_tabs->show();
        m_tabs->setFocus();
        m_convertData = v4lconvert_create(g_fd());
-       bool canStream = g_fd() >= 0 && v4l_type_is_capture(g_type()) &&
+       bool canStream = g_fd() >= 0 && (v4l_type_is_capture(g_type()) || has_vid_out()) &&
                                        !has_radio_rx() && !has_radio_tx();
        m_capStartAct->setEnabled(canStream);
        m_saveRawAct->setEnabled(canStream);
@@ -423,7 +433,7 @@ void ApplicationWindow::newCaptureWin()
         connect(m_capture, SIGNAL(close()), this, SLOT(closeCaptureWin()));
 }
 
-bool ApplicationWindow::startCapture()
+bool ApplicationWindow::startStreaming()
 {
        startAudio();
 
@@ -463,11 +473,23 @@ bool ApplicationWindow::startCapture()
                        break;
                }
 
-               for (unsigned i = 0; i < m_queue.g_buffers(); i++) {
-                       cv4l_buffer buf;
-       
-                       m_queue.buffer_init(buf, i);
-                       qbuf(buf); 
+               if (v4l_type_is_capture(g_type())) {
+                       for (unsigned i = 0; i < m_queue.g_buffers(); i++) {
+                               cv4l_buffer buf;
+
+                               m_queue.buffer_init(buf, i);
+                               qbuf(buf); 
+                       }
+               } else {
+                       for (unsigned i = 0; i < m_queue.g_buffers(); i++) {
+                               cv4l_buffer buf;
+
+                               m_queue.buffer_init(buf, i);
+                               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); 
+                               tpg_update_mv_count(&m_tpg, V4L2_FIELD_HAS_T_OR_B(m_tpgField));
+                       }
                }
 
                if (streamon()) {
@@ -688,6 +710,76 @@ void ApplicationWindow::capSdrFrame()
                refresh();
 }
 
+void ApplicationWindow::outFrame()
+{
+       cv4l_buffer buf(m_queue);
+       int s = 0;
+
+       switch (m_capMethod) {
+       case methodRead:
+               tpg_fillbuffer(&m_tpg, m_tpgStd, 0, (u8 *)m_frameData);
+               s = write(m_frameData, m_tpgSizeImage);
+               tpg_update_mv_count(&m_tpg, V4L2_FIELD_HAS_T_OR_B(m_tpgField));
+
+               if (s < 0) {
+                       if (errno != EAGAIN) {
+                               error("write");
+                               m_capStartAct->setChecked(false);
+                       }
+                       return;
+               }
+               break;
+
+       case methodMmap:
+       case methodUser:
+               if (dqbuf(buf)) {
+                       if (errno == EAGAIN)
+                               return;
+                       error("dqbuf");
+                       m_capStartAct->setChecked(false);
+                       return;
+               }
+               m_queue.buffer_init(buf, buf.g_index());
+               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));
+               break;
+       }
+
+       QString status, curStatus;
+       struct timeval tv, res;
+
+       if (m_frame == 0)
+               gettimeofday(&m_tv, NULL);
+       gettimeofday(&tv, NULL);
+       timersub(&tv, &m_tv, &res);
+       if (res.tv_sec) {
+               m_fps = (100 * (m_frame - m_lastFrame)) /
+                       (res.tv_sec * 100 + res.tv_usec / 10000);
+               m_lastFrame = m_frame;
+               m_tv = tv;
+       }
+
+
+       status = QString("Frame: %1 Fps: %2").arg(++m_frame).arg(m_fps);
+
+       if (m_capMethod == methodMmap || m_capMethod == methodUser) {
+               if (m_clear[buf.g_index()]) {
+                       for (unsigned p = 0; p < m_queue.g_num_planes(); p++)
+                               memset(m_queue.g_dataptr(buf.g_index(), p), 0, buf.g_length(p));
+                       m_clear[buf.g_index()] = false;
+               }
+                       
+               qbuf(buf);
+       }
+
+       curStatus = statusBar()->currentMessage();
+       if (curStatus.isEmpty() || curStatus.startsWith("Frame: "))
+               statusBar()->showMessage(status);
+       if (m_frame == 1)
+               refresh();
+}
+
 void ApplicationWindow::capFrame()
 {
        cv4l_buffer buf(m_queue);
@@ -825,8 +917,10 @@ void ApplicationWindow::capFrame()
                refresh();
 }
 
-void ApplicationWindow::stopCapture()
+void ApplicationWindow::stopStreaming()
 {
+       v4l2_encoder_cmd cmd;
+
        stopAudio();
 
        s_priority(V4L2_PRIORITY_DEFAULT);
@@ -834,16 +928,18 @@ void ApplicationWindow::stopCapture()
        if (!m_genTab->isSDR() && m_genTab->isRadio())
                return;
 
-       v4l2_encoder_cmd cmd;
+       if (v4l_type_is_capture(g_type()))
+               m_capture->stop();
 
-       m_capture->stop();
        m_snapshotAct->setDisabled(true);
        m_useGLAct->setEnabled(CaptureWinGL::isSupported());
        switch (m_capMethod) {
        case methodRead:
-               memset(&cmd, 0, sizeof(cmd));
-               cmd.cmd = V4L2_ENC_CMD_STOP;
-               cv4l_ioctl(VIDIOC_ENCODER_CMD, &cmd);
+               if (v4l_type_is_capture(g_type())) {
+                       memset(&cmd, 0, sizeof(cmd));
+                       cmd.cmd = V4L2_ENC_CMD_STOP;
+                       cv4l_ioctl(VIDIOC_ENCODER_CMD, &cmd);
+               }
                break;
 
        case methodMmap:
@@ -965,13 +1061,88 @@ void ApplicationWindow::closeCaptureWin()
        m_capStartAct->setChecked(false);
 }
 
+void ApplicationWindow::outStart(bool start)
+{
+       if (start) {
+               cv4l_fmt fmt;
+               v4l2_output out;
+               v4l2_control ctrl = { V4L2_CID_DV_TX_RGB_RANGE };
+               int factor = 1;
+
+               g_output(out.index);
+               enum_output(out, true, out.index);
+               m_frame = m_lastFrame = m_fps = 0;
+               m_capMethod = m_genTab->capMethod();
+               g_fmt(fmt);
+               if (out.capabilities & V4L2_OUT_CAP_STD)
+                       g_std(m_tpgStd);
+               else
+                       m_tpgStd = 0;
+               m_tpgField = fmt.g_field();
+               m_tpgSizeImage = fmt.g_sizeimage(0);
+               tpg_alloc(&m_tpg, fmt.g_width());
+               m_useTpg = tpg_s_fourcc(&m_tpg, fmt.g_pixelformat());
+               if (V4L2_FIELD_HAS_T_OR_B(fmt.g_field()))
+                       factor = 2;
+               tpg_reset_source(&m_tpg, fmt.g_width(), fmt.g_height() * factor, fmt.g_field());
+               tpg_init_mv_count(&m_tpg);
+               if (g_ctrl(ctrl))
+                       tpg_s_rgb_range(&m_tpg, V4L2_DV_RGB_RANGE_AUTO);
+               else
+                       tpg_s_rgb_range(&m_tpg, ctrl.value);
+               if (m_tpgColorspace == 0)
+                       fmt.s_colorspace(defaultColorspace(false));
+               else
+                       fmt.s_colorspace(m_tpgColorspace);
+               s_fmt(fmt);
+
+               if (out.capabilities & V4L2_OUT_CAP_STD) {
+                       tpg_s_pixel_aspect(&m_tpg, (m_tpgStd & V4L2_STD_525_60) ?
+                                       TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL);
+               } else if (out.capabilities & V4L2_OUT_CAP_DV_TIMINGS) {
+                       v4l2_dv_timings timings;
+
+                       g_dv_timings(timings);
+                       if (timings.bt.width == 720 && timings.bt.height <= 576)
+                               tpg_s_pixel_aspect(&m_tpg, timings.bt.height == 480 ?
+                                       TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL);
+                       else
+                               tpg_s_pixel_aspect(&m_tpg, TPG_PIXEL_ASPECT_SQUARE);
+               } else {
+                       tpg_s_pixel_aspect(&m_tpg, TPG_PIXEL_ASPECT_SQUARE);
+               }
+
+               tpg_s_colorspace(&m_tpg, m_tpgColorspace ? m_tpgColorspace : fmt.g_colorspace());
+               tpg_s_bytesperline(&m_tpg, 0, fmt.g_bytesperline(0));
+               tpg_s_bytesperline(&m_tpg, 1, fmt.g_bytesperline(1));
+               if (m_capMethod == methodRead)
+                       m_frameData = new unsigned char[fmt.g_sizeimage(0)];
+               if (startStreaming()) {
+                       m_outNotifier = new QSocketNotifier(g_fd(), QSocketNotifier::Write, m_tabs);
+                       connect(m_outNotifier, SIGNAL(activated(int)), this, SLOT(outFrame()));
+               }
+       } else {
+               stopStreaming();
+               tpg_free(&m_tpg);
+               delete m_frameData;
+               m_frameData = NULL;
+               delete m_outNotifier;
+               m_outNotifier = NULL;
+       }
+}
+
 void ApplicationWindow::capStart(bool start)
 {
+       if (has_vid_out()) {
+               outStart(start);
+               return;
+       }
+
        if (!m_genTab->isSDR() && m_genTab->isRadio()) {
                if (start)
-                       startCapture();
+                       startStreaming();
                else
-                       stopCapture();
+                       stopStreaming();
 
                return;
        }
@@ -982,7 +1153,7 @@ void ApplicationWindow::capStart(bool start)
        unsigned colorspace, field;
 
        if (!start) {
-               stopCapture();
+               stopStreaming();
                delete m_capNotifier;
                m_capNotifier = NULL;
                delete m_capImage;
@@ -1012,7 +1183,7 @@ void ApplicationWindow::capStart(bool start)
                m_vbiTab->slicedFormat(fmt.fmt.sliced);
                m_vbiSize = fmt.fmt.sliced.io_size;
                m_frameData = new unsigned char[m_vbiSize];
-               if (startCapture()) {
+               if (startStreaming()) {
                        m_capNotifier = new QSocketNotifier(g_fd(), QSocketNotifier::Read, m_tabs);
                        connect(m_capNotifier, SIGNAL(activated(int)), this, SLOT(capVbiFrame()));
                }
@@ -1054,7 +1225,7 @@ void ApplicationWindow::capStart(bool start)
                        m_capture->show();
 
                statusBar()->showMessage("No frame");
-               if (startCapture()) {
+               if (startStreaming()) {
                        m_capNotifier = new QSocketNotifier(g_fd(), QSocketNotifier::Read, m_tabs);
                        connect(m_capNotifier, SIGNAL(activated(int)), this, SLOT(capVbiFrame()));
                }
@@ -1083,7 +1254,7 @@ void ApplicationWindow::capStart(bool start)
                        m_capture->show();
 
                statusBar()->showMessage("No frame");
-               if (startCapture()) {
+               if (startStreaming()) {
                        m_capNotifier = new QSocketNotifier(g_fd(), QSocketNotifier::Read, m_tabs);
                        connect(m_capNotifier, SIGNAL(activated(int)), this, SLOT(capSdrFrame()));
                }
@@ -1159,7 +1330,7 @@ void ApplicationWindow::capStart(bool start)
                m_capture->show();
 
        statusBar()->showMessage("No frame");
-       if (startCapture()) {
+       if (startStreaming()) {
                m_capNotifier = new QSocketNotifier(g_fd(), QSocketNotifier::Read, m_tabs);
                connect(m_capNotifier, SIGNAL(activated(int)), this, SLOT(capFrame()));
        }
@@ -1186,6 +1357,10 @@ void ApplicationWindow::closeDevice()
                        m_capNotifier = NULL;
                        m_capImage = NULL;
                }
+               if (m_outNotifier) {
+                       delete m_outNotifier;
+                       m_outNotifier = NULL;
+               }
                if (m_ctrlNotifier) {
                        delete m_ctrlNotifier;
                        m_ctrlNotifier = NULL;
index 46a7265..fd64868 100644 (file)
 // Must come before cv4l-helpers.h
 #include <libv4l2.h>
 
+extern "C" {
+#include <vivid-tpg.h>
+}
 #include "cv4l-helpers.h"
 #include "raw2sliced.h"
 #include "capture-win.h"
 
 class QComboBox;
 class QSpinBox;
+class QCheckBox;
 class GeneralTab;
 class VbiTab;
 class QCloseEvent;
@@ -103,11 +107,14 @@ public:
 private:
        CaptureWin *m_capture;
 
-       bool startCapture();
-       void stopCapture();
+       bool startStreaming();
+       void stopStreaming();
        void newCaptureWin();
        void startAudio();
        void stopAudio();
+       bool startOutput();
+       void stopOutput();
+       __u32 defaultColorspace(bool capture);
 
        bool m_clear[64];
        cv4l_fmt m_capSrcFormat;
@@ -122,8 +129,10 @@ private:
 
 private slots:
        void capStart(bool);
+       void outStart(bool);
        void makeFullScreen(bool);
        void capFrame();
+       void outFrame();
        void ctrlEvent();
        void snapshot();
        void capVbiFrame();
@@ -150,6 +159,22 @@ private slots:
        void clearBuffers();
        void about();
 
+       // tpg
+private slots:
+       void testPatternChanged(int val);
+       void horMovementChanged(int val);
+       void vertMovementChanged(int val);
+       void showBorderChanged(int val);
+       void showSquareChanged(int val);
+       void insSAVChanged(int val);
+       void insEAVChanged(int val);
+       void videoAspectRatioChanged(int val);
+       void colorspaceChanged(int val);
+       void limRGBRangeChanged(int val);
+       void fillPercentageChanged(int val);
+       void alphaComponentChanged(int val);
+       void applyToRedChanged(int val);
+
 public:
        virtual void error(const QString &text);
        void error(int err);
@@ -158,6 +183,7 @@ public:
        void errorCtrl(unsigned id, int err, const QString &v);
        void info(const QString &info);
        virtual void closeEvent(QCloseEvent *event);
+       void updateLimRGBRange();
        QAction *m_resetScalingAct;
        QAction *m_useBlendingAct;
        QAction *m_snapshotAct;
@@ -171,6 +197,7 @@ private:
        }
        void fixWidth(QGridLayout *grid);
        void addTabs(int m_winWidth);
+       void addTpgTab(int m_winWidth);
        void finishGrid(QGridLayout *grid, unsigned ctrl_class);
        void addCtrl(QGridLayout *grid, const struct v4l2_query_ext_ctrl &qec);
        void updateCtrl(unsigned id);
@@ -198,6 +225,14 @@ private:
        void updateFreqChannel();
        bool showFrames();
 
+       struct tpg_data m_tpg;
+       v4l2_std_id m_tpgStd;
+       unsigned m_tpgField;
+       unsigned m_tpgSizeImage;
+       unsigned m_tpgColorspace;
+       bool m_useTpg;
+       QCheckBox *m_tpgLimRGBRange;
+
        cv4l_queue m_queue;
 
        const double m_pxw;
@@ -219,6 +254,7 @@ private:
        QSignalMapper *m_sigMapper;
        QTabWidget *m_tabs;
        QSocketNotifier *m_capNotifier;
+       QSocketNotifier *m_outNotifier;
        QSocketNotifier *m_ctrlNotifier;
        QImage *m_capImage;
        int m_row, m_col, m_cols;
diff --git a/utils/qv4l2/tpg-tab.cpp b/utils/qv4l2/tpg-tab.cpp
new file mode 100644 (file)
index 0000000..2b69d63
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * qv4l2 - Test Pattern Generator Tab
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "qv4l2.h"
+
+#include <QFrame>
+#include <QVBoxLayout>
+#include <QStatusBar>
+#include <QLineEdit>
+#include <QValidator>
+#include <QLayout>
+#include <QGridLayout>
+#include <QLabel>
+#include <QSlider>
+#include <QSpinBox>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QPushButton>
+#include <QToolButton>
+#include <QToolTip>
+
+#include <math.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+void ApplicationWindow::addTpgTab(int m_winWidth)
+{
+       QWidget *t = new QWidget(m_tabs);
+       QVBoxLayout *vbox = new QVBoxLayout(t);
+       QWidget *w = new QWidget(t);
+       QCheckBox *check;
+       QComboBox *combo;
+       QSpinBox *spin;
+
+       m_col = m_row = 0;
+       m_cols = 4;
+       for (int j = 0; j < m_cols; j++) {
+               m_maxw[j] = 0;
+       }
+
+       vbox->addWidget(w);
+
+       QGridLayout *grid = new QGridLayout(w);
+       QLabel *title_tab = new QLabel("Test Pattern Generator", parentWidget());
+       QFont f = title_tab->font();
+       f.setBold(true);
+       title_tab->setFont(f);
+       grid->addWidget(title_tab, m_row, m_col, 1, m_cols, Qt::AlignLeft);
+       grid->setRowMinimumHeight(m_row, 25);
+       m_row++;
+
+       QFrame *m_line = new QFrame(grid->parentWidget());
+       m_line->setFrameShape(QFrame::HLine);
+       m_line->setFrameShadow(QFrame::Sunken);
+       grid->addWidget(m_line, m_row, m_col, 1, m_cols, Qt::AlignVCenter);
+       m_row++;
+
+       m_tabs->addTab(t, "Test Pattern Generator");
+       grid->addWidget(new QWidget(w), grid->rowCount(), 0, 1, m_cols);
+
+       addLabel(grid, "Test Pattern");
+       combo = new QComboBox(w);
+       for (int i = 0; tpg_pattern_strings[i]; i++)
+               combo->addItem(tpg_pattern_strings[i]);
+       addWidget(grid, combo);
+       connect(combo, SIGNAL(activated(int)), SLOT(testPatternChanged(int)));
+
+       m_row++;
+       m_col = 0;
+
+       addLabel(grid, "Horizontal Movement");
+       combo = new QComboBox(w);
+       combo->addItem("Move Left Fast");
+       combo->addItem("Move Left");
+       combo->addItem("Move Left Slow");
+       combo->addItem("No Movement");
+       combo->addItem("Move Right Slow");
+       combo->addItem("Move Right");
+       combo->addItem("Move Right Fast");
+       combo->setCurrentIndex(3);
+       addWidget(grid, combo);
+       connect(combo, SIGNAL(activated(int)), SLOT(horMovementChanged(int)));
+
+       addLabel(grid, "Video Aspect Ratio");
+       combo = new QComboBox(w);
+       combo->addItem("Source Width x Height");
+       combo->addItem("4x3");
+       combo->addItem("16x9");
+       combo->addItem("16x9 Anamorphic");
+       addWidget(grid, combo);
+       connect(combo, SIGNAL(activated(int)), SLOT(videoAspectRatioChanged(int)));
+
+       addLabel(grid, "Vertical Movement");
+       combo = new QComboBox(w);
+       combo->addItem("Move Up Fast");
+       combo->addItem("Move Up");
+       combo->addItem("Move Up Slow");
+       combo->addItem("No Movement");
+       combo->addItem("Move Down Slow");
+       combo->addItem("Move Down");
+       combo->addItem("Move Down Fast");
+       combo->setCurrentIndex(3);
+       addWidget(grid, combo);
+       connect(combo, SIGNAL(activated(int)), SLOT(vertMovementChanged(int)));
+
+       m_tpgColorspace = 0;
+       addLabel(grid, "Colorspace");
+       combo = new QComboBox(w);
+       combo->addItem("Autodetect");
+       combo->addItem("SMPTE 170M");
+       combo->addItem("SMPTE 240M");
+       combo->addItem("REC 709");
+       combo->addItem("470 System M");
+       combo->addItem("470 System BG");
+       combo->addItem("sRGB");
+       addWidget(grid, combo);
+       connect(combo, SIGNAL(activated(int)), SLOT(colorspaceChanged(int)));
+
+       addLabel(grid, "Show Border");
+       check = new QCheckBox(w);
+       addWidget(grid, check);
+       connect(check, SIGNAL(stateChanged(int)), SLOT(showBorderChanged(int)));
+
+       addLabel(grid, "Insert SAV Code in Image");
+       check = new QCheckBox(w);
+       addWidget(grid, check);
+       connect(check, SIGNAL(stateChanged(int)), SLOT(insSAVChanged(int)));
+
+       addLabel(grid, "Show Square");
+       check = new QCheckBox(w);
+       addWidget(grid, check);
+       connect(check, SIGNAL(stateChanged(int)), SLOT(showSquareChanged(int)));
+
+       addLabel(grid, "Insert EAV Code in Image");
+       check = new QCheckBox(w);
+       addWidget(grid, check);
+       connect(check, SIGNAL(stateChanged(int)), SLOT(insEAVChanged(int)));
+
+       addLabel(grid, "Fill Percentage of Frame");
+       spin = new QSpinBox(w);
+       spin->setRange(0, 100);
+       spin->setValue(100);
+       addWidget(grid, spin);
+       connect(spin, SIGNAL(valueChanged(int)), SLOT(fillPercentageChanged(int)));
+
+       addLabel(grid, "Limited RGB Range (16-235)");
+       m_tpgLimRGBRange = new QCheckBox(w);
+       addWidget(grid, m_tpgLimRGBRange);
+       connect(m_tpgLimRGBRange, SIGNAL(stateChanged(int)), SLOT(limRGBRangeChanged(int)));
+
+       addLabel(grid, "Alpha Component");
+       spin = new QSpinBox(w);
+       spin->setRange(0, 255);
+       spin->setValue(0);
+       addWidget(grid, spin);
+       connect(spin, SIGNAL(valueChanged(int)), SLOT(alphaComponentChanged(int)));
+
+       addLabel(grid, "Apply Alpha To Red Only");
+       check = new QCheckBox(w);
+       addWidget(grid, check);
+       connect(check, SIGNAL(stateChanged(int)), SLOT(applyToRedChanged(int)));
+
+       m_row++;
+       m_col = 0;
+       addWidget(grid, new QWidget(w));
+       grid->setRowStretch(grid->rowCount() - 1, 1);
+       w = new QWidget(t);
+       vbox->addWidget(w);
+       fixWidth(grid);
+
+       int totalw = 0;
+       int diff = 0;
+       for (int i = 0; i < m_cols; i++) {
+               totalw += m_maxw[i] + m_pxw;
+       }
+       if (totalw > m_winWidth)
+               m_winWidth = totalw;
+       else {
+               diff = m_winWidth - totalw;
+               grid->setHorizontalSpacing(diff/5);
+       }
+}
+
+void ApplicationWindow::testPatternChanged(int val)
+{
+       tpg_s_pattern(&m_tpg, (tpg_pattern)val);
+}
+
+void ApplicationWindow::horMovementChanged(int val)
+{
+       tpg_s_mv_hor_mode(&m_tpg, (tpg_move_mode)val);
+}
+
+void ApplicationWindow::vertMovementChanged(int val)
+{
+       tpg_s_mv_vert_mode(&m_tpg, (tpg_move_mode)val);
+}
+
+void ApplicationWindow::showBorderChanged(int val)
+{
+       tpg_s_show_border(&m_tpg, val);
+}
+
+void ApplicationWindow::showSquareChanged(int val)
+{
+       tpg_s_show_square(&m_tpg, val);
+}
+
+void ApplicationWindow::insSAVChanged(int val)
+{
+       tpg_s_insert_sav(&m_tpg, val);
+}
+
+void ApplicationWindow::insEAVChanged(int val)
+{
+       tpg_s_insert_eav(&m_tpg, val);
+}
+
+void ApplicationWindow::videoAspectRatioChanged(int val)
+{
+       tpg_s_video_aspect(&m_tpg, (tpg_video_aspect)val);
+}
+
+void ApplicationWindow::updateLimRGBRange()
+{
+       if (m_tpgLimRGBRange == NULL)
+               return;
+
+       v4l2_output out;
+
+       g_output(out.index);
+       enum_output(out, true, out.index);
+
+       if (out.capabilities & V4L2_OUT_CAP_STD) {
+               m_tpgLimRGBRange->setChecked(false);
+       } else if (out.capabilities & V4L2_OUT_CAP_DV_TIMINGS) {
+               v4l2_dv_timings timings;
+
+               g_dv_timings(timings);
+               if (timings.bt.standards & V4L2_DV_BT_STD_CEA861)
+                       m_tpgLimRGBRange->setChecked(true);
+               else
+                       m_tpgLimRGBRange->setChecked(false);
+       } else {
+               m_tpgLimRGBRange->setChecked(false);
+       }
+}
+
+__u32 ApplicationWindow::defaultColorspace(bool capture)
+{
+       v4l2_dv_timings timings = { 0 };
+       v4l2_output out;
+       v4l2_input in;
+       __u32 io_caps;
+       bool dvi_d = false;
+
+       if (capture) {
+               g_input(in.index);
+               enum_input(in, true, in.index);
+               io_caps = in.capabilities;
+       } else {
+               g_output(out.index);
+               enum_output(out, true, out.index);
+               io_caps = out.capabilities;
+               v4l2_control ctrl = { V4L2_CID_DV_TX_MODE };
+
+               if (!g_ctrl(ctrl))
+                       dvi_d = ctrl.value == V4L2_DV_TX_MODE_DVI_D;
+       }
+
+       if (io_caps & V4L2_OUT_CAP_STD)
+               return V4L2_COLORSPACE_SMPTE170M;
+       if (!(io_caps & V4L2_OUT_CAP_DV_TIMINGS))
+               return V4L2_COLORSPACE_SRGB;
+
+       g_dv_timings(timings);
+
+       if (!(timings.bt.standards & V4L2_DV_BT_STD_CEA861) || dvi_d)
+               return V4L2_COLORSPACE_SRGB;
+       if (timings.bt.width == 720 && timings.bt.height <= 576)
+               return V4L2_COLORSPACE_SMPTE170M;
+       return V4L2_COLORSPACE_REC709;
+}
+
+void ApplicationWindow::colorspaceChanged(int val)
+{
+       switch (val) {
+       case 0:
+               m_tpgColorspace = 0;
+               break;
+       case 1:
+               m_tpgColorspace = V4L2_COLORSPACE_SMPTE170M;
+               break;
+       case 2:
+               m_tpgColorspace = V4L2_COLORSPACE_SMPTE240M;
+               break;
+       case 3:
+               m_tpgColorspace = V4L2_COLORSPACE_REC709;
+               break;
+       case 4:
+               m_tpgColorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+               break;
+       case 5:
+               m_tpgColorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+               break;
+       case 6:
+       default:
+               m_tpgColorspace = V4L2_COLORSPACE_SRGB;
+               break;
+       }
+
+       cv4l_fmt fmt;
+       v4l2_output out;
+
+       g_output(out.index);
+       enum_output(out, true, out.index);
+
+       g_fmt(fmt);
+       if (m_tpgColorspace == 0)
+               fmt.s_colorspace(defaultColorspace(false));
+       else
+               fmt.s_colorspace(m_tpgColorspace);
+       s_fmt(fmt);
+       tpg_s_colorspace(&m_tpg, m_tpgColorspace ? m_tpgColorspace : fmt.g_colorspace());
+}
+
+void ApplicationWindow::limRGBRangeChanged(int val)
+{
+       tpg_s_real_rgb_range(&m_tpg, val ? V4L2_DV_RGB_RANGE_LIMITED : V4L2_DV_RGB_RANGE_FULL);
+}
+
+void ApplicationWindow::fillPercentageChanged(int val)
+{
+       tpg_s_perc_fill(&m_tpg, val);
+}
+
+void ApplicationWindow::alphaComponentChanged(int val)
+{
+       tpg_s_alpha_component(&m_tpg, val);
+}
+
+void ApplicationWindow::applyToRedChanged(int val)
+{
+       tpg_s_alpha_mode(&m_tpg, val);
+}
index c0d3195..b18c2ac 100644 (file)
@@ -74,6 +74,16 @@ public:
                return v4l_query_ext_ctrl(this, &qec, next_ctrl, next_compound);
        }
 
+       int g_ctrl(v4l2_control &ctrl)
+       {
+               return cv4l_ioctl(VIDIOC_G_CTRL, &ctrl);
+       }
+
+       int s_ctrl(v4l2_control &ctrl)
+       {
+               return cv4l_ioctl(VIDIOC_S_CTRL, &ctrl);
+       }
+
        int g_ext_ctrls(v4l2_ext_controls &ec)
        {
                return v4l_g_ext_ctrls(this, &ec);
@@ -236,22 +246,22 @@ public:
                return cv4l_ioctl(VIDIOC_DQEVENT, &ev);
        }
 
-       int g_input(int &input)
+       int g_input(__u32 &input)
        {
                return cv4l_ioctl(VIDIOC_G_INPUT, &input);
        }
 
-       int s_input(int input)
+       int s_input(__u32 input)
        {
                return cv4l_ioctl(VIDIOC_S_INPUT, &input);
        }
 
-       int g_output(int &output)
+       int g_output(__u32 &output)
        {
                return cv4l_ioctl(VIDIOC_G_OUTPUT, &output);
        }
 
-       int s_output(int output)
+       int s_output(__u32 output)
        {
                return cv4l_ioctl(VIDIOC_S_OUTPUT, &output);
        }
@@ -262,7 +272,7 @@ public:
                return cv4l_ioctl(VIDIOC_G_AUDIO, &audio);
        }
 
-       int s_audio(int input)
+       int s_audio(__u32 input)
        {
                v4l2_audio audio;
 
@@ -277,7 +287,7 @@ public:
                return cv4l_ioctl(VIDIOC_G_AUDOUT, &audout);
        }
 
-       int s_audout(int output)
+       int s_audout(__u32 output)
        {
                v4l2_audioout audout;