blob: b07fc1baf17e1949e6e0227370973c8307356261 [file] [log] [blame]
/* 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 "general-tab.h"
#include "../libv4l2util/libv4l2util.h"
#include <QSpinBox>
#include <QSlider>
#include <QComboBox>
#include <QPushButton>
#include <QToolButton>
#include <QLineEdit>
#include <QDoubleValidator>
#include <math.h>
#include <stdio.h>
#include <errno.h>
#include <QRegExp>
bool GeneralTab::m_fullAudioName = false;
enum audioDeviceAdd {
AUDIO_ADD_NO,
AUDIO_ADD_READ,
AUDIO_ADD_WRITE,
AUDIO_ADD_READWRITE
};
static QString pixfmt2s(unsigned id)
{
QString pixfmt;
pixfmt += (char)(id & 0x7f);
pixfmt += (char)((id >> 8) & 0x7f);
pixfmt += (char)((id >> 16) & 0x7f);
pixfmt += (char)((id >> 24) & 0x7f);
if (id & (1U << 31))
pixfmt += "-BE";
return pixfmt;
}
GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *parent) :
QGridLayout(parent),
m_fd(fd),
m_row(0),
m_col(0),
m_cols(n),
m_minWidth(175),
m_pxw(25.0),
m_vMargin(10),
m_hMargin(20),
m_isRadio(false),
m_isSDR(false),
m_isTouch(false),
m_isVbi(false),
m_isOutput(false),
m_isSDTV(false),
m_freqFac(16),
m_freqRfFac(16),
m_isPlanar(false),
m_haveBuffers(false),
m_discreteSizes(false),
m_videoInput(NULL),
m_videoOutput(NULL),
m_audioInput(NULL),
m_audioOutput(NULL),
m_tvStandard(NULL),
m_qryStandard(NULL),
m_videoTimings(NULL),
m_pixelAspectRatio(NULL),
m_colorspace(NULL),
m_xferFunc(NULL),
m_ycbcrEnc(NULL),
m_quantRange(NULL),
m_cropping(NULL),
m_qryTimings(NULL),
m_freq(NULL),
m_freqTable(NULL),
m_freqChannel(NULL),
m_audioMode(NULL),
m_subchannels(NULL),
m_freqRf(NULL),
m_stereoMode(NULL),
m_rdsMode(NULL),
m_detectSubchans(NULL),
m_vidCapFormats(NULL),
m_vidFields(NULL),
m_frameSize(NULL),
m_frameWidth(NULL),
m_frameHeight(NULL),
m_frameInterval(NULL),
m_vidOutFormats(NULL),
m_capMethods(NULL),
m_numBuffers(NULL),
m_vbiMethods(NULL),
m_audioInDevice(NULL),
m_audioOutDevice(NULL),
m_cropWidth(NULL),
m_cropLeft(NULL),
m_cropHeight(NULL),
m_cropTop(NULL),
m_composeWidth(NULL),
m_composeLeft(NULL),
m_composeHeight(NULL),
m_composeTop(NULL)
{
bool hasStreamIO = false;
m_device.append(device);
setSizeConstraint(QLayout::SetMinimumSize);
for (int i = 0; i < n; i++) {
m_maxw[i] = 0;
}
querycap(m_querycap);
addTitle("General Information");
addLabel("Device");
addLabel(device + (m_fd->g_direct() ? "" : " (wrapped)"));
addLabel("Driver");
addLabel((char *)m_querycap.driver);
addLabel("Card");
addLabel((char *)m_querycap.card);
addLabel("Bus");
addLabel((char *)m_querycap.bus_info);
g_tuner(m_tuner);
g_tuner(m_tuner_rf, 1);
g_modulator(m_modulator);
v4l2_input vin;
v4l2_output vout;
v4l2_audio vaudio;
v4l2_audioout vaudout;
v4l2_fmtdesc fmt;
if (m_tuner.capability &&
(m_tuner.capability & (V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_1HZ)))
m_isRadio = true;
if (m_modulator.capability &&
(m_modulator.capability & (V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_1HZ)))
m_isRadio = true;
if (m_querycap.capabilities & V4L2_CAP_DEVICE_CAPS) {
m_isVbi = g_caps() & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE |
V4L2_CAP_VBI_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT);
m_isSDR = g_caps() & V4L2_CAP_SDR_CAPTURE;
m_isTouch = g_caps() & V4L2_CAP_TOUCH;
if (m_isSDR)
m_isRadio = true;
if (g_caps() & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE |
V4L2_CAP_META_OUTPUT |
V4L2_CAP_VBI_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT))
m_isOutput = true;
}
if (m_querycap.capabilities &
(V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE))
m_isPlanar = true;
m_stackedStandards = new QStackedWidget(parent);
m_stackedFrameSettings = new QStackedWidget(parent);
m_stackedFrequency = new QStackedWidget(parent);
if (!enum_input(vin, true) || m_tuner.capability) {
addTitle("Input Settings");
inputSection(vin);
}
if (m_modulator.capability || (!isRadio() && !enum_output(vout, true))) {
addTitle("Output Settings");
outputSection(vout);
}
if (hasAlsaAudio()) {
m_audioInDevice = new QComboBox(parent);
m_audioOutDevice = new QComboBox(parent);
}
if (!isVbi() && !isTouch() && (createAudioDeviceList() || (!isRadio() && !enum_audio(vaudio, true)) ||
(!isSDR() && m_tuner.capability) || (!isRadio() && !enum_audout(vaudout, true)))) {
addTitle("Audio Settings");
audioSection(vaudio, vaudout);
}
if (hasAlsaAudio() && !createAudioDeviceList())
{
delete m_audioInDevice;
delete m_audioOutDevice;
m_audioInDevice = NULL;
m_audioOutDevice = NULL;
}
if (!isSDR() && isRadio())
goto done;
addTitle("Format Settings");
if (isVbi()) {
addLabel("VBI Streaming Method");
m_vbiMethods = new QComboBox(parentWidget());
if (has_raw_vbi_cap() || has_raw_vbi_out())
m_vbiMethods->addItem("Raw");
if (has_sliced_vbi_cap() || has_sliced_vbi_out())
m_vbiMethods->addItem("Sliced");
addWidget(m_vbiMethods);
connect(m_vbiMethods, SIGNAL(activated(int)), SLOT(vbiMethodsChanged(int)));
vbiMethodsChanged(0);
if (m_isOutput)
updateVideoOutput();
else
updateVideoInput();
} else if (!isSDR()) {
formatSection(fmt);
}
addLabel("Streaming Method");
m_capMethods = new QComboBox(parent);
if (has_streaming()) {
cv4l_queue q;
// Yuck. The videobuf framework does not accept a reqbufs count of 0.
// This is out-of-spec, but it means that the only way to test which
// method is supported is to give it a non-zero count. But after that
// we have to reopen the device to clear the fact that there were
// buffers allocated. This is the only really portable way as long
// as there are still drivers around that do not support reqbufs(0).
q.init(g_type(), V4L2_MEMORY_USERPTR);
if (q.reqbufs(m_fd, 1) == 0) {
m_capMethods->addItem("User pointer I/O", QVariant(methodUser));
m_fd->reopen(true);
hasStreamIO = true;
}
q.init(g_type(), V4L2_MEMORY_MMAP);
if (q.reqbufs(m_fd, 1) == 0) {
m_capMethods->addItem("Memory mapped I/O", QVariant(methodMmap));
m_fd->reopen(true);
hasStreamIO = true;
}
}
if (has_rw()) {
if (v4l_type_is_output(g_type()))
m_capMethods->addItem("write()", QVariant(methodRead));
else
m_capMethods->addItem("read()", QVariant(methodRead));
}
addWidget(m_capMethods);
if (hasStreamIO) {
addLabel("Number of Buffers");
m_numBuffers = new QSpinBox(parent);
m_numBuffers->setRange(1, VIDEO_MAX_FRAME);
m_numBuffers->setValue(4);
addWidget(m_numBuffers);
}
addLabel("Use Record Priority");
m_recordPrio = new QCheckBox(parentWidget());
addWidget(m_recordPrio);
if (!isRadio() && !isVbi() && !isTouch() && (has_crop() || has_compose())) {
addTitle("Cropping & Compose Settings");
cropSection();
}
if (!isSDR()) {
if (m_isOutput)
updateVideoOutput();
else
updateVideoInput();
updateVidFormat();
}
done:
QGridLayout::addWidget(new QWidget(parent), rowCount(), 0, 1, n);
setRowStretch(rowCount() - 1, 1);
if (m_videoInput)
updateGUIInput(m_videoInput->currentIndex());
else if (m_videoOutput)
updateGUIOutput(m_videoOutput->currentIndex());
fixWidth();
}
void GeneralTab::sourceChangeSubscribe()
{
v4l2_input vin;
if (!enum_input(vin, true)) {
do {
struct v4l2_event_subscription sub = {
V4L2_EVENT_SOURCE_CHANGE, vin.index
};
subscribe_event(sub);
} while (!enum_input(vin));
}
}
void GeneralTab::inputSection(v4l2_input vin)
{
bool needsStd = false;
bool needsTimings = false;
if (!isRadio() && !enum_input(vin, true)) {
addLabel("Input");
m_videoInput = new QComboBox(parentWidget());
do {
m_videoInput->addItem((char *)vin.name);
if (vin.capabilities & V4L2_IN_CAP_STD)
needsStd = true;
if (vin.capabilities & V4L2_IN_CAP_DV_TIMINGS)
needsTimings = true;
} while (!enum_input(vin));
addWidget(m_videoInput);
connect(m_videoInput, SIGNAL(activated(int)), SLOT(inputChanged(int)));
m_row++;
m_col = 0;
}
QWidget *wStd = new QWidget();
QGridLayout *m_stdRow = new QGridLayout(wStd);
m_grids.append(m_stdRow);
if (needsStd) {
v4l2_std_id tmp;
m_tvStandard = new QComboBox(parentWidget());
m_stdRow->addWidget(new QLabel("TV Standard", parentWidget()), 0, 0, Qt::AlignLeft);
m_stdRow->addWidget(m_tvStandard, 0, 1, Qt::AlignLeft);
connect(m_tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int)));
refreshStandards();
m_isSDTV = true;
if (query_std(tmp) != ENOTTY) {
m_qryStandard = new QToolButton(parentWidget());
m_qryStandard->setIcon(QIcon(":/enterbutt.png"));
m_stdRow->addWidget(new QLabel("Query Standard", parentWidget()), 0, 2, Qt::AlignLeft);
m_stdRow->addWidget(m_qryStandard, 0, 3, Qt::AlignLeft);
connect(m_qryStandard, SIGNAL(clicked()), SLOT(qryStdClicked()));
}
}
QWidget *wTim = new QWidget();
QGridLayout *m_timRow = new QGridLayout(wTim);
m_grids.append(m_timRow);
if (needsTimings) {
m_videoTimings = new QComboBox(parentWidget());
m_timRow->addWidget(new QLabel("Video Timings", parentWidget()), 0, 0, Qt::AlignLeft);
m_timRow->addWidget(m_videoTimings, 0, 1, Qt::AlignLeft);
connect(m_videoTimings, SIGNAL(activated(int)), SLOT(timingsChanged(int)));
refreshTimings();
m_qryTimings = new QToolButton(parentWidget());
m_qryTimings->setIcon(QIcon(":/enterbutt.png"));
m_timRow->addWidget(new QLabel("Query Timings", parentWidget()), 0, 2, Qt::AlignLeft);
m_timRow->addWidget(m_qryTimings, 0, 3, Qt::AlignLeft);
connect(m_qryTimings, SIGNAL(clicked()), SLOT(qryTimingsClicked()));
}
m_stackedStandards->addWidget(wStd);
m_stackedStandards->addWidget(wTim);
QGridLayout::addWidget(m_stackedStandards, m_row, 0, 1, m_cols, Qt::AlignVCenter);
m_row++;
QWidget *wFreq = new QWidget();
QGridLayout *m_freqRows = new QGridLayout(wFreq);
m_grids.append(m_freqRows);
if (m_tuner.capability) {
const char *unit = (m_tuner.capability & V4L2_TUNER_CAP_LOW) ? " kHz" :
(m_tuner.capability & V4L2_TUNER_CAP_1HZ ? " Hz" : " MHz");
const char *name = m_isSDR ? "ADC Frequency" : "Frequency";
m_freqFac = (m_tuner.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16;
m_freq = new QDoubleSpinBox(parentWidget());
m_freq->setMinimum(m_tuner.rangelow / m_freqFac);
m_freq->setMaximum(m_tuner.rangehigh / m_freqFac);
m_freq->setSingleStep(1.0 / m_freqFac);
m_freq->setSuffix(unit);
m_freq->setDecimals((m_tuner.capability & V4L2_TUNER_CAP_1HZ) ? 0 : 4);
m_freq->setWhatsThis(QString("%1\nLow: %2 %4\nHigh: %3 %4")
.arg(name)
.arg((double)m_tuner.rangelow / m_freqFac, 0, 'f', 2)
.arg((double)m_tuner.rangehigh / m_freqFac, 0, 'f', 2)
.arg(unit));
m_freq->setStatusTip(m_freq->whatsThis());
connect(m_freq, SIGNAL(valueChanged(double)), SLOT(freqChanged(double)));
updateFreq();
m_freqRows->addWidget(new QLabel(name, parentWidget()), 0, 0, Qt::AlignLeft);
m_freqRows->addWidget(m_freq, 0, 1, Qt::AlignLeft);
}
if (m_tuner_rf.capability) {
const char *unit = (m_tuner_rf.capability & V4L2_TUNER_CAP_LOW) ? " kHz" :
(m_tuner_rf.capability & V4L2_TUNER_CAP_1HZ ? " Hz" : " MHz");
m_freqRfFac = (m_tuner_rf.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16;
m_freqRf = new QDoubleSpinBox(parentWidget());
m_freqRf->setMinimum(m_tuner_rf.rangelow / m_freqRfFac);
m_freqRf->setMaximum(m_tuner_rf.rangehigh / m_freqRfFac);
m_freqRf->setSingleStep(1.0 / m_freqRfFac);
m_freqRf->setSuffix(unit);
m_freqRf->setDecimals((m_tuner_rf.capability & V4L2_TUNER_CAP_1HZ) ? 0 : 4);
m_freqRf->setWhatsThis(QString("RF Frequency\nLow: %1 %3\nHigh: %2 %3")
.arg((double)m_tuner_rf.rangelow / m_freqRfFac, 0, 'f', 2)
.arg((double)m_tuner_rf.rangehigh / m_freqRfFac, 0, 'f', 2)
.arg(unit));
m_freqRf->setStatusTip(m_freqRf->whatsThis());
connect(m_freqRf, SIGNAL(valueChanged(double)), SLOT(freqRfChanged(double)));
updateFreqRf();
m_freqRows->addWidget(new QLabel("RF Frequency", parentWidget()), 0, 2, Qt::AlignLeft);
m_freqRows->addWidget(m_freqRf, 0, 3, Qt::AlignLeft);
} else if (!isSDR()) {
QLabel *l = new QLabel("Refresh Tuner Status", parentWidget());
QWidget *w = new QWidget(parentWidget());
QHBoxLayout *box = new QHBoxLayout(w);
#if QT_VERSION < 0x060000
box->setMargin(0);
#else
box->setContentsMargins(0, 0, 0, 0);
#endif
m_detectSubchans = new QToolButton(w);
m_detectSubchans->setIcon(QIcon(":/enterbutt.png"));
m_subchannels = new QLabel("", w);
box->addWidget(m_detectSubchans, 0, Qt::AlignLeft);
box->addWidget(m_subchannels, 0, Qt::AlignLeft);
m_freqRows->addWidget(l, 0, 2, Qt::AlignLeft);
m_freqRows->addWidget(w, 0, 3, Qt::AlignLeft);
connect(m_detectSubchans, SIGNAL(clicked()), SLOT(detectSubchansClicked()));
detectSubchansClicked();
}
if (m_tuner.capability && !isRadio()) {
m_freqTable = new QComboBox(parentWidget());
for (int i = 0; v4l2_channel_lists[i].name; i++) {
m_freqTable->addItem(v4l2_channel_lists[i].name);
}
m_freqRows->addWidget(new QLabel("Frequency Table", parentWidget()), 1, 0, Qt::AlignLeft);
m_freqRows->addWidget(m_freqTable, 1, 1, Qt::AlignLeft);
connect(m_freqTable, SIGNAL(activated(int)), SLOT(freqTableChanged(int)));
m_freqChannel = new QComboBox(parentWidget());
m_freqRows->addWidget(new QLabel("Channels", parentWidget()), 1, 2, Qt::AlignLeft);
m_freqRows->addWidget(m_freqChannel, 1, 3, Qt::AlignLeft);
connect(m_freqChannel, SIGNAL(activated(int)), SLOT(freqChannelChanged(int)));
updateFreqChannel();
}
m_stackedFrequency->addWidget(wFreq);
QGridLayout::addWidget(m_stackedFrequency, m_row, 0, 2, m_cols, Qt::AlignVCenter);
m_row += 2;
if (isRadio() || isVbi())
return;
QWidget *wFrameWH = new QWidget();
QWidget *wFrameSR = new QWidget();
QGridLayout *m_wh = new QGridLayout(wFrameWH);
QGridLayout *m_sr = new QGridLayout(wFrameSR);
m_grids.append(m_wh);
m_grids.append(m_sr);
m_wh->addWidget(new QLabel("Frame Width", parentWidget()), 0, 0, Qt::AlignLeft);
m_frameWidth = new QSpinBox(parentWidget());
m_wh->addWidget(m_frameWidth, 0, 1, Qt::AlignLeft);
connect(m_frameWidth, SIGNAL(editingFinished()), SLOT(frameWidthChanged()));
m_wh->addWidget(new QLabel("Frame Height", parentWidget()), 0, 2, Qt::AlignLeft);
m_frameHeight = new QSpinBox(parentWidget());
m_wh->addWidget(m_frameHeight, 0, 3, Qt::AlignLeft);
connect(m_frameHeight, SIGNAL(editingFinished()), SLOT(frameHeightChanged()));
m_sr->addWidget(new QLabel("Frame Size", parentWidget()), 0, 0, Qt::AlignLeft);
m_frameSize = new QComboBox(parentWidget());
m_sr->addWidget(m_frameSize, 0, 1, Qt::AlignLeft);
connect(m_frameSize, SIGNAL(activated(int)), SLOT(frameSizeChanged(int)));
m_sr->addWidget(new QLabel("Frame Rate", parentWidget()), 0, 2, Qt::AlignLeft);
m_frameInterval = new QComboBox(parentWidget());
m_sr->addWidget(m_frameInterval, 0, 3, Qt::AlignLeft);
connect(m_frameInterval, SIGNAL(activated(int)), SLOT(frameIntervalChanged(int)));
m_stackedFrameSettings->addWidget(wFrameWH);
m_stackedFrameSettings->addWidget(wFrameSR);
QGridLayout::addWidget(m_stackedFrameSettings, m_row, 0, 1, m_cols, Qt::AlignVCenter);
m_row++;
}
void GeneralTab::outputSection(v4l2_output vout)
{
bool needsStd = false;
bool needsTimings = false;
if (!isRadio() && !enum_output(vout, true)) {
addLabel("Output");
m_videoOutput = new QComboBox(parentWidget());
do {
m_videoOutput->addItem((char *)vout.name);
if (vout.capabilities & V4L2_OUT_CAP_STD)
needsStd = true;
if (vout.capabilities & V4L2_OUT_CAP_DV_TIMINGS)
needsTimings = true;
} while (!enum_output(vout));
addWidget(m_videoOutput);
connect(m_videoOutput, SIGNAL(activated(int)), SLOT(outputChanged(int)));
updateVideoOutput();
m_row++;
m_col = 0;
}
QWidget *wStd = new QWidget();
QGridLayout *m_stdRow = new QGridLayout(wStd);
m_grids.append(m_stdRow);
if (needsStd) {
m_tvStandard = new QComboBox(parentWidget());
m_stdRow->addWidget(new QLabel("TV Standard", parentWidget()), 0, 0, Qt::AlignLeft);
m_stdRow->addWidget(m_tvStandard, 0, 1, Qt::AlignLeft);
connect(m_tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int)));
m_isSDTV = true;
refreshStandards();
}
QWidget *wTim = new QWidget();
QGridLayout *m_timRow = new QGridLayout(wTim);
m_grids.append(m_timRow);
if (needsTimings) {
m_videoTimings = new QComboBox(parentWidget());
m_timRow->addWidget(new QLabel("Video Timings", parentWidget()), 0, 0, Qt::AlignLeft);
m_timRow->addWidget(m_videoTimings, 0, 1, Qt::AlignLeft);
connect(m_videoTimings, SIGNAL(activated(int)), SLOT(timingsChanged(int)));
refreshTimings();
}
m_stackedStandards->addWidget(wStd);
m_stackedStandards->addWidget(wTim);
QGridLayout::addWidget(m_stackedStandards, m_row, 0, 1, m_cols, Qt::AlignVCenter);
m_row++;
if (m_modulator.capability) {
const char *unit = (m_modulator.capability & V4L2_TUNER_CAP_LOW) ? " kHz" :
(m_modulator.capability & V4L2_TUNER_CAP_1HZ ? " Hz" : " MHz");
m_freqFac = (m_modulator.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16;
m_freq = new QDoubleSpinBox(parentWidget());
m_freq->setMinimum(m_modulator.rangelow / m_freqFac);
m_freq->setMaximum(m_modulator.rangehigh / m_freqFac);
m_freq->setSingleStep(1.0 / m_freqFac);
m_freq->setSuffix(unit);
m_freq->setDecimals((m_modulator.capability & V4L2_TUNER_CAP_1HZ) ? 0 : 4);
m_freq->setWhatsThis(QString("Frequency\nLow: %1 %3\nHigh: %2 %3")
.arg((double)m_modulator.rangelow / m_freqFac, 0, 'f', 2)
.arg((double)m_modulator.rangehigh / m_freqFac, 0, 'f', 2)
.arg(unit));
m_freq->setStatusTip(m_freq->whatsThis());
connect(m_freq, SIGNAL(valueChanged(double)), SLOT(freqChanged(double)));
updateFreq();
addLabel("Frequency");
addWidget(m_freq);
if (m_modulator.capability & V4L2_TUNER_CAP_STEREO) {
addLabel("Stereo");
m_stereoMode = new QCheckBox(parentWidget());
m_stereoMode->setCheckState((m_modulator.txsubchans & V4L2_TUNER_SUB_STEREO) ?
Qt::Checked : Qt::Unchecked);
addWidget(m_stereoMode);
connect(m_stereoMode, SIGNAL(clicked()), SLOT(stereoModeChanged()));
}
if (m_modulator.capability & V4L2_TUNER_CAP_RDS) {
addLabel("RDS");
m_rdsMode = new QCheckBox(parentWidget());
m_rdsMode->setCheckState((m_modulator.txsubchans & V4L2_TUNER_SUB_RDS) ?
Qt::Checked : Qt::Unchecked);
addWidget(m_rdsMode);
connect(m_rdsMode, SIGNAL(clicked()), SLOT(rdsModeChanged()));
}
}
if (isRadio())
return;
QWidget *wFrameWH = new QWidget();
QGridLayout *m_wh = new QGridLayout(wFrameWH);
m_grids.append(m_wh);
m_wh->addWidget(new QLabel("Frame Width", parentWidget()), 0, 0, Qt::AlignLeft);
m_frameWidth = new QSpinBox(parentWidget());
m_wh->addWidget(m_frameWidth, 0, 1, Qt::AlignLeft);
connect(m_frameWidth, SIGNAL(editingFinished()), SLOT(frameWidthChanged()));
m_wh->addWidget(new QLabel("Frame Height", parentWidget()), 0, 2, Qt::AlignLeft);
m_frameHeight = new QSpinBox(parentWidget());
m_wh->addWidget(m_frameHeight, 0, 3, Qt::AlignLeft);
connect(m_frameHeight, SIGNAL(editingFinished()), SLOT(frameHeightChanged()));
m_stackedFrameSettings->addWidget(wFrameWH);
QGridLayout::addWidget(m_stackedFrameSettings, m_row, 0, 1, m_cols, Qt::AlignVCenter);
m_row++;
}
void GeneralTab::audioSection(v4l2_audio vaudio, v4l2_audioout vaudout)
{
if (hasAlsaAudio() && !m_isOutput) {
if (createAudioDeviceList()) {
addLabel("Audio Input Device");
connect(m_audioInDevice, SIGNAL(activated(int)), SLOT(changeAudioDevice()));
addWidget(m_audioInDevice);
addLabel("Audio Output Device");
connect(m_audioOutDevice, SIGNAL(activated(int)), SLOT(changeAudioDevice()));
addWidget(m_audioOutDevice);
if (isRadio()) {
setAudioDeviceBufferSize(75);
} else {
v4l2_fract fract;
if (m_fd->get_interval(fract)) {
// Default values are for 30 FPS
fract.numerator = 33;
fract.denominator = 1000;
}
// Standard capacity is two frames
setAudioDeviceBufferSize((fract.numerator * 2000) / fract.denominator);
}
} else {
delete m_audioInDevice;
delete m_audioOutDevice;
m_audioInDevice = NULL;
m_audioOutDevice = NULL;
}
}
if (!isRadio() && !enum_audio(vaudio, true)) {
addLabel("Input Audio");
m_audioInput = new QComboBox(parentWidget());
m_audioInput->setMinimumContentsLength(10);
do {
m_audioInput->addItem((char *)vaudio.name);
} while (!enum_audio(vaudio));
addWidget(m_audioInput);
connect(m_audioInput, SIGNAL(activated(int)), SLOT(inputAudioChanged(int)));
updateAudioInput();
}
if (m_tuner.capability && !isSDR()) {
addLabel("Audio Mode");
m_audioMode = new QComboBox(parentWidget());
m_audioMode->setMinimumContentsLength(12);
m_audioMode->addItem("Mono");
int audIdx = 0;
m_audioModes[audIdx++] = V4L2_TUNER_MODE_MONO;
if (m_tuner.capability & V4L2_TUNER_CAP_STEREO) {
m_audioMode->addItem("Stereo");
m_audioModes[audIdx++] = V4L2_TUNER_MODE_STEREO;
}
if (m_tuner.capability & V4L2_TUNER_CAP_LANG1) {
m_audioMode->addItem("Language 1");
m_audioModes[audIdx++] = V4L2_TUNER_MODE_LANG1;
}
if (m_tuner.capability & V4L2_TUNER_CAP_LANG2) {
m_audioMode->addItem("Language 2");
m_audioModes[audIdx++] = V4L2_TUNER_MODE_LANG2;
}
if ((m_tuner.capability & (V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2)) ==
(V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2)) {
m_audioMode->addItem("Language 1+2");
m_audioModes[audIdx++] = V4L2_TUNER_MODE_LANG1_LANG2;
}
addWidget(m_audioMode);
for (int i = 0; i < audIdx; i++)
if (m_audioModes[i] == m_tuner.audmode)
m_audioMode->setCurrentIndex(i);
connect(m_audioMode, SIGNAL(activated(int)), SLOT(audioModeChanged(int)));
}
if (!isRadio() && !enum_audout(vaudout, true)) {
addLabel("Output Audio");
m_audioOutput = new QComboBox(parentWidget());
m_audioOutput->setMinimumContentsLength(10);
do {
m_audioOutput->addItem((char *)vaudout.name);
} while (!enum_audout(vaudout));
addWidget(m_audioOutput);
connect(m_audioOutput, SIGNAL(activated(int)), SLOT(outputAudioChanged(int)));
updateAudioOutput();
}
}
void GeneralTab::formatSection(v4l2_fmtdesc fmt)
{
if (m_isOutput) {
addLabel("Output Image Formats");
m_vidOutFormats = new QComboBox(parentWidget());
m_vidOutFormats->setMinimumContentsLength(20);
if (!enum_fmt(fmt, true)) {
do {
m_vidOutFormats->addItem(pixfmt2s(fmt.pixelformat) +
" (" + (const char *)fmt.description + ")");
} while (!enum_fmt(fmt));
}
addWidget(m_vidOutFormats);
connect(m_vidOutFormats, SIGNAL(activated(int)), SLOT(vidOutFormatChanged(int)));
} else {
addLabel("Capture Image Formats");
m_vidCapFormats = new QComboBox(parentWidget());
m_vidCapFormats->setMinimumContentsLength(20);
if (!enum_fmt(fmt, true)) {
do {
QString s(pixfmt2s(fmt.pixelformat) + " (");
if (fmt.flags & V4L2_FMT_FLAG_EMULATED)
m_vidCapFormats->addItem(s + "Emulated)");
else
m_vidCapFormats->addItem(s + (const char *)fmt.description + ")");
} while (!enum_fmt(fmt));
}
addWidget(m_vidCapFormats);
connect(m_vidCapFormats, SIGNAL(activated(int)), SLOT(vidCapFormatChanged(int)));
}
addLabel("Field");
m_vidFields = new QComboBox(parentWidget());
m_vidFields->setMinimumContentsLength(21);
addWidget(m_vidFields);
connect(m_vidFields, SIGNAL(activated(int)), SLOT(vidFieldChanged(int)));
if (!isRadio() && !isVbi()) {
m_colorspace = new QComboBox(parentWidget());
m_colorspace->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_COLORSPACE_DEFAULT));
m_colorspace->addItem("SMPTE 170M", QVariant(V4L2_COLORSPACE_SMPTE170M));
m_colorspace->addItem("Rec. 709", QVariant(V4L2_COLORSPACE_REC709));
m_colorspace->addItem("sRGB", QVariant(V4L2_COLORSPACE_SRGB));
m_colorspace->addItem("opRGB", QVariant(V4L2_COLORSPACE_OPRGB));
m_colorspace->addItem("BT.2020", QVariant(V4L2_COLORSPACE_BT2020));
m_colorspace->addItem("DCI-P3", QVariant(V4L2_COLORSPACE_DCI_P3));
m_colorspace->addItem("SMPTE 240M", QVariant(V4L2_COLORSPACE_SMPTE240M));
m_colorspace->addItem("470 System M", QVariant(V4L2_COLORSPACE_470_SYSTEM_M));
m_colorspace->addItem("470 System BG", QVariant(V4L2_COLORSPACE_470_SYSTEM_BG));
m_colorspace->addItem("JPEG", QVariant(V4L2_COLORSPACE_JPEG));
m_colorspace->addItem("Raw", QVariant(V4L2_COLORSPACE_RAW));
addLabel("Colorspace");
addWidget(m_colorspace);
connect(m_colorspace, SIGNAL(activated(int)), SLOT(colorspaceChanged(int)));
m_xferFunc = new QComboBox(parentWidget());
m_xferFunc->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_XFER_FUNC_DEFAULT));
m_xferFunc->addItem("Rec. 709", QVariant(V4L2_XFER_FUNC_709));
m_xferFunc->addItem("sRGB", QVariant(V4L2_XFER_FUNC_SRGB));
m_xferFunc->addItem("opRGB", QVariant(V4L2_XFER_FUNC_OPRGB));
m_xferFunc->addItem("DCI-P3", QVariant(V4L2_XFER_FUNC_DCI_P3));
m_xferFunc->addItem("SMPTE 2084", QVariant(V4L2_XFER_FUNC_SMPTE2084));
m_xferFunc->addItem("SMPTE 240M", QVariant(V4L2_XFER_FUNC_SMPTE240M));
m_xferFunc->addItem("None", QVariant(V4L2_XFER_FUNC_NONE));
addLabel("Transfer Function");
addWidget(m_xferFunc);
connect(m_xferFunc, SIGNAL(activated(int)), SLOT(xferFuncChanged(int)));
m_ycbcrEnc = new QComboBox(parentWidget());
m_ycbcrEnc->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_YCBCR_ENC_DEFAULT));
m_ycbcrEnc->addItem("ITU-R 601", QVariant(V4L2_YCBCR_ENC_601));
m_ycbcrEnc->addItem("Rec. 709", QVariant(V4L2_YCBCR_ENC_709));
m_ycbcrEnc->addItem("xvYCC 601", QVariant(V4L2_YCBCR_ENC_XV601));
m_ycbcrEnc->addItem("xvYCC 709", QVariant(V4L2_YCBCR_ENC_XV709));
m_ycbcrEnc->addItem("BT.2020", QVariant(V4L2_YCBCR_ENC_BT2020));
m_ycbcrEnc->addItem("BT.2020 Constant Luminance", QVariant(V4L2_YCBCR_ENC_BT2020_CONST_LUM));
m_ycbcrEnc->addItem("SMPTE 240M", QVariant(V4L2_YCBCR_ENC_SMPTE240M));
m_ycbcrEnc->addItem("HSV with Hue 0-179", QVariant(V4L2_HSV_ENC_180));
m_ycbcrEnc->addItem("HSV with Hue 0-255", QVariant(V4L2_HSV_ENC_256));
addLabel("Y'CbCr/HSV Encoding");
addWidget(m_ycbcrEnc);
connect(m_ycbcrEnc, SIGNAL(activated(int)), SLOT(ycbcrEncChanged(int)));
m_quantRange = new QComboBox(parentWidget());
m_quantRange->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_QUANTIZATION_DEFAULT));
m_quantRange->addItem("Full Range", QVariant(V4L2_QUANTIZATION_FULL_RANGE));
m_quantRange->addItem("Limited Range", QVariant(V4L2_QUANTIZATION_LIM_RANGE));
addLabel("Quantization");
addWidget(m_quantRange);
connect(m_quantRange, SIGNAL(activated(int)), SLOT(quantRangeChanged(int)));
}
if (m_isOutput || isTouch())
return;
m_cropping = new QComboBox(parentWidget());
m_cropping->addItem("Source Width and Height");
m_cropping->addItem("Crop Top and Bottom Line");
m_cropping->addItem("Traditional 4:3");
m_cropping->addItem("Widescreen 14:9");
m_cropping->addItem("Widescreen 16:9");
m_cropping->addItem("Cinema 1.85:1");
m_cropping->addItem("Cinema 2.39:1");
addLabel("Video Aspect Ratio");
addWidget(m_cropping);
connect(m_cropping, SIGNAL(activated(int)), SIGNAL(croppingChanged()));
if (!isRadio() && !isVbi()) {
m_pixelAspectRatio = new QComboBox(parentWidget());
m_pixelAspectRatio->addItem("Autodetect");
m_pixelAspectRatio->addItem("Square");
m_pixelAspectRatio->addItem("NTSC/PAL-M/PAL-60");
m_pixelAspectRatio->addItem("NTSC/PAL-M/PAL-60, Anamorphic");
m_pixelAspectRatio->addItem("PAL/SECAM");
m_pixelAspectRatio->addItem("PAL/SECAM, Anamorphic");
// Update hints by calling a get
getPixelAspectRatio();
addLabel("Pixel Aspect Ratio");
addWidget(m_pixelAspectRatio);
connect(m_pixelAspectRatio, SIGNAL(activated(int)), SLOT(changePixelAspectRatio()));
}
}
void GeneralTab::cropSection()
{
if (has_crop()) {
m_cropWidth = new QSlider(Qt::Horizontal, parentWidget());
m_cropWidth->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_cropWidth->setRange(1, 100);
m_cropWidth->setSliderPosition(100);
addLabel("Crop Width");
addWidget(m_cropWidth);
connect(m_cropWidth, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
m_cropLeft = new QSlider(Qt::Horizontal, parentWidget());
m_cropLeft->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_cropLeft->setRange(0, 100);
m_cropLeft->setSliderPosition(0);
addLabel("Crop Left Offset");
addWidget(m_cropLeft);
connect(m_cropLeft, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
m_cropHeight = new QSlider(Qt::Horizontal, parentWidget());
m_cropHeight->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_cropHeight->setRange(1, 100);
m_cropHeight->setSliderPosition(100);
addLabel("Crop Height");
addWidget(m_cropHeight);
connect(m_cropHeight, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
m_cropTop = new QSlider(Qt::Horizontal, parentWidget());
m_cropTop->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_cropTop->setRange(0, 100);
m_cropTop->setSliderPosition(0);
addLabel("Crop Top Offset");
addWidget(m_cropTop);
connect(m_cropTop, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
}
if (has_compose()) {
m_composeWidth = new QSlider(Qt::Horizontal, parentWidget());
m_composeWidth->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_composeWidth->setRange(1, 100);
m_composeWidth->setSliderPosition(100);
addLabel("Compose Width");
addWidget(m_composeWidth);
connect(m_composeWidth, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
m_composeLeft = new QSlider(Qt::Horizontal, parentWidget());
m_composeLeft->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_composeLeft->setRange(0, 100);
m_composeLeft->setSliderPosition(0);
addLabel("Compose Left Offset");
addWidget(m_composeLeft);
connect(m_composeLeft, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
m_composeHeight = new QSlider(Qt::Horizontal, parentWidget());
m_composeHeight->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_composeHeight->setRange(1, 100);
m_composeHeight->setSliderPosition(100);
addLabel("Compose Height");
addWidget(m_composeHeight);
connect(m_composeHeight, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
m_composeTop = new QSlider(Qt::Horizontal, parentWidget());
m_composeTop->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
m_composeTop->setRange(0, 100);
m_composeTop->setSliderPosition(0);
addLabel("Compose Top Offset");
addWidget(m_composeTop);
connect(m_composeTop, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
}
}
void GeneralTab::fixWidth()
{
setContentsMargins(m_hMargin, m_vMargin, m_hMargin, m_vMargin);
setColumnStretch(3, 1);
QList<QWidget *> list = parentWidget()->findChildren<QWidget *>();
for (const auto &child : list) {
if (!qobject_cast<QComboBox *>(child) && !qobject_cast<QSpinBox *>(child) &&
!qobject_cast<QSlider *>(child))
continue;
if ((child->sizeHint().width()) > m_minWidth) {
m_increment = (int)ceil((child->sizeHint().width() - m_minWidth) / m_pxw);
child->setMinimumWidth(m_minWidth + m_increment * m_pxw); // for stepsize expansion of widgets
}
}
// fix width of subgrids
for (const auto &grid : m_grids) {
grid->setColumnStretch(3, 1);
grid->setContentsMargins(0, 0, 0, 0);
for (int n = 0; n < grid->count(); n++) {
if (grid->itemAt(n)->widget()->sizeHint().width() > m_maxw[n % 4]) {
m_maxw[n % 4] = grid->itemAt(n)->widget()->sizeHint().width();
}
if (n % 2) {
if (!qobject_cast<QToolButton *>(grid->itemAt(n)->widget()))
grid->itemAt(n)->widget()->setMinimumWidth(m_minWidth);
} else {
grid->itemAt(n)->widget()->setMinimumWidth(m_maxw[n % 4]);
}
}
for (int j = 0; j < m_cols; j++) {
if (j % 2)
grid->setColumnMinimumWidth(j, m_maxw[j] + m_pxw);
else
grid->setColumnMinimumWidth(j, m_maxw[j]);
}
}
for (int j = 0; j < m_cols; j++) {
if (j % 2)
setColumnMinimumWidth(j, m_maxw[j] + m_pxw);
else
setColumnMinimumWidth(j, m_maxw[j]);
}
}
unsigned GeneralTab::getNumBuffers() const
{
return m_numBuffers ? m_numBuffers->value() : 4;
}
void GeneralTab::setHaveBuffers(bool haveBuffers)
{
m_haveBuffers = haveBuffers;
if (m_videoInput)
m_videoInput->setDisabled(haveBuffers);
if (m_videoOutput)
m_videoOutput->setDisabled(haveBuffers);
if (m_tvStandard)
m_tvStandard->setDisabled(haveBuffers);
if (m_videoTimings)
m_videoTimings->setDisabled(haveBuffers);
if (m_vidCapFormats)
m_vidCapFormats->setDisabled(haveBuffers);
if (m_vidFields)
m_vidFields->setDisabled(haveBuffers);
if (m_frameSize && m_discreteSizes)
m_frameSize->setDisabled(haveBuffers);
if (m_frameWidth && !m_discreteSizes)
m_frameWidth->setDisabled(haveBuffers);
if (m_frameHeight && !m_discreteSizes)
m_frameHeight->setDisabled(haveBuffers);
if (m_vidOutFormats)
m_vidOutFormats->setDisabled(haveBuffers);
if (m_capMethods)
m_capMethods->setDisabled(haveBuffers);
if (m_vbiMethods)
m_vbiMethods->setDisabled(haveBuffers);
}
bool GeneralTab::filterAudioDevice(QString &deviceName)
{
// Only show hw devices
return deviceName.contains("hw") && !deviceName.contains("plughw");
}
int GeneralTab::addAudioDevice(void *hint, int deviceNum)
{
int added = 0;
#ifdef HAVE_ALSA
char *name;
char *iotype;
QString deviceName;
QString listName;
QStringList deviceType;
iotype = snd_device_name_get_hint(hint, "IOID");
name = snd_device_name_get_hint(hint, "NAME");
deviceName.append(name);
snd_card_get_name(deviceNum, &name);
listName.append(name);
deviceType = deviceName.split(":");
// Add device io capability to list name
if (m_fullAudioName) {
listName.append(" ");
// Makes the surround name more readable
if (deviceName.contains("surround"))
listName.append(QString("surround %1.%2")
.arg(deviceType.value(0)[8]).arg(deviceType.value(0)[9]));
else
listName.append(deviceType.value(0));
} else if (!deviceType.value(0).contains("default")) {
listName.append(" ").append(deviceType.value(0));
}
// Add device number if it is not 0
if (deviceName.contains("DEV=")) {
int devNo;
QStringList deviceNo = deviceName.split("DEV=");
devNo = deviceNo.value(1).toInt();
if (devNo)
listName.append(QString(" %1").arg(devNo));
}
if ((iotype == NULL || strncmp(iotype, "Input", 5) == 0) && filterAudioDevice(deviceName)) {
m_audioInDeviceMap[m_audioInDevice->count()] = snd_device_name_get_hint(hint, "NAME");
m_audioInDevice->addItem(listName);
added += AUDIO_ADD_READ;
}
if ((iotype == NULL || strncmp(iotype, "Output", 6) == 0) && filterAudioDevice(deviceName)) {
m_audioOutDeviceMap[m_audioOutDevice->count()] = snd_device_name_get_hint(hint, "NAME");
m_audioOutDevice->addItem(listName);
added += AUDIO_ADD_WRITE;
}
#endif
return added;
}
bool GeneralTab::createAudioDeviceList()
{
#ifdef HAVE_ALSA
if (m_audioInDevice == NULL || m_audioOutDevice == NULL || m_isOutput)
return false;
m_audioInDevice->clear();
m_audioOutDevice->clear();
m_audioInDeviceMap.clear();
m_audioOutDeviceMap.clear();
m_audioInDevice->addItem("None");
m_audioOutDevice->addItem("Default");
m_audioInDeviceMap[0] = "None";
m_audioOutDeviceMap[0] = "default";
int deviceNum = -1;
int audioDevices = 0;
int matchDevice = matchAudioDevice();
int indexDevice = -1;
int indexCount = 0;
while (snd_card_next(&deviceNum) >= 0) {
if (deviceNum == -1)
break;
audioDevices++;
if (deviceNum == matchDevice && indexDevice == -1)
indexDevice = indexCount;
void **hint;
if (snd_device_name_hint(deviceNum, "pcm", &hint)) {
audioDevices--;
continue;
}
for (int i = 0; hint[i] != NULL; i++) {
int addAs = addAudioDevice(hint[i], deviceNum);
if (addAs == AUDIO_ADD_READ || addAs == AUDIO_ADD_READWRITE)
indexCount++;
}
snd_device_name_free_hint(hint);
}
snd_config_update_free_global();
m_audioInDevice->setCurrentIndex(indexDevice + 1);
changeAudioDevice();
return m_audioInDeviceMap.size() > 1 && m_audioOutDeviceMap.size() > 1 && audioDevices > 1;
#else
return false;
#endif
}
void GeneralTab::changeAudioDevice()
{
m_audioOutDevice->setEnabled(getAudioInDevice() != nullptr ? getAudioInDevice().compare("None") : false);
emit audioDeviceChanged();
}
void GeneralTab::addWidget(QWidget *w, Qt::Alignment align)
{
if (m_col % 2 && !qobject_cast<QToolButton*>(w))
w->setMinimumWidth(m_minWidth);
if (w->sizeHint().width() > m_maxw[m_col])
m_maxw[m_col] = w->sizeHint().width();
QGridLayout::addWidget(w, m_row, m_col, align | Qt::AlignVCenter);
m_col++;
if (m_col == m_cols) {
m_col = 0;
m_row++;
}
}
void GeneralTab::addTitle(const QString &titlename)
{
m_row++;
QLabel *title_info = new QLabel(titlename, parentWidget());
QFont f = title_info->font();
f.setBold(true);
title_info->setFont(f);
QGridLayout::addWidget(title_info, m_row, 0, 1, m_cols, Qt::AlignLeft);
setRowMinimumHeight(m_row, 25);
m_row++;
QFrame *m_line = new QFrame(parentWidget());
m_line->setFrameShape(QFrame::HLine);
m_line->setFrameShadow(QFrame::Sunken);
QGridLayout::addWidget(m_line, m_row, 0, 1, m_cols, Qt::AlignVCenter);
m_row++;
m_col = 0;
}
int GeneralTab::getWidth()
{
int total = 0;
for (int i = 0; i < m_cols; i++) {
total += m_maxw[i] + m_pxw;
}
return total;
}
bool GeneralTab::isSlicedVbi() const
{
return m_vbiMethods && m_vbiMethods->currentText() == "Sliced";
}
CapMethod GeneralTab::capMethod()
{
return (CapMethod)m_capMethods->itemData(m_capMethods->currentIndex()).toInt();
}
void GeneralTab::updateGUIInput(__u32 input)
{
v4l2_input in;
enum_input(in, true, input);
if (g_input(input) || m_isRadio) {
m_stackedFrameSettings->hide();
return;
}
if ((in.capabilities & V4L2_IN_CAP_STD) && in.type == V4L2_INPUT_TYPE_TUNER) {
m_stackedFrameSettings->setCurrentIndex(0);
m_stackedFrameSettings->show();
m_stackedStandards->setCurrentIndex(0);
m_stackedStandards->show();
m_stackedFrequency->setCurrentIndex(0);
m_stackedFrequency->show();
} else if (in.capabilities & V4L2_IN_CAP_STD) {
m_stackedFrameSettings->setCurrentIndex(0);
m_stackedFrameSettings->show();
m_stackedStandards->setCurrentIndex(0);
m_stackedStandards->show();
m_stackedFrequency->hide();
} else if (in.capabilities & V4L2_IN_CAP_DV_TIMINGS) {
m_stackedFrameSettings->setCurrentIndex(0);
m_stackedFrameSettings->show();
m_stackedStandards->setCurrentIndex(1);
m_stackedStandards->show();
m_stackedFrequency->hide();
} else {
m_stackedFrameSettings->setCurrentIndex(1);
m_stackedFrameSettings->show();
m_stackedStandards->hide();
m_stackedFrequency->hide();
}
if (isVbi() || isTouch()) {
m_stackedFrameSettings->hide();
}
}
void GeneralTab::updateGUIOutput(__u32 output)
{
v4l2_output out;
enum_output(out, true, output);
if (g_output(output) || m_isRadio) {
m_stackedFrameSettings->hide();
return;
}
if (out.capabilities & V4L2_OUT_CAP_STD) {
m_stackedFrameSettings->setCurrentIndex(0);
m_stackedFrameSettings->show();
m_stackedStandards->setCurrentIndex(0);
m_stackedStandards->show();
m_stackedFrequency->hide();
} else if (out.capabilities & V4L2_OUT_CAP_DV_TIMINGS) {
m_stackedFrameSettings->setCurrentIndex(0);
m_stackedFrameSettings->show();
m_stackedStandards->setCurrentIndex(1);
m_stackedStandards->show();
m_stackedFrequency->hide();
} else {
m_stackedFrameSettings->setCurrentIndex(1);
m_stackedFrameSettings->show();
m_stackedStandards->hide();
m_stackedFrequency->hide();
}
if (isVbi()) {
m_stackedFrameSettings->hide();
}
}
void GeneralTab::inputChanged(int input)
{
s_input((__u32)input);
if (m_audioInput)
updateAudioInput();
updateVideoInput();
updateVidCapFormat();
updateGUIInput(input);
}
void GeneralTab::outputChanged(int output)
{
s_output((__u32)output);
if (m_audioOutput)
updateAudioOutput();
updateVideoOutput();
updateVidOutFormat();
updateGUIOutput(output);
}
void GeneralTab::inputAudioChanged(int input)
{
s_audio((__u32)input);
updateAudioInput();
}
void GeneralTab::outputAudioChanged(int output)
{
s_audout((__u32)output);
updateAudioOutput();
}
void GeneralTab::standardChanged(int std)
{
v4l2_standard vs;
enum_std(vs, true, std);
s_std(vs.id);
updateStandard();
}
void GeneralTab::timingsChanged(int index)
{
v4l2_enum_dv_timings timings;
enum_dv_timings(timings, true, index);
s_dv_timings(timings.timings);
updateTimings();
}
void GeneralTab::freqTableChanged(int)
{
updateFreqChannel();
freqChannelChanged(0);
}
void GeneralTab::freqChannelChanged(int idx)
{
double f = v4l2_channel_lists[m_freqTable->currentIndex()].list[idx].freq;
m_freq->setValue(f / 1000.0);
freqChanged(m_freq->value());
}
void GeneralTab::freqChanged(double f)
{
v4l2_frequency freq;
if (!m_freq->isEnabled())
return;
g_frequency(freq);
freq.frequency = f * m_freqFac;
s_frequency(freq);
updateFreq();
}
void GeneralTab::freqRfChanged(double f)
{
v4l2_frequency freq;
if (!m_freqRf->isEnabled())
return;
g_frequency(freq, 1);
freq.frequency = f * m_freqRfFac;
s_frequency(freq);
updateFreqRf();
}
void GeneralTab::audioModeChanged(int)
{
m_tuner.audmode = m_audioModes[m_audioMode->currentIndex()];
s_tuner(m_tuner);
}
void GeneralTab::detectSubchansClicked()
{
QString chans;
g_tuner(m_tuner);
if (m_tuner.rxsubchans & V4L2_TUNER_SUB_MONO)
chans += "Mono ";
if (m_tuner.rxsubchans & V4L2_TUNER_SUB_STEREO)
chans += "Stereo ";
if (m_tuner.rxsubchans & V4L2_TUNER_SUB_LANG1)
chans += "Lang1 ";
if (m_tuner.rxsubchans & V4L2_TUNER_SUB_LANG2)
chans += "Lang2 ";
if (m_tuner.rxsubchans & V4L2_TUNER_SUB_RDS)
chans += "RDS ";
chans += "(" + QString::number((int)(m_tuner.signal / 655.35 + 0.5)) + "%";
if (m_tuner.signal && m_tuner.afc)
chans += m_tuner.afc < 0 ? " too low" : " too high";
chans += ")";
m_subchannels->setText(chans);
fixWidth();
}
void GeneralTab::stereoModeChanged()
{
v4l2_modulator mod;
bool val = m_stereoMode->checkState() == Qt::Checked;
g_modulator(mod);
mod.txsubchans &= ~(V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO);
mod.txsubchans |= val ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
s_modulator(mod);
}
void GeneralTab::rdsModeChanged()
{
v4l2_modulator mod;
bool val = m_rdsMode->checkState() == Qt::Checked;
g_modulator(mod);
mod.txsubchans &= ~V4L2_TUNER_SUB_RDS;
mod.txsubchans |= val ? V4L2_TUNER_SUB_RDS : 0;
s_modulator(mod);
}
void GeneralTab::colorspaceChanged(int idx)
{
cv4l_fmt fmt;
g_fmt(fmt);
fmt.s_colorspace(m_colorspace->itemData(idx).toInt());
fmt.s_xfer_func(m_xferFunc->itemData(m_xferFunc->currentIndex()).toInt());
fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(m_ycbcrEnc->currentIndex()).toInt());
fmt.s_quantization(m_quantRange->itemData(m_quantRange->currentIndex()).toInt());
if (try_fmt(fmt) == 0)
s_fmt(fmt);
updateVidFormat();
}
void GeneralTab::xferFuncChanged(int idx)
{
cv4l_fmt fmt;
g_fmt(fmt);
fmt.s_colorspace(m_colorspace->itemData(m_colorspace->currentIndex()).toInt());
fmt.s_xfer_func(m_xferFunc->itemData(idx).toInt());
fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(m_ycbcrEnc->currentIndex()).toInt());
fmt.s_quantization(m_quantRange->itemData(m_quantRange->currentIndex()).toInt());
if (try_fmt(fmt) == 0)
s_fmt(fmt);
updateVidFormat();
}
void GeneralTab::ycbcrEncChanged(int idx)
{
cv4l_fmt fmt;
g_fmt(fmt);
fmt.s_colorspace(m_colorspace->itemData(m_colorspace->currentIndex()).toInt());
fmt.s_xfer_func(m_xferFunc->itemData(m_xferFunc->currentIndex()).toInt());
fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(idx).toInt());
fmt.s_quantization(m_quantRange->itemData(m_quantRange->currentIndex()).toInt());
if (try_fmt(fmt) == 0)
s_fmt(fmt);
updateVidFormat();
}
void GeneralTab::quantRangeChanged(int idx)
{
cv4l_fmt fmt;
g_fmt(fmt);
fmt.s_colorspace(m_colorspace->itemData(m_colorspace->currentIndex()).toInt());
fmt.s_xfer_func(m_xferFunc->itemData(m_xferFunc->currentIndex()).toInt());
fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(m_ycbcrEnc->currentIndex()).toInt());
fmt.s_quantization(m_quantRange->itemData(idx).toInt());
if (try_fmt(fmt) == 0)
s_fmt(fmt);
updateVidFormat();
}
void GeneralTab::clearColorspace(cv4l_fmt &fmt)
{
if (m_colorspace->currentIndex() == 0)
fmt.s_colorspace(V4L2_COLORSPACE_DEFAULT);
if (m_xferFunc->currentIndex() == 0)
fmt.s_xfer_func(V4L2_XFER_FUNC_DEFAULT);
if (m_ycbcrEnc->currentIndex() == 0)
fmt.s_ycbcr_enc(V4L2_YCBCR_ENC_DEFAULT);
if (m_quantRange->currentIndex() == 0)
fmt.s_quantization(V4L2_QUANTIZATION_DEFAULT);
}
void GeneralTab::vidCapFormatChanged(int idx)
{
v4l2_fmtdesc desc;
enum_fmt(desc, true, idx);
cv4l_fmt fmt;
g_fmt(fmt);
fmt.s_pixelformat(desc.pixelformat);
clearColorspace(fmt);
if (try_fmt(fmt) == 0)
s_fmt(fmt);
updateVidCapFormat();
}
static const char *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";
case V4L2_FIELD_INTERLACED_TB:
return "Interlaced Top-Bottom";
case V4L2_FIELD_INTERLACED_BT:
return "Interlaced Bottom-Top";
default:
return "";
}
}
void GeneralTab::vidFieldChanged(int idx)
{
cv4l_fmt fmt;
g_fmt(fmt);
for (__u32 f = V4L2_FIELD_NONE; f <= V4L2_FIELD_INTERLACED_BT; f++) {
if (m_vidFields->currentText() == QString(field2s(f))) {
fmt.s_field(f);
clearColorspace(fmt);
s_fmt(fmt);
break;
}
}
updateVidFormat();
}
void GeneralTab::frameWidthChanged()
{
cv4l_fmt fmt;
int val = m_frameWidth->value();
if (m_frameWidth->isEnabled()) {
g_fmt(fmt);
fmt.s_width(val);
clearColorspace(fmt);
if (try_fmt(fmt) == 0)
s_fmt(fmt);
}
updateVidFormat();
}
void GeneralTab::frameHeightChanged()
{
cv4l_fmt fmt;
int val = m_frameHeight->value();
if (m_frameHeight->isEnabled()) {
g_fmt(fmt);
fmt.s_height(val);
clearColorspace(fmt);
if (try_fmt(fmt) == 0)
s_fmt(fmt);
}
updateVidFormat();
}
void GeneralTab::frameSizeChanged(int idx)
{
v4l2_frmsizeenum frmsize = { 0 };
if (!enum_framesizes(frmsize, m_pixelformat, idx)) {
cv4l_fmt fmt;
g_fmt(fmt);
fmt.s_width(frmsize.discrete.width);
fmt.s_height(frmsize.discrete.height);
clearColorspace(fmt);
if (try_fmt(fmt) == 0)
s_fmt(fmt);
}
updateVidFormat();
}
void GeneralTab::frameIntervalChanged(int idx)
{
v4l2_frmivalenum frmival = { 0 };
if (!enum_frameintervals(frmival, m_pixelformat, m_width, m_height, idx)
&& frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
if (!set_interval(frmival.discrete))
m_interval = frmival.discrete;
}
}
void GeneralTab::vidOutFormatChanged(int idx)
{
v4l2_fmtdesc desc;
enum_fmt(desc, true, idx);
cv4l_fmt fmt;
g_fmt(fmt);
fmt.s_pixelformat(desc.pixelformat);
clearColorspace(fmt);
if (try_fmt(fmt) == 0)
s_fmt(fmt);
updateVidOutFormat();
}
void GeneralTab::vbiMethodsChanged(int idx)
{
if (isSlicedVbi())
s_type(m_isOutput ? V4L2_BUF_TYPE_SLICED_VBI_OUTPUT :
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE);
else
s_type(m_isOutput ? V4L2_BUF_TYPE_VBI_OUTPUT :
V4L2_BUF_TYPE_VBI_CAPTURE);
cv4l_fmt fmt;
g_fmt(fmt);
s_fmt(fmt);
}
void GeneralTab::cropChanged()
{
v4l2_selection sel = { 0 };
if (!m_cropWidth->isEnabled() || !cur_io_has_crop())
return;
sel.type = g_selection_type();
sel.target = V4L2_SEL_TGT_CROP;
sel.r.width = m_cropWidth->value();
sel.r.left = m_cropLeft->value();
sel.r.height = m_cropHeight->value();
sel.r.top = m_cropTop->value();
s_selection(sel);
updateVidFormat();
}
void GeneralTab::composeChanged()
{
v4l2_selection sel = { 0 };
if (!m_composeWidth->isEnabled() || !cur_io_has_compose())
return;
sel.type = g_selection_type();
sel.target = V4L2_SEL_TGT_COMPOSE;
sel.r.width = m_composeWidth->value();
sel.r.left = m_composeLeft->value();
sel.r.height = m_composeHeight->value();
sel.r.top = m_composeTop->value();
s_selection(sel);
updateVidFormat();
}
void GeneralTab::updateVideoInput()
{
__u32 input;
v4l2_input in;
if (g_input(input))
return;
enum_input(in, true, input);
m_videoInput->setCurrentIndex(input);
m_isSDTV = false;
if (m_tvStandard) {
refreshStandards();
updateStandard();
m_tvStandard->setEnabled(in.capabilities & V4L2_IN_CAP_STD);
if (m_qryStandard)
m_qryStandard->setEnabled(in.capabilities & V4L2_IN_CAP_STD);
if (in.capabilities & V4L2_IN_CAP_STD)
m_isSDTV = true;
bool enableFreq = in.type == V4L2_INPUT_TYPE_TUNER;
if (m_freq)
m_freq->setEnabled(enableFreq);
if (m_freqTable)
m_freqTable->setEnabled(enableFreq);
if (m_freqChannel)
m_freqChannel->setEnabled(enableFreq);
if (m_detectSubchans) {
m_detectSubchans->setEnabled(enableFreq);
if (!enableFreq) {
m_subchannels->setText("");
fixWidth();
}
else
detectSubchansClicked();
}
}
if (m_videoTimings) {
refreshTimings();
updateTimings();
m_videoTimings->setEnabled(in.capabilities & V4L2_IN_CAP_DV_TIMINGS);
if (m_qryTimings)
m_qryTimings->setEnabled(in.capabilities & V4L2_IN_CAP_DV_TIMINGS);
}
if (m_audioInput)
m_audioInput->setEnabled(in.audioset);
if (m_cropWidth) {
bool has_crop = cur_io_has_crop();
m_cropWidth->setEnabled(has_crop);
m_cropLeft->setEnabled(has_crop);
m_cropHeight->setEnabled(has_crop);
m_cropTop->setEnabled(has_crop);
}
if (m_composeWidth) {
bool has_compose = cur_io_has_compose();
m_composeWidth->setEnabled(has_compose);
m_composeLeft->setEnabled(has_compose);
m_composeHeight->setEnabled(has_compose);
m_composeTop->setEnabled(has_compose);
}
}
void GeneralTab::updateVideoOutput()
{
__u32 output;
v4l2_output out;
if (g_output(output))
return;
enum_output(out, true, output);
m_videoOutput->setCurrentIndex(output);
m_isSDTV = false;
if (m_tvStandard) {
refreshStandards();
updateStandard();
m_tvStandard->setEnabled(out.capabilities & V4L2_OUT_CAP_STD);
if (m_qryStandard)
m_qryStandard->setEnabled(out.capabilities & V4L2_OUT_CAP_STD);
if (out.capabilities & V4L2_OUT_CAP_STD)
m_isSDTV = true;
}
if (m_videoTimings) {
refreshTimings();
updateTimings();
m_videoTimings->setEnabled(out.capabilities & V4L2_OUT_CAP_DV_TIMINGS);
}
if (m_audioOutput)
m_audioOutput->setEnabled(out.audioset);
if (m_cropWidth) {
bool has_crop = cur_io_has_crop();
m_cropWidth->setEnabled(has_crop);
m_cropLeft->setEnabled(has_crop);
m_cropHeight->setEnabled(has_crop);
m_cropTop->setEnabled(has_crop);
}
if (m_composeWidth) {
bool has_compose = cur_io_has_compose();
m_composeWidth->setEnabled(has_compose);
m_composeLeft->setEnabled(has_compose);
m_composeHeight->setEnabled(has_compose);
m_composeTop->setEnabled(has_compose);
}
g_mw->updateLimRGBRange();
}
void GeneralTab::updateAudioInput()
{
v4l2_audio audio;
QString what;
g_audio(audio);
m_audioInput->setCurrentIndex(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";
m_audioInput->setStatusTip(what);
m_audioInput->setWhatsThis(what);
}
void GeneralTab::updateAudioOutput()
{
v4l2_audioout audio;
g_audout(audio);
m_audioOutput->setCurrentIndex(audio.index);
}
void GeneralTab::refreshStandards()
{
v4l2_standard vs;
m_tvStandard->clear();
if (!enum_std(vs, true)) {
do {
m_tvStandard->addItem((char *)vs.name);
} while (!enum_std(vs));
}
}
void GeneralTab::updateStandard()
{
v4l2_std_id std;
v4l2_standard vs;
QString what;
if (g_std(std))
return;
if (enum_std(vs, true))
return;
do {
if (vs.id == std)
break;
} while (!enum_std(vs));
if (vs.id != std) {
if (!enum_std(vs, true)) {
do {
if (vs.id & std)
break;
} while (!enum_std(vs));
}
}
if ((vs.id & std) == 0)
return;
m_tvStandard->setCurrentIndex(vs.index);
#if QT_VERSION < 0x050500
what.sprintf(
#else
what.asprintf(
#endif
"TV Standard (0x%llX)\n"
"Frame period: %f (%d/%d)\n"
"Frame lines: %d", (long long int)std,
(double)vs.frameperiod.numerator / vs.frameperiod.denominator,
vs.frameperiod.numerator, vs.frameperiod.denominator,
vs.framelines);
m_tvStandard->setStatusTip(what);
m_tvStandard->setWhatsThis(what);
updateVidFormat();
if (!isVbi() && !isTouch() && !m_isOutput)
changePixelAspectRatio();
}
void GeneralTab::qryStdClicked()
{
v4l2_std_id std;
if (query_std(std))
return;
if (std == V4L2_STD_UNKNOWN) {
info("No standard detected\n");
} else {
s_std(std);
updateStandard();
}
}
void GeneralTab::refreshTimings()
{
v4l2_enum_dv_timings timings;
m_videoTimings->clear();
if (!enum_dv_timings(timings, true)) {
do {
v4l2_bt_timings &bt = timings.timings.bt;
double tot_height = bt.height +
bt.vfrontporch + bt.vsync + bt.vbackporch +
bt.il_vfrontporch + bt.il_vsync + bt.il_vbackporch;
double tot_width = bt.width +
bt.hfrontporch + bt.hsync + bt.hbackporch;
char buf[100];
if (bt.interlaced)
sprintf(buf, "%ux%ui%.2f", bt.width, bt.height,
(double)bt.pixelclock / (tot_width * (tot_height / 2)));
else
sprintf(buf, "%ux%up%.2f", bt.width, bt.height,
(double)bt.pixelclock / (tot_width * tot_height));
m_videoTimings->addItem(buf);
} while (!enum_dv_timings(timings));
}
}
void GeneralTab::updateTimings()
{
v4l2_dv_timings timings;
v4l2_enum_dv_timings p;
QString what;
if (g_dv_timings(timings))
return;
if (enum_dv_timings(p, true))
return;
do {
if (!memcmp(&timings, &p.timings, sizeof(timings)))
break;
} while (!enum_dv_timings(p));
if (memcmp(&timings, &p.timings, sizeof(timings)))
return;
m_videoTimings->setCurrentIndex(p.index);
#if QT_VERSION < 0x050500
what.sprintf(
#else
what.asprintf(
#endif
"Video Timings (%u)\n"
"Frame %ux%u\n",
p.index, p.timings.bt.width, p.timings.bt.height);
m_isSDTV = p.timings.bt.width <= 720 && p.timings.bt.height <= 576;
m_videoTimings->setStatusTip(what);
m_videoTimings->setWhatsThis(what);
updateVidFormat();
g_mw->updateLimRGBRange();
}
void GeneralTab::qryTimingsClicked()
{
v4l2_dv_timings timings;
int err = query_dv_timings(timings);
switch (err) {
case ENOLINK:
info("No signal found\n");
break;
case ENOLCK:
info("Could not lock to signal\n");
break;
case ERANGE:
info("Frequency out of range\n");
break;
case 0:
s_dv_timings(timings);
updateTimings();
break;
default:
error(err);
break;
}
}
void GeneralTab::sourceChange(const v4l2_event &ev)
{
if (!m_videoInput || (int)ev.id != m_videoInput->currentIndex())
return;
if (m_qryStandard && m_qryStandard->isEnabled())
m_qryStandard->click();
else if (m_qryTimings && m_qryTimings->isEnabled())
m_qryTimings->click();
if (has_vid_cap() || has_vid_out())
updateColorspace();
}
void GeneralTab::updateFreq()
{
v4l2_frequency f;
g_frequency(f);
/* m_freq listens to valueChanged block it to avoid recursion */
m_freq->blockSignals(true);
m_freq->setValue((double)f.frequency / m_freqFac);
m_freq->blockSignals(false);
}
void GeneralTab::updateFreqChannel()
{
m_freqChannel->clear();
int tbl = m_freqTable->currentIndex();
const struct v4l2_channel_list *list = v4l2_channel_lists[tbl].list;
for (unsigned i = 0; i < v4l2_channel_lists[tbl].count; i++)
m_freqChannel->addItem(list[i].name);
}
void GeneralTab::updateFreqRf()
{
v4l2_frequency f;
g_frequency(f, 1);
/* m_freqRf listens to valueChanged block it to avoid recursion */
m_freqRf->blockSignals(true);
m_freqRf->setValue((double)f.frequency / m_freqRfFac);
m_freqRf->blockSignals(false);
}
void GeneralTab::updateColorspace()
{
cv4l_fmt fmt;
int idx;
g_fmt(fmt);
idx = m_colorspace->findData(fmt.g_colorspace());
if (m_colorspace->currentIndex())
m_colorspace->setCurrentIndex(idx >= 0 ? idx : 0);
idx = m_xferFunc->findData(fmt.g_xfer_func());
if (m_xferFunc->currentIndex())
m_xferFunc->setCurrentIndex(idx >= 0 ? idx : 0);
idx = m_ycbcrEnc->findData(fmt.g_ycbcr_enc());
if (m_ycbcrEnc->currentIndex())
m_ycbcrEnc->setCurrentIndex(idx >= 0 ? idx : 0);
idx = m_quantRange->findData(fmt.g_quantization());
if (m_quantRange->currentIndex())
m_quantRange->setCurrentIndex(idx >= 0 ? idx : 0);
g_mw->updateColorspace();
}
void GeneralTab::updateVidCapFormat()
{
v4l2_fmtdesc desc;
cv4l_fmt fmt;
if (isVbi())
return;
g_fmt(fmt);
m_pixelformat = fmt.g_pixelformat();
m_width = fmt.g_width();
m_height = fmt.g_height();
updateColorspace();
updateFrameSize();
updateFrameInterval();
if (!enum_fmt(desc, true)) {
do {
if (desc.pixelformat == m_pixelformat)
break;
} while (!enum_fmt(desc));
}
if (desc.pixelformat != m_pixelformat)
return;
m_vidCapFormats->setCurrentIndex(desc.index);
updateVidFields();
updateCrop();
updateCompose();
}
void GeneralTab::updateVidOutFormat()
{
v4l2_fmtdesc desc;
cv4l_fmt fmt;
if (isVbi())
return;
g_fmt(fmt);
m_pixelformat = fmt.g_pixelformat();
m_width = fmt.g_width();
m_height = fmt.g_height();
updateColorspace();
updateFrameSize();
if (!enum_fmt(desc, true)) {
do {
if (desc.pixelformat == m_pixelformat)
break;
} while (!enum_fmt(desc));
}
if (desc.pixelformat != m_pixelformat)
return;
m_vidOutFormats->setCurrentIndex(desc.index);
updateVidFields();
updateCrop();
updateCompose();
}
void GeneralTab::updateVidFields()
{
cv4l_fmt fmt;
cv4l_fmt tmp;
bool first = true;
g_fmt(fmt);
for (__u32 f = V4L2_FIELD_NONE; f <= V4L2_FIELD_INTERLACED_BT; f++) {
tmp = fmt;
tmp.s_field(f);
if (try_fmt(tmp) || tmp.g_field() != f)
continue;
if (first) {
m_vidFields->clear();
first = false;
}
m_vidFields->addItem(field2s(f));
if (fmt.g_field() == f)
m_vidFields->setCurrentIndex(m_vidFields->count() - 1);
}
}
void GeneralTab::updateCrop()
{
if (m_cropWidth == NULL || !m_cropWidth->isEnabled())
return;
v4l2_selection sel = { 0 };
v4l2_rect &r = sel.r;
v4l2_rect b = { 0, 0, m_width, m_height };
sel.type = g_selection_type();
if (sel.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
if (g_selection(sel))
return;
b = sel.r;
}
sel.target = V4L2_SEL_TGT_CROP;
if (g_selection(sel))
return;
m_cropWidth->blockSignals(true);
m_cropLeft->blockSignals(true);
m_cropHeight->blockSignals(true);
m_cropTop->blockSignals(true);
m_cropWidth->setRange(8, b.width);
m_cropWidth->setSliderPosition(r.width);
if (b.width != r.width) {
m_cropLeft->setRange(b.left, b.left + b.width - r.width);
m_cropLeft->setSliderPosition(r.left);
}
m_cropHeight->setRange(8, b.height);
m_cropHeight->setSliderPosition(r.height);
if (b.height != r.height) {
m_cropTop->setRange(b.top, b.top + b.height - r.height);
m_cropTop->setSliderPosition(r.top);
}
m_cropWidth->blockSignals(false);
m_cropLeft->blockSignals(false);
m_cropHeight->blockSignals(false);
m_cropTop->blockSignals(false);
}
void GeneralTab::updateCompose()
{
if (m_composeWidth == NULL || !m_composeWidth->isEnabled())
return;
v4l2_selection sel = { 0 };
v4l2_rect &r = sel.r;
v4l2_rect b = { 0, 0, m_width, m_height };
sel.type = g_selection_type();
if (sel.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
sel.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
if (g_selection(sel))
return;
b = sel.r;
}
sel.target = V4L2_SEL_TGT_COMPOSE;
if (g_selection(sel))
return;
m_composeWidth->blockSignals(true);
m_composeLeft->blockSignals(true);
m_composeHeight->blockSignals(true);
m_composeTop->blockSignals(true);
m_composeWidth->setRange(8, b.width);
m_composeWidth->setSliderPosition(r.width);
if (b.width != r.width) {
m_composeLeft->setRange(b.left, b.left + b.width - r.width);
m_composeLeft->setSliderPosition(r.left);
}
m_composeHeight->setRange(8, b.height);
m_composeHeight->setSliderPosition(r.height);
if (b.height != r.height) {
m_composeTop->setRange(b.top, b.top + b.height - r.height);
m_composeTop->setSliderPosition(r.top);
}
m_composeWidth->blockSignals(false);
m_composeLeft->blockSignals(false);
m_composeHeight->blockSignals(false);
m_composeTop->blockSignals(false);
emit clearBuffers();
}
void GeneralTab::updateFrameSize()
{
v4l2_frmsizeenum frmsize;
bool ok = false;
if (m_frameSize)
m_frameSize->clear();
ok = !enum_framesizes(frmsize, m_pixelformat);
if (ok && frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
if (m_frameSize) {
do {
m_frameSize->addItem(QString("%1x%2")
.arg(frmsize.discrete.width)
.arg(frmsize.discrete.height));
if (frmsize.discrete.width == m_width &&
frmsize.discrete.height == m_height)
m_frameSize->setCurrentIndex(frmsize.index);
} while (!enum_framesizes(frmsize));
}
m_discreteSizes = true;
m_frameWidth->setEnabled(false);
m_frameWidth->blockSignals(true);
m_frameWidth->setMinimum(m_width);
m_frameWidth->setMaximum(m_width);
m_frameWidth->setValue(m_width);
m_frameWidth->blockSignals(false);
m_frameHeight->setEnabled(false);
m_frameHeight->blockSignals(true);
m_frameHeight->setMinimum(m_height);
m_frameHeight->setMaximum(m_height);
m_frameHeight->setValue(m_height);
m_frameHeight->blockSignals(false);
if (m_frameSize)
m_frameSize->setEnabled(!m_haveBuffers);
updateFrameInterval();
return;
}
if (!ok) {
frmsize.stepwise.min_width = 8;
frmsize.stepwise.max_width = 4096;
frmsize.stepwise.step_width = 1;
frmsize.stepwise.min_height = 8;
frmsize.stepwise.max_height = 2160;
frmsize.stepwise.step_height = 1;
}
m_discreteSizes = false;
if (m_frameSize)
m_frameSize->setEnabled(false);
if (!m_frameWidth) {
updateFrameInterval();
return;
}
m_frameWidth->setEnabled(!m_haveBuffers);
m_frameWidth->blockSignals(true);
m_frameWidth->setMinimum(frmsize.stepwise.min_width);
m_frameWidth->setMaximum(frmsize.stepwise.max_width);
m_frameWidth->setSingleStep(frmsize.stepwise.step_width);
m_frameWidth->setValue(m_width);
m_frameWidth->blockSignals(false);
m_frameHeight->setEnabled(!m_haveBuffers);
m_frameHeight->blockSignals(true);
m_frameHeight->setMinimum(frmsize.stepwise.min_height);
m_frameHeight->setMaximum(frmsize.stepwise.max_height);
m_frameHeight->setSingleStep(frmsize.stepwise.step_height);
m_frameHeight->setValue(m_height);
m_frameHeight->blockSignals(false);
updateFrameInterval();
}
CropMethod GeneralTab::getCropMethod()
{
switch (m_cropping->currentIndex()) {
case 1:
return QV4L2_CROP_TB;
case 2:
return QV4L2_CROP_P43;
case 3:
return QV4L2_CROP_W149;
case 4:
return QV4L2_CROP_W169;
case 5:
return QV4L2_CROP_C185;
case 6:
return QV4L2_CROP_C239;
default:
return QV4L2_CROP_NONE;
}
}
void GeneralTab::changePixelAspectRatio()
{
// Update hints by calling a get
getPixelAspectRatio();
info("");
emit pixelAspectRatioChanged();
}
double GeneralTab::getPixelAspectRatio()
{
v4l2_fract ratio = { 1, 1 };
unsigned w = 0, h = 0;
ratio = g_pixel_aspect(w, h);
if (!m_pixelAspectRatio)
return 1;
switch (m_pixelAspectRatio->currentIndex()) {
// override ratio if hardcoded, but keep w and h
case 1:
ratio.numerator = 1;
ratio.denominator = 1;
break;
case 2:
ratio.numerator = 11;
ratio.denominator = 10;
break;
case 3:
ratio.numerator = 33;
ratio.denominator = 40;
break;
case 4:
ratio.numerator = 54;
ratio.denominator = 59;
break;
case 5:
ratio.numerator = 81;
ratio.denominator = 118;
break;
default:
break;
}
m_pixelAspectRatio->setWhatsThis(QString("Pixel Aspect Ratio y:x = %1:%2")
.arg(ratio.numerator).arg(ratio.denominator));
m_pixelAspectRatio->setStatusTip(m_pixelAspectRatio->whatsThis());
cv4l_fmt fmt;
unsigned cur_width, cur_height;
unsigned cur_field;
g_fmt(fmt);
cur_width = fmt.g_width();
cur_height = fmt.g_height();
cur_field = fmt.g_field();
if (w == 0)
w = cur_width;
if (cur_field == V4L2_FIELD_TOP ||
cur_field == V4L2_FIELD_BOTTOM ||
cur_field == V4L2_FIELD_ALTERNATE) {
// If we only capture a single field, then each pixel is twice
// as high and the default image height is half the reported
// height.
ratio.numerator *= 2;
h /= 2;
}
if (h == 0)
h = cur_height;
// Note: ratio is y / x, whereas we want x / y, so we return
// denominator / numerator.
// In addition, the ratio is for the unscaled image (i.e., the default
// image rectangle as returned by VIDIOC_CROPCAP). So we have to
// compensate for the current scaling factor.
return (((double)ratio.denominator * w) / cur_width) /
(((double)ratio.numerator * h) / cur_height);
}
void GeneralTab::updateFrameInterval()
{
v4l2_frmivalenum frmival = { 0 };
v4l2_fract curr = { 1, 1 };
bool curr_ok, ok;
if (m_frameInterval == NULL)
return;
m_frameInterval->clear();
ok = !enum_frameintervals(frmival, m_pixelformat, m_width, m_height);
m_has_interval = ok && frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE;
m_frameInterval->setEnabled(m_has_interval);
if (m_has_interval) {
m_interval = frmival.discrete;
curr_ok = !m_fd->get_interval(curr);
do {
m_frameInterval->addItem(QString("%1 fps")
.arg((double)frmival.discrete.denominator / frmival.discrete.numerator));
if (curr_ok &&
frmival.discrete.numerator == curr.numerator &&
frmival.discrete.denominator == curr.denominator) {
m_frameInterval->setCurrentIndex(frmival.index);
m_interval = frmival.discrete;
}
} while (!enum_frameintervals(frmival));
}
}
bool GeneralTab::get_interval(struct v4l2_fract &interval)
{
if (m_has_interval)
interval = m_interval;
return m_has_interval;
}
QString GeneralTab::getAudioInDevice()
{
if (m_audioInDevice == NULL)
return NULL;
return m_audioInDeviceMap[m_audioInDevice->currentIndex()];
}
QString GeneralTab::getAudioOutDevice()
{
if (m_audioOutDevice == NULL)
return NULL;
return m_audioOutDeviceMap[m_audioOutDevice->currentIndex()];
}
void GeneralTab::setAudioDeviceBufferSize(int size)
{
m_audioDeviceBufferSize = size;
}
int GeneralTab::getAudioDeviceBufferSize()
{
return m_audioDeviceBufferSize;
}
#ifdef HAVE_ALSA
int GeneralTab::checkMatchAudioDevice(void *md, const char *vid, enum device_type type)
{
const char *devname = NULL;
enum device_type dtype = isRadio() ? MEDIA_V4L_RADIO : MEDIA_V4L_VIDEO;
while ((devname = get_associated_device(md, devname, type, vid, dtype)) != NULL) {
if (type == MEDIA_SND_CAP) {
#if QT_VERSION < 0x060000
QStringList devAddr = QString(devname).split(QRegExp("[:,]"));
return devAddr.value(1).toInt();
#else
QRegExp rx("[:,]");
rx.indexIn(devname);
return rx.cap(1).toInt();
#endif
}
}
return -1;
}
int GeneralTab::matchAudioDevice()
{
QStringList devPath = m_device.split("/");
QString curDev = devPath.value(devPath.count() - 1);
void *media;
int match;
media = discover_media_devices();
if ((match = checkMatchAudioDevice(media, curDev.toLatin1(), MEDIA_SND_CAP)) != -1)
return match;
return -1;
}
#endif
bool GeneralTab::hasAlsaAudio()
{
#ifdef HAVE_ALSA
return !isVbi() && !isTouch();
#else
return false;
#endif
}