qv4l2: add SDR support.
authorHans Verkuil <hans.verkuil@cisco.com>
Thu, 6 Mar 2014 21:17:06 +0000 (22:17 +0100)
committerHans Verkuil <hans.verkuil@cisco.com>
Thu, 6 Mar 2014 21:17:06 +0000 (22:17 +0100)
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
utils/qv4l2/general-tab.cpp
utils/qv4l2/general-tab.h
utils/qv4l2/qv4l2.1
utils/qv4l2/qv4l2.cpp
utils/qv4l2/v4l2-api.cpp
utils/qv4l2/v4l2-api.h

index 69242c2..09cb01f 100644 (file)
@@ -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;
index f3f32a4..2057f35 100644 (file)
@@ -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;
index c6abe7c..0e54806 100644 (file)
@@ -14,11 +14,14 @@ However, it does not (yet) support compressed video streams other than MJPEG
 \fB\-d\fR, \fB\-\-device\fR=\fI<dev>\fR
 Use device <dev> as the video device. If <dev> is a number, then /dev/video<dev> is used.
 .TP
+\fB\-V\fR, \fB\-\-vbi-device\fR=\fI<dev>\fR
+Use device <dev> as the vbi device. If <dev> is a number, then /dev/vbi<dev> is used.
+.TP
 \fB\-r\fR, \fB\-\-radio-device\fR=\fI<dev>\fR
 Use device <dev> as the radio device. If <dev> is a number, then /dev/radio<dev> is used.
 .TP
-\fB\-V\fR, \fB\-\-vbi-device\fR=\fI<dev>\fR
-Use device <dev> as the vbi device. If <dev> is a number, then /dev/vbi<dev> is used.
+\fB\-S\fR, \fB\-\-sdr-device\fR=\fI<dev>\fR
+Use device <dev> as the SDR device. If <dev> is a number, then /dev/swradio<dev> is used.
 .TP
 \fB\-R\fR, \fB\-\-raw\fR
 Open device in raw mode, i.e. without using the libv4l2 wrapper functions.
index 7d03c4a..d54a5e2 100644 (file)
@@ -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 <dev>] [-r <dev>] [-V <dev>]\n"
               "\n  -d, --device=<dev> use device <dev> as the video device\n"
               "                     if <dev> is a number, then /dev/video<dev> is used\n"
-              "  -r, --radio-device=<dev> use device <dev> as the radio device\n"
-              "                     if <dev> is a number, then /dev/radio<dev> is used\n"
               "  -V, --vbi-device=<dev> use device <dev> as the vbi device\n"
               "                     if <dev> is a number, then /dev/vbi<dev> is used\n"
+              "  -r, --radio-device=<dev> use device <dev> as the radio device\n"
+              "                     if <dev> is a number, then /dev/radio<dev> is used\n"
+              "  -S, --sdr-device=<dev> use device <dev> as the SDR device\n"
+              "                     if <dev> is a number, then /dev/swradio<dev> 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";
 
index 8baf4af..894a8c1 100644 (file)
@@ -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;
 }
index 1a0d066..ad3f15c 100644 (file)
@@ -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);