From c0582015ef98aaa0046f649b48dc320ae096c802 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Mar 2014 22:17:06 +0100 Subject: [PATCH] qv4l2: add SDR support. Signed-off-by: Hans Verkuil --- utils/qv4l2/general-tab.cpp | 139 ++++++++++++++++++++++++++++++++++---------- utils/qv4l2/general-tab.h | 8 +++ utils/qv4l2/qv4l2.1 | 7 ++- utils/qv4l2/qv4l2.cpp | 60 ++++++++++++------- utils/qv4l2/v4l2-api.cpp | 6 +- utils/qv4l2/v4l2-api.h | 4 +- 6 files changed, 166 insertions(+), 58 deletions(-) diff --git a/utils/qv4l2/general-tab.cpp b/utils/qv4l2/general-tab.cpp index 69242c2..09cb01f 100644 --- a/utils/qv4l2/general-tab.cpp +++ b/utils/qv4l2/general-tab.cpp @@ -48,7 +48,10 @@ GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent) m_col(0), m_cols(n), m_isRadio(false), + m_isSDR(false), m_isVbi(false), + m_freqFac(16), + m_freqRfFac(16), m_videoInput(NULL), m_videoOutput(NULL), m_audioInput(NULL), @@ -64,6 +67,7 @@ GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent) m_freqChannel(NULL), m_audioMode(NULL), m_subchannels(NULL), + m_freqRf(NULL), m_stereoMode(NULL), m_rdsMode(NULL), m_detectSubchans(NULL), @@ -100,18 +104,23 @@ GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent) } g_tuner(m_tuner); + g_tuner(m_tuner_rf, 1); g_modulator(m_modulator); v4l2_input vin; bool needsStd = false; bool needsTimings = false; - if (m_tuner.capability && m_tuner.capability & V4L2_TUNER_CAP_LOW) + 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) + 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) + if (m_querycap.capabilities & V4L2_CAP_DEVICE_CAPS) { m_isVbi = caps() & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE); + m_isSDR = m_isRadio = caps() & V4L2_CAP_SDR_CAPTURE; + } if (hasAlsaAudio()) { m_audioInDevice = new QComboBox(parent); @@ -262,41 +271,48 @@ GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent) if (m_tuner.capability) { QDoubleValidator *val; - const char *unit = (m_tuner.capability & V4L2_TUNER_CAP_LOW) ? "kHz" : "MHz"; + const char *unit = (m_tuner.capability & V4L2_TUNER_CAP_LOW) ? "kHz" : + (m_tuner.capability & V4L2_TUNER_CAP_1HZ ? "Hz" : "MHz"); - val = new QDoubleValidator(m_tuner.rangelow / 16, m_tuner.rangehigh / 16, 3, parent); + m_freqFac = (m_tuner.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16; + val = new QDoubleValidator(m_tuner.rangelow / m_freqFac, m_tuner.rangehigh / m_freqFac, 3, parent); m_freq = new QLineEdit(parent); m_freq->setValidator(val); m_freq->setWhatsThis(QString("Frequency\nLow: %1 %3\nHigh: %2 %3") - .arg(m_tuner.rangelow / 16) - .arg((double)m_tuner.rangehigh / 16, 0, 'f', 2) + .arg(m_tuner.rangelow / m_freqFac) + .arg((double)m_tuner.rangehigh / m_freqFac, 0, 'f', 2) .arg(unit)); m_freq->setStatusTip(m_freq->whatsThis()); connect(m_freq, SIGNAL(lostFocus()), SLOT(freqChanged())); connect(m_freq, SIGNAL(returnPressed()), SLOT(freqChanged())); updateFreq(); - if (m_tuner.capability & V4L2_TUNER_CAP_LOW) + if (m_tuner.capability & V4L2_TUNER_CAP_1HZ) + addLabel("Frequency (Hz)"); + else if (m_tuner.capability & V4L2_TUNER_CAP_LOW) addLabel("Frequency (kHz)"); else addLabel("Frequency (MHz)"); addWidget(m_freq); + } - if (!(m_tuner.capability & V4L2_TUNER_CAP_LOW)) { - addLabel("Frequency Table"); - m_freqTable = new QComboBox(parent); - for (int i = 0; v4l2_channel_lists[i].name; i++) { - m_freqTable->addItem(v4l2_channel_lists[i].name); - } - addWidget(m_freqTable); - connect(m_freqTable, SIGNAL(activated(int)), SLOT(freqTableChanged(int))); - - addLabel("Channels"); - m_freqChannel = new QComboBox(parent); - m_freqChannel->setSizeAdjustPolicy(QComboBox::AdjustToContents); - addWidget(m_freqChannel); - connect(m_freqChannel, SIGNAL(activated(int)), SLOT(freqChannelChanged(int))); - updateFreqChannel(); + if (m_tuner.capability && !isRadio()) { + addLabel("Frequency Table"); + m_freqTable = new QComboBox(parent); + for (int i = 0; v4l2_channel_lists[i].name; i++) { + m_freqTable->addItem(v4l2_channel_lists[i].name); } + addWidget(m_freqTable); + connect(m_freqTable, SIGNAL(activated(int)), SLOT(freqTableChanged(int))); + + addLabel("Channels"); + m_freqChannel = new QComboBox(parent); + m_freqChannel->setSizeAdjustPolicy(QComboBox::AdjustToContents); + addWidget(m_freqChannel); + connect(m_freqChannel, SIGNAL(activated(int)), SLOT(freqChannelChanged(int))); + updateFreqChannel(); + } + + if (m_tuner.capability && !isSDR()) { addLabel("Audio Mode"); m_audioMode = new QComboBox(parent); m_audioMode->setMinimumContentsLength(12); @@ -334,26 +350,59 @@ GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent) detectSubchansClicked(); } + if (m_tuner_rf.capability) { + QDoubleValidator *val; + 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; + val = new QDoubleValidator(m_tuner_rf.rangelow / m_freqRfFac, m_tuner_rf.rangehigh / m_freqRfFac, 3, parent); + val->setNotation(QDoubleValidator::StandardNotation); + m_freqRf = new QLineEdit(parent); + m_freqRf->setValidator(val); + m_freqRf->setWhatsThis(QString("RF Frequency\nLow: %1 %3\nHigh: %2 %3") + .arg(m_tuner_rf.rangelow / m_freqRfFac) + .arg((double)m_tuner_rf.rangehigh / m_freqRfFac, 0, 'f', 2) + .arg(unit)); + m_freqRf->setStatusTip(m_freqRf->whatsThis()); + connect(m_freqRf, SIGNAL(lostFocus()), SLOT(freqRfChanged())); + connect(m_freqRf, SIGNAL(returnPressed()), SLOT(freqRfChanged())); + updateFreqRf(); + if (m_tuner_rf.capability & V4L2_TUNER_CAP_1HZ) + addLabel("RF Frequency (Hz)"); + else if (m_tuner_rf.capability & V4L2_TUNER_CAP_LOW) + addLabel("RF Frequency (kHz)"); + else + addLabel("RF Frequency (MHz)"); + addWidget(m_freqRf); + } + if (m_modulator.capability) { QDoubleValidator *val; - const char *unit = (m_modulator.capability & V4L2_TUNER_CAP_LOW) ? "kHz" : "MHz"; + const char *unit = (m_tuner.capability & V4L2_TUNER_CAP_LOW) ? "kHz" : + (m_tuner.capability & V4L2_TUNER_CAP_1HZ ? "Hz" : "MHz"); - val = new QDoubleValidator(m_modulator.rangelow / 16, m_modulator.rangehigh / 16, 3, parent); + m_freqFac = (m_tuner.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16; + val = new QDoubleValidator(m_modulator.rangelow / m_freqFac, m_modulator.rangehigh / m_freqFac, 3, parent); m_freq = new QLineEdit(parent); m_freq->setValidator(val); m_freq->setWhatsThis(QString("Frequency\nLow: %1 %3\nHigh: %2 %3") - .arg(m_tuner.rangelow / 16) - .arg((double)m_tuner.rangehigh / 16, 0, 'f', 2) + .arg(m_tuner.rangelow / m_freqFac) + .arg((double)m_tuner.rangehigh / m_freqFac, 0, 'f', 2) .arg(unit)); m_freq->setStatusTip(m_freq->whatsThis()); connect(m_freq, SIGNAL(lostFocus()), SLOT(freqChanged())); connect(m_freq, SIGNAL(returnPressed()), SLOT(freqChanged())); updateFreq(); - if (m_modulator.capability & V4L2_TUNER_CAP_LOW) + if (m_modulator.capability & V4L2_TUNER_CAP_1HZ) + addLabel("Frequency (Hz)"); + else if (m_modulator.capability & V4L2_TUNER_CAP_LOW) addLabel("Frequency (kHz)"); else addLabel("Frequency (MHz)"); addWidget(m_freq); + } + if (m_modulator.capability && !isSDR()) { if (m_modulator.capability & V4L2_TUNER_CAP_STEREO) { addLabel("Stereo"); m_stereoMode = new QCheckBox(parent); @@ -772,12 +821,29 @@ void GeneralTab::freqChannelChanged(int idx) void GeneralTab::freqChanged() { double f = m_freq->text().toDouble(); + v4l2_frequency freq; - if (m_freq->hasAcceptableInput()) - s_frequency(f * 16, m_isRadio); + if (m_freq->hasAcceptableInput()) { + g_frequency(freq); + freq.frequency = f * m_freqFac; + s_frequency(freq); + } updateFreq(); } +void GeneralTab::freqRfChanged() +{ + double f = m_freqRf->text().toDouble(); + v4l2_frequency freq; + + if (m_freqRf->hasAcceptableInput()) { + 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()]; @@ -1181,7 +1247,7 @@ void GeneralTab::updateFreq() g_frequency(f); /* m_freq listens to valueChanged block it to avoid recursion */ m_freq->blockSignals(true); - m_freq->setText(QString::number(f.frequency / 16.0)); + m_freq->setText(QString::number(f.frequency / m_freqFac)); m_freq->blockSignals(false); } @@ -1194,6 +1260,17 @@ void GeneralTab::updateFreqChannel() 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->setText(QString::number(f.frequency / m_freqRfFac)); + m_freqRf->blockSignals(false); +} + void GeneralTab::updateVidCapFormat() { v4l2_fmtdesc desc; diff --git a/utils/qv4l2/general-tab.h b/utils/qv4l2/general-tab.h index f3f32a4..2057f35 100644 --- a/utils/qv4l2/general-tab.h +++ b/utils/qv4l2/general-tab.h @@ -65,6 +65,7 @@ public: int width() const { return m_width; } int height() const { return m_height; } bool isRadio() const { return m_isRadio; } + bool isSDR() const { return m_isSDR; } bool isVbi() const { return m_isVbi; } bool isSlicedVbi() const; __u32 bufType() const { return m_buftype; } @@ -110,6 +111,7 @@ private slots: void freqTableChanged(int); void freqChannelChanged(int); void freqChanged(); + void freqRfChanged(); void audioModeChanged(int); void detectSubchansClicked(); void stereoModeChanged(); @@ -136,6 +138,7 @@ private: void updateTimings(); void updateFreq(); void updateFreqChannel(); + void updateFreqRf(); void updateVidCapFormat(); void updateVidCapFields(); void updateFrameSize(); @@ -168,11 +171,15 @@ private: int m_col; int m_cols; bool m_isRadio; + bool m_isSDR; bool m_isVbi; + double m_freqFac; + double m_freqRfFac; __u32 m_buftype; __u32 m_audioModes[5]; QString m_device; struct v4l2_tuner m_tuner; + struct v4l2_tuner m_tuner_rf; struct v4l2_modulator m_modulator; struct v4l2_capability m_querycap; __u32 m_pixelformat; @@ -200,6 +207,7 @@ private: QComboBox *m_freqChannel; QComboBox *m_audioMode; QLabel *m_subchannels; + QLineEdit *m_freqRf; QCheckBox *m_stereoMode; QCheckBox *m_rdsMode; QPushButton *m_detectSubchans; diff --git a/utils/qv4l2/qv4l2.1 b/utils/qv4l2/qv4l2.1 index c6abe7c..0e54806 100644 --- a/utils/qv4l2/qv4l2.1 +++ b/utils/qv4l2/qv4l2.1 @@ -14,11 +14,14 @@ However, it does not (yet) support compressed video streams other than MJPEG \fB\-d\fR, \fB\-\-device\fR=\fI\fR Use device as the video device. If is a number, then /dev/video is used. .TP +\fB\-V\fR, \fB\-\-vbi-device\fR=\fI\fR +Use device as the vbi device. If is a number, then /dev/vbi is used. +.TP \fB\-r\fR, \fB\-\-radio-device\fR=\fI\fR Use device as the radio device. If is a number, then /dev/radio is used. .TP -\fB\-V\fR, \fB\-\-vbi-device\fR=\fI\fR -Use device as the vbi device. If is a number, then /dev/vbi is used. +\fB\-S\fR, \fB\-\-sdr-device\fR=\fI\fR +Use device as the SDR device. If is a number, then /dev/swradio is used. .TP \fB\-R\fR, \fB\-\-raw\fR Open device in raw mode, i.e. without using the libv4l2 wrapper functions. diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp index 7d03c4a..d54a5e2 100644 --- a/utils/qv4l2/qv4l2.cpp +++ b/utils/qv4l2/qv4l2.cpp @@ -252,7 +252,7 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen) void ApplicationWindow::opendev() { - QFileDialog d(this, "Select v4l device", "/dev", "V4L Devices (video* vbi* radio*)"); + QFileDialog d(this, "Select v4l device", "/dev", "V4L Devices (video* vbi* radio* swradio*)"); d.setFilter(QDir::AllDirs | QDir::Files | QDir::System); d.setFileMode(QFileDialog::ExistingFile); @@ -262,7 +262,7 @@ void ApplicationWindow::opendev() void ApplicationWindow::openrawdev() { - QFileDialog d(this, "Select v4l device", "/dev", "V4L Devices (video* vbi* radio*)"); + QFileDialog d(this, "Select v4l device", "/dev", "V4L Devices (video* vbi* radio* swradio*)"); d.setFilter(QDir::AllDirs | QDir::Files | QDir::System); d.setFileMode(QFileDialog::ExistingFile); @@ -1205,10 +1205,12 @@ static void usage() " qv4l2 [-R] [-h] [-d ] [-r ] [-V ]\n" "\n -d, --device= use device as the video device\n" " if is a number, then /dev/video is used\n" - " -r, --radio-device= use device as the radio device\n" - " if is a number, then /dev/radio is used\n" " -V, --vbi-device= use device as the vbi device\n" " if is a number, then /dev/vbi is used\n" + " -r, --radio-device= use device as the radio device\n" + " if is a number, then /dev/radio is used\n" + " -S, --sdr-device= use device as the SDR device\n" + " if is a number, then /dev/swradio is used\n" " -h, --help display this help message\n" " -R, --raw open device in raw mode.\n"); } @@ -1232,8 +1234,9 @@ int main(int argc, char **argv) bool raw = false; QString device; QString video_device; - QString radio_device; QString vbi_device; + QString radio_device; + QString sdr_device; a.setWindowIcon(QIcon(":/qv4l2.png")); g_mw = new ApplicationWindow(); @@ -1253,7 +1256,18 @@ int main(int argc, char **argv) usageError("-d"); return 0; } + } else if (args[i] == "-V" || args[i] == "--vbi-device") { + ++i; + if (i >= args.size()) { + usageError("-V"); + return 0; + } + vbi_device = args[i]; + if (vbi_device.startsWith("-")) { + usageError("-V"); + return 0; + } } else if (args[i] == "-r" || args[i] == "--radio-device") { ++i; if (i >= args.size()) { @@ -1266,20 +1280,18 @@ int main(int argc, char **argv) usageError("-r"); return 0; } - - } else if (args[i] == "-V" || args[i] == "--vbi-device") { + } else if (args[i] == "-S" || args[i] == "--sdr-device") { ++i; if (i >= args.size()) { - usageError("-V"); + usageError("-S"); return 0; } - vbi_device = args[i]; - if (vbi_device.startsWith("-")) { - usageError("-V"); + sdr_device = args[i]; + if (sdr_device.startsWith("-")) { + usageError("-S"); return 0; } - } else if (args[i].startsWith("--device")) { QStringList param = args[i].split("="); if (param.size() == 2) { @@ -1288,7 +1300,14 @@ int main(int argc, char **argv) usageError("--device"); return 0; } - + } else if (args[i].startsWith("--vbi-device")) { + QStringList param = args[i].split("="); + if (param.size() == 2) { + vbi_device = param[1]; + } else { + usageError("--vbi-device"); + return 0; + } } else if (args[i].startsWith("--radio-device")) { QStringList param = args[i].split("="); if (param.size() == 2) { @@ -1297,17 +1316,14 @@ int main(int argc, char **argv) usageError("--radio-device"); return 0; } - - - } else if (args[i].startsWith("--vbi-device")) { + } else if (args[i].startsWith("--sdr-device")) { QStringList param = args[i].split("="); if (param.size() == 2) { - vbi_device = param[1]; + sdr_device = param[1]; } else { - usageError("--vbi-device"); + usageError("--sdr-device"); return 0; } - } else if (args[i] == "-h" || args[i] == "--help") { usage(); return 0; @@ -1324,10 +1340,12 @@ int main(int argc, char **argv) if (video_device != NULL) device = getDeviceName("/dev/video", video_device); - else if (radio_device != NULL) - device = getDeviceName("/dev/radio", radio_device); else if (vbi_device != NULL) device = getDeviceName("/dev/vbi", vbi_device); + else if (radio_device != NULL) + device = getDeviceName("/dev/radio", radio_device); + else if (sdr_device != NULL) + device = getDeviceName("/dev/swradio", sdr_device); else device = "/dev/video0"; diff --git a/utils/qv4l2/v4l2-api.cpp b/utils/qv4l2/v4l2-api.cpp index 8baf4af..894a8c1 100644 --- a/utils/qv4l2/v4l2-api.cpp +++ b/utils/qv4l2/v4l2-api.cpp @@ -145,9 +145,10 @@ bool v4l2::querymenu(v4l2_querymenu &qm) return ioctl(VIDIOC_QUERYMENU, &qm) >= 0; } -bool v4l2::g_tuner(v4l2_tuner &tuner) +bool v4l2::g_tuner(v4l2_tuner &tuner, unsigned index) { memset(&tuner, 0, sizeof(tuner)); + tuner.index = index; if (ioctl(VIDIOC_G_TUNER, &tuner) < 0) return false; if (tuner.rangehigh > INT_MAX) @@ -253,9 +254,10 @@ bool v4l2::query_dv_timings(v4l2_dv_timings &timings) } -bool v4l2::g_frequency(v4l2_frequency &freq) +bool v4l2::g_frequency(v4l2_frequency &freq, unsigned index) { memset(&freq, 0, sizeof(freq)); + freq.tuner = index; freq.type = V4L2_TUNER_ANALOG_TV; return ioctl(VIDIOC_G_FREQUENCY, &freq) >= 0; } diff --git a/utils/qv4l2/v4l2-api.h b/utils/qv4l2/v4l2-api.h index 1a0d066..ad3f15c 100644 --- a/utils/qv4l2/v4l2-api.h +++ b/utils/qv4l2/v4l2-api.h @@ -59,7 +59,7 @@ public: bool querycap(v4l2_capability &cap); bool queryctrl(v4l2_queryctrl &qc); bool querymenu(v4l2_querymenu &qm); - bool g_tuner(v4l2_tuner &tuner); + bool g_tuner(v4l2_tuner &tuner, unsigned index = 0); bool s_tuner(v4l2_tuner &tuner); bool g_modulator(v4l2_modulator &modulator); bool s_modulator(v4l2_modulator &modulator); @@ -77,7 +77,7 @@ public: bool s_dv_timings(v4l2_dv_timings &timings); bool g_dv_timings(v4l2_dv_timings &timings); bool query_dv_timings(v4l2_dv_timings &timings); - bool g_frequency(v4l2_frequency &freq); + bool g_frequency(v4l2_frequency &freq, unsigned index = 0); bool s_frequency(v4l2_frequency &freq); bool s_frequency(int freq, bool low = false); bool g_fmt_cap(v4l2_format &fmt); -- 2.7.4