| /* 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 |
| } |