diff options
Diffstat (limited to 'v4l2-apps/util')
-rw-r--r-- | v4l2-apps/util/Makefile | 22 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/ctrl-tab.cpp | 592 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/fileopen.xpm | 22 | ||||
-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 | 178 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/qv4l2.h | 99 | ||||
-rw-r--r-- | v4l2-apps/util/qv4l2/qv4l2.pro | 12 | ||||
-rw-r--r-- | v4l2-apps/util/v4l2-ctl.cpp | 1731 |
9 files changed, 3053 insertions, 0 deletions
diff --git a/v4l2-apps/util/Makefile b/v4l2-apps/util/Makefile new file mode 100644 index 000000000..8555ef5b6 --- /dev/null +++ b/v4l2-apps/util/Makefile @@ -0,0 +1,22 @@ +# Makefile for linuxtv.org v4l2-apps/util + +CPPFLAGS += -I../../linux/include + +binaries = v4l2-ctl + +.PHONY: all clean install qv4l2 + +all: $(binaries) qv4l2 + +clean:: + rm -f $(binaries) + -if [ -f qv4l2/Makefile ]; then make -C qv4l2 $@; fi + -rm -f qv4l2/qv4l2 qv4l2/Makefile + +qv4l2: + if [ ! -f qv4l2/Makefile ]; then (cd qv4l2; qmake); fi + make -C qv4l2 + +install: + +include ../Make.rules 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/fileopen.xpm b/v4l2-apps/util/qv4l2/fileopen.xpm new file mode 100644 index 000000000..880417eee --- /dev/null +++ b/v4l2-apps/util/qv4l2/fileopen.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static const char *fileopen[] = { +" 16 13 5 1", +". c #040404", +"# c #808304", +"a c None", +"b c #f3f704", +"c c #f3f7f3", +"aaaaaaaaa...aaaa", +"aaaaaaaa.aaa.a.a", +"aaaaaaaaaaaaa..a", +"a...aaaaaaaa...a", +".bcb.......aaaaa", +".cbcbcbcbc.aaaaa", +".bcbcbcbcb.aaaaa", +".cbcb...........", +".bcb.#########.a", +".cb.#########.aa", +".b.#########.aaa", +"..#########.aaaa", +"...........aaaaa" +}; 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 new file mode 100644 index 000000000..f9fb07e09 --- /dev/null +++ b/v4l2-apps/util/qv4l2/qv4l2.cpp @@ -0,0 +1,178 @@ + +#include "qv4l2.h" +#include "general-tab.h" +#include "v4l2.h" + +#include <qimage.h> +#include <qpixmap.h> +#include <qtoolbar.h> +#include <qtoolbutton.h> +#include <qpopupmenu.h> +#include <qmenubar.h> +#include <qfile.h> +#include <qfiledialog.h> +#include <qstatusbar.h> +#include <qapplication.h> +#include <qmessagebox.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> + +#include "fileopen.xpm" + +ApplicationWindow::ApplicationWindow() + : QMainWindow( 0, "V4L2 main window", WDestructiveClose | WGroupLeader ) +{ + QPixmap openIcon, saveIcon; + + fd = -1; + + sigMapper = NULL; + QToolBar * fileTools = new QToolBar( this, "file operations" ); + fileTools->setLabel( "File Operations" ); + + openIcon = QPixmap( fileopen ); + QToolButton * fileOpen + = new QToolButton( openIcon, "Open File", QString::null, + this, SLOT(choose()), fileTools, "open file" ); + + (void)QWhatsThis::whatsThisButton( fileTools ); + + const char * fileOpenText = "<p><img source=\"fileopen\"> " + "Click this button to open a <em>new v4l device</em>.<br>" + "You can also select the <b>Open</b> command " + "from the <b>File</b> menu.</p>"; + + QWhatsThis::add( fileOpen, fileOpenText ); + + QMimeSourceFactory::defaultFactory()->setPixmap( "fileopen", openIcon ); + + QPopupMenu * file = new QPopupMenu( this ); + menuBar()->insertItem( "&File", file ); + + + int id; + id = file->insertItem( openIcon, "&Open...", + this, SLOT(choose()), CTRL+Key_O ); + file->setWhatsThis( id, fileOpenText ); + + file->insertSeparator(); + + file->insertItem( "&Close", this, SLOT(close()), CTRL+Key_W ); + + file->insertItem( "&Quit", qApp, SLOT( closeAllWindows() ), CTRL+Key_Q ); + + menuBar()->insertSeparator(); + + QPopupMenu * help = new QPopupMenu( this ); + menuBar()->insertItem( "&Help", help ); + + help->insertItem( "&About", this, SLOT(about()), Key_F1 ); + help->insertItem( "What's &This", this, SLOT(whatsThis()), SHIFT+Key_F1 ); + + statusBar()->message( "Ready", 2000 ); + + tabs = new QTabWidget(this); + tabs->setMargin(3); + + //resize( 450, 600 ); +} + + +ApplicationWindow::~ApplicationWindow() +{ + if (fd >= 0) ::close(fd); +} + + +void ApplicationWindow::setDevice(const QString &device) +{ + if (fd >= 0) ::close(fd); + while (QWidget *page = tabs->page(0)) { + tabs->removePage(page); + delete page; + } + delete tabs; + delete sigMapper; + tabs = new QTabWidget(this); + tabs->setMargin(3); + sigMapper = new QSignalMapper(this); + connect(sigMapper, SIGNAL(mapped(int)), this, SLOT(ctrlAction(int))); + ctrlMap.clear(); + widgetMap.clear(); + classMap.clear(); + + fd = ::open(device, O_RDONLY); + if (fd >= 0) { + tabs->addTab(new GeneralTab(fd, 4, tabs), "General"); + addTabs(); + } + if (QWidget *current = tabs->currentPage()) { + current->show(); + } + tabs->show(); + tabs->setFocus(); + setCentralWidget(tabs); +} + +void ApplicationWindow::choose() +{ + QString fn = QFileDialog::getOpenFileName( "/dev/v4l", QString::null, + this); + if ( !fn.isEmpty() ) { + setDevice(fn); + } + else + statusBar()->message( "Loading aborted", 2000 ); +} + + +void ApplicationWindow::closeEvent( QCloseEvent* ce ) +{ + ce->accept(); +} + +bool ApplicationWindow::doIoctl(QString descr, unsigned cmd, void *arg) +{ + statusBar()->clear(); + int err = ioctl(fd, cmd, arg); + + if (err == -1) { + QString s = strerror(errno); + statusBar()->message(descr + ": " + s, 10000); + } + return err != -1; +} + +void ApplicationWindow::about() +{ + QMessageBox::about( this, "V4L2 Control Panel", + "This program allows easy experimenting with video4linux devices."); +} + +ApplicationWindow *g_mw; + +int main(int argc, char **argv) +{ + QApplication a(argc, argv); + 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 new file mode 100644 index 000000000..1a0a8e15d --- /dev/null +++ b/v4l2-apps/util/qv4l2/qv4l2.h @@ -0,0 +1,99 @@ +/* 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> +#include <qsignalmapper.h> +#include <qgrid.h> +#include <map> +#include <vector> + +#include <linux/videodev2.h> + +class QComboBox; +class QSpinBox; + +typedef std::vector<unsigned> ClassIDVec; +typedef std::map<unsigned, ClassIDVec> ClassMap; +typedef std::map<unsigned, struct v4l2_queryctrl> CtrlMap; +typedef std::map<unsigned, QWidget *> WidgetMap; + +enum { + CTRL_UPDATE_ON_CHANGE = 0x10, + CTRL_DEFAULTS, + CTRL_REFRESH, + CTRL_UPDATE +}; + +class ApplicationWindow: public QMainWindow +{ + Q_OBJECT + +public: + ApplicationWindow(); + ~ApplicationWindow(); + + void setDevice(const QString &device); + bool doIoctl(QString descr, unsigned cmd, void *arg); + +protected: + void closeEvent( QCloseEvent* ); + +private slots: + void choose(); + void ctrlAction(int); + + void about(); + +private: + void addTabs(); + void finishGrid(QWidget *vbox, QGrid *grid, unsigned ctrl_class, bool odd); + void addCtrl(QGrid *grid, const struct v4l2_queryctrl &qctrl); + void updateCtrl(unsigned id); + void refresh(unsigned ctrl_class); + void setDefaults(unsigned ctrl_class); + int getVal(unsigned id); + long long getVal64(unsigned id); + void setVal(unsigned id, int v); + void setVal64(unsigned id, long long v); + QString getCtrlFlags(unsigned flags); + void setWhat(QWidget *w, unsigned id, long long v); + void updateVideoInput(); + void updateVideoOutput(); + void updateAudioInput(); + void updateAudioOutput(); + void updateStandard(); + void updateFreq(); + void updateFreqChannel(); + + QString filename; + QSignalMapper *sigMapper; + QTabWidget *tabs; + int fd; + CtrlMap ctrlMap; + WidgetMap widgetMap; + ClassMap classMap; +}; + +extern ApplicationWindow *g_mw; + +#endif diff --git a/v4l2-apps/util/qv4l2/qv4l2.pro b/v4l2-apps/util/qv4l2/qv4l2.pro new file mode 100644 index 000000000..c53a098b5 --- /dev/null +++ b/v4l2-apps/util/qv4l2/qv4l2.pro @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (1.07a) Sat Jun 17 12:35:16 2006 +###################################################################### + +TEMPLATE = app +INCLUDEPATH += . ../../../linux/include ../../lib +CONFIG += debug + +# Input +HEADERS += qv4l2.h general-tab.h +SOURCES += qv4l2.cpp general-tab.cpp ctrl-tab.cpp +LIBS += -lv4l2 -L../../lib diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp new file mode 100644 index 000000000..bb7b4bd81 --- /dev/null +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -0,0 +1,1731 @@ +/* + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo dot com> + + Cleanup and VBI and audio in/out options: + Copyright (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <unistd.h> +#include <features.h> /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <math.h> +#include <sys/klog.h> + +#include <linux/videodev2.h> + +#include <list> +#include <vector> +#include <map> +#include <string> + +/* Short option list + + Please keep in alphabetical order. + That makes it easier to see which short options are still free. + + In general the lower case is used to set something and the upper + case is used to retrieve a setting. */ +enum Option { + OptGetAudioInput = 'A', + OptSetAudioInput = 'a', + OptGetAudioOutput = 'B', + OptSetAudioOutput = 'b', + OptGetCtrl = 'C', + OptSetCtrl = 'c', + OptSetDevice = 'd', + OptGetDriverInfo = 'D', + OptGetFreq = 'F', + OptSetFreq = 'f', + OptHelp = 'h', + OptGetInput = 'I', + OptSetInput = 'i', + OptListCtrls = 'l', + OptListCtrlsMenus = 'L', + OptListOutputs = 'N', + OptListInputs = 'n', + OptGetOutput = 'O', + OptSetOutput = 'o', + OptListAudioOutputs = 'Q', + OptListAudioInputs = 'q', + OptGetStandard = 'S', + OptSetStandard = 's', + OptGetTuner = 'T', + OptSetTuner = 't', + OptGetVideoFormat = 'V', + OptSetVideoFormat = 'v', + + OptGetSlicedVbiFormat = 128, + OptSetSlicedVbiFormat, + OptGetSlicedVbiOutFormat, + OptSetSlicedVbiOutFormat, + OptGetOverlayFormat, + //OptSetOverlayFormat, TODO + OptGetVbiFormat, + //OptSetVbiFormat, TODO + OptGetVbiOutFormat, + //OptSetVbiOutFormat, TODO + OptAll, + OptStreamOff, + OptStreamOn, + OptListStandards, + OptLogStatus, + OptVerbose, + OptGetVideoOutFormat, + OptSetVideoOutFormat, + OptGetSlicedVbiCap, + OptGetSlicedVbiOutCap, + OptLast = 256 +}; + +static char options[OptLast]; + +typedef std::vector<struct v4l2_ext_control> ctrl_list; +static ctrl_list user_ctrls; +static ctrl_list mpeg_ctrls; + +typedef std::map<std::string, unsigned> ctrl_strmap; +static ctrl_strmap ctrl_str2id; +typedef std::map<unsigned, std::string> ctrl_idmap; +static ctrl_idmap ctrl_id2str; + +typedef std::list<std::string> ctrl_get_list; +static ctrl_get_list get_ctrls; + +typedef std::map<std::string,std::string> ctrl_set_map; +static ctrl_set_map set_ctrls; + +typedef struct { + unsigned flag; + const char *str; +} flag_def; + +static const flag_def service_def[] = { + { V4L2_SLICED_TELETEXT_B, "teletext" }, + { V4L2_SLICED_VPS, "vps" }, + { V4L2_SLICED_CAPTION_525, "cc" }, + { V4L2_SLICED_WSS_625, "wss" }, + { 0, NULL } +}; + +/* fmts specified */ +#define FMTWidth (1L<<0) +#define FMTHeight (1L<<1) + +static struct option long_options[] = { + {"list-audio-inputs", no_argument, 0, OptListAudioInputs}, + {"list-audio-outputs", no_argument, 0, OptListAudioOutputs}, + {"all", no_argument, 0, OptAll}, + {"device", required_argument, 0, OptSetDevice}, + {"get-fmt-video", no_argument, 0, OptGetVideoFormat}, + {"set-fmt-video", required_argument, 0, OptSetVideoFormat}, + {"get-fmt-video-out", no_argument, 0, OptGetVideoOutFormat}, + {"set-fmt-video-out", required_argument, 0, OptSetVideoOutFormat}, + {"help", no_argument, 0, OptHelp}, + {"get-output", no_argument, 0, OptGetOutput}, + {"set-output", required_argument, 0, OptSetOutput}, + {"list-outputs", no_argument, 0, OptListOutputs}, + {"list-inputs", no_argument, 0, OptListInputs}, + {"get-input", no_argument, 0, OptGetInput}, + {"set-input", required_argument, 0, OptSetInput}, + {"get-audio-input", no_argument, 0, OptGetAudioInput}, + {"set-audio-input", required_argument, 0, OptSetAudioInput}, + {"get-audio-output", no_argument, 0, OptGetAudioOutput}, + {"set-audio-output", required_argument, 0, OptSetAudioOutput}, + {"get-freq", no_argument, 0, OptGetFreq}, + {"set-freq", required_argument, 0, OptSetFreq}, + {"streamoff", no_argument, 0, OptStreamOff}, + {"streamon", no_argument, 0, OptStreamOn}, + {"list-standards", no_argument, 0, OptListStandards}, + {"get-standard", no_argument, 0, OptGetStandard}, + {"set-standard", required_argument, 0, OptSetStandard}, + {"info", no_argument, 0, OptGetDriverInfo}, + {"list-ctrls", no_argument, 0, OptListCtrls}, + {"list-ctrls-menus", no_argument, 0, OptListCtrlsMenus}, + {"set-ctrl", required_argument, 0, OptSetCtrl}, + {"get-ctrl", required_argument, 0, OptGetCtrl}, + {"get-tuner", no_argument, 0, OptGetTuner}, + {"set-tuner", required_argument, 0, OptSetTuner}, + {"verbose", no_argument, 0, OptVerbose}, + {"log-status", no_argument, 0, OptLogStatus}, + {"get-fmt-overlay", no_argument, 0, OptGetOverlayFormat}, + {"get-fmt-sliced-vbi", no_argument, 0, OptGetSlicedVbiFormat}, + {"set-fmt-sliced-vbi", required_argument, 0, OptSetSlicedVbiFormat}, + {"get-fmt-sliced-vbi-out", no_argument, 0, OptGetSlicedVbiOutFormat}, + {"set-fmt-sliced-vbi-out", required_argument, 0, OptSetSlicedVbiOutFormat}, + {"get-fmt-vbi", no_argument, 0, OptGetVbiFormat}, + {"get-fmt-vbi-out", no_argument, 0, OptGetVbiOutFormat}, + {"get-sliced-vbi-cap", no_argument, 0, OptGetSlicedVbiCap}, + {"get-sliced-vbi-out-cap", no_argument, 0, OptGetSlicedVbiOutCap}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + printf("Usage:\n"); + printf(" --all display all information available\n"); + printf(" -A, --get-audio-input\n"); + printf(" query the audio input [VIDIOC_G_AUDIO]\n"); + printf(" -a, --set-audio-input=<num>\n"); + printf(" set the audio input to <num> [VIDIOC_S_AUDIO]\n"); + printf(" -B, --get-audio-output\n"); + printf(" query the audio output [VIDIOC_G_AUDOUT]\n"); + printf(" -b, --set-audio-output=<num>\n"); + printf(" set the audio output to <num> [VIDIOC_S_AUDOUT]\n"); + printf(" -C, --get-ctrl=<ctrl>[,<ctrl>...]\n"); + printf(" get the value of the controls [VIDIOC_G_EXT_CTRLS]\n"); + printf(" -c, --set-ctrl=<ctrl>=<val>[,<ctrl>=<val>...]\n"); + printf(" set the controls to the values specified [VIDIOC_S_EXT_CTRLS]\n"); + printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); + printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); + printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); + printf(" -F, --get-freq query the frequency [VIDIOC_G_FREQUENCY]\n"); + printf(" -f, --set-freq=<freq>\n"); + printf(" set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n"); + printf(" -h, --help display this help message\n"); + printf(" -I, --get-input query the video input [VIDIOC_G_INPUT]\n"); + printf(" -i, --set-input=<num>\n"); + printf(" set the video input to <num> [VIDIOC_S_INPUT]\n"); + printf(" -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL]\n"); + printf(" -L, --list-ctrls-menus\n"); + printf(" display all controls, their values and the menus [VIDIOC_QUERYMENU]\n"); + printf(" -N, --list-outputs display video outputs [VIDIOC_ENUMOUTPUT]\n"); + printf(" -n, --list-inputs display video inputs [VIDIOC_ENUMINPUT]\n"); + printf(" -O, --get-output query the video output [VIDIOC_G_OUTPUT]\n"); + printf(" -o, --set-output=<num>\n"); + printf(" set the video output to <num> [VIDIOC_S_OUTPUT]\n"); + printf(" -Q, --list-audio-outputs\n"); + printf(" display audio outputs [VIDIOC_ENUMAUDOUT]\n"); + printf(" -q, --list-audio-inputs\n"); + printf(" display audio inputs [VIDIOC_ENUMAUDIO]\n"); + printf(" -S, --get-standard\n"); + printf(" query the video standard [VIDIOC_G_STD]\n"); + printf(" -s, --set-standard=<num>\n"); + printf(" set the video standard to <num> [VIDIOC_S_STD]\n"); + printf(" <num> can be a numerical v4l2_std value, or it can be one of:\n"); + printf(" pal-X (X = B/G/H/N/Nc/I/D/K/M) or just 'pal' (V4L2_STD_PAL)\n"); + printf(" ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n"); + printf(" secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n"); + printf(" --list-standards display supported video standards [VIDIOC_ENUMSTD]\n"); + printf(" -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n"); + printf(" -t, --set-tuner=<mode>\n"); + printf(" set the audio mode of the tuner [VIDIOC_S_TUNER]\n"); + printf(" Possible values: 0 (mono), 1 (stereo), 2 (lang2), 3 (lang1), 4 (both)\n"); + printf(" -V, --get-fmt-video\n"); + printf(" query the video capture format [VIDIOC_G_FMT]\n"); + printf(" -v, --set-fmt-video=width=<x>,height=<y>\n"); + printf(" set the video capture format [VIDIOC_S_FMT]\n"); + printf(" --get-fmt-video-out\n"); + printf(" query the video output format [VIDIOC_G_FMT]\n"); + printf(" --set-fmt-video-out=width=<x>,height=<y>\n"); + printf(" set the video output format [VIDIOC_S_FMT]\n"); + printf(" --get-fmt-overlay\n"); + printf(" query the video overlay format [VIDIOC_G_FMT]\n"); + printf(" --get-sliced-vbi-cap\n"); + printf(" query the sliced VBI capture capabilities [VIDIOC_G_SLICED_VBI_CAP]\n"); + printf(" --get-sliced-vbi-out-cap\n"); + printf(" query the sliced VBI output capabilities [VIDIOC_G_SLICED_VBI_CAP]\n"); + printf(" --get-fmt-sliced-vbi\n"); + printf(" query the sliced VBI capture format [VIDIOC_G_FMT]\n"); + printf(" --set-fmt-sliced-vbi=<mode>\n"); + printf(" set the sliced VBI capture format to <mode> [VIDIOC_S_FMT]\n"); + printf(" <mode> is a comma separated list of:\n"); + printf(" off: turn off sliced VBI (cannot be combined with other modes)\n"); + printf(" teletext: teletext (PAL/SECAM)\n"); + printf(" cc: closed caption (NTSC)\n"); + printf(" wss: widescreen signal (PAL/SECAM)\n"); + printf(" vps: VPS (PAL/SECAM)\n"); + printf(" --get-fmt-sliced-vbi-out\n"); + printf(" query the sliced VBI output format [VIDIOC_G_FMT]\n"); + printf(" --set-fmt-sliced-vbi-out=<mode>\n"); + printf(" set the sliced VBI output format to <mode> [VIDIOC_S_FMT]\n"); + printf(" <mode> is a comma separated list of:\n"); + printf(" off: turn off sliced VBI (cannot be combined with other modes)\n"); + printf(" teletext: teletext (PAL/SECAM)\n"); + printf(" cc: closed caption (NTSC)\n"); + printf(" wss: widescreen signal (PAL/SECAM)\n"); + printf(" vps: VPS (PAL/SECAM)\n"); + printf(" --get-fmt-vbi query the VBI capture format [VIDIOC_G_FMT]\n"); + printf(" --get-fmt-vbi-out query the VBI output format [VIDIOC_G_FMT]\n"); + printf(" --verbose turn on verbose ioctl error reporting.\n"); + printf("\n"); + printf("Expert options:\n"); + printf(" --streamoff turn the stream off [VIDIOC_STREAMOFF]\n"); + printf(" --streamon turn the stream on [VIDIOC_STREAMOFF]\n"); + printf(" --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); + exit(0); +} + +static std::string num2s(unsigned num) +{ + char buf[10]; + + sprintf(buf, "%08x", num); + return buf; +} + +static std::string buftype2s(int type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return "Video Capture"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return "Video Output"; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return "Video Overlay"; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return "VBI Capture"; + case V4L2_BUF_TYPE_VBI_OUTPUT: + return "VBI Output"; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return "Sliced VBI Capture"; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return "Sliced VBI Output"; + case V4L2_BUF_TYPE_PRIVATE: + return "Private"; + default: + return "Unknown (" + num2s(type) + ")"; + } +} + +static std::string fcc2s(unsigned int val) +{ + std::string s; + + s += val & 0xff; + s += (val >> 8) & 0xff; + s += (val >> 16) & 0xff; + s += (val >> 24) & 0xff; + return s; +} + +static std::string field2s(int val) +{ + switch (val) { + case V4L2_FIELD_ANY: + return "Any"; + case V4L2_FIELD_NONE: + return "None"; + case V4L2_FIELD_TOP: + return "Top"; + case V4L2_FIELD_BOTTOM: + return "Bottom"; + case V4L2_FIELD_INTERLACED: + return "Interlaced"; + case V4L2_FIELD_SEQ_TB: + return "Sequential Top-Bottom"; + case V4L2_FIELD_SEQ_BT: + return "Sequential Bottom-Top"; + case V4L2_FIELD_ALTERNATE: + return "Alternating"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string colorspace2s(int val) +{ + switch (val) { + case V4L2_COLORSPACE_SMPTE170M: + return "Broadcast NTSC/PAL (SMPTE170M/ITU601)"; + case V4L2_COLORSPACE_SMPTE240M: + return "1125-Line (US) HDTV (SMPTE240M)"; + case V4L2_COLORSPACE_REC709: + return "HDTV and modern devices (ITU709)"; + case V4L2_COLORSPACE_BT878: + return "Broken Bt878"; + case V4L2_COLORSPACE_470_SYSTEM_M: + return "NTSC/M (ITU470/ITU601)"; + case V4L2_COLORSPACE_470_SYSTEM_BG: + return "PAL/SECAM BG (ITU470/ITU601)"; + case V4L2_COLORSPACE_JPEG: + return "JPEG (JFIF/ITU601)"; + case V4L2_COLORSPACE_SRGB: + return "SRGB"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string flags2s(unsigned val, const flag_def *def) +{ + std::string s; + + while (def->flag) { + if (val & def->flag) { + if (s.length()) s += " "; + s += def->str; + } + def++; + } + return s; +} + +static void print_sliced_vbi_cap(struct v4l2_sliced_vbi_cap &cap) +{ +// printf("\tType : %s\n", buftype2s(vfmt.type).c_str()); + printf("\tService Set : %s\n", + flags2s(cap.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(cap.service_lines[0][i], service_def).c_str(), + flags2s(cap.service_lines[1][i], service_def).c_str()); + } +} + +static std::string name2var(unsigned char *name) +{ + std::string s; + + while (*name) { + if (*name == ' ') s += "_"; + else s += std::string(1, tolower(*name)); + name++; + } + return s; +} + +static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, + struct v4l2_ext_control *ctrl, int show_menus) +{ + struct v4l2_querymenu qmenu = { 0 }; + std::string s = name2var(queryctrl->name); + int i; + + qmenu.id = queryctrl->id; + switch (queryctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + printf("%31s (int) : min=%d max=%d step=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, queryctrl->default_value, + ctrl->value); + break; + case V4L2_CTRL_TYPE_INTEGER64: + printf("%31s (int64): value=%lld", queryctrl->name, ctrl->value64); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + printf("%31s (bool) : default=%d value=%d", + s.c_str(), + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_MENU: + printf("%31s (menu) : min=%d max=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_BUTTON: + printf("%31s (button)\n", s.c_str()); + break; + default: break; + } + if (queryctrl->flags) { + const flag_def def[] = { + { V4L2_CTRL_FLAG_GRABBED, "grabbed" }, + { V4L2_CTRL_FLAG_READ_ONLY, "readonly" }, + { V4L2_CTRL_FLAG_UPDATE, "update" }, + { V4L2_CTRL_FLAG_INACTIVE, "inactive" }, + { V4L2_CTRL_FLAG_SLIDER, "slider" }, + { 0, NULL } + }; + printf(" flags=%s", flags2s(queryctrl->flags, def).c_str()); + } + printf("\n"); + if (queryctrl->type == V4L2_CTRL_TYPE_MENU && show_menus) { + for (i = 0; i <= queryctrl->maximum; i++) { + qmenu.index = i; + if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) + continue; + printf("\t\t\t\t%d: %s\n", i, qmenu.name); + } + } +} + +static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) +{ + struct v4l2_control ctrl = { 0 }; + struct v4l2_ext_control ext_ctrl = { 0 }; + struct v4l2_ext_controls ctrls = { 0 }; + + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) + return 1; + if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) { + printf("\n%s\n\n", qctrl.name); + return 1; + } + ext_ctrl.id = qctrl.id; + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id); + ctrls.count = 1; + ctrls.controls = &ext_ctrl; + if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER && + qctrl.id < V4L2_CID_PRIVATE_BASE) { + if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) { + printf("error %d getting ext_ctrl %s\n", + errno, qctrl.name); + return 0; + } + } + else { + ctrl.id = qctrl.id; + if (ioctl(fd, VIDIOC_G_CTRL, &ctrl)) { + printf("error %d getting ctrl %s\n", + errno, qctrl.name); + return 0; + } + ext_ctrl.value = ctrl.value; + } + print_qctrl(fd, &qctrl, &ext_ctrl, show_menus); + return 1; +} + +static void list_controls(int fd, int show_menus) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + print_control(fd, qctrl, show_menus); + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) + print_control(fd, qctrl, show_menus); + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + print_control(fd, qctrl, show_menus); + } +} + +static void find_controls(int fd) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS) { + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_id2str[qctrl.id] = name2var(qctrl.name); + } + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } +} + +static int printfmt(struct v4l2_format vfmt) +{ + const flag_def vbi_def[] = { + { V4L2_VBI_UNSYNC, "unsynchronized" }, + { V4L2_VBI_INTERLACED, "interlaced" }, + { 0, NULL } + }; + printf("Format:\n"); + + switch (vfmt.type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + printf("\tType : %s\n", buftype2s(vfmt.type).c_str()); + printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height); + printf("\tPixel Format : %s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); + printf("\tField : %s\n", field2s(vfmt.fmt.pix.field).c_str()); + printf("\tBytes per Line: %u\n", vfmt.fmt.pix.bytesperline); + printf("\tSize Image : %u\n", vfmt.fmt.pix.sizeimage); + printf("\tColorspace : %s\n", colorspace2s(vfmt.fmt.pix.colorspace).c_str()); + if (vfmt.fmt.pix.priv) + printf("\tCustom Info : %08x\n", vfmt.fmt.pix.priv); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + printf("\tType : %s\n", buftype2s(vfmt.type).c_str()); + printf("\tLeft/Top : %d/%d\n", + vfmt.fmt.win.w.left, vfmt.fmt.win.w.top); + printf("\tWidth/Height: %d/%d\n", + vfmt.fmt.win.w.width, vfmt.fmt.win.w.height); + printf("\tField : %s\n", field2s(vfmt.fmt.win.field).c_str()); + // TODO: check G_FBUF capabilities + printf("\tChroma Key : %08x\n", vfmt.fmt.win.chromakey); + printf("\tClip Count : %u\n", vfmt.fmt.win.clipcount); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + printf("\tType : %s\n", buftype2s(vfmt.type).c_str()); + printf("\tSampling Rate : %u Hz\n", vfmt.fmt.vbi.sampling_rate); + printf("\tOffset : %u samples (%g secs after leading edge)\n", + vfmt.fmt.vbi.offset, + (double)vfmt.fmt.vbi.offset / (double)vfmt.fmt.vbi.sampling_rate); + printf("\tSamples per Line: %u\n", vfmt.fmt.vbi.samples_per_line); + printf("\tSample Format : %s\n", fcc2s(vfmt.fmt.vbi.sample_format).c_str()); + printf("\tStart 1st Field : %u\n", vfmt.fmt.vbi.start[0]); + printf("\tCount 1st Field : %u\n", vfmt.fmt.vbi.count[0]); + printf("\tStart 2nd Field : %u\n", vfmt.fmt.vbi.start[1]); + printf("\tCount 2nd Field : %u\n", vfmt.fmt.vbi.count[1]); + if (vfmt.fmt.vbi.flags) + printf("\tFlags : %s\n", flags2s(vfmt.fmt.vbi.flags, vbi_def).c_str()); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + printf("\tType : %s\n", buftype2s(vfmt.type).c_str()); + printf("\tService Set : %s\n", + flags2s(vfmt.fmt.sliced.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(vfmt.fmt.sliced.service_lines[0][i], service_def).c_str(), + flags2s(vfmt.fmt.sliced.service_lines[1][i], service_def).c_str()); + } + printf("\tI/O Size : %u\n", vfmt.fmt.sliced.io_size); + break; + case V4L2_BUF_TYPE_PRIVATE: + printf("\tType: %s\n", buftype2s(vfmt.type).c_str()); + break; + default: + printf("\tType: %s\n", buftype2s(vfmt.type).c_str()); + return -1; + } + return 0; +} + +static char *pts_to_string(char *str, unsigned long pts) +{ + static char buf[256]; + int hours, minutes, seconds, fracsec; + float fps; + int frame; + char *p = (str) ? str : buf; + + static const int MPEG_CLOCK_FREQ = 90000; + seconds = pts / MPEG_CLOCK_FREQ; + fracsec = pts % MPEG_CLOCK_FREQ; + + minutes = seconds / 60; + seconds = seconds % 60; + + hours = minutes / 60; + minutes = minutes % 60; + + fps = 30; + frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps); + + snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds, + frame); + return p; +} + +static const char *audmode2s(int audmode) +{ + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: return "stereo"; + case V4L2_TUNER_MODE_LANG1: return "lang1"; + case V4L2_TUNER_MODE_LANG2: return "lang2"; + case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual"; + case V4L2_TUNER_MODE_MONO: return "mono"; + default: return "unknown"; + } +} + +static std::string rxsubchans2s(int rxsubchans) +{ + std::string s; + + if (rxsubchans & V4L2_TUNER_SUB_MONO) + s += "mono "; + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + s += "stereo "; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + s += "lang1 "; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + s += "lang2 "; + return s; +} + +static std::string tcap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_TUNER_CAP_LOW) + s += "62.5 Hz "; + else + s += "62.5 kHz "; + if (cap & V4L2_TUNER_CAP_NORM) + s += "multi-standard "; + if (cap & V4L2_TUNER_CAP_STEREO) + s += "stereo "; + if (cap & V4L2_TUNER_CAP_LANG1) + s += "lang1 "; + if (cap & V4L2_TUNER_CAP_LANG2) + s += "lang2 "; + return s; +} + +static std::string cap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_CAP_VIDEO_CAPTURE) + s += "\t\tVideo Capture\n"; + if (cap & V4L2_CAP_VIDEO_OUTPUT) + s += "\t\tVideo Output\n"; + if (cap & V4L2_CAP_VIDEO_OVERLAY) + s += "\t\tVideo Overlay\n"; + if (cap & V4L2_CAP_VBI_CAPTURE) + s += "\t\tVBI Capture\n"; + if (cap & V4L2_CAP_VBI_OUTPUT) + s += "\t\tVBI Output\n"; + if (cap & V4L2_CAP_SLICED_VBI_CAPTURE) + s += "\t\tSliced VBI Capture\n"; + if (cap & V4L2_CAP_SLICED_VBI_OUTPUT) + s += "\t\tSliced VBI Output\n"; + if (cap & V4L2_CAP_RDS_CAPTURE) + s += "\t\tRDS Capture\n"; + if (cap & V4L2_CAP_TUNER) + s += "\t\tTuner\n"; + if (cap & V4L2_CAP_AUDIO) + s += "\t\tAudio\n"; + if (cap & V4L2_CAP_RADIO) + s += "\t\tRadio\n"; + if (cap & V4L2_CAP_READWRITE) + s += "\t\tRead/Write\n"; + if (cap & V4L2_CAP_ASYNCIO) + s += "\t\tAsync I/O\n"; + if (cap & V4L2_CAP_STREAMING) + s += "\t\tStreaming\n"; + return s; +} + +static v4l2_std_id parse_pal(const char *pal) +{ + if (pal[0] == '-') { + switch (pal[1]) { + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[2] == 'c' || pal[2] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + } + } + fprintf(stderr, "pal specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_secam(const char *secam) +{ + if (secam[0] == '-') { + switch (secam[1]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[2] == 'C' || secam[2] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + } + } + fprintf(stderr, "secam specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_ntsc(const char *ntsc) +{ + if (ntsc[0] == '-') { + switch (ntsc[1]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + } + } + fprintf(stderr, "ntsc specifier not recognised\n"); + return 0; +} + +static int doioctl(int fd, int request, void *parm, const char *name) +{ + int retVal; + + if (!options[OptVerbose]) return ioctl(fd, request, parm); + retVal = ioctl(fd, request, parm); + printf("%s: ", name); + if (retVal < 0) + printf("failed: %s\n", strerror(errno)); + else + printf("ok\n"); + + return retVal; +} + +static int parse_subopt(char **subs, char * const *subopts, char **value) +{ + int opt = getsubopt(subs, subopts, value); + + if (opt == -1) { + fprintf(stderr, "Invalid suboptions specified\n"); + usage(); + exit(1); + } + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } + return opt; +} + +static void parse_next_subopt(char **subs, char **value) +{ + static char *const subopts[] = { + NULL + }; + int opt = getsubopt(subs, subopts, value); + + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } +} + +static void print_std(const char *prefix, const char *stds[], unsigned long long std) +{ + int first = 1; + + printf("\t%s-", prefix); + while (*stds) { + if (std & 1) { + if (!first) + printf("/"); + first = 0; + printf("%s", *stds); + } + stds++; + std >>= 1; + } + printf("\n"); +} + +int main(int argc, char **argv) +{ + char *value, *subs; + int i; + + int fd = -1; + + /* bitfield for fmts */ + unsigned int set_fmts = 0; + unsigned int set_fmts_out = 0; + + int mode = V4L2_TUNER_MODE_STEREO; /* set audio mode */ + + /* command args */ + int ch; + char *device = strdup("/dev/video0"); /* -d device */ + struct v4l2_format vfmt; /* set_format/get_format for video */ + struct v4l2_format vfmt_out; /* set_format/get_format video output */ + struct v4l2_format vbi_fmt; /* set_format/get_format for sliced VBI */ + struct v4l2_format vbi_fmt_out; /* set_format/get_format for sliced VBI output */ + struct v4l2_format raw_fmt; /* set_format/get_format for VBI */ + struct v4l2_format raw_fmt_out; /* set_format/get_format for VBI output */ + struct v4l2_tuner tuner; /* set_tuner/get_tuner */ + struct v4l2_capability vcap; /* list_cap */ + struct v4l2_input vin; /* list_inputs */ + struct v4l2_output vout; /* list_outputs */ + struct v4l2_audio vaudio; /* list audio inputs */ + struct v4l2_audioout vaudout; /* audio outputs */ + int input; /* set_input/get_input */ + int output; /* set_output/get_output */ + v4l2_std_id std; /* get_std/set_std */ + double freq = 0; /* get/set frequency */ + struct v4l2_frequency vf; /* get_freq/set_freq */ + struct v4l2_standard vs; /* list_std */ + char short_options[26 * 2 * 2 + 1]; + int idx = 0; + + memset(&vfmt, 0, sizeof(vfmt)); + memset(&vbi_fmt, 0, sizeof(vbi_fmt)); + memset(&raw_fmt, 0, sizeof(raw_fmt)); + memset(&vfmt_out, 0, sizeof(vfmt_out)); + memset(&vbi_fmt_out, 0, sizeof(vbi_fmt_out)); + memset(&raw_fmt_out, 0, sizeof(raw_fmt_out)); + memset(&tuner, 0, sizeof(tuner)); + memset(&vcap, 0, sizeof(vcap)); + memset(&vin, 0, sizeof(vin)); + memset(&vout, 0, sizeof(vout)); + memset(&vaudio, 0, sizeof(vaudio)); + memset(&vaudout, 0, sizeof(vaudout)); + memset(&vf, 0, sizeof(vf)); + memset(&vs, 0, sizeof(vs)); + + if (argc == 1) { + usage(); + return 0; + } + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + while (1) { + int option_index = 0; + + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + options[(int)ch] = 1; + switch (ch) { + case OptHelp: + usage(); + return 0; + case OptSetDevice: + device = strdup(optarg); + if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + char dev = device[0]; + + sprintf(device, "/dev/video%c", dev); + } + break; + case OptSetVideoFormat: + subs = optarg; + while (*subs != '\0') { + static char *const subopts[] = { + "width", + "height", + NULL + }; + + switch (parse_subopt(&subs, subopts, &value)) { + case 0: + vfmt.fmt.pix.width = strtol(value, 0L, 0); + set_fmts |= FMTWidth; + break; + case 1: + vfmt.fmt.pix.height = strtol(value, 0L, 0); + set_fmts |= FMTHeight; + break; + } + } + break; + case OptSetVideoOutFormat: + subs = optarg; + while (*subs != '\0') { + static char *const subopts[] = { + "width", + "height", + NULL + }; + + switch (parse_subopt(&subs, subopts, &value)) { + case 0: + vfmt_out.fmt.pix.width = strtol(value, 0L, 0); + set_fmts_out |= FMTWidth; + break; + case 1: + vfmt_out.fmt.pix.height = strtol(value, 0L, 0); + set_fmts_out |= FMTHeight; + break; + } + } + break; + case OptSetInput: + input = strtol(optarg, 0L, 0); + break; + case OptSetOutput: + output = strtol(optarg, 0L, 0); + break; + case OptSetAudioInput: + vaudio.index = strtol(optarg, 0L, 0); + break; + case OptSetAudioOutput: + vaudout.index = strtol(optarg, 0L, 0); + break; + case OptSetFreq: + freq = strtod(optarg, NULL); + break; + case OptSetStandard: + if (!strncmp(optarg, "pal", 3)) { + if (optarg[3]) + std = parse_pal(optarg + 3); + else + std = V4L2_STD_PAL; + } + else if (!strncmp(optarg, "ntsc", 4)) { + if (optarg[4]) + std = parse_ntsc(optarg + 4); + else + std = V4L2_STD_NTSC; + } + else if (!strncmp(optarg, "secam", 5)) { + if (optarg[5]) + std = parse_secam(optarg + 5); + else + std = V4L2_STD_SECAM; + } + else { + std = strtol(optarg, 0L, 0); + } + break; + case OptGetCtrl: + subs = optarg; + while (*subs != '\0') { + parse_next_subopt(&subs, &value); + if (strchr(value, '=')) { + usage(); + exit(1); + } + else { + get_ctrls.push_back(value); + } + } + break; + case OptSetCtrl: + subs = optarg; + while (*subs != '\0') { + parse_next_subopt(&subs, &value); + if (const char *equal = strchr(value, '=')) { + set_ctrls[std::string(value, (equal - value))] = equal + 1; + } + else { + fprintf(stderr, "control '%s' without '='\n", value); + exit(1); + } + } + break; + case OptSetTuner: + if (!strcmp(optarg, "stereo")) + mode = V4L2_TUNER_MODE_STEREO; + else if (!strcmp(optarg, "lang1")) + mode = V4L2_TUNER_MODE_LANG1; + else if (!strcmp(optarg, "lang2")) + mode = V4L2_TUNER_MODE_LANG2; + else if (!strcmp(optarg, "bilingual")) + mode = V4L2_TUNER_MODE_LANG1_LANG2; + else if (!strcmp(optarg, "mono")) + mode = V4L2_TUNER_MODE_MONO; + else { + fprintf(stderr, "Unknown audio mode\n"); + usage(); + return 1; + } + break; + case OptSetSlicedVbiFormat: + case OptSetSlicedVbiOutFormat: + { + bool foundOff = false; + v4l2_format *fmt = &vbi_fmt; + + if (ch == OptSetSlicedVbiOutFormat) + fmt = &vbi_fmt_out; + fmt->fmt.sliced.service_set = 0; + subs = optarg; + while (*subs != '\0') { + static char *const subopts[] = { + "off", + "teletext", + "cc", + "wss", + "vps", + NULL + }; + + switch (parse_subopt(&subs, subopts, &value)) { + case 0: + foundOff = true; + break; + case 1: + fmt->fmt.sliced.service_set |= + V4L2_SLICED_TELETEXT_B; + break; + case 2: + fmt->fmt.sliced.service_set |= + V4L2_SLICED_CAPTION_525; + break; + case 3: + fmt->fmt.sliced.service_set |= + V4L2_SLICED_WSS_625; + break; + case 4: + fmt->fmt.sliced.service_set |= + V4L2_SLICED_VPS; + break; + } + } + if (foundOff && fmt->fmt.sliced.service_set) { + fprintf(stderr, "Sliced VBI mode 'off' cannot be combined with other modes\n"); + usage(); + return 1; + } + break; + } + case ':': + fprintf(stderr, "Option `%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + fprintf(stderr, "Unknown argument `%s'\n", + argv[optind]); + usage(); + return 1; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + + if ((fd = open(device, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + free(device); + + doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); + find_controls(fd); + for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { + if (ctrl_str2id.find(*iter) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", (*iter).c_str()); + exit(1); + } + } + for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) { + if (ctrl_str2id.find(iter->first) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", iter->first.c_str()); + exit(1); + } + } + + if (options[OptAll]) { + options[OptGetVideoFormat] = 1; + options[OptGetVideoOutFormat] = 1; + options[OptGetDriverInfo] = 1; + options[OptGetInput] = 1; + options[OptGetOutput] = 1; + options[OptGetAudioInput] = 1; + options[OptGetAudioOutput] = 1; + options[OptGetStandard] = 1; + options[OptGetFreq] = 1; + options[OptGetTuner] = 1; + options[OptGetOverlayFormat] = 1; + options[OptGetVbiFormat] = 1; + options[OptGetVbiOutFormat] = 1; + options[OptGetSlicedVbiFormat] = 1; + options[OptGetSlicedVbiOutFormat] = 1; + } + + /* Information Opts */ + + if (options[OptGetDriverInfo]) { + printf("Driver info:\n"); + printf("\tDriver name : %s\n", vcap.driver); + printf("\tCard type : %s\n", vcap.card); + printf("\tBus info : %s\n", vcap.bus_info); + printf("\tDriver version: %d\n", vcap.version); + printf("\tCapabilities : 0x%08X\n", vcap.capabilities); + printf("%s", cap2s(vcap.capabilities).c_str()); + } + + /* Set options */ + + if (options[OptStreamOff]) { + int dummy = 0; + doioctl(fd, VIDIOC_STREAMOFF, &dummy, "VIDIOC_STREAMOFF"); + } + + if (options[OptSetFreq]) { + double fac = 16; + + if (doioctl(fd, VIDIOC_G_TUNER, &tuner, "VIDIOC_G_TUNER") == 0) { + fac = (tuner.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + } + vf.tuner = 0; + vf.type = tuner.type; + vf.frequency = __u32(freq * fac); + if (doioctl(fd, VIDIOC_S_FREQUENCY, &vf, + "VIDIOC_S_FREQUENCY") == 0) + printf("Frequency set to %d (%f MHz)\n", vf.frequency, + vf.frequency / fac); + } + + if (options[OptSetStandard]) { + if (std < 16) { + vs.index = std; + if (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { + std = vs.id; + } + } + if (doioctl(fd, VIDIOC_S_STD, &std, "VIDIOC_S_STD") == 0) + printf("Standard set to %08llx\n", (unsigned long long)std); + } + + if (options[OptSetInput]) { + if (doioctl(fd, VIDIOC_S_INPUT, &input, "VIDIOC_S_INPUT") == 0) { + printf("Video input set to %d", input); + vin.index = input; + if (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) + printf(" (%s)", vin.name); + printf("\n"); + } + } + + if (options[OptSetOutput]) { + if (doioctl(fd, VIDIOC_S_OUTPUT, &output, "VIDIOC_S_OUTPUT") == 0) + printf("Output set to %d\n", output); + } + + if (options[OptSetAudioInput]) { + if (doioctl(fd, VIDIOC_S_AUDIO, &vaudio, "VIDIOC_S_AUDIO") == 0) + printf("Audio input set to %d\n", vaudio.index); + } + + if (options[OptSetAudioOutput]) { + if (doioctl(fd, VIDIOC_S_AUDOUT, &vaudout, "VIDIOC_S_AUDOUT") == 0) + printf("Audio output set to %d\n", vaudout.index); + } + + if (options[OptSetTuner]) { + struct v4l2_tuner vt; + + memset(&vt, 0, sizeof(struct v4l2_tuner)); + if (ioctl(fd, VIDIOC_G_TUNER, &vt) < 0) { + fprintf(stderr, "ioctl: VIDIOC_G_TUNER failed\n"); + exit(1); + } + vt.audmode = mode; + doioctl(fd, VIDIOC_S_TUNER, &vt, "VIDIOC_S_TUNER"); + } + + if (options[OptSetVideoFormat]) { + struct v4l2_format in_vfmt; + printf("ioctl: VIDIOC_S_FMT\n"); + in_vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(fd, VIDIOC_G_FMT, &in_vfmt) < 0) + fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n"); + else { + if (set_fmts & FMTWidth) + in_vfmt.fmt.pix.width = vfmt.fmt.pix.width; + if (set_fmts & FMTHeight) + in_vfmt.fmt.pix.height = vfmt.fmt.pix.height; + if (ioctl(fd, VIDIOC_S_FMT, &in_vfmt) < 0) + fprintf(stderr, "ioctl: VIDIOC_S_FMT failed\n"); + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(fd, VIDIOC_G_FMT, &vfmt) < 0) + fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n"); + } + } + + if (options[OptSetVideoOutFormat]) { + struct v4l2_format in_vfmt; + printf("ioctl: VIDIOC_S_FMT\n"); + in_vfmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (ioctl(fd, VIDIOC_G_FMT, &in_vfmt) < 0) + fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n"); + else { + if (set_fmts & FMTWidth) + in_vfmt.fmt.pix.width = vfmt.fmt.pix.width; + if (set_fmts & FMTHeight) + in_vfmt.fmt.pix.height = vfmt.fmt.pix.height; + if (ioctl(fd, VIDIOC_S_FMT, &in_vfmt) < 0) + fprintf(stderr, "ioctl: VIDIOC_S_FMT failed\n"); + vfmt_out.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (ioctl(fd, VIDIOC_G_FMT, &vfmt_out) < 0) + fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n"); + } + } + + if (options[OptSetSlicedVbiFormat]) { + if (vbi_fmt.fmt.sliced.service_set == 0) { + // switch to raw mode + vbi_fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE; + if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt, "VIDIOC_G_FMT") == 0) + doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); + } else { + vbi_fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); + } + } + + if (options[OptSetSlicedVbiOutFormat]) { + if (vbi_fmt_out.fmt.sliced.service_set == 0) { + // switch to raw mode + vbi_fmt_out.type = V4L2_BUF_TYPE_VBI_OUTPUT; + if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt_out, "VIDIOC_G_FMT") == 0) + doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); + } else { + vbi_fmt_out.type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); + } + } + + if (options[OptSetCtrl] && !set_ctrls.empty()) { + struct v4l2_ext_controls ctrls = { 0 }; + + for (ctrl_set_map::iterator iter = set_ctrls.begin(); + iter != set_ctrls.end(); ++iter) { + struct v4l2_ext_control ctrl = { 0 }; + + ctrl.id = ctrl_str2id[iter->first]; + ctrl.value = atol(iter->second.c_str()); + if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_MPEG) + mpeg_ctrls.push_back(ctrl); + else + user_ctrls.push_back(ctrl); + } + for (unsigned i = 0; i < user_ctrls.size(); i++) { + struct v4l2_control ctrl; + + ctrl.id = user_ctrls[i].id; + ctrl.value = user_ctrls[i].value; + if (doioctl(fd, VIDIOC_S_CTRL, &ctrl, "VIDIOC_S_CTRL")) { + fprintf(stderr, "%s: %s\n", + ctrl_id2str[ctrl.id].c_str(), + strerror(errno)); + } + } + if (mpeg_ctrls.size()) { + ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; + ctrls.count = mpeg_ctrls.size(); + ctrls.controls = &mpeg_ctrls[0]; + if (doioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls, "VIDIOC_S_EXT_CTRLS")) { + if (ctrls.error_idx >= ctrls.count) { + fprintf(stderr, "Error setting MPEG controls: %s\n", + strerror(errno)); + } + else { + fprintf(stderr, "%s: %s\n", + ctrl_id2str[mpeg_ctrls[ctrls.error_idx].id].c_str(), + strerror(errno)); + } + } + } + } + + /* Get options */ + + if (options[OptGetVideoFormat]) { + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (doioctl(fd, VIDIOC_G_FMT, &vfmt, "VIDIOC_G_FMT") == 0) + if (printfmt(vfmt) != 0) + fprintf(stderr, "error printing result\n"); + } + + if (options[OptGetVideoOutFormat]) { + vfmt_out.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (doioctl(fd, VIDIOC_G_FMT, &vfmt_out, "VIDIOC_G_FMT") == 0) + if (printfmt(vfmt_out) != 0) + fprintf(stderr, "error printing result\n"); + } + + if (options[OptGetOverlayFormat]) { + struct v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + if (doioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT") == 0) + if (printfmt(fmt) != 0) + fprintf(stderr, "error printing result\n"); + } + + if (options[OptGetSlicedVbiFormat]) { + vbi_fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt, + "VIDIOC_G_FMT") == 0) + if (printfmt(vbi_fmt) != 0) + fprintf(stderr, + "error printing result\n"); + } + + if (options[OptGetSlicedVbiOutFormat]) { + vbi_fmt_out.type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt_out, + "VIDIOC_G_FMT") == 0) + if (printfmt(vbi_fmt_out) != 0) + fprintf(stderr, + "error printing result\n"); + } + + if (options[OptGetVbiFormat]) { + raw_fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE; + if (doioctl(fd, VIDIOC_G_FMT, &raw_fmt, + "VIDIOC_G_FMT") == 0) + if (printfmt(raw_fmt) != 0) + fprintf(stderr, + "error printing result\n"); + } + + if (options[OptGetVbiOutFormat]) { + raw_fmt_out.type = V4L2_BUF_TYPE_VBI_OUTPUT; + if (doioctl(fd, VIDIOC_G_FMT, &raw_fmt_out, + "VIDIOC_G_FMT") == 0) + if (printfmt(raw_fmt_out) != 0) + fprintf(stderr, + "error printing result\n"); + } + + if (options[OptGetInput]) { + if (doioctl(fd, VIDIOC_G_INPUT, &input, "VIDIOC_G_INPUT") == 0) { + printf("Video input : %d", input); + vin.index = input; + if (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) + printf(" (%s)", vin.name); + printf("\n"); + } + } + + if (options[OptGetOutput]) { + if (doioctl(fd, VIDIOC_G_OUTPUT, &output, "VIDIOC_G_OUTPUT") == 0) { + printf("Video output: %d", output); + vout.index = output; + if (ioctl(fd, VIDIOC_ENUMOUTPUT, &vout) >= 0) { + printf(" (%s)", vout.name); + } + printf("\n"); + } + } + + if (options[OptGetAudioInput]) { + if (doioctl(fd, VIDIOC_G_AUDIO, &vaudio, "VIDIOC_G_AUDIO") == 0) + printf("Audio input : %d (%s)\n", vaudio.index, vaudio.name); + } + + if (options[OptGetAudioOutput]) { + if (doioctl(fd, VIDIOC_G_AUDOUT, &vaudout, "VIDIOC_G_AUDOUT") == 0) + printf("Audio output: %d (%s)\n", vaudout.index, vaudout.name); + } + + if (options[OptGetFreq]) { + double fac = 16; + + if (doioctl(fd, VIDIOC_G_TUNER, &tuner, "VIDIOC_G_TUNER") == 0) { + fac = (tuner.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + } + vf.tuner = 0; + if (doioctl(fd, VIDIOC_G_FREQUENCY, &vf, "VIDIOC_G_FREQUENCY") == 0) + printf("Frequency: %d (%f MHz)\n", vf.frequency, + vf.frequency / fac); + } + + if (options[OptGetStandard]) { + if (doioctl(fd, VIDIOC_G_STD, &std, "VIDIOC_G_STD") == 0) { + static const char *pal[] = { + "B", "B1", "G", "H", "I", "D", "D1", "K", + "M", "N", "Nc", "60", + NULL + }; + static const char *ntsc[] = { + "M", "M-JP", "443", "M-KR", + NULL + }; + static const char *secam[] = { + "B", "D", "G", "H", "K", "K1", "L", "Lc", + NULL + }; + static const char *atsc[] = { + "ATSC-8-VSB", "ATSC-16-VSB", + NULL + }; + + printf("Video standard = 0x%08llx\n", (unsigned long long)std); + if (std & 0xfff) { + print_std("PAL", pal, std); + } + if (std & 0xf000) { + print_std("NTSC", ntsc, std >> 12); + } + if (std & 0xff0000) { + print_std("SECAM", secam, std >> 16); + } + if (std & 0xf000000) { + print_std("ATSC/HDTV", atsc, std >> 24); + } + } + } + + if (options[OptGetCtrl] && !get_ctrls.empty()) { + struct v4l2_ext_controls ctrls = { 0 }; + + mpeg_ctrls.clear(); + user_ctrls.clear(); + for (ctrl_get_list::iterator iter = get_ctrls.begin(); + iter != get_ctrls.end(); ++iter) { + struct v4l2_ext_control ctrl = { 0 }; + + ctrl.id = ctrl_str2id[*iter]; + if (V4L2_CTRL_ID2CLASS(ctrl.id) == V4L2_CTRL_CLASS_MPEG) + mpeg_ctrls.push_back(ctrl); + else + user_ctrls.push_back(ctrl); + } + for (unsigned i = 0; i < user_ctrls.size(); i++) { + struct v4l2_control ctrl; + + ctrl.id = user_ctrls[i].id; + doioctl(fd, VIDIOC_G_CTRL, &ctrl, "VIDIOC_G_CTRL"); + printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + } + if (mpeg_ctrls.size()) { + ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; + ctrls.count = mpeg_ctrls.size(); + ctrls.controls = &mpeg_ctrls[0]; + doioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls, "VIDIOC_G_EXT_CTRLS"); + for (unsigned i = 0; i < mpeg_ctrls.size(); i++) { + struct v4l2_ext_control ctrl = mpeg_ctrls[i]; + + printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + } + } + } + + if (options[OptGetTuner]) { + struct v4l2_tuner vt; + memset(&vt, 0, sizeof(struct v4l2_tuner)); + if (doioctl(fd, VIDIOC_G_TUNER, &vt, "VIDIOC_G_TUNER") == 0) { + printf("Tuner:\n"); + printf("\tCapabilities : %s\n", tcap2s(vt.capability).c_str()); + if (vt.capability & V4L2_TUNER_CAP_LOW) + printf("\tFrequency range : %.1f MHz - %.1f MHz\n", + vt.rangelow / 16000.0, vt.rangehigh / 16000.0); + else + printf("\tFrequency range : %.1f MHz - %.1f MHz\n", + vt.rangelow / 16.0, vt.rangehigh / 16.0); + printf("\tSignal strength : %d%%\n", (int)(vt.signal / 655.35)); + printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode)); + printf("\tAvailable subchannels: %s\n", + rxsubchans2s(vt.rxsubchans).c_str()); + } + } + + if (options[OptLogStatus]) { + static char buf[40960]; + int len; + + if (doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS") == 0) { + printf("\nStatus Log:\n\n"); + len = klogctl(3, buf, sizeof(buf) - 1); + if (len >= 0) { + char *p = buf; + char *q; + + buf[len] = 0; + while ((q = strstr(p, "START STATUS CARD #"))) { + p = q + 1; + } + if (p) { + while (p > buf && *p != '<') p--; + q = p; + while ((q = strstr(q, "<6>"))) { + memcpy(q, " ", 3); + } + printf("%s", p); + } + } + } + } + + /* List options */ + + if (options[OptListInputs]) { + vin.index = 0; + printf("ioctl: VIDIOC_ENUMINPUT\n"); + while (ioctl(fd, VIDIOC_ENUMINPUT, &vin) >= 0) { + if (vin.index) + printf("\n"); + printf("\tInput : %d\n", vin.index); + printf("\tName : %s\n", vin.name); + printf("\tType : 0x%08X\n", vin.type); + printf("\tAudioset: 0x%08X\n", vin.audioset); + printf("\tTuner : 0x%08X\n", vin.tuner); + printf("\tStandard: 0x%016llX ( ", (unsigned long long)vin.std); + if (vin.std & 0x000FFF) + printf("PAL "); // hack + if (vin.std & 0x00F000) + printf("NTSC "); // hack + if (vin.std & 0x7F0000) + printf("SECAM "); // hack + printf(")\n"); + printf("\tStatus : %d\n", vin.status); + vin.index++; + } + } + + if (options[OptListOutputs]) { + vout.index = 0; + printf("ioctl: VIDIOC_ENUMOUTPUT\n"); + while (ioctl(fd, VIDIOC_ENUMOUTPUT, &vout) >= 0) { + if (vout.index) + printf("\n"); + printf("\tOutput : %d\n", vout.index); + printf("\tName : %s\n", vout.name); + printf("\tType : 0x%08X\n", vout.type); + printf("\tAudioset: 0x%08X\n", vout.audioset); + printf("\tStandard: 0x%016llX ( ", (unsigned long long)vout.std); + if (vout.std & 0x000FFF) + printf("PAL "); // hack + if (vout.std & 0x00F000) + printf("NTSC "); // hack + if (vout.std & 0x7F0000) + printf("SECAM "); // hack + printf(")\n"); + vout.index++; + } + } + + if (options[OptListAudioInputs]) { + struct v4l2_audio vaudio; /* list audio inputs */ + vaudio.index = 0; + printf("ioctl: VIDIOC_ENUMAUDIO\n"); + while (ioctl(fd, VIDIOC_ENUMAUDIO, &vaudio) >= 0) { + if (vaudio.index) + printf("\n"); + printf("\tInput : %d\n", vaudio.index); + printf("\tName : %s\n", vaudio.name); + vaudio.index++; + } + } + + if (options[OptListAudioOutputs]) { + struct v4l2_audioout vaudio; /* list audio outputs */ + vaudio.index = 0; + printf("ioctl: VIDIOC_ENUMAUDOUT\n"); + while (ioctl(fd, VIDIOC_ENUMAUDOUT, &vaudio) >= 0) { + if (vaudio.index) + printf("\n"); + printf("\tOutput : %d\n", vaudio.index); + printf("\tName : %s\n", vaudio.name); + vaudio.index++; + } + } + + if (options[OptListStandards]) { + printf("ioctl: VIDIOC_ENUMSTD\n"); + vs.index = 0; + while (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { + if (vs.index) + printf("\n"); + printf("\tindex : %d\n", vs.index); + printf("\tID : 0x%016llX\n", (unsigned long long)vs.id); + printf("\tName : %s\n", vs.name); + printf("\tFrame period: %d/%d\n", + vs.frameperiod.numerator, + vs.frameperiod.denominator); + printf("\tFrame lines : %d\n", vs.framelines); + vs.index++; + } + } + + if (options[OptGetSlicedVbiCap]) { + struct v4l2_sliced_vbi_cap cap; + + if (doioctl(fd, VIDIOC_G_SLICED_VBI_CAP, &cap, "VIDIOC_G_SLICED_VBI_CAP") == 0) { + print_sliced_vbi_cap(cap); + } + } + + if (options[OptGetSlicedVbiOutCap]) { + struct v4l2_sliced_vbi_cap cap; + + if (doioctl(fd, VIDIOC_G_SLICED_VBI_CAP, &cap, "VIDIOC_G_SLICED_VBI_CAP") == 0) { + print_sliced_vbi_cap(cap); + } + } + + if (options[OptListCtrlsMenus]) { + list_controls(fd, 1); + } + + if (options[OptListCtrls]) { + list_controls(fd, 0); + } + + if (options[OptStreamOn]) { + int dummy = 0; + doioctl(fd, VIDIOC_STREAMON, &dummy, "VIDIOC_STREAMON"); + } + + close(fd); + exit(0); +} |