From 9f2bd0777bfa1c1118b506663f99d2086805b59f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 25 Jul 2014 16:24:39 +0200 Subject: [PATCH] qv4l2: add support for output video devices 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 --- utils/qv4l2/Makefile.am | 7 +- utils/qv4l2/general-tab.cpp | 18 +- utils/qv4l2/general-tab.h | 16 +- utils/qv4l2/qv4l2.cpp | 215 +++++++++++++++++++-- utils/qv4l2/qv4l2.h | 40 +++- utils/qv4l2/tpg-tab.cpp | 362 +++++++++++++++++++++++++++++++++++ utils/v4l2-compliance/cv4l-helpers.h | 22 ++- 7 files changed, 633 insertions(+), 47 deletions(-) create mode 100644 utils/qv4l2/tpg-tab.cpp diff --git a/utils/qv4l2/Makefile.am b/utils/qv4l2/Makefile.am index 1e3d081..ab57056 100644 --- a/utils/qv4l2/Makefile.am +++ b/utils/qv4l2/Makefile.am @@ -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) diff --git a/utils/qv4l2/general-tab.cpp b/utils/qv4l2/general-tab.cpp index e94d99f..8a716b7 100644 --- a/utils/qv4l2/general-tab.cpp +++ b/utils/qv4l2/general-tab.cpp @@ -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() diff --git a/utils/qv4l2/general-tab.h b/utils/qv4l2/general-tab.h index ac9868e..e39589c 100644 --- a/utils/qv4l2/general-tab.h +++ b/utils/qv4l2/general-tab.h @@ -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); } diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp index c4c071a..3147d68 100644 --- a/utils/qv4l2/qv4l2.cpp +++ b/utils/qv4l2/qv4l2.cpp @@ -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; diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h index 46a7265..fd64868 100644 --- a/utils/qv4l2/qv4l2.h +++ b/utils/qv4l2/qv4l2.h @@ -36,12 +36,16 @@ // Must come before cv4l-helpers.h #include +extern "C" { +#include +} #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 index 0000000..2b69d63 --- /dev/null +++ b/utils/qv4l2/tpg-tab.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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); +} diff --git a/utils/v4l2-compliance/cv4l-helpers.h b/utils/v4l2-compliance/cv4l-helpers.h index c0d3195..b18c2ac 100644 --- a/utils/v4l2-compliance/cv4l-helpers.h +++ b/utils/v4l2-compliance/cv4l-helpers.h @@ -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; -- 2.7.4