diff options
author | Hans Verkuil <hverkuil@xs4all.nl> | 2006-07-01 18:04:09 +0200 |
---|---|---|
committer | Hans Verkuil <hverkuil@xs4all.nl> | 2006-07-01 18:04:09 +0200 |
commit | 46b7750285822f2a41f56d5a1e211d472b4783be (patch) | |
tree | 00602df94c9d87c5e72f85e79dff3428ee83d632 /v4l2-apps/util | |
parent | 6c65b97fa5448a31004227926f04f7f13bf14b88 (diff) | |
download | mediapointer-dvb-s2-46b7750285822f2a41f56d5a1e211d472b4783be.tar.gz mediapointer-dvb-s2-46b7750285822f2a41f56d5a1e211d472b4783be.tar.bz2 |
Split qv4l2.cpp into multiple sources.
From: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Diffstat (limited to 'v4l2-apps/util')
-rw-r--r-- | v4l2-apps/util/qv4l2/ctrl-tab.cpp | 592 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/general-tab.cpp | 326 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/general-tab.h | 71 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/qv4l2.cpp | 887 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/qv4l2.h | 57 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/qv4l2.pro | 5 |
6 files changed, 1023 insertions, 915 deletions
diff --git a/v4l2-apps/util/qv4l2/ctrl-tab.cpp b/v4l2-apps/util/qv4l2/ctrl-tab.cpp new file mode 100644 index 000000000..c7d1a275c --- /dev/null +++ b/v4l2-apps/util/qv4l2/ctrl-tab.cpp @@ -0,0 +1,592 @@ + +#include "qv4l2.h" +#include "v4l2.h" + +#include <qstatusbar.h> +#include <qlineedit.h> +#include <qvalidator.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qslider.h> +#include <qspinbox.h> +#include <qcombobox.h> +#include <qcheckbox.h> +#include <qpushbutton.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +void ApplicationWindow::addTabs() +{ + struct v4l2_queryctrl qctrl; + unsigned ctrl_class; + unsigned i; + int id; + + memset(&qctrl, 0, sizeof(qctrl)); + qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; + while (::ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + if ((qctrl.flags & V4L2_CTRL_FLAG_DISABLED) == 0) { + ctrlMap[qctrl.id] = qctrl; + if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS) + classMap[V4L2_CTRL_ID2CLASS(qctrl.id)].push_back(qctrl.id); + } + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id == V4L2_CTRL_FLAG_NEXT_CTRL) { + strcpy((char *)qctrl.name, "User Controls"); + qctrl.id = V4L2_CTRL_CLASS_USER | 1; + qctrl.type = V4L2_CTRL_TYPE_CTRL_CLASS; + ctrlMap[qctrl.id] = qctrl; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (::ioctl(fd, VIDIOC_QUERYCTRL, &qctrl)) + continue; + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) + continue; + ctrlMap[qctrl.id] = qctrl; + classMap[V4L2_CTRL_CLASS_USER].push_back(qctrl.id); + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ::ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) + continue; + ctrlMap[qctrl.id] = qctrl; + classMap[V4L2_CTRL_CLASS_USER].push_back(qctrl.id); + } + } + + for (ClassMap::iterator iter = classMap.begin(); iter != classMap.end(); ++iter) { + ctrl_class = V4L2_CTRL_ID2CLASS(iter->second[0]); + id = ctrl_class | 1; + const struct v4l2_queryctrl &qctrl = ctrlMap[id]; + QVBox *vbox = new QVBox(tabs); + QGrid *grid = new QGrid(4, vbox); + grid->setSpacing(3); + tabs->addTab(vbox, (char *)qctrl.name); + for (i = 0; i < iter->second.size(); i++) { + if (i & 1) + id = iter->second[(1+iter->second.size()) / 2 + i / 2]; + else + id = iter->second[i / 2]; + addCtrl(grid, ctrlMap[id]); + } + finishGrid(vbox, grid, ctrl_class, i & 1); + } +} + +void ApplicationWindow::finishGrid(QWidget *vbox, QGrid *grid, unsigned ctrl_class, bool odd) +{ + if (odd) { + new QWidget(grid); + new QWidget(grid); + } + QWidget *stretch = new QWidget(grid); + stretch->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + + QFrame *frame = new QFrame(vbox); + frame->setFrameShape(QFrame::HLine); + frame->setFrameShadow(QFrame::Sunken); + frame->setMargin(3); + + QHBox *hbox = new QHBox(vbox); + hbox->setSpacing(3); + + QCheckBox *cbox = new QCheckBox("Update on change", hbox); + widgetMap[ctrl_class | CTRL_UPDATE_ON_CHANGE] = cbox; + connect(cbox, SIGNAL(clicked()), sigMapper, SLOT(map())); + sigMapper->setMapping(cbox, ctrl_class | CTRL_UPDATE_ON_CHANGE); + + stretch = new QWidget(hbox); + stretch->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + + QPushButton *defBut = new QPushButton("Set Defaults", hbox); + widgetMap[ctrl_class | CTRL_DEFAULTS] = defBut; + connect(defBut, SIGNAL(clicked()), sigMapper, SLOT(map())); + sigMapper->setMapping(defBut, ctrl_class | CTRL_DEFAULTS); + + QPushButton *refreshBut = new QPushButton("Refresh", hbox); + widgetMap[ctrl_class | CTRL_REFRESH] = refreshBut; + connect(refreshBut, SIGNAL(clicked()), sigMapper, SLOT(map())); + sigMapper->setMapping(refreshBut, ctrl_class | CTRL_REFRESH); + + QPushButton *button = new QPushButton("Update", hbox); + widgetMap[ctrl_class | CTRL_UPDATE] = button; + connect(button, SIGNAL(clicked()), sigMapper, SLOT(map())); + sigMapper->setMapping(button, ctrl_class | CTRL_UPDATE); + connect(cbox, SIGNAL(toggled(bool)), button, SLOT(setDisabled(bool))); + + cbox->setChecked(ctrl_class == V4L2_CTRL_CLASS_USER); + + refresh(ctrl_class); +} + +void ApplicationWindow::addCtrl(QGrid *grid, const struct v4l2_queryctrl &qctrl) +{ + QIntValidator *val; + QLineEdit *edit; + QString name((char *)qctrl.name); + QComboBox *combo; + struct v4l2_querymenu qmenu; + + QLabel *label = new QLabel(name, grid); + label->setAlignment(Qt::AlignRight); + + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + if (qctrl.flags & V4L2_CTRL_FLAG_SLIDER) { + widgetMap[qctrl.id] = + new QSlider(qctrl.minimum, qctrl.maximum, + qctrl.step, qctrl.default_value, + Horizontal, grid); + connect(widgetMap[qctrl.id], SIGNAL(valueChanged(int)), + sigMapper, SLOT(map())); + break; + } + + if (qctrl.maximum - qctrl.minimum <= 255) { + widgetMap[qctrl.id] = + new QSpinBox(qctrl.minimum, qctrl.maximum, 1, grid); + connect(widgetMap[qctrl.id], SIGNAL(valueChanged(int)), + sigMapper, SLOT(map())); + break; + } + + val = new QIntValidator(qctrl.minimum, qctrl.maximum, grid); + edit = new QLineEdit(grid); + edit->setValidator(val); + widgetMap[qctrl.id] = edit; + connect(widgetMap[qctrl.id], SIGNAL(lostFocus()), + sigMapper, SLOT(map())); + connect(widgetMap[qctrl.id], SIGNAL(returnPressed()), + sigMapper, SLOT(map())); + break; + + case V4L2_CTRL_TYPE_INTEGER64: + widgetMap[qctrl.id] = new QLineEdit(grid); + connect(widgetMap[qctrl.id], SIGNAL(lostFocus()), + sigMapper, SLOT(map())); + connect(widgetMap[qctrl.id], SIGNAL(returnPressed()), + sigMapper, SLOT(map())); + break; + + case V4L2_CTRL_TYPE_BOOLEAN: + label->setText(""); + widgetMap[qctrl.id] = new QCheckBox(name, grid); + connect(widgetMap[qctrl.id], SIGNAL(clicked()), + sigMapper, SLOT(map())); + break; + + case V4L2_CTRL_TYPE_BUTTON: + label->setText(""); + widgetMap[qctrl.id] = new QPushButton((char *)qctrl.name, grid); + connect(widgetMap[qctrl.id], SIGNAL(clicked()), + sigMapper, SLOT(map())); + break; + + case V4L2_CTRL_TYPE_MENU: + combo = new QComboBox(grid); + widgetMap[qctrl.id] = combo; + for (int i = qctrl.minimum; i <= qctrl.maximum; i++) { + qmenu.id = qctrl.id; + qmenu.index = i; + if (::ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) + continue; + combo->insertItem((char *)qmenu.name); + } + connect(widgetMap[qctrl.id], SIGNAL(activated(int)), + sigMapper, SLOT(map())); + break; + + default: + return; + } + sigMapper->setMapping(widgetMap[qctrl.id], qctrl.id); + if (qctrl.flags & (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)) + widgetMap[qctrl.id]->setDisabled(true); +} + +void ApplicationWindow::ctrlAction(int id) +{ + unsigned ctrl_class = V4L2_CTRL_ID2CLASS(id); + if (ctrl_class == V4L2_CID_PRIVATE_BASE) + ctrl_class = V4L2_CTRL_CLASS_USER; + unsigned ctrl = id & 0xffff; + QCheckBox *cbox = static_cast<QCheckBox *>(widgetMap[ctrl_class | CTRL_UPDATE_ON_CHANGE]); + bool update = cbox->isChecked(); + bool all = (ctrl == CTRL_UPDATE || (update && ctrl == CTRL_UPDATE_ON_CHANGE)); + + if (ctrl == CTRL_DEFAULTS) { + setDefaults(ctrl_class); + return; + } + if (ctrl == CTRL_REFRESH) { + refresh(ctrl_class); + return; + } + if (!update && !all && ctrlMap[id].type != V4L2_CTRL_TYPE_BUTTON) + return; + if (ctrl_class == V4L2_CTRL_CLASS_USER) { + if (!all) { + updateCtrl(id); + return; + } + for (unsigned i = 0; i < classMap[ctrl_class].size(); i++) { + updateCtrl(classMap[ctrl_class][i]); + } + return; + } + if (!all) { + updateCtrl(id); + return; + } + unsigned count = classMap[ctrl_class].size(); + struct v4l2_ext_control *c = new v4l2_ext_control[count]; + struct v4l2_ext_controls ctrls; + int idx = 0; + + for (unsigned i = 0; i < count; i++) { + unsigned id = classMap[ctrl_class][i]; + + if (ctrlMap[id].flags & (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)) + continue; + c[idx].id = id; + if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) + c[idx].value64 = getVal64(id); + else + c[idx].value = getVal(id); + idx++; + } + memset(&ctrls, 0, sizeof(ctrls)); + ctrls.count = idx; + ctrls.ctrl_class = ctrl_class; + ctrls.controls = c; + if (::ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) { + int err = errno; + + if (ctrls.error_idx >= ctrls.count) { + printf("error: %s\n", strerror(err)); + } + else { + id = c[ctrls.error_idx].id; + printf("error %08x (%s): %s\n", id, + ctrlMap[id].name, strerror(err)); + } + } + delete [] c; + refresh(ctrl_class); +} + +long long ApplicationWindow::getVal64(unsigned id) +{ + const v4l2_queryctrl &qctrl = ctrlMap[id]; + QWidget *w = widgetMap[qctrl.id]; + long long v = 0; + + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER64: + v = static_cast<QLineEdit *>(w)->text().toLongLong(); + break; + default: + break; + } + setWhat(w, id, v); + return v; +} + +int ApplicationWindow::getVal(unsigned id) +{ + const v4l2_queryctrl &qctrl = ctrlMap[id]; + QWidget *w = widgetMap[qctrl.id]; + v4l2_querymenu qmenu; + int i, idx; + int v = 0; + + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + if (qctrl.flags & V4L2_CTRL_FLAG_SLIDER) { + v = static_cast<QSlider *>(w)->value(); + break; + } + + if (qctrl.maximum - qctrl.minimum <= 255) { + v = static_cast<QSpinBox *>(w)->value(); + break; + } + v = static_cast<QLineEdit *>(w)->text().toInt(); + break; + + case V4L2_CTRL_TYPE_BOOLEAN: + v = static_cast<QCheckBox *>(w)->isChecked(); + break; + + case V4L2_CTRL_TYPE_MENU: + idx = static_cast<QComboBox *>(w)->currentItem(); + for (i = qctrl.minimum; i <= qctrl.maximum; i++) { + qmenu.id = qctrl.id; + qmenu.index = i; + if (::ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) + continue; + if (idx-- == 0) + break; + } + v = i; + break; + + default: + break; + } + setWhat(w, id, v); + return v; +} + +void ApplicationWindow::updateCtrl(unsigned id) +{ + unsigned ctrl_class = V4L2_CTRL_ID2CLASS(id); + if (ctrl_class == V4L2_CID_PRIVATE_BASE) + ctrl_class = V4L2_CTRL_CLASS_USER; + + if (ctrlMap[id].flags & (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)) + return; + + if (ctrl_class == V4L2_CTRL_CLASS_USER) { + struct v4l2_control c; + + c.id = id; + c.value = getVal(id); + if (::ioctl(fd, VIDIOC_S_CTRL, &c)) { + int err = errno; + char buf[200]; + + sprintf(buf, "Error %08x (%s): %s", id, + ctrlMap[id].name, strerror(err)); + statusBar()->message(buf, 10000); + } + return; + } + struct v4l2_ext_control c; + struct v4l2_ext_controls ctrls; + + memset(&c, 0, sizeof(c)); + memset(&ctrls, 0, sizeof(ctrls)); + c.id = id; + if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) + c.value64 = getVal64(id); + else + c.value = getVal(id); + ctrls.count = 1; + ctrls.ctrl_class = ctrl_class; + ctrls.controls = &c; + if (::ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) { + int err = errno; + char buf[200]; + + sprintf(buf, "Error %08x (%s): %s", id, + ctrlMap[id].name, strerror(err)); + statusBar()->message(buf, 10000); + } + else if (ctrlMap[id].flags & V4L2_CTRL_FLAG_UPDATE) + refresh(ctrl_class); + else { + if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) + setVal64(id, c.value64); + else + setVal(id, c.value); + } +} + +void ApplicationWindow::refresh(unsigned ctrl_class) +{ + if (ctrl_class == V4L2_CTRL_CLASS_USER) { + for (unsigned i = 0; i < classMap[ctrl_class].size(); i++) { + unsigned id = classMap[ctrl_class][i]; + + v4l2_control c; + + c.id = id; + if (::ioctl(fd, VIDIOC_G_CTRL, &c)) { + int err = errno; + char buf[200]; + + sprintf(buf, "Error %08x (%s): %s", id, + ctrlMap[id].name, strerror(err)); + statusBar()->message(buf, 10000); + } + setVal(id, c.value); + } + return; + } + unsigned count = classMap[ctrl_class].size(); + struct v4l2_ext_control *c = new v4l2_ext_control[count]; + struct v4l2_ext_controls ctrls; + + for (unsigned i = 0; i < count; i++) { + c[i].id = classMap[ctrl_class][i]; + } + memset(&ctrls, 0, sizeof(ctrls)); + ctrls.count = count; + ctrls.ctrl_class = ctrl_class; + ctrls.controls = c; + if (::ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) { + int err = errno; + + if (ctrls.error_idx >= ctrls.count) { + statusBar()->message(strerror(err), 10000); + } + else { + unsigned id = c[ctrls.error_idx].id; + char buf[200]; + + sprintf(buf, "Error %08x (%s): %s", id, + ctrlMap[id].name, strerror(err)); + statusBar()->message(buf, 10000); + } + } + else { + for (unsigned i = 0; i < ctrls.count; i++) { + unsigned id = c[i].id; + if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) + setVal64(id, c[i].value64); + else + setVal(id, c[i].value); + ::ioctl(fd, VIDIOC_QUERYCTRL, &ctrlMap[id]); + widgetMap[id]->setDisabled(ctrlMap[id].flags & + (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)); + } + } + delete [] c; +} + +void ApplicationWindow::setWhat(QWidget *w, unsigned id, long long v) +{ + const v4l2_queryctrl &qctrl = ctrlMap[id]; + QString what; + QString flags = getCtrlFlags(qctrl.flags); + + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + QWhatsThis::add(w, what.sprintf("Integer type control\n" + "Minimum: %d\n" + "Maximum: %d\n" + "Current: %d\n" + "Default: %d\n", + qctrl.minimum, qctrl.maximum, (int)v, qctrl.default_value) + flags); + break; + + case V4L2_CTRL_TYPE_INTEGER64: + QWhatsThis::add(w, what.sprintf("64-bit Integer type control\n" + "Current: %lld\n", v) + flags); + break; + + case V4L2_CTRL_TYPE_BUTTON: + QWhatsThis::add(w, what.sprintf("Button type control\n") + flags); + break; + + case V4L2_CTRL_TYPE_BOOLEAN: + QWhatsThis::add(w, what.sprintf("Boolean type control\n" + "Current: %d\n" + "Default: %d\n", + (int)v, qctrl.default_value) + flags); + break; + + case V4L2_CTRL_TYPE_MENU: + QWhatsThis::add(w, what.sprintf("Menu type control\n" + "Minimum: %d\n" + "Maximum: %d\n" + "Current: %d\n" + "Default: %d\n", + qctrl.minimum, qctrl.maximum, (int)v, qctrl.default_value) + flags); + break; + default: + break; + } +} + +void ApplicationWindow::setVal(unsigned id, int v) +{ + const v4l2_queryctrl &qctrl = ctrlMap[id]; + v4l2_querymenu qmenu; + QWidget *w = widgetMap[qctrl.id]; + int i, idx; + + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + if (qctrl.flags & V4L2_CTRL_FLAG_SLIDER) + static_cast<QSlider *>(w)->setValue(v); + else if (qctrl.maximum - qctrl.minimum <= 255) + static_cast<QSpinBox *>(w)->setValue(v); + else + static_cast<QLineEdit *>(w)->setText(QString::number(v)); + break; + + case V4L2_CTRL_TYPE_BOOLEAN: + static_cast<QCheckBox *>(w)->setChecked(v); + break; + + case V4L2_CTRL_TYPE_MENU: + idx = 0; + for (i = qctrl.minimum; i <= v; i++) { + qmenu.id = id; + qmenu.index = i; + if (::ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) + continue; + idx++; + } + static_cast<QComboBox *>(w)->setCurrentItem(idx - 1); + break; + default: + break; + } + setWhat(w, id, v); +} + +void ApplicationWindow::setVal64(unsigned id, long long v) +{ + const v4l2_queryctrl &qctrl = ctrlMap[id]; + QWidget *w = widgetMap[qctrl.id]; + + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER64: + static_cast<QLineEdit *>(w)->setText(QString::number(v)); + break; + default: + break; + } + setWhat(w, id, v); +} + +void ApplicationWindow::setDefaults(unsigned ctrl_class) +{ + for (unsigned i = 0; i < classMap[ctrl_class].size(); i++) { + unsigned id = classMap[ctrl_class][i]; + + if (ctrlMap[id].type != V4L2_CTRL_TYPE_INTEGER64 && + ctrlMap[id].type != V4L2_CTRL_TYPE_BUTTON) + setVal(id, ctrlMap[id].default_value); + } + ctrlAction(ctrl_class | CTRL_UPDATE); +} + +QString ApplicationWindow::getCtrlFlags(unsigned flags) +{ + QString s; + + if (flags & V4L2_CTRL_FLAG_GRABBED) + s += "grabbed "; + if (flags & V4L2_CTRL_FLAG_READ_ONLY) + s += "readonly "; + if (flags & V4L2_CTRL_FLAG_UPDATE) + s += "update "; + if (flags & V4L2_CTRL_FLAG_INACTIVE) + s += "inactive "; + if (flags & V4L2_CTRL_FLAG_SLIDER) + s += "slider "; + if (s.length()) s = QString("Flags: ") + s; + return s; +} + diff --git a/v4l2-apps/util/qv4l2/general-tab.cpp b/v4l2-apps/util/qv4l2/general-tab.cpp new file mode 100644 index 000000000..a19cf911b --- /dev/null +++ b/v4l2-apps/util/qv4l2/general-tab.cpp @@ -0,0 +1,326 @@ +/* qv4l2: a control panel controlling v4l2 devices. + * + * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "qv4l2.h" +#include "general-tab.h" +#include "v4l2.h" + +#include <qlabel.h> +#include <qspinbox.h> +#include <qcombobox.h> +#include <qwhatsthis.h> + +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +GeneralTab::GeneralTab(int _fd, int n, QWidget *parent) : + QGrid(n, parent), + fd(_fd) +{ + int cnt = 0; + + setSpacing(3); + + memset(&tuner, 0, sizeof(tuner)); + ioctl(fd, VIDIOC_G_TUNER, &tuner); + + struct v4l2_input vin; + memset(&vin, 0, sizeof(vin)); + if (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) { + QLabel *label = new QLabel("Input", this); + label->setAlignment(Qt::AlignRight); + videoInput = new QComboBox(this); + while (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) { + videoInput->insertItem((char *)vin.name); + vin.index++; + } + connect(videoInput, SIGNAL(activated(int)), SLOT(inputChanged(int))); + updateVideoInput(); + cnt++; + } + + struct v4l2_output vout; + memset(&vout, 0, sizeof(vout)); + if (ioctl(fd, VIDIOC_ENUMOUTPUT, &vout) >= 0) { + QLabel *label = new QLabel("Output", this); + label->setAlignment(Qt::AlignRight); + videoOutput = new QComboBox(this); + while (ioctl(fd, VIDIOC_ENUMOUTPUT, &vout) >= 0) { + videoOutput->insertItem((char *)vout.name); + vout.index++; + } + connect(videoOutput, SIGNAL(activated(int)), SLOT(outputChanged(int))); + updateVideoOutput(); + cnt++; + } + + struct v4l2_audio vaudio; + memset(&vaudio, 0, sizeof(vaudio)); + if (ioctl(fd, VIDIOC_ENUMAUDIO, &vaudio) >= 0) { + QLabel *label = new QLabel("Input Audio", this); + label->setAlignment(Qt::AlignRight); + audioInput = new QComboBox(this); + vaudio.index = 0; + while (ioctl(fd, VIDIOC_ENUMAUDIO, &vaudio) >= 0) { + audioInput->insertItem((char *)vaudio.name); + vaudio.index++; + } + connect(audioInput, SIGNAL(activated(int)), SLOT(inputAudioChanged(int))); + updateAudioInput(); + cnt++; + } + + struct v4l2_audioout vaudioout; + memset(&vaudioout, 0, sizeof(vaudioout)); + if (ioctl(fd, VIDIOC_ENUMAUDOUT, &vaudioout) >= 0) { + QLabel *label = new QLabel("Output Audio", this); + label->setAlignment(Qt::AlignRight); + audioOutput = new QComboBox(this); + while (ioctl(fd, VIDIOC_ENUMAUDOUT, &vaudioout) >= 0) { + audioOutput->insertItem((char *)vaudioout.name); + vaudioout.index++; + } + connect(audioOutput, SIGNAL(activated(int)), SLOT(outputAudioChanged(int))); + updateAudioOutput(); + cnt++; + } + + struct v4l2_standard vs; + memset(&vs, 0, sizeof(vs)); + if (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { + QLabel *label = new QLabel("TV Standard", this); + label->setAlignment(Qt::AlignRight); + tvStandard = new QComboBox(this); + while (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { + tvStandard->insertItem((char *)vs.name); + vs.index++; + } + connect(tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int))); + updateStandard(); + cnt++; + } + + bool first = cnt & 1; + + if (first) { + QString what; + QLabel *label = new QLabel("Frequency", this); + label->setAlignment(Qt::AlignRight); + freq = new QSpinBox(tuner.rangelow, tuner.rangehigh, 1, this); + QWhatsThis::add(freq, what.sprintf("Frequency\n" + "Low: %d\n" + "High: %d\n", + tuner.rangelow, tuner.rangehigh)); + connect(freq, SIGNAL(valueChanged(int)), SLOT(freqChanged(int))); + updateFreq(); + cnt++; + } + + { + QLabel *label = new QLabel("Frequency Tables", this); + label->setAlignment(Qt::AlignRight); + freqTable = new QComboBox(this); + for (int i = 0; v4l2_channel_lists[i].name; i++) { + freqTable->insertItem(v4l2_channel_lists[i].name); + } + connect(freqTable, SIGNAL(activated(int)), SLOT(freqTableChanged(int))); + + label = new QLabel("Channels", this); + label->setAlignment(Qt::AlignRight); + freqChannel = new QComboBox(this); + connect(freqChannel, SIGNAL(activated(int)), SLOT(freqChannelChanged(int))); + updateFreqChannel(); + } + + if (!first) { + QString what; + QLabel *label = new QLabel("Frequency", this); + label->setAlignment(Qt::AlignRight); + freq = new QSpinBox(tuner.rangelow, tuner.rangehigh, 1, this); + QWhatsThis::add(freq, what.sprintf("Frequency\n" + "Low: %d\n" + "High: %d\n", + tuner.rangelow, tuner.rangehigh)); + connect(freq, SIGNAL(valueChanged(int)), SLOT(freqChanged(int))); + updateFreq(); + cnt++; + } + + if (cnt & 1) { + new QWidget(this); + new QWidget(this); + } + QWidget *stretch = new QWidget(this); + stretch->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); +} + +void GeneralTab::inputChanged(int input) +{ + g_mw->doIoctl("Set Input", VIDIOC_S_INPUT, &input); + struct v4l2_audio vaudio; + memset(&vaudio, 0, sizeof(vaudio)); + if (audioInput && ioctl(fd, VIDIOC_G_AUDIO, &vaudio) >= 0) { + audioInput->setCurrentItem(vaudio.index); + updateAudioInput(); + } + updateVideoInput(); +} + +void GeneralTab::outputChanged(int output) +{ + g_mw->doIoctl("Set Output", VIDIOC_S_OUTPUT, &output); + updateVideoOutput(); +} + +void GeneralTab::inputAudioChanged(int input) +{ + struct v4l2_audio vaudio; + memset(&vaudio, 0, sizeof(vaudio)); + vaudio.index = input; + g_mw->doIoctl("Set Audio Input", VIDIOC_S_AUDIO, &vaudio); + updateAudioInput(); +} + +void GeneralTab::outputAudioChanged(int output) +{ + struct v4l2_audioout vaudioout; + memset(&vaudioout, 0, sizeof(vaudioout)); + vaudioout.index = output; + g_mw->doIoctl("Set Audio Output", VIDIOC_S_AUDOUT, &vaudioout); + updateAudioOutput(); +} + +void GeneralTab::standardChanged(int std) +{ + struct v4l2_standard vs; + memset(&vs, 0, sizeof(vs)); + vs.index = std; + ioctl(fd, VIDIOC_ENUMSTD, &vs); + g_mw->doIoctl("Set TV Standard", VIDIOC_S_STD, &vs.id); + updateStandard(); +} + +void GeneralTab::freqTableChanged(int) +{ + updateFreqChannel(); + freqChannelChanged(0); +} + +void GeneralTab::freqChannelChanged(int idx) +{ + freq->setValue((int)(v4l2_channel_lists[freqTable->currentItem()].list[idx].freq / 62.5)); +} + +void GeneralTab::freqChanged(int val) +{ + struct v4l2_frequency f; + + memset(&f, 0, sizeof(f)); + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = val; + g_mw->doIoctl("Set frequency", VIDIOC_S_FREQUENCY, &f); +} + +void GeneralTab::updateVideoInput() +{ + int input; + + ioctl(fd, VIDIOC_G_INPUT, &input); + videoInput->setCurrentItem(input); +} + +void GeneralTab::updateVideoOutput() +{ + int output; + + ioctl(fd, VIDIOC_G_OUTPUT, &output); + videoOutput->setCurrentItem(output); +} + +void GeneralTab::updateAudioInput() +{ + struct v4l2_audio audio; + QString what; + + memset(&audio, 0, sizeof(audio)); + ioctl(fd, VIDIOC_G_AUDIO, &audio); + audioInput->setCurrentItem(audio.index); + if (audio.capability & V4L2_AUDCAP_STEREO) + what = "stereo input"; + else + what = "mono input"; + if (audio.capability & V4L2_AUDCAP_AVL) + what += ", has AVL"; + if (audio.mode & V4L2_AUDMODE_AVL) + what += ", AVL is on"; + QWhatsThis::add(audioInput, what); +} + +void GeneralTab::updateAudioOutput() +{ + struct v4l2_audioout audio; + + memset(&audio, 0, sizeof(audio)); + ioctl(fd, VIDIOC_G_AUDOUT, &audio); + audioOutput->setCurrentItem(audio.index); +} + +void GeneralTab::updateStandard() +{ + v4l2_std_id std; + struct v4l2_standard vs; + QString what; + ioctl(fd, VIDIOC_G_STD, &std); + memset(&vs, 0, sizeof(vs)); + while (ioctl(fd, VIDIOC_ENUMSTD, &vs) != -1) { + if (vs.id & std) { + tvStandard->setCurrentItem(vs.index); + what.sprintf("TV Standard (0x%llX)\n" + "Frame period: %f (%d/%d)\n" + "Frame lines: %d\n", std, + (double)vs.frameperiod.numerator / vs.frameperiod.denominator, + vs.frameperiod.numerator, vs.frameperiod.denominator, + vs.framelines); + QWhatsThis::add(tvStandard, what); + return; + } + vs.index++; + } +} + +void GeneralTab::updateFreq() +{ + struct v4l2_frequency f; + + memset(&f, 0, sizeof(f)); + ioctl(fd, VIDIOC_G_FREQUENCY, &f); + freq->setValue(f.frequency); +} + +void GeneralTab::updateFreqChannel() +{ + freqChannel->clear(); + int tbl = freqTable->currentItem(); + const struct v4l2_channel_list *list = v4l2_channel_lists[tbl].list; + for (unsigned i = 0; i < v4l2_channel_lists[tbl].count; i++) + freqChannel->insertItem(list[i].name); +} + diff --git a/v4l2-apps/util/qv4l2/general-tab.h b/v4l2-apps/util/qv4l2/general-tab.h new file mode 100644 index 000000000..44003fd40 --- /dev/null +++ b/v4l2-apps/util/qv4l2/general-tab.h @@ -0,0 +1,71 @@ +/* qv4l2: a control panel controlling v4l2 devices. + * + * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef GENERAL_TAB_H +#define GENERAL_TAB_H + +#include <linux/videodev2.h> +#include <qgrid.h> + +class QComboBox; +class QSpinBox; + +class GeneralTab: public QGrid +{ + Q_OBJECT + +public: + GeneralTab(int fd, int n, QWidget *parent = 0); + virtual ~GeneralTab() {} + +private slots: + void inputChanged(int); + void outputChanged(int); + void inputAudioChanged(int); + void outputAudioChanged(int); + void standardChanged(int); + void freqTableChanged(int); + void freqChannelChanged(int); + void freqChanged(int); + +private: + void updateVideoInput(); + void updateVideoOutput(); + void updateAudioInput(); + void updateAudioOutput(); + void updateStandard(); + void updateFreq(); + void updateFreqChannel(); + + int fd; + struct v4l2_tuner tuner; + + // General tab + QComboBox *videoInput; + QComboBox *videoOutput; + QComboBox *audioInput; + QComboBox *audioOutput; + QComboBox *tvStandard; + QSpinBox *freq; + QComboBox *freqTable; + QComboBox *freqChannel; +}; + +#endif diff --git a/v4l2-apps/util/qv4l2/qv4l2.cpp b/v4l2-apps/util/qv4l2/qv4l2.cpp index 1a6ef6a47..f9fb07e09 100644 --- a/v4l2-apps/util/qv4l2/qv4l2.cpp +++ b/v4l2-apps/util/qv4l2/qv4l2.cpp @@ -1,5 +1,6 @@ #include "qv4l2.h" +#include "general-tab.h" #include "v4l2.h" #include <qimage.h> @@ -11,9 +12,8 @@ #include <qfile.h> #include <qfiledialog.h> #include <qstatusbar.h> -#include <qmessagebox.h> #include <qapplication.h> -#include <qaccel.h> +#include <qmessagebox.h> #include <qlineedit.h> #include <qvalidator.h> #include <qlayout.h> @@ -26,8 +26,6 @@ #include <qcheckbox.h> #include <qpushbutton.h> #include <qtooltip.h> -#include <qpainter.h> -#include <qpaintdevicemetrics.h> #include <qwhatsthis.h> #include <fcntl.h> @@ -117,18 +115,10 @@ void ApplicationWindow::setDevice(const QString &device) ctrlMap.clear(); widgetMap.clear(); classMap.clear(); - videoInput = NULL; - videoOutput = NULL; - audioInput = NULL; - audioOutput = NULL; - tvStandard = NULL; - freq = NULL; - freqChannel = NULL; - freqTable = NULL; fd = ::open(device, O_RDONLY); if (fd >= 0) { - addGeneralTab(); + tabs->addTab(new GeneralTab(fd, 4, tabs), "General"); addTabs(); } if (QWidget *current = tabs->currentPage()) { @@ -139,716 +129,6 @@ void ApplicationWindow::setDevice(const QString &device) setCentralWidget(tabs); } -void ApplicationWindow::addGeneralTab() -{ - int cnt = 0; - QVBox *vbox = new QVBox(tabs); - QGrid *grid = new QGrid(4, vbox); - grid->setSpacing(3); - tabs->addTab(vbox, "General"); - - memset(&tuner, 0, sizeof(tuner)); - ioctl(fd, VIDIOC_G_TUNER, &tuner); - - struct v4l2_input vin; - memset(&vin, 0, sizeof(vin)); - if (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) { - QLabel *label = new QLabel("Input", grid); - label->setAlignment(Qt::AlignRight); - videoInput = new QComboBox(grid); - while (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) { - videoInput->insertItem((char *)vin.name); - vin.index++; - } - connect(videoInput, SIGNAL(activated(int)), SLOT(inputChanged(int))); - updateVideoInput(); - cnt++; - } - - struct v4l2_output vout; - memset(&vout, 0, sizeof(vout)); - if (ioctl(fd, VIDIOC_ENUMOUTPUT, &vout) >= 0) { - QLabel *label = new QLabel("Output", grid); - label->setAlignment(Qt::AlignRight); - videoOutput = new QComboBox(grid); - while (ioctl(fd, VIDIOC_ENUMOUTPUT, &vout) >= 0) { - videoOutput->insertItem((char *)vout.name); - vout.index++; - } - connect(videoOutput, SIGNAL(activated(int)), SLOT(outputChanged(int))); - updateVideoOutput(); - cnt++; - } - - struct v4l2_audio vaudio; - memset(&vaudio, 0, sizeof(vaudio)); - if (ioctl(fd, VIDIOC_ENUMAUDIO, &vaudio) >= 0) { - QLabel *label = new QLabel("Input Audio", grid); - label->setAlignment(Qt::AlignRight); - audioInput = new QComboBox(grid); - vaudio.index = 0; - while (ioctl(fd, VIDIOC_ENUMAUDIO, &vaudio) >= 0) { - audioInput->insertItem((char *)vaudio.name); - vaudio.index++; - } - connect(audioInput, SIGNAL(activated(int)), SLOT(inputAudioChanged(int))); - updateAudioInput(); - cnt++; - } - - struct v4l2_audioout vaudioout; - memset(&vaudioout, 0, sizeof(vaudioout)); - if (ioctl(fd, VIDIOC_ENUMAUDOUT, &vaudioout) >= 0) { - QLabel *label = new QLabel("Output Audio", grid); - label->setAlignment(Qt::AlignRight); - audioOutput = new QComboBox(grid); - while (ioctl(fd, VIDIOC_ENUMAUDOUT, &vaudioout) >= 0) { - audioOutput->insertItem((char *)vaudioout.name); - vaudioout.index++; - } - connect(audioOutput, SIGNAL(activated(int)), SLOT(outputAudioChanged(int))); - updateAudioOutput(); - cnt++; - } - - struct v4l2_standard vs; - memset(&vs, 0, sizeof(vs)); - if (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { - QLabel *label = new QLabel("TV Standard", grid); - label->setAlignment(Qt::AlignRight); - tvStandard = new QComboBox(grid); - while (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { - tvStandard->insertItem((char *)vs.name); - vs.index++; - } - connect(tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int))); - updateStandard(); - cnt++; - } - - bool first = cnt & 1; - - if (first) { - QString what; - QLabel *label = new QLabel("Frequency", grid); - label->setAlignment(Qt::AlignRight); - freq = new QSpinBox(tuner.rangelow, tuner.rangehigh, 1, grid); - QWhatsThis::add(freq, what.sprintf("Frequency\n" - "Low: %d\n" - "High: %d\n", - tuner.rangelow, tuner.rangehigh)); - connect(freq, SIGNAL(valueChanged(int)), SLOT(freqChanged(int))); - updateFreq(); - cnt++; - } - - { - QLabel *label = new QLabel("Frequency Tables", grid); - label->setAlignment(Qt::AlignRight); - freqTable = new QComboBox(grid); - for (int i = 0; v4l2_channel_lists[i].name; i++) { - freqTable->insertItem(v4l2_channel_lists[i].name); - } - connect(freqTable, SIGNAL(activated(int)), SLOT(freqTableChanged(int))); - - label = new QLabel("Channels", grid); - label->setAlignment(Qt::AlignRight); - freqChannel = new QComboBox(grid); - connect(freqChannel, SIGNAL(activated(int)), SLOT(freqChannelChanged(int))); - updateFreqChannel(); - } - - if (!first) { - QString what; - QLabel *label = new QLabel("Frequency", grid); - label->setAlignment(Qt::AlignRight); - freq = new QSpinBox(tuner.rangelow, tuner.rangehigh, 1, grid); - QWhatsThis::add(freq, what.sprintf("Frequency\n" - "Low: %d\n" - "High: %d\n", - tuner.rangelow, tuner.rangehigh)); - connect(freq, SIGNAL(valueChanged(int)), SLOT(freqChanged(int))); - updateFreq(); - cnt++; - } - - if (cnt & 1) { - new QWidget(grid); - new QWidget(grid); - } - QWidget *stretch = new QWidget(grid); - stretch->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); -} - -void ApplicationWindow::addTabs() -{ - struct v4l2_queryctrl qctrl; - unsigned ctrl_class; - unsigned i; - int id; - - memset(&qctrl, 0, sizeof(qctrl)); - qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; - while (::ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { - if ((qctrl.flags & V4L2_CTRL_FLAG_DISABLED) == 0) { - ctrlMap[qctrl.id] = qctrl; - if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS) - classMap[V4L2_CTRL_ID2CLASS(qctrl.id)].push_back(qctrl.id); - } - qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; - } - if (qctrl.id == V4L2_CTRL_FLAG_NEXT_CTRL) { - strcpy((char *)qctrl.name, "User Controls"); - qctrl.id = V4L2_CTRL_CLASS_USER | 1; - qctrl.type = V4L2_CTRL_TYPE_CTRL_CLASS; - ctrlMap[qctrl.id] = qctrl; - for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { - qctrl.id = id; - if (::ioctl(fd, VIDIOC_QUERYCTRL, &qctrl)) - continue; - if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) - continue; - ctrlMap[qctrl.id] = qctrl; - classMap[V4L2_CTRL_CLASS_USER].push_back(qctrl.id); - } - for (qctrl.id = V4L2_CID_PRIVATE_BASE; - ::ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { - if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) - continue; - ctrlMap[qctrl.id] = qctrl; - classMap[V4L2_CTRL_CLASS_USER].push_back(qctrl.id); - } - } - - for (ClassMap::iterator iter = classMap.begin(); iter != classMap.end(); ++iter) { - ctrl_class = V4L2_CTRL_ID2CLASS(iter->second[0]); - id = ctrl_class | 1; - const struct v4l2_queryctrl &qctrl = ctrlMap[id]; - QVBox *vbox = new QVBox(tabs); - QGrid *grid = new QGrid(4, vbox); - grid->setSpacing(3); - tabs->addTab(vbox, (char *)qctrl.name); - for (i = 0; i < iter->second.size(); i++) { - if (i & 1) - id = iter->second[(1+iter->second.size()) / 2 + i / 2]; - else - id = iter->second[i / 2]; - addCtrl(grid, ctrlMap[id]); - } - finishGrid(vbox, grid, ctrl_class, i & 1); - } -} - -void ApplicationWindow::finishGrid(QWidget *vbox, QGrid *grid, unsigned ctrl_class, bool odd) -{ - if (odd) { - new QWidget(grid); - new QWidget(grid); - } - QWidget *stretch = new QWidget(grid); - stretch->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); - - QFrame *frame = new QFrame(vbox); - frame->setFrameShape(QFrame::HLine); - frame->setFrameShadow(QFrame::Sunken); - frame->setMargin(3); - - QHBox *hbox = new QHBox(vbox); - hbox->setSpacing(3); - - QCheckBox *cbox = new QCheckBox("Update on change", hbox); - widgetMap[ctrl_class | CTRL_UPDATE_ON_CHANGE] = cbox; - connect(cbox, SIGNAL(clicked()), sigMapper, SLOT(map())); - sigMapper->setMapping(cbox, ctrl_class | CTRL_UPDATE_ON_CHANGE); - - stretch = new QWidget(hbox); - stretch->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - - QPushButton *defBut = new QPushButton("Set Defaults", hbox); - widgetMap[ctrl_class | CTRL_DEFAULTS] = defBut; - connect(defBut, SIGNAL(clicked()), sigMapper, SLOT(map())); - sigMapper->setMapping(defBut, ctrl_class | CTRL_DEFAULTS); - - QPushButton *refreshBut = new QPushButton("Refresh", hbox); - widgetMap[ctrl_class | CTRL_REFRESH] = refreshBut; - connect(refreshBut, SIGNAL(clicked()), sigMapper, SLOT(map())); - sigMapper->setMapping(refreshBut, ctrl_class | CTRL_REFRESH); - - QPushButton *button = new QPushButton("Update", hbox); - widgetMap[ctrl_class | CTRL_UPDATE] = button; - connect(button, SIGNAL(clicked()), sigMapper, SLOT(map())); - sigMapper->setMapping(button, ctrl_class | CTRL_UPDATE); - connect(cbox, SIGNAL(toggled(bool)), button, SLOT(setDisabled(bool))); - - cbox->setChecked(ctrl_class == V4L2_CTRL_CLASS_USER); - - refresh(ctrl_class); -} - -void ApplicationWindow::addCtrl(QGrid *grid, const struct v4l2_queryctrl &qctrl) -{ - QIntValidator *val; - QLineEdit *edit; - QString name((char *)qctrl.name); - QComboBox *combo; - struct v4l2_querymenu qmenu; - - QLabel *label = new QLabel(name, grid); - label->setAlignment(Qt::AlignRight); - - switch (qctrl.type) { - case V4L2_CTRL_TYPE_INTEGER: - if (qctrl.flags & V4L2_CTRL_FLAG_SLIDER) { - widgetMap[qctrl.id] = - new QSlider(qctrl.minimum, qctrl.maximum, - qctrl.step, qctrl.default_value, - Horizontal, grid); - connect(widgetMap[qctrl.id], SIGNAL(valueChanged(int)), - sigMapper, SLOT(map())); - break; - } - - if (qctrl.maximum - qctrl.minimum <= 255) { - widgetMap[qctrl.id] = - new QSpinBox(qctrl.minimum, qctrl.maximum, 1, grid); - connect(widgetMap[qctrl.id], SIGNAL(valueChanged(int)), - sigMapper, SLOT(map())); - break; - } - - val = new QIntValidator(qctrl.minimum, qctrl.maximum, grid); - edit = new QLineEdit(grid); - edit->setValidator(val); - widgetMap[qctrl.id] = edit; - connect(widgetMap[qctrl.id], SIGNAL(lostFocus()), - sigMapper, SLOT(map())); - connect(widgetMap[qctrl.id], SIGNAL(returnPressed()), - sigMapper, SLOT(map())); - break; - - case V4L2_CTRL_TYPE_INTEGER64: - widgetMap[qctrl.id] = new QLineEdit(grid); - connect(widgetMap[qctrl.id], SIGNAL(lostFocus()), - sigMapper, SLOT(map())); - connect(widgetMap[qctrl.id], SIGNAL(returnPressed()), - sigMapper, SLOT(map())); - break; - - case V4L2_CTRL_TYPE_BOOLEAN: - label->setText(""); - widgetMap[qctrl.id] = new QCheckBox(name, grid); - connect(widgetMap[qctrl.id], SIGNAL(clicked()), - sigMapper, SLOT(map())); - break; - - case V4L2_CTRL_TYPE_BUTTON: - label->setText(""); - widgetMap[qctrl.id] = new QPushButton((char *)qctrl.name, grid); - connect(widgetMap[qctrl.id], SIGNAL(clicked()), - sigMapper, SLOT(map())); - break; - - case V4L2_CTRL_TYPE_MENU: - combo = new QComboBox(grid); - widgetMap[qctrl.id] = combo; - for (int i = qctrl.minimum; i <= qctrl.maximum; i++) { - qmenu.id = qctrl.id; - qmenu.index = i; - if (::ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) - continue; - combo->insertItem((char *)qmenu.name); - } - connect(widgetMap[qctrl.id], SIGNAL(activated(int)), - sigMapper, SLOT(map())); - break; - - default: - return; - } - sigMapper->setMapping(widgetMap[qctrl.id], qctrl.id); - if (qctrl.flags & (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)) - widgetMap[qctrl.id]->setDisabled(true); -} - -void ApplicationWindow::ctrlAction(int id) -{ - unsigned ctrl_class = V4L2_CTRL_ID2CLASS(id); - if (ctrl_class == V4L2_CID_PRIVATE_BASE) - ctrl_class = V4L2_CTRL_CLASS_USER; - unsigned ctrl = id & 0xffff; - QCheckBox *cbox = static_cast<QCheckBox *>(widgetMap[ctrl_class | CTRL_UPDATE_ON_CHANGE]); - bool update = cbox->isChecked(); - bool all = (ctrl == CTRL_UPDATE || (update && ctrl == CTRL_UPDATE_ON_CHANGE)); - - if (ctrl == CTRL_DEFAULTS) { - setDefaults(ctrl_class); - return; - } - if (ctrl == CTRL_REFRESH) { - refresh(ctrl_class); - return; - } - if (!update && !all && ctrlMap[id].type != V4L2_CTRL_TYPE_BUTTON) - return; - if (ctrl_class == V4L2_CTRL_CLASS_USER) { - if (!all) { - updateCtrl(id); - return; - } - for (unsigned i = 0; i < classMap[ctrl_class].size(); i++) { - updateCtrl(classMap[ctrl_class][i]); - } - return; - } - if (!all) { - updateCtrl(id); - return; - } - unsigned count = classMap[ctrl_class].size(); - struct v4l2_ext_control *c = new v4l2_ext_control[count]; - struct v4l2_ext_controls ctrls; - int idx = 0; - - for (unsigned i = 0; i < count; i++) { - unsigned id = classMap[ctrl_class][i]; - - if (ctrlMap[id].flags & (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)) - continue; - c[idx].id = id; - if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) - c[idx].value64 = getVal64(id); - else - c[idx].value = getVal(id); - idx++; - } - memset(&ctrls, 0, sizeof(ctrls)); - ctrls.count = idx; - ctrls.ctrl_class = ctrl_class; - ctrls.controls = c; - if (::ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) { - int err = errno; - - if (ctrls.error_idx >= ctrls.count) { - printf("error: %s\n", strerror(err)); - } - else { - id = c[ctrls.error_idx].id; - printf("error %08x (%s): %s\n", id, - ctrlMap[id].name, strerror(err)); - } - } - delete [] c; - refresh(ctrl_class); -} - -long long ApplicationWindow::getVal64(unsigned id) -{ - const v4l2_queryctrl &qctrl = ctrlMap[id]; - QWidget *w = widgetMap[qctrl.id]; - long long v = 0; - - switch (qctrl.type) { - case V4L2_CTRL_TYPE_INTEGER64: - v = static_cast<QLineEdit *>(w)->text().toLongLong(); - break; - default: - break; - } - setWhat(w, id, v); - return v; -} - -int ApplicationWindow::getVal(unsigned id) -{ - const v4l2_queryctrl &qctrl = ctrlMap[id]; - QWidget *w = widgetMap[qctrl.id]; - v4l2_querymenu qmenu; - int i, idx; - int v = 0; - - switch (qctrl.type) { - case V4L2_CTRL_TYPE_INTEGER: - if (qctrl.flags & V4L2_CTRL_FLAG_SLIDER) { - v = static_cast<QSlider *>(w)->value(); - break; - } - - if (qctrl.maximum - qctrl.minimum <= 255) { - v = static_cast<QSpinBox *>(w)->value(); - break; - } - v = static_cast<QLineEdit *>(w)->text().toInt(); - break; - - case V4L2_CTRL_TYPE_BOOLEAN: - v = static_cast<QCheckBox *>(w)->isChecked(); - break; - - case V4L2_CTRL_TYPE_MENU: - idx = static_cast<QComboBox *>(w)->currentItem(); - for (i = qctrl.minimum; i <= qctrl.maximum; i++) { - qmenu.id = qctrl.id; - qmenu.index = i; - if (::ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) - continue; - if (idx-- == 0) - break; - } - v = i; - break; - - default: - break; - } - setWhat(w, id, v); - return v; -} - -void ApplicationWindow::updateCtrl(unsigned id) -{ - unsigned ctrl_class = V4L2_CTRL_ID2CLASS(id); - if (ctrl_class == V4L2_CID_PRIVATE_BASE) - ctrl_class = V4L2_CTRL_CLASS_USER; - - if (ctrlMap[id].flags & (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)) - return; - - if (ctrl_class == V4L2_CTRL_CLASS_USER) { - struct v4l2_control c; - - c.id = id; - c.value = getVal(id); - if (::ioctl(fd, VIDIOC_S_CTRL, &c)) { - int err = errno; - char buf[200]; - - sprintf(buf, "Error %08x (%s): %s", id, - ctrlMap[id].name, strerror(err)); - statusBar()->message(buf, 10000); - } - return; - } - struct v4l2_ext_control c; - struct v4l2_ext_controls ctrls; - - memset(&c, 0, sizeof(c)); - memset(&ctrls, 0, sizeof(ctrls)); - c.id = id; - if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) - c.value64 = getVal64(id); - else - c.value = getVal(id); - ctrls.count = 1; - ctrls.ctrl_class = ctrl_class; - ctrls.controls = &c; - if (::ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) { - int err = errno; - char buf[200]; - - sprintf(buf, "Error %08x (%s): %s", id, - ctrlMap[id].name, strerror(err)); - statusBar()->message(buf, 10000); - } - else if (ctrlMap[id].flags & V4L2_CTRL_FLAG_UPDATE) - refresh(ctrl_class); - else { - if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) - setVal64(id, c.value64); - else - setVal(id, c.value); - } -} - -void ApplicationWindow::refresh(unsigned ctrl_class) -{ - if (ctrl_class == V4L2_CTRL_CLASS_USER) { - for (unsigned i = 0; i < classMap[ctrl_class].size(); i++) { - unsigned id = classMap[ctrl_class][i]; - - v4l2_control c; - - c.id = id; - if (::ioctl(fd, VIDIOC_G_CTRL, &c)) { - int err = errno; - char buf[200]; - - sprintf(buf, "Error %08x (%s): %s", id, - ctrlMap[id].name, strerror(err)); - statusBar()->message(buf, 10000); - } - setVal(id, c.value); - } - return; - } - unsigned count = classMap[ctrl_class].size(); - struct v4l2_ext_control *c = new v4l2_ext_control[count]; - struct v4l2_ext_controls ctrls; - - for (unsigned i = 0; i < count; i++) { - c[i].id = classMap[ctrl_class][i]; - } - memset(&ctrls, 0, sizeof(ctrls)); - ctrls.count = count; - ctrls.ctrl_class = ctrl_class; - ctrls.controls = c; - if (::ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) { - int err = errno; - - if (ctrls.error_idx >= ctrls.count) { - statusBar()->message(strerror(err), 10000); - } - else { - unsigned id = c[ctrls.error_idx].id; - char buf[200]; - - sprintf(buf, "Error %08x (%s): %s", id, - ctrlMap[id].name, strerror(err)); - statusBar()->message(buf, 10000); - } - } - else { - for (unsigned i = 0; i < ctrls.count; i++) { - unsigned id = c[i].id; - if (ctrlMap[id].type == V4L2_CTRL_TYPE_INTEGER64) - setVal64(id, c[i].value64); - else - setVal(id, c[i].value); - ::ioctl(fd, VIDIOC_QUERYCTRL, &ctrlMap[id]); - widgetMap[id]->setDisabled(ctrlMap[id].flags & - (V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_INACTIVE)); - } - } - delete [] c; -} - -void ApplicationWindow::setWhat(QWidget *w, unsigned id, long long v) -{ - const v4l2_queryctrl &qctrl = ctrlMap[id]; - QString what; - QString flags = getCtrlFlags(qctrl.flags); - - switch (qctrl.type) { - case V4L2_CTRL_TYPE_INTEGER: - QWhatsThis::add(w, what.sprintf("Integer type control\n" - "Minimum: %d\n" - "Maximum: %d\n" - "Current: %d\n" - "Default: %d\n", - qctrl.minimum, qctrl.maximum, (int)v, qctrl.default_value) + flags); - break; - - case V4L2_CTRL_TYPE_INTEGER64: - QWhatsThis::add(w, what.sprintf("64-bit Integer type control\n" - "Current: %lld\n", v) + flags); - break; - - case V4L2_CTRL_TYPE_BUTTON: - QWhatsThis::add(w, what.sprintf("Button type control\n") + flags); - break; - - case V4L2_CTRL_TYPE_BOOLEAN: - QWhatsThis::add(w, what.sprintf("Boolean type control\n" - "Current: %d\n" - "Default: %d\n", - (int)v, qctrl.default_value) + flags); - break; - - case V4L2_CTRL_TYPE_MENU: - QWhatsThis::add(w, what.sprintf("Menu type control\n" - "Minimum: %d\n" - "Maximum: %d\n" - "Current: %d\n" - "Default: %d\n", - qctrl.minimum, qctrl.maximum, (int)v, qctrl.default_value) + flags); - break; - default: - break; - } -} - -void ApplicationWindow::setVal(unsigned id, int v) -{ - const v4l2_queryctrl &qctrl = ctrlMap[id]; - v4l2_querymenu qmenu; - QWidget *w = widgetMap[qctrl.id]; - int i, idx; - - switch (qctrl.type) { - case V4L2_CTRL_TYPE_INTEGER: - if (qctrl.flags & V4L2_CTRL_FLAG_SLIDER) - static_cast<QSlider *>(w)->setValue(v); - else if (qctrl.maximum - qctrl.minimum <= 255) - static_cast<QSpinBox *>(w)->setValue(v); - else - static_cast<QLineEdit *>(w)->setText(QString::number(v)); - break; - - case V4L2_CTRL_TYPE_BOOLEAN: - static_cast<QCheckBox *>(w)->setChecked(v); - break; - - case V4L2_CTRL_TYPE_MENU: - idx = 0; - for (i = qctrl.minimum; i <= v; i++) { - qmenu.id = id; - qmenu.index = i; - if (::ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) - continue; - idx++; - } - static_cast<QComboBox *>(w)->setCurrentItem(idx - 1); - break; - default: - break; - } - setWhat(w, id, v); -} - -void ApplicationWindow::setVal64(unsigned id, long long v) -{ - const v4l2_queryctrl &qctrl = ctrlMap[id]; - QWidget *w = widgetMap[qctrl.id]; - - switch (qctrl.type) { - case V4L2_CTRL_TYPE_INTEGER64: - static_cast<QLineEdit *>(w)->setText(QString::number(v)); - break; - default: - break; - } - setWhat(w, id, v); -} - -void ApplicationWindow::setDefaults(unsigned ctrl_class) -{ - for (unsigned i = 0; i < classMap[ctrl_class].size(); i++) { - unsigned id = classMap[ctrl_class][i]; - - if (ctrlMap[id].type != V4L2_CTRL_TYPE_INTEGER64 && - ctrlMap[id].type != V4L2_CTRL_TYPE_BUTTON) - setVal(id, ctrlMap[id].default_value); - } - ctrlAction(ctrl_class | CTRL_UPDATE); -} - -QString ApplicationWindow::getCtrlFlags(unsigned flags) -{ - QString s; - - if (flags & V4L2_CTRL_FLAG_GRABBED) - s += "grabbed "; - if (flags & V4L2_CTRL_FLAG_READ_ONLY) - s += "readonly "; - if (flags & V4L2_CTRL_FLAG_UPDATE) - s += "update "; - if (flags & V4L2_CTRL_FLAG_INACTIVE) - s += "inactive "; - if (flags & V4L2_CTRL_FLAG_SLIDER) - s += "slider "; - if (s.length()) s = QString("Flags: ") + s; - return s; -} - void ApplicationWindow::choose() { QString fn = QFileDialog::getOpenFileName( "/dev/v4l", QString::null, @@ -866,158 +146,6 @@ void ApplicationWindow::closeEvent( QCloseEvent* ce ) ce->accept(); } -void ApplicationWindow::inputChanged(int input) -{ - doIoctl("Set Input", VIDIOC_S_INPUT, &input); - struct v4l2_audio vaudio; - memset(&vaudio, 0, sizeof(vaudio)); - if (audioInput && ioctl(fd, VIDIOC_G_AUDIO, &vaudio) >= 0) { - audioInput->setCurrentItem(vaudio.index); - updateAudioInput(); - } - updateVideoInput(); -} - -void ApplicationWindow::outputChanged(int output) -{ - doIoctl("Set Output", VIDIOC_S_OUTPUT, &output); - updateVideoOutput(); -} - -void ApplicationWindow::inputAudioChanged(int input) -{ - struct v4l2_audio vaudio; - memset(&vaudio, 0, sizeof(vaudio)); - vaudio.index = input; - doIoctl("Set Audio Input", VIDIOC_S_AUDIO, &vaudio); - updateAudioInput(); -} - -void ApplicationWindow::outputAudioChanged(int output) -{ - struct v4l2_audioout vaudioout; - memset(&vaudioout, 0, sizeof(vaudioout)); - vaudioout.index = output; - doIoctl("Set Audio Output", VIDIOC_S_AUDOUT, &vaudioout); - updateAudioOutput(); -} - -void ApplicationWindow::standardChanged(int std) -{ - struct v4l2_standard vs; - memset(&vs, 0, sizeof(vs)); - vs.index = std; - ioctl(fd, VIDIOC_ENUMSTD, &vs); - doIoctl("Set TV Standard", VIDIOC_S_STD, &vs.id); - updateStandard(); -} - -void ApplicationWindow::freqTableChanged(int) -{ - updateFreqChannel(); - freqChannelChanged(0); -} - -void ApplicationWindow::freqChannelChanged(int idx) -{ - freq->setValue((int)(v4l2_channel_lists[freqTable->currentItem()].list[idx].freq / 62.5)); -} - -void ApplicationWindow::freqChanged(int val) -{ - struct v4l2_frequency f; - - memset(&f, 0, sizeof(f)); - f.type = V4L2_TUNER_ANALOG_TV; - f.frequency = val; - doIoctl("Set frequency", VIDIOC_S_FREQUENCY, &f); -} - -void ApplicationWindow::updateVideoInput() -{ - int input; - - ioctl(fd, VIDIOC_G_INPUT, &input); - videoInput->setCurrentItem(input); -} - -void ApplicationWindow::updateVideoOutput() -{ - int output; - - ioctl(fd, VIDIOC_G_OUTPUT, &output); - videoOutput->setCurrentItem(output); -} - -void ApplicationWindow::updateAudioInput() -{ - struct v4l2_audio audio; - QString what; - - memset(&audio, 0, sizeof(audio)); - ioctl(fd, VIDIOC_G_AUDIO, &audio); - audioInput->setCurrentItem(audio.index); - if (audio.capability & V4L2_AUDCAP_STEREO) - what = "stereo input"; - else - what = "mono input"; - if (audio.capability & V4L2_AUDCAP_AVL) - what += ", has AVL"; - if (audio.mode & V4L2_AUDMODE_AVL) - what += ", AVL is on"; - QWhatsThis::add(audioInput, what); -} - -void ApplicationWindow::updateAudioOutput() -{ - struct v4l2_audioout audio; - - memset(&audio, 0, sizeof(audio)); - ioctl(fd, VIDIOC_G_AUDOUT, &audio); - audioOutput->setCurrentItem(audio.index); -} - -void ApplicationWindow::updateStandard() -{ - v4l2_std_id std; - struct v4l2_standard vs; - QString what; - ioctl(fd, VIDIOC_G_STD, &std); - memset(&vs, 0, sizeof(vs)); - while (ioctl(fd, VIDIOC_ENUMSTD, &vs) != -1) { - if (vs.id & std) { - tvStandard->setCurrentItem(vs.index); - what.sprintf("TV Standard (0x%llX)\n" - "Frame period: %f (%d/%d)\n" - "Frame lines: %d\n", std, - (double)vs.frameperiod.numerator / vs.frameperiod.denominator, - vs.frameperiod.numerator, vs.frameperiod.denominator, - vs.framelines); - QWhatsThis::add(tvStandard, what); - return; - } - vs.index++; - } -} - -void ApplicationWindow::updateFreq() -{ - struct v4l2_frequency f; - - memset(&f, 0, sizeof(f)); - ioctl(fd, VIDIOC_G_FREQUENCY, &f); - freq->setValue(f.frequency); -} - -void ApplicationWindow::updateFreqChannel() -{ - freqChannel->clear(); - int tbl = freqTable->currentItem(); - const struct v4l2_channel_list *list = v4l2_channel_lists[tbl].list; - for (unsigned i = 0; i < v4l2_channel_lists[tbl].count; i++) - freqChannel->insertItem(list[i].name); -} - bool ApplicationWindow::doIoctl(QString descr, unsigned cmd, void *arg) { statusBar()->clear(); @@ -1036,14 +164,15 @@ void ApplicationWindow::about() "This program allows easy experimenting with video4linux devices."); } +ApplicationWindow *g_mw; int main(int argc, char **argv) { QApplication a(argc, argv); - ApplicationWindow *mw = new ApplicationWindow(); - mw->setCaption( "V4L2 Control Panel" ); - mw->setDevice("/dev/video0"); - mw->show(); + g_mw = new ApplicationWindow(); + g_mw->setCaption( "V4L2 Control Panel" ); + g_mw->setDevice("/dev/video0"); + g_mw->show(); a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) ); return a.exec(); } diff --git a/v4l2-apps/util/qv4l2/qv4l2.h b/v4l2-apps/util/qv4l2/qv4l2.h index 1145af417..1a0a8e15d 100644 --- a/v4l2-apps/util/qv4l2/qv4l2.h +++ b/v4l2-apps/util/qv4l2/qv4l2.h @@ -1,15 +1,24 @@ -/**************************************************************************** -** $Id: qt/application.h 3.3.6 edited Aug 31 2005 $ -** -** Copyright (C) 1992-2005 Trolltech AS. All rights reserved. -** -** This file is part of an example program for Qt. This example -** program may be used, distributed and modified without limitation. -** -*****************************************************************************/ - -#ifndef APPLICATION_H -#define APPLICATION_H +/* qv4l2: a control panel controlling v4l2 devices. + * + * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef QV4L2_H +#define QV4L2_H #include <qmainwindow.h> #include <qtabwidget.h> @@ -18,7 +27,6 @@ #include <map> #include <vector> -#define __user #include <linux/videodev2.h> class QComboBox; @@ -45,6 +53,7 @@ public: ~ApplicationWindow(); void setDevice(const QString &device); + bool doIoctl(QString descr, unsigned cmd, void *arg); protected: void closeEvent( QCloseEvent* ); @@ -52,20 +61,11 @@ protected: private slots: void choose(); void ctrlAction(int); - void inputChanged(int); - void outputChanged(int); - void inputAudioChanged(int); - void outputAudioChanged(int); - void standardChanged(int); - void freqTableChanged(int); - void freqChannelChanged(int); - void freqChanged(int); void about(); private: void addTabs(); - void addGeneralTab(); void finishGrid(QWidget *vbox, QGrid *grid, unsigned ctrl_class, bool odd); void addCtrl(QGrid *grid, const struct v4l2_queryctrl &qctrl); void updateCtrl(unsigned id); @@ -77,7 +77,6 @@ private: void setVal64(unsigned id, long long v); QString getCtrlFlags(unsigned flags); void setWhat(QWidget *w, unsigned id, long long v); - bool doIoctl(QString descr, unsigned cmd, void *arg); void updateVideoInput(); void updateVideoOutput(); void updateAudioInput(); @@ -93,18 +92,8 @@ private: CtrlMap ctrlMap; WidgetMap widgetMap; ClassMap classMap; - struct v4l2_tuner tuner; - - // General tab - QComboBox *videoInput; - QComboBox *videoOutput; - QComboBox *audioInput; - QComboBox *audioOutput; - QComboBox *tvStandard; - QSpinBox *freq; - QComboBox *freqTable; - QComboBox *freqChannel; }; +extern ApplicationWindow *g_mw; #endif diff --git a/v4l2-apps/util/qv4l2/qv4l2.pro b/v4l2-apps/util/qv4l2/qv4l2.pro index d9b5162aa..c53a098b5 100644 --- a/v4l2-apps/util/qv4l2/qv4l2.pro +++ b/v4l2-apps/util/qv4l2/qv4l2.pro @@ -4,8 +4,9 @@ TEMPLATE = app INCLUDEPATH += . ../../../linux/include ../../lib +CONFIG += debug # Input -HEADERS += qv4l2.h -SOURCES += qv4l2.cpp +HEADERS += qv4l2.h general-tab.h +SOURCES += qv4l2.cpp general-tab.cpp ctrl-tab.cpp LIBS += -lv4l2 -L../../lib |