1 /* qv4l2: a control panel controlling v4l2 devices.
2 *
3 * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20
21 #include "general-tab.h"
22 #include "../libv4l2util/libv4l2util.h"
23
24 #include <QSpinBox>
25 #include <QSlider>
26 #include <QComboBox>
27 #include <QPushButton>
28 #include <QToolButton>
29 #include <QLineEdit>
30 #include <QDoubleValidator>
31
32 #include <math.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include <QRegExp>
36
37 bool GeneralTab::m_fullAudioName = false;
38
39 enum audioDeviceAdd {
40 AUDIO_ADD_NO,
41 AUDIO_ADD_READ,
42 AUDIO_ADD_WRITE,
43 AUDIO_ADD_READWRITE
44 };
45
pixfmt2s(unsigned id)46 static QString pixfmt2s(unsigned id)
47 {
48 QString pixfmt;
49
50 pixfmt += (char)(id & 0x7f);
51 pixfmt += (char)((id >> 8) & 0x7f);
52 pixfmt += (char)((id >> 16) & 0x7f);
53 pixfmt += (char)((id >> 24) & 0x7f);
54 if (id & (1U << 31))
55 pixfmt += "-BE";
56 return pixfmt;
57 }
58
GeneralTab(const QString & device,cv4l_fd * fd,int n,QWidget * parent)59 GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *parent) :
60 QGridLayout(parent),
61 m_fd(fd),
62 m_row(0),
63 m_col(0),
64 m_cols(n),
65 m_minWidth(175),
66 m_pxw(25.0),
67 m_vMargin(10),
68 m_hMargin(20),
69 m_isRadio(false),
70 m_isSDR(false),
71 m_isTouch(false),
72 m_isVbi(false),
73 m_isOutput(false),
74 m_isSDTV(false),
75 m_freqFac(16),
76 m_freqRfFac(16),
77 m_isPlanar(false),
78 m_haveBuffers(false),
79 m_discreteSizes(false),
80 m_videoInput(NULL),
81 m_videoOutput(NULL),
82 m_audioInput(NULL),
83 m_audioOutput(NULL),
84 m_tvStandard(NULL),
85 m_qryStandard(NULL),
86 m_videoTimings(NULL),
87 m_pixelAspectRatio(NULL),
88 m_colorspace(NULL),
89 m_xferFunc(NULL),
90 m_ycbcrEnc(NULL),
91 m_quantRange(NULL),
92 m_cropping(NULL),
93 m_qryTimings(NULL),
94 m_freq(NULL),
95 m_freqTable(NULL),
96 m_freqChannel(NULL),
97 m_audioMode(NULL),
98 m_subchannels(NULL),
99 m_freqRf(NULL),
100 m_stereoMode(NULL),
101 m_rdsMode(NULL),
102 m_detectSubchans(NULL),
103 m_vidCapFormats(NULL),
104 m_vidFields(NULL),
105 m_frameSize(NULL),
106 m_frameWidth(NULL),
107 m_frameHeight(NULL),
108 m_frameInterval(NULL),
109 m_vidOutFormats(NULL),
110 m_capMethods(NULL),
111 m_numBuffers(NULL),
112 m_vbiMethods(NULL),
113 m_audioInDevice(NULL),
114 m_audioOutDevice(NULL),
115 m_cropWidth(NULL),
116 m_cropLeft(NULL),
117 m_cropHeight(NULL),
118 m_cropTop(NULL),
119 m_composeWidth(NULL),
120 m_composeLeft(NULL),
121 m_composeHeight(NULL),
122 m_composeTop(NULL)
123 {
124 bool hasStreamIO = false;
125
126 m_device.append(device);
127 setSizeConstraint(QLayout::SetMinimumSize);
128
129 for (int i = 0; i < n; i++) {
130 m_maxw[i] = 0;
131 }
132
133 querycap(m_querycap);
134
135 addTitle("General Information");
136
137 addLabel("Device");
138 addLabel(device + (m_fd->g_direct() ? "" : " (wrapped)"));
139
140 addLabel("Driver");
141 addLabel((char *)m_querycap.driver);
142
143 addLabel("Card");
144 addLabel((char *)m_querycap.card);
145
146 addLabel("Bus");
147 addLabel((char *)m_querycap.bus_info);
148
149 g_tuner(m_tuner);
150 g_tuner(m_tuner_rf, 1);
151 g_modulator(m_modulator);
152
153 v4l2_input vin;
154 v4l2_output vout;
155 v4l2_audio vaudio;
156 v4l2_audioout vaudout;
157 v4l2_fmtdesc fmt;
158
159 if (m_tuner.capability &&
160 (m_tuner.capability & (V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_1HZ)))
161 m_isRadio = true;
162 if (m_modulator.capability &&
163 (m_modulator.capability & (V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_1HZ)))
164 m_isRadio = true;
165 if (m_querycap.capabilities & V4L2_CAP_DEVICE_CAPS) {
166 m_isVbi = g_caps() & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE |
167 V4L2_CAP_VBI_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT);
168 m_isSDR = g_caps() & V4L2_CAP_SDR_CAPTURE;
169 m_isTouch = g_caps() & V4L2_CAP_TOUCH;
170 if (m_isSDR)
171 m_isRadio = true;
172 if (g_caps() & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE |
173 V4L2_CAP_META_OUTPUT |
174 V4L2_CAP_VBI_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT))
175 m_isOutput = true;
176 }
177
178 if (m_querycap.capabilities &
179 (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE))
180 m_isPlanar = true;
181
182 m_stackedStandards = new QStackedWidget(parent);
183 m_stackedFrameSettings = new QStackedWidget(parent);
184 m_stackedFrequency = new QStackedWidget(parent);
185
186 if (!enum_input(vin, true) || m_tuner.capability) {
187 addTitle("Input Settings");
188 inputSection(vin);
189 }
190
191 if (m_modulator.capability || (!isRadio() && !enum_output(vout, true))) {
192 addTitle("Output Settings");
193 outputSection(vout);
194 }
195
196 if (hasAlsaAudio()) {
197 m_audioInDevice = new QComboBox(parent);
198 m_audioOutDevice = new QComboBox(parent);
199 }
200
201 if (!isVbi() && !isTouch() && (createAudioDeviceList() || (!isRadio() && !enum_audio(vaudio, true)) ||
202 (!isSDR() && m_tuner.capability) || (!isRadio() && !enum_audout(vaudout, true)))) {
203 addTitle("Audio Settings");
204 audioSection(vaudio, vaudout);
205 }
206
207 if (hasAlsaAudio() && !createAudioDeviceList())
208 {
209 delete m_audioInDevice;
210 delete m_audioOutDevice;
211 m_audioInDevice = NULL;
212 m_audioOutDevice = NULL;
213 }
214
215 if (!isSDR() && isRadio())
216 goto done;
217
218 addTitle("Format Settings");
219 if (isVbi()) {
220 addLabel("VBI Streaming Method");
221 m_vbiMethods = new QComboBox(parentWidget());
222 if (has_raw_vbi_cap() || has_raw_vbi_out())
223 m_vbiMethods->addItem("Raw");
224 if (has_sliced_vbi_cap() || has_sliced_vbi_out())
225 m_vbiMethods->addItem("Sliced");
226 addWidget(m_vbiMethods);
227 connect(m_vbiMethods, SIGNAL(activated(int)), SLOT(vbiMethodsChanged(int)));
228 vbiMethodsChanged(0);
229 if (m_isOutput)
230 updateVideoOutput();
231 else
232 updateVideoInput();
233 } else if (!isSDR()) {
234 formatSection(fmt);
235 }
236
237 addLabel("Streaming Method");
238 m_capMethods = new QComboBox(parent);
239 if (has_streaming()) {
240 cv4l_queue q;
241
242 // Yuck. The videobuf framework does not accept a reqbufs count of 0.
243 // This is out-of-spec, but it means that the only way to test which
244 // method is supported is to give it a non-zero count. But after that
245 // we have to reopen the device to clear the fact that there were
246 // buffers allocated. This is the only really portable way as long
247 // as there are still drivers around that do not support reqbufs(0).
248 q.init(g_type(), V4L2_MEMORY_USERPTR);
249 if (q.reqbufs(m_fd, 1) == 0) {
250 m_capMethods->addItem("User pointer I/O", QVariant(methodUser));
251 m_fd->reopen(true);
252 hasStreamIO = true;
253 }
254 q.init(g_type(), V4L2_MEMORY_MMAP);
255 if (q.reqbufs(m_fd, 1) == 0) {
256 m_capMethods->addItem("Memory mapped I/O", QVariant(methodMmap));
257 m_fd->reopen(true);
258 hasStreamIO = true;
259 }
260 }
261 if (has_rw()) {
262 if (v4l_type_is_output(g_type()))
263 m_capMethods->addItem("write()", QVariant(methodRead));
264 else
265 m_capMethods->addItem("read()", QVariant(methodRead));
266 }
267 addWidget(m_capMethods);
268
269 if (hasStreamIO) {
270 addLabel("Number of Buffers");
271 m_numBuffers = new QSpinBox(parent);
272 m_numBuffers->setRange(1, VIDEO_MAX_FRAME);
273 m_numBuffers->setValue(4);
274 addWidget(m_numBuffers);
275 }
276
277 addLabel("Use Record Priority");
278 m_recordPrio = new QCheckBox(parentWidget());
279 addWidget(m_recordPrio);
280
281 if (!isRadio() && !isVbi() && !isTouch() && (has_crop() || has_compose())) {
282 addTitle("Cropping & Compose Settings");
283 cropSection();
284 }
285
286 if (!isSDR()) {
287 if (m_isOutput)
288 updateVideoOutput();
289 else
290 updateVideoInput();
291 updateVidFormat();
292 }
293
294 done:
295 QGridLayout::addWidget(new QWidget(parent), rowCount(), 0, 1, n);
296 setRowStretch(rowCount() - 1, 1);
297 if (m_videoInput)
298 updateGUIInput(m_videoInput->currentIndex());
299 else if (m_videoOutput)
300 updateGUIOutput(m_videoOutput->currentIndex());
301 fixWidth();
302 }
303
sourceChangeSubscribe()304 void GeneralTab::sourceChangeSubscribe()
305 {
306 v4l2_input vin;
307
308 if (!enum_input(vin, true)) {
309 do {
310 struct v4l2_event_subscription sub = {
311 V4L2_EVENT_SOURCE_CHANGE, vin.index
312 };
313
314 subscribe_event(sub);
315 } while (!enum_input(vin));
316 }
317 }
318
inputSection(v4l2_input vin)319 void GeneralTab::inputSection(v4l2_input vin)
320 {
321 bool needsStd = false;
322 bool needsTimings = false;
323
324 if (!isRadio() && !enum_input(vin, true)) {
325 addLabel("Input");
326 m_videoInput = new QComboBox(parentWidget());
327 do {
328 m_videoInput->addItem((char *)vin.name);
329 if (vin.capabilities & V4L2_IN_CAP_STD)
330 needsStd = true;
331 if (vin.capabilities & V4L2_IN_CAP_DV_TIMINGS)
332 needsTimings = true;
333 } while (!enum_input(vin));
334 addWidget(m_videoInput);
335 connect(m_videoInput, SIGNAL(activated(int)), SLOT(inputChanged(int)));
336 m_row++;
337 m_col = 0;
338 }
339
340 QWidget *wStd = new QWidget();
341 QGridLayout *m_stdRow = new QGridLayout(wStd);
342 m_grids.append(m_stdRow);
343
344 if (needsStd) {
345 v4l2_std_id tmp;
346
347 m_tvStandard = new QComboBox(parentWidget());
348 m_stdRow->addWidget(new QLabel("TV Standard", parentWidget()), 0, 0, Qt::AlignLeft);
349 m_stdRow->addWidget(m_tvStandard, 0, 1, Qt::AlignLeft);
350 connect(m_tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int)));
351 refreshStandards();
352 m_isSDTV = true;
353 if (query_std(tmp) != ENOTTY) {
354 m_qryStandard = new QToolButton(parentWidget());
355 m_qryStandard->setIcon(QIcon(":/enterbutt.png"));
356 m_stdRow->addWidget(new QLabel("Query Standard", parentWidget()), 0, 2, Qt::AlignLeft);
357 m_stdRow->addWidget(m_qryStandard, 0, 3, Qt::AlignLeft);
358 connect(m_qryStandard, SIGNAL(clicked()), SLOT(qryStdClicked()));
359 }
360 }
361
362 QWidget *wTim = new QWidget();
363 QGridLayout *m_timRow = new QGridLayout(wTim);
364 m_grids.append(m_timRow);
365
366 if (needsTimings) {
367 m_videoTimings = new QComboBox(parentWidget());
368 m_timRow->addWidget(new QLabel("Video Timings", parentWidget()), 0, 0, Qt::AlignLeft);
369 m_timRow->addWidget(m_videoTimings, 0, 1, Qt::AlignLeft);
370 connect(m_videoTimings, SIGNAL(activated(int)), SLOT(timingsChanged(int)));
371 refreshTimings();
372 m_qryTimings = new QToolButton(parentWidget());
373 m_qryTimings->setIcon(QIcon(":/enterbutt.png"));
374 m_timRow->addWidget(new QLabel("Query Timings", parentWidget()), 0, 2, Qt::AlignLeft);
375 m_timRow->addWidget(m_qryTimings, 0, 3, Qt::AlignLeft);
376 connect(m_qryTimings, SIGNAL(clicked()), SLOT(qryTimingsClicked()));
377 }
378
379 m_stackedStandards->addWidget(wStd);
380 m_stackedStandards->addWidget(wTim);
381 QGridLayout::addWidget(m_stackedStandards, m_row, 0, 1, m_cols, Qt::AlignVCenter);
382 m_row++;
383
384 QWidget *wFreq = new QWidget();
385 QGridLayout *m_freqRows = new QGridLayout(wFreq);
386 m_grids.append(m_freqRows);
387
388 if (m_tuner.capability) {
389 const char *unit = (m_tuner.capability & V4L2_TUNER_CAP_LOW) ? " kHz" :
390 (m_tuner.capability & V4L2_TUNER_CAP_1HZ ? " Hz" : " MHz");
391 const char *name = m_isSDR ? "ADC Frequency" : "Frequency";
392
393 m_freqFac = (m_tuner.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16;
394 m_freq = new QDoubleSpinBox(parentWidget());
395 m_freq->setMinimum(m_tuner.rangelow / m_freqFac);
396 m_freq->setMaximum(m_tuner.rangehigh / m_freqFac);
397 m_freq->setSingleStep(1.0 / m_freqFac);
398 m_freq->setSuffix(unit);
399 m_freq->setDecimals((m_tuner.capability & V4L2_TUNER_CAP_1HZ) ? 0 : 4);
400 m_freq->setWhatsThis(QString("%1\nLow: %2 %4\nHigh: %3 %4")
401 .arg(name)
402 .arg((double)m_tuner.rangelow / m_freqFac, 0, 'f', 2)
403 .arg((double)m_tuner.rangehigh / m_freqFac, 0, 'f', 2)
404 .arg(unit));
405 m_freq->setStatusTip(m_freq->whatsThis());
406 connect(m_freq, SIGNAL(valueChanged(double)), SLOT(freqChanged(double)));
407 updateFreq();
408 m_freqRows->addWidget(new QLabel(name, parentWidget()), 0, 0, Qt::AlignLeft);
409 m_freqRows->addWidget(m_freq, 0, 1, Qt::AlignLeft);
410 }
411
412 if (m_tuner_rf.capability) {
413 const char *unit = (m_tuner_rf.capability & V4L2_TUNER_CAP_LOW) ? " kHz" :
414 (m_tuner_rf.capability & V4L2_TUNER_CAP_1HZ ? " Hz" : " MHz");
415
416 m_freqRfFac = (m_tuner_rf.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16;
417 m_freqRf = new QDoubleSpinBox(parentWidget());
418 m_freqRf->setMinimum(m_tuner_rf.rangelow / m_freqRfFac);
419 m_freqRf->setMaximum(m_tuner_rf.rangehigh / m_freqRfFac);
420 m_freqRf->setSingleStep(1.0 / m_freqRfFac);
421 m_freqRf->setSuffix(unit);
422 m_freqRf->setDecimals((m_tuner_rf.capability & V4L2_TUNER_CAP_1HZ) ? 0 : 4);
423 m_freqRf->setWhatsThis(QString("RF Frequency\nLow: %1 %3\nHigh: %2 %3")
424 .arg((double)m_tuner_rf.rangelow / m_freqRfFac, 0, 'f', 2)
425 .arg((double)m_tuner_rf.rangehigh / m_freqRfFac, 0, 'f', 2)
426 .arg(unit));
427 m_freqRf->setStatusTip(m_freqRf->whatsThis());
428 connect(m_freqRf, SIGNAL(valueChanged(double)), SLOT(freqRfChanged(double)));
429 updateFreqRf();
430 m_freqRows->addWidget(new QLabel("RF Frequency", parentWidget()), 0, 2, Qt::AlignLeft);
431 m_freqRows->addWidget(m_freqRf, 0, 3, Qt::AlignLeft);
432 } else if (!isSDR()) {
433 QLabel *l = new QLabel("Refresh Tuner Status", parentWidget());
434 QWidget *w = new QWidget(parentWidget());
435 QHBoxLayout *box = new QHBoxLayout(w);
436 #if QT_VERSION < 0x060000
437 box->setMargin(0);
438 #else
439 box->setContentsMargins(0, 0, 0, 0);
440 #endif
441 m_detectSubchans = new QToolButton(w);
442 m_detectSubchans->setIcon(QIcon(":/enterbutt.png"));
443 m_subchannels = new QLabel("", w);
444 box->addWidget(m_detectSubchans, 0, Qt::AlignLeft);
445 box->addWidget(m_subchannels, 0, Qt::AlignLeft);
446 m_freqRows->addWidget(l, 0, 2, Qt::AlignLeft);
447 m_freqRows->addWidget(w, 0, 3, Qt::AlignLeft);
448 connect(m_detectSubchans, SIGNAL(clicked()), SLOT(detectSubchansClicked()));
449 detectSubchansClicked();
450 }
451
452 if (m_tuner.capability && !isRadio()) {
453 m_freqTable = new QComboBox(parentWidget());
454 for (int i = 0; v4l2_channel_lists[i].name; i++) {
455 m_freqTable->addItem(v4l2_channel_lists[i].name);
456 }
457 m_freqRows->addWidget(new QLabel("Frequency Table", parentWidget()), 1, 0, Qt::AlignLeft);
458 m_freqRows->addWidget(m_freqTable, 1, 1, Qt::AlignLeft);
459 connect(m_freqTable, SIGNAL(activated(int)), SLOT(freqTableChanged(int)));
460
461 m_freqChannel = new QComboBox(parentWidget());
462 m_freqRows->addWidget(new QLabel("Channels", parentWidget()), 1, 2, Qt::AlignLeft);
463 m_freqRows->addWidget(m_freqChannel, 1, 3, Qt::AlignLeft);
464 connect(m_freqChannel, SIGNAL(activated(int)), SLOT(freqChannelChanged(int)));
465 updateFreqChannel();
466 }
467
468 m_stackedFrequency->addWidget(wFreq);
469 QGridLayout::addWidget(m_stackedFrequency, m_row, 0, 2, m_cols, Qt::AlignVCenter);
470 m_row += 2;
471
472 if (isRadio() || isVbi())
473 return;
474
475 QWidget *wFrameWH = new QWidget();
476 QWidget *wFrameSR = new QWidget();
477 QGridLayout *m_wh = new QGridLayout(wFrameWH);
478 QGridLayout *m_sr = new QGridLayout(wFrameSR);
479 m_grids.append(m_wh);
480 m_grids.append(m_sr);
481
482 m_wh->addWidget(new QLabel("Frame Width", parentWidget()), 0, 0, Qt::AlignLeft);
483 m_frameWidth = new QSpinBox(parentWidget());
484 m_wh->addWidget(m_frameWidth, 0, 1, Qt::AlignLeft);
485 connect(m_frameWidth, SIGNAL(editingFinished()), SLOT(frameWidthChanged()));
486
487 m_wh->addWidget(new QLabel("Frame Height", parentWidget()), 0, 2, Qt::AlignLeft);
488 m_frameHeight = new QSpinBox(parentWidget());
489 m_wh->addWidget(m_frameHeight, 0, 3, Qt::AlignLeft);
490 connect(m_frameHeight, SIGNAL(editingFinished()), SLOT(frameHeightChanged()));
491
492 m_sr->addWidget(new QLabel("Frame Size", parentWidget()), 0, 0, Qt::AlignLeft);
493 m_frameSize = new QComboBox(parentWidget());
494 m_sr->addWidget(m_frameSize, 0, 1, Qt::AlignLeft);
495 connect(m_frameSize, SIGNAL(activated(int)), SLOT(frameSizeChanged(int)));
496
497 m_sr->addWidget(new QLabel("Frame Rate", parentWidget()), 0, 2, Qt::AlignLeft);
498 m_frameInterval = new QComboBox(parentWidget());
499 m_sr->addWidget(m_frameInterval, 0, 3, Qt::AlignLeft);
500 connect(m_frameInterval, SIGNAL(activated(int)), SLOT(frameIntervalChanged(int)));
501
502 m_stackedFrameSettings->addWidget(wFrameWH);
503 m_stackedFrameSettings->addWidget(wFrameSR);
504
505 QGridLayout::addWidget(m_stackedFrameSettings, m_row, 0, 1, m_cols, Qt::AlignVCenter);
506 m_row++;
507 }
508
outputSection(v4l2_output vout)509 void GeneralTab::outputSection(v4l2_output vout)
510 {
511 bool needsStd = false;
512 bool needsTimings = false;
513
514 if (!isRadio() && !enum_output(vout, true)) {
515 addLabel("Output");
516 m_videoOutput = new QComboBox(parentWidget());
517 do {
518 m_videoOutput->addItem((char *)vout.name);
519 if (vout.capabilities & V4L2_OUT_CAP_STD)
520 needsStd = true;
521 if (vout.capabilities & V4L2_OUT_CAP_DV_TIMINGS)
522 needsTimings = true;
523 } while (!enum_output(vout));
524 addWidget(m_videoOutput);
525 connect(m_videoOutput, SIGNAL(activated(int)), SLOT(outputChanged(int)));
526 updateVideoOutput();
527 m_row++;
528 m_col = 0;
529 }
530
531 QWidget *wStd = new QWidget();
532 QGridLayout *m_stdRow = new QGridLayout(wStd);
533 m_grids.append(m_stdRow);
534
535 if (needsStd) {
536 m_tvStandard = new QComboBox(parentWidget());
537 m_stdRow->addWidget(new QLabel("TV Standard", parentWidget()), 0, 0, Qt::AlignLeft);
538 m_stdRow->addWidget(m_tvStandard, 0, 1, Qt::AlignLeft);
539 connect(m_tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int)));
540 m_isSDTV = true;
541 refreshStandards();
542 }
543
544 QWidget *wTim = new QWidget();
545 QGridLayout *m_timRow = new QGridLayout(wTim);
546 m_grids.append(m_timRow);
547
548 if (needsTimings) {
549 m_videoTimings = new QComboBox(parentWidget());
550 m_timRow->addWidget(new QLabel("Video Timings", parentWidget()), 0, 0, Qt::AlignLeft);
551 m_timRow->addWidget(m_videoTimings, 0, 1, Qt::AlignLeft);
552 connect(m_videoTimings, SIGNAL(activated(int)), SLOT(timingsChanged(int)));
553 refreshTimings();
554 }
555
556 m_stackedStandards->addWidget(wStd);
557 m_stackedStandards->addWidget(wTim);
558 QGridLayout::addWidget(m_stackedStandards, m_row, 0, 1, m_cols, Qt::AlignVCenter);
559 m_row++;
560
561 if (m_modulator.capability) {
562 const char *unit = (m_modulator.capability & V4L2_TUNER_CAP_LOW) ? " kHz" :
563 (m_modulator.capability & V4L2_TUNER_CAP_1HZ ? " Hz" : " MHz");
564
565 m_freqFac = (m_modulator.capability & V4L2_TUNER_CAP_1HZ) ? 1 : 16;
566 m_freq = new QDoubleSpinBox(parentWidget());
567 m_freq->setMinimum(m_modulator.rangelow / m_freqFac);
568 m_freq->setMaximum(m_modulator.rangehigh / m_freqFac);
569 m_freq->setSingleStep(1.0 / m_freqFac);
570 m_freq->setSuffix(unit);
571 m_freq->setDecimals((m_modulator.capability & V4L2_TUNER_CAP_1HZ) ? 0 : 4);
572 m_freq->setWhatsThis(QString("Frequency\nLow: %1 %3\nHigh: %2 %3")
573 .arg((double)m_modulator.rangelow / m_freqFac, 0, 'f', 2)
574 .arg((double)m_modulator.rangehigh / m_freqFac, 0, 'f', 2)
575 .arg(unit));
576 m_freq->setStatusTip(m_freq->whatsThis());
577 connect(m_freq, SIGNAL(valueChanged(double)), SLOT(freqChanged(double)));
578 updateFreq();
579 addLabel("Frequency");
580 addWidget(m_freq);
581
582 if (m_modulator.capability & V4L2_TUNER_CAP_STEREO) {
583 addLabel("Stereo");
584 m_stereoMode = new QCheckBox(parentWidget());
585 m_stereoMode->setCheckState((m_modulator.txsubchans & V4L2_TUNER_SUB_STEREO) ?
586 Qt::Checked : Qt::Unchecked);
587 addWidget(m_stereoMode);
588 connect(m_stereoMode, SIGNAL(clicked()), SLOT(stereoModeChanged()));
589 }
590 if (m_modulator.capability & V4L2_TUNER_CAP_RDS) {
591 addLabel("RDS");
592 m_rdsMode = new QCheckBox(parentWidget());
593 m_rdsMode->setCheckState((m_modulator.txsubchans & V4L2_TUNER_SUB_RDS) ?
594 Qt::Checked : Qt::Unchecked);
595 addWidget(m_rdsMode);
596 connect(m_rdsMode, SIGNAL(clicked()), SLOT(rdsModeChanged()));
597 }
598 }
599
600 if (isRadio())
601 return;
602
603 QWidget *wFrameWH = new QWidget();
604 QGridLayout *m_wh = new QGridLayout(wFrameWH);
605 m_grids.append(m_wh);
606
607 m_wh->addWidget(new QLabel("Frame Width", parentWidget()), 0, 0, Qt::AlignLeft);
608 m_frameWidth = new QSpinBox(parentWidget());
609 m_wh->addWidget(m_frameWidth, 0, 1, Qt::AlignLeft);
610 connect(m_frameWidth, SIGNAL(editingFinished()), SLOT(frameWidthChanged()));
611
612 m_wh->addWidget(new QLabel("Frame Height", parentWidget()), 0, 2, Qt::AlignLeft);
613 m_frameHeight = new QSpinBox(parentWidget());
614 m_wh->addWidget(m_frameHeight, 0, 3, Qt::AlignLeft);
615 connect(m_frameHeight, SIGNAL(editingFinished()), SLOT(frameHeightChanged()));
616
617 m_stackedFrameSettings->addWidget(wFrameWH);
618
619 QGridLayout::addWidget(m_stackedFrameSettings, m_row, 0, 1, m_cols, Qt::AlignVCenter);
620 m_row++;
621 }
622
audioSection(v4l2_audio vaudio,v4l2_audioout vaudout)623 void GeneralTab::audioSection(v4l2_audio vaudio, v4l2_audioout vaudout)
624 {
625 if (hasAlsaAudio() && !m_isOutput) {
626 if (createAudioDeviceList()) {
627 addLabel("Audio Input Device");
628 connect(m_audioInDevice, SIGNAL(activated(int)), SLOT(changeAudioDevice()));
629 addWidget(m_audioInDevice);
630
631 addLabel("Audio Output Device");
632 connect(m_audioOutDevice, SIGNAL(activated(int)), SLOT(changeAudioDevice()));
633 addWidget(m_audioOutDevice);
634
635 if (isRadio()) {
636 setAudioDeviceBufferSize(75);
637 } else {
638 v4l2_fract fract;
639 if (m_fd->get_interval(fract)) {
640 // Default values are for 30 FPS
641 fract.numerator = 33;
642 fract.denominator = 1000;
643 }
644 // Standard capacity is two frames
645 setAudioDeviceBufferSize((fract.numerator * 2000) / fract.denominator);
646 }
647 } else {
648 delete m_audioInDevice;
649 delete m_audioOutDevice;
650 m_audioInDevice = NULL;
651 m_audioOutDevice = NULL;
652 }
653 }
654
655 if (!isRadio() && !enum_audio(vaudio, true)) {
656 addLabel("Input Audio");
657 m_audioInput = new QComboBox(parentWidget());
658 m_audioInput->setMinimumContentsLength(10);
659 do {
660 m_audioInput->addItem((char *)vaudio.name);
661 } while (!enum_audio(vaudio));
662 addWidget(m_audioInput);
663 connect(m_audioInput, SIGNAL(activated(int)), SLOT(inputAudioChanged(int)));
664 updateAudioInput();
665 }
666
667 if (m_tuner.capability && !isSDR()) {
668 addLabel("Audio Mode");
669 m_audioMode = new QComboBox(parentWidget());
670 m_audioMode->setMinimumContentsLength(12);
671 m_audioMode->addItem("Mono");
672 int audIdx = 0;
673 m_audioModes[audIdx++] = V4L2_TUNER_MODE_MONO;
674 if (m_tuner.capability & V4L2_TUNER_CAP_STEREO) {
675 m_audioMode->addItem("Stereo");
676 m_audioModes[audIdx++] = V4L2_TUNER_MODE_STEREO;
677 }
678 if (m_tuner.capability & V4L2_TUNER_CAP_LANG1) {
679 m_audioMode->addItem("Language 1");
680 m_audioModes[audIdx++] = V4L2_TUNER_MODE_LANG1;
681 }
682 if (m_tuner.capability & V4L2_TUNER_CAP_LANG2) {
683 m_audioMode->addItem("Language 2");
684 m_audioModes[audIdx++] = V4L2_TUNER_MODE_LANG2;
685 }
686 if ((m_tuner.capability & (V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2)) ==
687 (V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2)) {
688 m_audioMode->addItem("Language 1+2");
689 m_audioModes[audIdx++] = V4L2_TUNER_MODE_LANG1_LANG2;
690 }
691 addWidget(m_audioMode);
692 for (int i = 0; i < audIdx; i++)
693 if (m_audioModes[i] == m_tuner.audmode)
694 m_audioMode->setCurrentIndex(i);
695 connect(m_audioMode, SIGNAL(activated(int)), SLOT(audioModeChanged(int)));
696 }
697
698 if (!isRadio() && !enum_audout(vaudout, true)) {
699 addLabel("Output Audio");
700 m_audioOutput = new QComboBox(parentWidget());
701 m_audioOutput->setMinimumContentsLength(10);
702 do {
703 m_audioOutput->addItem((char *)vaudout.name);
704 } while (!enum_audout(vaudout));
705 addWidget(m_audioOutput);
706 connect(m_audioOutput, SIGNAL(activated(int)), SLOT(outputAudioChanged(int)));
707 updateAudioOutput();
708 }
709 }
710
formatSection(v4l2_fmtdesc fmt)711 void GeneralTab::formatSection(v4l2_fmtdesc fmt)
712 {
713 if (m_isOutput) {
714 addLabel("Output Image Formats");
715 m_vidOutFormats = new QComboBox(parentWidget());
716 m_vidOutFormats->setMinimumContentsLength(20);
717 if (!enum_fmt(fmt, true)) {
718 do {
719 m_vidOutFormats->addItem(pixfmt2s(fmt.pixelformat) +
720 " (" + (const char *)fmt.description + ")");
721 } while (!enum_fmt(fmt));
722 }
723 addWidget(m_vidOutFormats);
724 connect(m_vidOutFormats, SIGNAL(activated(int)), SLOT(vidOutFormatChanged(int)));
725 } else {
726 addLabel("Capture Image Formats");
727 m_vidCapFormats = new QComboBox(parentWidget());
728 m_vidCapFormats->setMinimumContentsLength(20);
729 if (!enum_fmt(fmt, true)) {
730 do {
731 QString s(pixfmt2s(fmt.pixelformat) + " (");
732
733 if (fmt.flags & V4L2_FMT_FLAG_EMULATED)
734 m_vidCapFormats->addItem(s + "Emulated)");
735 else
736 m_vidCapFormats->addItem(s + (const char *)fmt.description + ")");
737 } while (!enum_fmt(fmt));
738 }
739 addWidget(m_vidCapFormats);
740 connect(m_vidCapFormats, SIGNAL(activated(int)), SLOT(vidCapFormatChanged(int)));
741 }
742
743 addLabel("Field");
744 m_vidFields = new QComboBox(parentWidget());
745 m_vidFields->setMinimumContentsLength(21);
746 addWidget(m_vidFields);
747 connect(m_vidFields, SIGNAL(activated(int)), SLOT(vidFieldChanged(int)));
748
749 if (!isRadio() && !isVbi()) {
750 m_colorspace = new QComboBox(parentWidget());
751 m_colorspace->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_COLORSPACE_DEFAULT));
752 m_colorspace->addItem("SMPTE 170M", QVariant(V4L2_COLORSPACE_SMPTE170M));
753 m_colorspace->addItem("Rec. 709", QVariant(V4L2_COLORSPACE_REC709));
754 m_colorspace->addItem("sRGB", QVariant(V4L2_COLORSPACE_SRGB));
755 m_colorspace->addItem("opRGB", QVariant(V4L2_COLORSPACE_OPRGB));
756 m_colorspace->addItem("BT.2020", QVariant(V4L2_COLORSPACE_BT2020));
757 m_colorspace->addItem("DCI-P3", QVariant(V4L2_COLORSPACE_DCI_P3));
758 m_colorspace->addItem("SMPTE 240M", QVariant(V4L2_COLORSPACE_SMPTE240M));
759 m_colorspace->addItem("470 System M", QVariant(V4L2_COLORSPACE_470_SYSTEM_M));
760 m_colorspace->addItem("470 System BG", QVariant(V4L2_COLORSPACE_470_SYSTEM_BG));
761 m_colorspace->addItem("JPEG", QVariant(V4L2_COLORSPACE_JPEG));
762 m_colorspace->addItem("Raw", QVariant(V4L2_COLORSPACE_RAW));
763
764 addLabel("Colorspace");
765 addWidget(m_colorspace);
766 connect(m_colorspace, SIGNAL(activated(int)), SLOT(colorspaceChanged(int)));
767
768 m_xferFunc = new QComboBox(parentWidget());
769 m_xferFunc->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_XFER_FUNC_DEFAULT));
770 m_xferFunc->addItem("Rec. 709", QVariant(V4L2_XFER_FUNC_709));
771 m_xferFunc->addItem("sRGB", QVariant(V4L2_XFER_FUNC_SRGB));
772 m_xferFunc->addItem("opRGB", QVariant(V4L2_XFER_FUNC_OPRGB));
773 m_xferFunc->addItem("DCI-P3", QVariant(V4L2_XFER_FUNC_DCI_P3));
774 m_xferFunc->addItem("SMPTE 2084", QVariant(V4L2_XFER_FUNC_SMPTE2084));
775 m_xferFunc->addItem("SMPTE 240M", QVariant(V4L2_XFER_FUNC_SMPTE240M));
776 m_xferFunc->addItem("None", QVariant(V4L2_XFER_FUNC_NONE));
777
778 addLabel("Transfer Function");
779 addWidget(m_xferFunc);
780 connect(m_xferFunc, SIGNAL(activated(int)), SLOT(xferFuncChanged(int)));
781
782 m_ycbcrEnc = new QComboBox(parentWidget());
783 m_ycbcrEnc->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_YCBCR_ENC_DEFAULT));
784 m_ycbcrEnc->addItem("ITU-R 601", QVariant(V4L2_YCBCR_ENC_601));
785 m_ycbcrEnc->addItem("Rec. 709", QVariant(V4L2_YCBCR_ENC_709));
786 m_ycbcrEnc->addItem("xvYCC 601", QVariant(V4L2_YCBCR_ENC_XV601));
787 m_ycbcrEnc->addItem("xvYCC 709", QVariant(V4L2_YCBCR_ENC_XV709));
788 m_ycbcrEnc->addItem("BT.2020", QVariant(V4L2_YCBCR_ENC_BT2020));
789 m_ycbcrEnc->addItem("BT.2020 Constant Luminance", QVariant(V4L2_YCBCR_ENC_BT2020_CONST_LUM));
790 m_ycbcrEnc->addItem("SMPTE 240M", QVariant(V4L2_YCBCR_ENC_SMPTE240M));
791 m_ycbcrEnc->addItem("HSV with Hue 0-179", QVariant(V4L2_HSV_ENC_180));
792 m_ycbcrEnc->addItem("HSV with Hue 0-255", QVariant(V4L2_HSV_ENC_256));
793
794 addLabel("Y'CbCr/HSV Encoding");
795 addWidget(m_ycbcrEnc);
796 connect(m_ycbcrEnc, SIGNAL(activated(int)), SLOT(ycbcrEncChanged(int)));
797
798 m_quantRange = new QComboBox(parentWidget());
799 m_quantRange->addItem(m_isOutput ? "Default" : "Autodetect", QVariant(V4L2_QUANTIZATION_DEFAULT));
800 m_quantRange->addItem("Full Range", QVariant(V4L2_QUANTIZATION_FULL_RANGE));
801 m_quantRange->addItem("Limited Range", QVariant(V4L2_QUANTIZATION_LIM_RANGE));
802
803 addLabel("Quantization");
804 addWidget(m_quantRange);
805 connect(m_quantRange, SIGNAL(activated(int)), SLOT(quantRangeChanged(int)));
806 }
807
808 if (m_isOutput || isTouch())
809 return;
810
811 m_cropping = new QComboBox(parentWidget());
812 m_cropping->addItem("Source Width and Height");
813 m_cropping->addItem("Crop Top and Bottom Line");
814 m_cropping->addItem("Traditional 4:3");
815 m_cropping->addItem("Widescreen 14:9");
816 m_cropping->addItem("Widescreen 16:9");
817 m_cropping->addItem("Cinema 1.85:1");
818 m_cropping->addItem("Cinema 2.39:1");
819
820 addLabel("Video Aspect Ratio");
821 addWidget(m_cropping);
822 connect(m_cropping, SIGNAL(activated(int)), SIGNAL(croppingChanged()));
823
824 if (!isRadio() && !isVbi()) {
825 m_pixelAspectRatio = new QComboBox(parentWidget());
826 m_pixelAspectRatio->addItem("Autodetect");
827 m_pixelAspectRatio->addItem("Square");
828 m_pixelAspectRatio->addItem("NTSC/PAL-M/PAL-60");
829 m_pixelAspectRatio->addItem("NTSC/PAL-M/PAL-60, Anamorphic");
830 m_pixelAspectRatio->addItem("PAL/SECAM");
831 m_pixelAspectRatio->addItem("PAL/SECAM, Anamorphic");
832
833 // Update hints by calling a get
834 getPixelAspectRatio();
835
836 addLabel("Pixel Aspect Ratio");
837 addWidget(m_pixelAspectRatio);
838 connect(m_pixelAspectRatio, SIGNAL(activated(int)), SLOT(changePixelAspectRatio()));
839 }
840 }
841
cropSection()842 void GeneralTab::cropSection()
843 {
844 if (has_crop()) {
845 m_cropWidth = new QSlider(Qt::Horizontal, parentWidget());
846 m_cropWidth->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
847 m_cropWidth->setRange(1, 100);
848 m_cropWidth->setSliderPosition(100);
849 addLabel("Crop Width");
850 addWidget(m_cropWidth);
851 connect(m_cropWidth, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
852
853 m_cropLeft = new QSlider(Qt::Horizontal, parentWidget());
854 m_cropLeft->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
855 m_cropLeft->setRange(0, 100);
856 m_cropLeft->setSliderPosition(0);
857 addLabel("Crop Left Offset");
858 addWidget(m_cropLeft);
859 connect(m_cropLeft, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
860
861 m_cropHeight = new QSlider(Qt::Horizontal, parentWidget());
862 m_cropHeight->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
863 m_cropHeight->setRange(1, 100);
864 m_cropHeight->setSliderPosition(100);
865 addLabel("Crop Height");
866 addWidget(m_cropHeight);
867 connect(m_cropHeight, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
868
869 m_cropTop = new QSlider(Qt::Horizontal, parentWidget());
870 m_cropTop->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
871 m_cropTop->setRange(0, 100);
872 m_cropTop->setSliderPosition(0);
873 addLabel("Crop Top Offset");
874 addWidget(m_cropTop);
875 connect(m_cropTop, SIGNAL(valueChanged(int)), SLOT(cropChanged()));
876 }
877
878 if (has_compose()) {
879 m_composeWidth = new QSlider(Qt::Horizontal, parentWidget());
880 m_composeWidth->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
881 m_composeWidth->setRange(1, 100);
882 m_composeWidth->setSliderPosition(100);
883 addLabel("Compose Width");
884 addWidget(m_composeWidth);
885 connect(m_composeWidth, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
886
887 m_composeLeft = new QSlider(Qt::Horizontal, parentWidget());
888 m_composeLeft->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
889 m_composeLeft->setRange(0, 100);
890 m_composeLeft->setSliderPosition(0);
891 addLabel("Compose Left Offset");
892 addWidget(m_composeLeft);
893 connect(m_composeLeft, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
894
895 m_composeHeight = new QSlider(Qt::Horizontal, parentWidget());
896 m_composeHeight->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
897 m_composeHeight->setRange(1, 100);
898 m_composeHeight->setSliderPosition(100);
899 addLabel("Compose Height");
900 addWidget(m_composeHeight);
901 connect(m_composeHeight, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
902
903 m_composeTop = new QSlider(Qt::Horizontal, parentWidget());
904 m_composeTop->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
905 m_composeTop->setRange(0, 100);
906 m_composeTop->setSliderPosition(0);
907 addLabel("Compose Top Offset");
908 addWidget(m_composeTop);
909 connect(m_composeTop, SIGNAL(valueChanged(int)), SLOT(composeChanged()));
910 }
911
912 }
913
fixWidth()914 void GeneralTab::fixWidth()
915 {
916 setContentsMargins(m_hMargin, m_vMargin, m_hMargin, m_vMargin);
917 setColumnStretch(3, 1);
918
919 QList<QWidget *> list = parentWidget()->findChildren<QWidget *>();
920 for (const auto &child : list) {
921 if (!qobject_cast<QComboBox *>(child) && !qobject_cast<QSpinBox *>(child) &&
922 !qobject_cast<QSlider *>(child))
923 continue;
924
925 if ((child->sizeHint().width()) > m_minWidth) {
926 m_increment = (int)ceil((child->sizeHint().width() - m_minWidth) / m_pxw);
927 child->setMinimumWidth(m_minWidth + m_increment * m_pxw); // for stepsize expansion of widgets
928 }
929 }
930
931 // fix width of subgrids
932 for (const auto &grid : m_grids) {
933 grid->setColumnStretch(3, 1);
934 grid->setContentsMargins(0, 0, 0, 0);
935 for (int n = 0; n < grid->count(); n++) {
936 if (grid->itemAt(n)->widget()->sizeHint().width() > m_maxw[n % 4]) {
937 m_maxw[n % 4] = grid->itemAt(n)->widget()->sizeHint().width();
938 }
939 if (n % 2) {
940 if (!qobject_cast<QToolButton *>(grid->itemAt(n)->widget()))
941 grid->itemAt(n)->widget()->setMinimumWidth(m_minWidth);
942 } else {
943 grid->itemAt(n)->widget()->setMinimumWidth(m_maxw[n % 4]);
944 }
945 }
946 for (int j = 0; j < m_cols; j++) {
947 if (j % 2)
948 grid->setColumnMinimumWidth(j, m_maxw[j] + m_pxw);
949 else
950 grid->setColumnMinimumWidth(j, m_maxw[j]);
951 }
952 }
953
954 for (int j = 0; j < m_cols; j++) {
955 if (j % 2)
956 setColumnMinimumWidth(j, m_maxw[j] + m_pxw);
957 else
958 setColumnMinimumWidth(j, m_maxw[j]);
959 }
960 }
961
getNumBuffers() const962 unsigned GeneralTab::getNumBuffers() const
963 {
964 return m_numBuffers ? m_numBuffers->value() : 4;
965 }
966
setHaveBuffers(bool haveBuffers)967 void GeneralTab::setHaveBuffers(bool haveBuffers)
968 {
969 m_haveBuffers = haveBuffers;
970
971 if (m_videoInput)
972 m_videoInput->setDisabled(haveBuffers);
973 if (m_videoOutput)
974 m_videoOutput->setDisabled(haveBuffers);
975 if (m_tvStandard)
976 m_tvStandard->setDisabled(haveBuffers);
977 if (m_videoTimings)
978 m_videoTimings->setDisabled(haveBuffers);
979 if (m_vidCapFormats)
980 m_vidCapFormats->setDisabled(haveBuffers);
981 if (m_vidFields)
982 m_vidFields->setDisabled(haveBuffers);
983 if (m_frameSize && m_discreteSizes)
984 m_frameSize->setDisabled(haveBuffers);
985 if (m_frameWidth && !m_discreteSizes)
986 m_frameWidth->setDisabled(haveBuffers);
987 if (m_frameHeight && !m_discreteSizes)
988 m_frameHeight->setDisabled(haveBuffers);
989 if (m_vidOutFormats)
990 m_vidOutFormats->setDisabled(haveBuffers);
991 if (m_capMethods)
992 m_capMethods->setDisabled(haveBuffers);
993 if (m_vbiMethods)
994 m_vbiMethods->setDisabled(haveBuffers);
995 }
996
filterAudioDevice(QString & deviceName)997 bool GeneralTab::filterAudioDevice(QString &deviceName)
998 {
999 // Only show hw devices
1000 return deviceName.contains("hw") && !deviceName.contains("plughw");
1001 }
1002
addAudioDevice(void * hint,int deviceNum)1003 int GeneralTab::addAudioDevice(void *hint, int deviceNum)
1004 {
1005 int added = 0;
1006 #ifdef HAVE_ALSA
1007 char *name;
1008 char *iotype;
1009 QString deviceName;
1010 QString listName;
1011 QStringList deviceType;
1012 iotype = snd_device_name_get_hint(hint, "IOID");
1013 name = snd_device_name_get_hint(hint, "NAME");
1014 deviceName.append(name);
1015
1016 snd_card_get_name(deviceNum, &name);
1017 listName.append(name);
1018
1019 deviceType = deviceName.split(":");
1020
1021 // Add device io capability to list name
1022 if (m_fullAudioName) {
1023 listName.append(" ");
1024
1025 // Makes the surround name more readable
1026 if (deviceName.contains("surround"))
1027 listName.append(QString("surround %1.%2")
1028 .arg(deviceType.value(0)[8]).arg(deviceType.value(0)[9]));
1029 else
1030 listName.append(deviceType.value(0));
1031
1032 } else if (!deviceType.value(0).contains("default")) {
1033 listName.append(" ").append(deviceType.value(0));
1034 }
1035
1036 // Add device number if it is not 0
1037 if (deviceName.contains("DEV=")) {
1038 int devNo;
1039 QStringList deviceNo = deviceName.split("DEV=");
1040 devNo = deviceNo.value(1).toInt();
1041 if (devNo)
1042 listName.append(QString(" %1").arg(devNo));
1043 }
1044
1045 if ((iotype == NULL || strncmp(iotype, "Input", 5) == 0) && filterAudioDevice(deviceName)) {
1046 m_audioInDeviceMap[m_audioInDevice->count()] = snd_device_name_get_hint(hint, "NAME");
1047 m_audioInDevice->addItem(listName);
1048 added += AUDIO_ADD_READ;
1049 }
1050
1051 if ((iotype == NULL || strncmp(iotype, "Output", 6) == 0) && filterAudioDevice(deviceName)) {
1052 m_audioOutDeviceMap[m_audioOutDevice->count()] = snd_device_name_get_hint(hint, "NAME");
1053 m_audioOutDevice->addItem(listName);
1054 added += AUDIO_ADD_WRITE;
1055 }
1056 #endif
1057 return added;
1058 }
1059
createAudioDeviceList()1060 bool GeneralTab::createAudioDeviceList()
1061 {
1062 #ifdef HAVE_ALSA
1063 if (m_audioInDevice == NULL || m_audioOutDevice == NULL || m_isOutput)
1064 return false;
1065
1066 m_audioInDevice->clear();
1067 m_audioOutDevice->clear();
1068 m_audioInDeviceMap.clear();
1069 m_audioOutDeviceMap.clear();
1070
1071 m_audioInDevice->addItem("None");
1072 m_audioOutDevice->addItem("Default");
1073 m_audioInDeviceMap[0] = "None";
1074 m_audioOutDeviceMap[0] = "default";
1075
1076 int deviceNum = -1;
1077 int audioDevices = 0;
1078 int matchDevice = matchAudioDevice();
1079 int indexDevice = -1;
1080 int indexCount = 0;
1081
1082 while (snd_card_next(&deviceNum) >= 0) {
1083 if (deviceNum == -1)
1084 break;
1085
1086 audioDevices++;
1087 if (deviceNum == matchDevice && indexDevice == -1)
1088 indexDevice = indexCount;
1089
1090 void **hint;
1091
1092 if (snd_device_name_hint(deviceNum, "pcm", &hint)) {
1093 audioDevices--;
1094 continue;
1095 }
1096 for (int i = 0; hint[i] != NULL; i++) {
1097 int addAs = addAudioDevice(hint[i], deviceNum);
1098 if (addAs == AUDIO_ADD_READ || addAs == AUDIO_ADD_READWRITE)
1099 indexCount++;
1100 }
1101 snd_device_name_free_hint(hint);
1102 }
1103
1104 snd_config_update_free_global();
1105 m_audioInDevice->setCurrentIndex(indexDevice + 1);
1106 changeAudioDevice();
1107 return m_audioInDeviceMap.size() > 1 && m_audioOutDeviceMap.size() > 1 && audioDevices > 1;
1108 #else
1109 return false;
1110 #endif
1111 }
1112
changeAudioDevice()1113 void GeneralTab::changeAudioDevice()
1114 {
1115 m_audioOutDevice->setEnabled(getAudioInDevice() != nullptr ? getAudioInDevice().compare("None") : false);
1116 emit audioDeviceChanged();
1117 }
1118
addWidget(QWidget * w,Qt::Alignment align)1119 void GeneralTab::addWidget(QWidget *w, Qt::Alignment align)
1120 {
1121 if (m_col % 2 && !qobject_cast<QToolButton*>(w))
1122 w->setMinimumWidth(m_minWidth);
1123 if (w->sizeHint().width() > m_maxw[m_col])
1124 m_maxw[m_col] = w->sizeHint().width();
1125 QGridLayout::addWidget(w, m_row, m_col, align | Qt::AlignVCenter);
1126 m_col++;
1127 if (m_col == m_cols) {
1128 m_col = 0;
1129 m_row++;
1130 }
1131 }
1132
addTitle(const QString & titlename)1133 void GeneralTab::addTitle(const QString &titlename)
1134 {
1135 m_row++;
1136 QLabel *title_info = new QLabel(titlename, parentWidget());
1137 QFont f = title_info->font();
1138 f.setBold(true);
1139 title_info->setFont(f);
1140
1141 QGridLayout::addWidget(title_info, m_row, 0, 1, m_cols, Qt::AlignLeft);
1142 setRowMinimumHeight(m_row, 25);
1143 m_row++;
1144
1145 QFrame *m_line = new QFrame(parentWidget());
1146 m_line->setFrameShape(QFrame::HLine);
1147 m_line->setFrameShadow(QFrame::Sunken);
1148 QGridLayout::addWidget(m_line, m_row, 0, 1, m_cols, Qt::AlignVCenter);
1149 m_row++;
1150 m_col = 0;
1151 }
1152
getWidth()1153 int GeneralTab::getWidth()
1154 {
1155 int total = 0;
1156 for (int i = 0; i < m_cols; i++) {
1157 total += m_maxw[i] + m_pxw;
1158 }
1159 return total;
1160 }
1161
isSlicedVbi() const1162 bool GeneralTab::isSlicedVbi() const
1163 {
1164 return m_vbiMethods && m_vbiMethods->currentText() == "Sliced";
1165 }
1166
capMethod()1167 CapMethod GeneralTab::capMethod()
1168 {
1169 return (CapMethod)m_capMethods->itemData(m_capMethods->currentIndex()).toInt();
1170 }
1171
updateGUIInput(__u32 input)1172 void GeneralTab::updateGUIInput(__u32 input)
1173 {
1174 v4l2_input in;
1175 enum_input(in, true, input);
1176 if (g_input(input) || m_isRadio) {
1177 m_stackedFrameSettings->hide();
1178 return;
1179 }
1180
1181 if ((in.capabilities & V4L2_IN_CAP_STD) && in.type == V4L2_INPUT_TYPE_TUNER) {
1182 m_stackedFrameSettings->setCurrentIndex(0);
1183 m_stackedFrameSettings->show();
1184 m_stackedStandards->setCurrentIndex(0);
1185 m_stackedStandards->show();
1186 m_stackedFrequency->setCurrentIndex(0);
1187 m_stackedFrequency->show();
1188 } else if (in.capabilities & V4L2_IN_CAP_STD) {
1189 m_stackedFrameSettings->setCurrentIndex(0);
1190 m_stackedFrameSettings->show();
1191 m_stackedStandards->setCurrentIndex(0);
1192 m_stackedStandards->show();
1193 m_stackedFrequency->hide();
1194 } else if (in.capabilities & V4L2_IN_CAP_DV_TIMINGS) {
1195 m_stackedFrameSettings->setCurrentIndex(0);
1196 m_stackedFrameSettings->show();
1197 m_stackedStandards->setCurrentIndex(1);
1198 m_stackedStandards->show();
1199 m_stackedFrequency->hide();
1200 } else {
1201 m_stackedFrameSettings->setCurrentIndex(1);
1202 m_stackedFrameSettings->show();
1203 m_stackedStandards->hide();
1204 m_stackedFrequency->hide();
1205 }
1206
1207 if (isVbi() || isTouch()) {
1208 m_stackedFrameSettings->hide();
1209 }
1210 }
1211
updateGUIOutput(__u32 output)1212 void GeneralTab::updateGUIOutput(__u32 output)
1213 {
1214 v4l2_output out;
1215 enum_output(out, true, output);
1216 if (g_output(output) || m_isRadio) {
1217 m_stackedFrameSettings->hide();
1218 return;
1219 }
1220
1221 if (out.capabilities & V4L2_OUT_CAP_STD) {
1222 m_stackedFrameSettings->setCurrentIndex(0);
1223 m_stackedFrameSettings->show();
1224 m_stackedStandards->setCurrentIndex(0);
1225 m_stackedStandards->show();
1226 m_stackedFrequency->hide();
1227 } else if (out.capabilities & V4L2_OUT_CAP_DV_TIMINGS) {
1228 m_stackedFrameSettings->setCurrentIndex(0);
1229 m_stackedFrameSettings->show();
1230 m_stackedStandards->setCurrentIndex(1);
1231 m_stackedStandards->show();
1232 m_stackedFrequency->hide();
1233 } else {
1234 m_stackedFrameSettings->setCurrentIndex(1);
1235 m_stackedFrameSettings->show();
1236 m_stackedStandards->hide();
1237 m_stackedFrequency->hide();
1238 }
1239
1240 if (isVbi()) {
1241 m_stackedFrameSettings->hide();
1242 }
1243 }
1244
inputChanged(int input)1245 void GeneralTab::inputChanged(int input)
1246 {
1247 s_input((__u32)input);
1248
1249 if (m_audioInput)
1250 updateAudioInput();
1251
1252 updateVideoInput();
1253 updateVidCapFormat();
1254 updateGUIInput(input);
1255 }
1256
outputChanged(int output)1257 void GeneralTab::outputChanged(int output)
1258 {
1259 s_output((__u32)output);
1260
1261 if (m_audioOutput)
1262 updateAudioOutput();
1263
1264 updateVideoOutput();
1265 updateVidOutFormat();
1266 updateGUIOutput(output);
1267 }
1268
inputAudioChanged(int input)1269 void GeneralTab::inputAudioChanged(int input)
1270 {
1271 s_audio((__u32)input);
1272 updateAudioInput();
1273 }
1274
outputAudioChanged(int output)1275 void GeneralTab::outputAudioChanged(int output)
1276 {
1277 s_audout((__u32)output);
1278 updateAudioOutput();
1279 }
1280
standardChanged(int std)1281 void GeneralTab::standardChanged(int std)
1282 {
1283 v4l2_standard vs;
1284
1285 enum_std(vs, true, std);
1286 s_std(vs.id);
1287 updateStandard();
1288 }
1289
timingsChanged(int index)1290 void GeneralTab::timingsChanged(int index)
1291 {
1292 v4l2_enum_dv_timings timings;
1293
1294 enum_dv_timings(timings, true, index);
1295 s_dv_timings(timings.timings);
1296 updateTimings();
1297 }
1298
freqTableChanged(int)1299 void GeneralTab::freqTableChanged(int)
1300 {
1301 updateFreqChannel();
1302 freqChannelChanged(0);
1303 }
1304
freqChannelChanged(int idx)1305 void GeneralTab::freqChannelChanged(int idx)
1306 {
1307 double f = v4l2_channel_lists[m_freqTable->currentIndex()].list[idx].freq;
1308
1309 m_freq->setValue(f / 1000.0);
1310 freqChanged(m_freq->value());
1311 }
1312
freqChanged(double f)1313 void GeneralTab::freqChanged(double f)
1314 {
1315 v4l2_frequency freq;
1316
1317 if (!m_freq->isEnabled())
1318 return;
1319
1320 g_frequency(freq);
1321 freq.frequency = f * m_freqFac;
1322 s_frequency(freq);
1323 updateFreq();
1324 }
1325
freqRfChanged(double f)1326 void GeneralTab::freqRfChanged(double f)
1327 {
1328 v4l2_frequency freq;
1329
1330 if (!m_freqRf->isEnabled())
1331 return;
1332
1333 g_frequency(freq, 1);
1334 freq.frequency = f * m_freqRfFac;
1335 s_frequency(freq);
1336 updateFreqRf();
1337 }
1338
audioModeChanged(int)1339 void GeneralTab::audioModeChanged(int)
1340 {
1341 m_tuner.audmode = m_audioModes[m_audioMode->currentIndex()];
1342 s_tuner(m_tuner);
1343 }
1344
detectSubchansClicked()1345 void GeneralTab::detectSubchansClicked()
1346 {
1347 QString chans;
1348
1349 g_tuner(m_tuner);
1350 if (m_tuner.rxsubchans & V4L2_TUNER_SUB_MONO)
1351 chans += "Mono ";
1352 if (m_tuner.rxsubchans & V4L2_TUNER_SUB_STEREO)
1353 chans += "Stereo ";
1354 if (m_tuner.rxsubchans & V4L2_TUNER_SUB_LANG1)
1355 chans += "Lang1 ";
1356 if (m_tuner.rxsubchans & V4L2_TUNER_SUB_LANG2)
1357 chans += "Lang2 ";
1358 if (m_tuner.rxsubchans & V4L2_TUNER_SUB_RDS)
1359 chans += "RDS ";
1360 chans += "(" + QString::number((int)(m_tuner.signal / 655.35 + 0.5)) + "%";
1361 if (m_tuner.signal && m_tuner.afc)
1362 chans += m_tuner.afc < 0 ? " too low" : " too high";
1363 chans += ")";
1364
1365 m_subchannels->setText(chans);
1366 fixWidth();
1367 }
1368
stereoModeChanged()1369 void GeneralTab::stereoModeChanged()
1370 {
1371 v4l2_modulator mod;
1372 bool val = m_stereoMode->checkState() == Qt::Checked;
1373
1374 g_modulator(mod);
1375 mod.txsubchans &= ~(V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO);
1376 mod.txsubchans |= val ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
1377 s_modulator(mod);
1378 }
1379
rdsModeChanged()1380 void GeneralTab::rdsModeChanged()
1381 {
1382 v4l2_modulator mod;
1383 bool val = m_rdsMode->checkState() == Qt::Checked;
1384
1385 g_modulator(mod);
1386 mod.txsubchans &= ~V4L2_TUNER_SUB_RDS;
1387 mod.txsubchans |= val ? V4L2_TUNER_SUB_RDS : 0;
1388 s_modulator(mod);
1389 }
1390
colorspaceChanged(int idx)1391 void GeneralTab::colorspaceChanged(int idx)
1392 {
1393 cv4l_fmt fmt;
1394
1395 g_fmt(fmt);
1396 fmt.s_colorspace(m_colorspace->itemData(idx).toInt());
1397 fmt.s_xfer_func(m_xferFunc->itemData(m_xferFunc->currentIndex()).toInt());
1398 fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(m_ycbcrEnc->currentIndex()).toInt());
1399 fmt.s_quantization(m_quantRange->itemData(m_quantRange->currentIndex()).toInt());
1400 if (try_fmt(fmt) == 0)
1401 s_fmt(fmt);
1402 updateVidFormat();
1403 }
1404
xferFuncChanged(int idx)1405 void GeneralTab::xferFuncChanged(int idx)
1406 {
1407 cv4l_fmt fmt;
1408
1409 g_fmt(fmt);
1410 fmt.s_colorspace(m_colorspace->itemData(m_colorspace->currentIndex()).toInt());
1411 fmt.s_xfer_func(m_xferFunc->itemData(idx).toInt());
1412 fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(m_ycbcrEnc->currentIndex()).toInt());
1413 fmt.s_quantization(m_quantRange->itemData(m_quantRange->currentIndex()).toInt());
1414 if (try_fmt(fmt) == 0)
1415 s_fmt(fmt);
1416 updateVidFormat();
1417 }
1418
ycbcrEncChanged(int idx)1419 void GeneralTab::ycbcrEncChanged(int idx)
1420 {
1421 cv4l_fmt fmt;
1422
1423 g_fmt(fmt);
1424 fmt.s_colorspace(m_colorspace->itemData(m_colorspace->currentIndex()).toInt());
1425 fmt.s_xfer_func(m_xferFunc->itemData(m_xferFunc->currentIndex()).toInt());
1426 fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(idx).toInt());
1427 fmt.s_quantization(m_quantRange->itemData(m_quantRange->currentIndex()).toInt());
1428 if (try_fmt(fmt) == 0)
1429 s_fmt(fmt);
1430 updateVidFormat();
1431 }
1432
quantRangeChanged(int idx)1433 void GeneralTab::quantRangeChanged(int idx)
1434 {
1435 cv4l_fmt fmt;
1436
1437 g_fmt(fmt);
1438 fmt.s_colorspace(m_colorspace->itemData(m_colorspace->currentIndex()).toInt());
1439 fmt.s_xfer_func(m_xferFunc->itemData(m_xferFunc->currentIndex()).toInt());
1440 fmt.s_ycbcr_enc(m_ycbcrEnc->itemData(m_ycbcrEnc->currentIndex()).toInt());
1441 fmt.s_quantization(m_quantRange->itemData(idx).toInt());
1442 if (try_fmt(fmt) == 0)
1443 s_fmt(fmt);
1444 updateVidFormat();
1445 }
1446
clearColorspace(cv4l_fmt & fmt)1447 void GeneralTab::clearColorspace(cv4l_fmt &fmt)
1448 {
1449 if (m_colorspace->currentIndex() == 0)
1450 fmt.s_colorspace(V4L2_COLORSPACE_DEFAULT);
1451 if (m_xferFunc->currentIndex() == 0)
1452 fmt.s_xfer_func(V4L2_XFER_FUNC_DEFAULT);
1453 if (m_ycbcrEnc->currentIndex() == 0)
1454 fmt.s_ycbcr_enc(V4L2_YCBCR_ENC_DEFAULT);
1455 if (m_quantRange->currentIndex() == 0)
1456 fmt.s_quantization(V4L2_QUANTIZATION_DEFAULT);
1457 }
1458
vidCapFormatChanged(int idx)1459 void GeneralTab::vidCapFormatChanged(int idx)
1460 {
1461 v4l2_fmtdesc desc;
1462
1463 enum_fmt(desc, true, idx);
1464
1465 cv4l_fmt fmt;
1466
1467 g_fmt(fmt);
1468 fmt.s_pixelformat(desc.pixelformat);
1469 clearColorspace(fmt);
1470 if (try_fmt(fmt) == 0)
1471 s_fmt(fmt);
1472
1473 updateVidCapFormat();
1474 }
1475
field2s(int val)1476 static const char *field2s(int val)
1477 {
1478 switch (val) {
1479 case V4L2_FIELD_ANY:
1480 return "Any";
1481 case V4L2_FIELD_NONE:
1482 return "None";
1483 case V4L2_FIELD_TOP:
1484 return "Top";
1485 case V4L2_FIELD_BOTTOM:
1486 return "Bottom";
1487 case V4L2_FIELD_INTERLACED:
1488 return "Interlaced";
1489 case V4L2_FIELD_SEQ_TB:
1490 return "Sequential Top-Bottom";
1491 case V4L2_FIELD_SEQ_BT:
1492 return "Sequential Bottom-Top";
1493 case V4L2_FIELD_ALTERNATE:
1494 return "Alternating";
1495 case V4L2_FIELD_INTERLACED_TB:
1496 return "Interlaced Top-Bottom";
1497 case V4L2_FIELD_INTERLACED_BT:
1498 return "Interlaced Bottom-Top";
1499 default:
1500 return "";
1501 }
1502 }
1503
vidFieldChanged(int idx)1504 void GeneralTab::vidFieldChanged(int idx)
1505 {
1506 cv4l_fmt fmt;
1507
1508 g_fmt(fmt);
1509 for (__u32 f = V4L2_FIELD_NONE; f <= V4L2_FIELD_INTERLACED_BT; f++) {
1510 if (m_vidFields->currentText() == QString(field2s(f))) {
1511 fmt.s_field(f);
1512 clearColorspace(fmt);
1513 s_fmt(fmt);
1514 break;
1515 }
1516 }
1517 updateVidFormat();
1518 }
1519
frameWidthChanged()1520 void GeneralTab::frameWidthChanged()
1521 {
1522 cv4l_fmt fmt;
1523 int val = m_frameWidth->value();
1524
1525 if (m_frameWidth->isEnabled()) {
1526 g_fmt(fmt);
1527 fmt.s_width(val);
1528 clearColorspace(fmt);
1529 if (try_fmt(fmt) == 0)
1530 s_fmt(fmt);
1531 }
1532
1533 updateVidFormat();
1534 }
1535
frameHeightChanged()1536 void GeneralTab::frameHeightChanged()
1537 {
1538 cv4l_fmt fmt;
1539 int val = m_frameHeight->value();
1540
1541 if (m_frameHeight->isEnabled()) {
1542 g_fmt(fmt);
1543 fmt.s_height(val);
1544 clearColorspace(fmt);
1545 if (try_fmt(fmt) == 0)
1546 s_fmt(fmt);
1547 }
1548
1549 updateVidFormat();
1550 }
1551
frameSizeChanged(int idx)1552 void GeneralTab::frameSizeChanged(int idx)
1553 {
1554 v4l2_frmsizeenum frmsize = { 0 };
1555
1556 if (!enum_framesizes(frmsize, m_pixelformat, idx)) {
1557 cv4l_fmt fmt;
1558
1559 g_fmt(fmt);
1560 fmt.s_width(frmsize.discrete.width);
1561 fmt.s_height(frmsize.discrete.height);
1562 clearColorspace(fmt);
1563 if (try_fmt(fmt) == 0)
1564 s_fmt(fmt);
1565 }
1566 updateVidFormat();
1567 }
1568
frameIntervalChanged(int idx)1569 void GeneralTab::frameIntervalChanged(int idx)
1570 {
1571 v4l2_frmivalenum frmival = { 0 };
1572
1573 if (!enum_frameintervals(frmival, m_pixelformat, m_width, m_height, idx)
1574 && frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
1575 if (!set_interval(frmival.discrete))
1576 m_interval = frmival.discrete;
1577 }
1578 }
1579
vidOutFormatChanged(int idx)1580 void GeneralTab::vidOutFormatChanged(int idx)
1581 {
1582 v4l2_fmtdesc desc;
1583
1584 enum_fmt(desc, true, idx);
1585
1586 cv4l_fmt fmt;
1587
1588 g_fmt(fmt);
1589 fmt.s_pixelformat(desc.pixelformat);
1590 clearColorspace(fmt);
1591 if (try_fmt(fmt) == 0)
1592 s_fmt(fmt);
1593 updateVidOutFormat();
1594 }
1595
vbiMethodsChanged(int idx)1596 void GeneralTab::vbiMethodsChanged(int idx)
1597 {
1598 if (isSlicedVbi())
1599 s_type(m_isOutput ? V4L2_BUF_TYPE_SLICED_VBI_OUTPUT :
1600 V4L2_BUF_TYPE_SLICED_VBI_CAPTURE);
1601 else
1602 s_type(m_isOutput ? V4L2_BUF_TYPE_VBI_OUTPUT :
1603 V4L2_BUF_TYPE_VBI_CAPTURE);
1604 cv4l_fmt fmt;
1605
1606 g_fmt(fmt);
1607 s_fmt(fmt);
1608 }
1609
cropChanged()1610 void GeneralTab::cropChanged()
1611 {
1612 v4l2_selection sel = { 0 };
1613
1614 if (!m_cropWidth->isEnabled() || !cur_io_has_crop())
1615 return;
1616
1617 sel.type = g_selection_type();
1618 sel.target = V4L2_SEL_TGT_CROP;
1619 sel.r.width = m_cropWidth->value();
1620 sel.r.left = m_cropLeft->value();
1621 sel.r.height = m_cropHeight->value();
1622 sel.r.top = m_cropTop->value();
1623 s_selection(sel);
1624 updateVidFormat();
1625 }
1626
composeChanged()1627 void GeneralTab::composeChanged()
1628 {
1629 v4l2_selection sel = { 0 };
1630
1631 if (!m_composeWidth->isEnabled() || !cur_io_has_compose())
1632 return;
1633
1634 sel.type = g_selection_type();
1635 sel.target = V4L2_SEL_TGT_COMPOSE;
1636 sel.r.width = m_composeWidth->value();
1637 sel.r.left = m_composeLeft->value();
1638 sel.r.height = m_composeHeight->value();
1639 sel.r.top = m_composeTop->value();
1640 s_selection(sel);
1641 updateVidFormat();
1642 }
1643
updateVideoInput()1644 void GeneralTab::updateVideoInput()
1645 {
1646 __u32 input;
1647 v4l2_input in;
1648
1649 if (g_input(input))
1650 return;
1651 enum_input(in, true, input);
1652 m_videoInput->setCurrentIndex(input);
1653 m_isSDTV = false;
1654 if (m_tvStandard) {
1655 refreshStandards();
1656 updateStandard();
1657 m_tvStandard->setEnabled(in.capabilities & V4L2_IN_CAP_STD);
1658 if (m_qryStandard)
1659 m_qryStandard->setEnabled(in.capabilities & V4L2_IN_CAP_STD);
1660 if (in.capabilities & V4L2_IN_CAP_STD)
1661 m_isSDTV = true;
1662 bool enableFreq = in.type == V4L2_INPUT_TYPE_TUNER;
1663 if (m_freq)
1664 m_freq->setEnabled(enableFreq);
1665 if (m_freqTable)
1666 m_freqTable->setEnabled(enableFreq);
1667 if (m_freqChannel)
1668 m_freqChannel->setEnabled(enableFreq);
1669 if (m_detectSubchans) {
1670 m_detectSubchans->setEnabled(enableFreq);
1671 if (!enableFreq) {
1672 m_subchannels->setText("");
1673 fixWidth();
1674 }
1675 else
1676 detectSubchansClicked();
1677 }
1678 }
1679 if (m_videoTimings) {
1680 refreshTimings();
1681 updateTimings();
1682 m_videoTimings->setEnabled(in.capabilities & V4L2_IN_CAP_DV_TIMINGS);
1683 if (m_qryTimings)
1684 m_qryTimings->setEnabled(in.capabilities & V4L2_IN_CAP_DV_TIMINGS);
1685 }
1686 if (m_audioInput)
1687 m_audioInput->setEnabled(in.audioset);
1688 if (m_cropWidth) {
1689 bool has_crop = cur_io_has_crop();
1690
1691 m_cropWidth->setEnabled(has_crop);
1692 m_cropLeft->setEnabled(has_crop);
1693 m_cropHeight->setEnabled(has_crop);
1694 m_cropTop->setEnabled(has_crop);
1695 }
1696 if (m_composeWidth) {
1697 bool has_compose = cur_io_has_compose();
1698
1699 m_composeWidth->setEnabled(has_compose);
1700 m_composeLeft->setEnabled(has_compose);
1701 m_composeHeight->setEnabled(has_compose);
1702 m_composeTop->setEnabled(has_compose);
1703 }
1704 }
1705
updateVideoOutput()1706 void GeneralTab::updateVideoOutput()
1707 {
1708 __u32 output;
1709 v4l2_output out;
1710
1711 if (g_output(output))
1712 return;
1713 enum_output(out, true, output);
1714 m_videoOutput->setCurrentIndex(output);
1715 m_isSDTV = false;
1716 if (m_tvStandard) {
1717 refreshStandards();
1718 updateStandard();
1719 m_tvStandard->setEnabled(out.capabilities & V4L2_OUT_CAP_STD);
1720 if (m_qryStandard)
1721 m_qryStandard->setEnabled(out.capabilities & V4L2_OUT_CAP_STD);
1722 if (out.capabilities & V4L2_OUT_CAP_STD)
1723 m_isSDTV = true;
1724 }
1725 if (m_videoTimings) {
1726 refreshTimings();
1727 updateTimings();
1728 m_videoTimings->setEnabled(out.capabilities & V4L2_OUT_CAP_DV_TIMINGS);
1729 }
1730 if (m_audioOutput)
1731 m_audioOutput->setEnabled(out.audioset);
1732 if (m_cropWidth) {
1733 bool has_crop = cur_io_has_crop();
1734
1735 m_cropWidth->setEnabled(has_crop);
1736 m_cropLeft->setEnabled(has_crop);
1737 m_cropHeight->setEnabled(has_crop);
1738 m_cropTop->setEnabled(has_crop);
1739 }
1740 if (m_composeWidth) {
1741 bool has_compose = cur_io_has_compose();
1742
1743 m_composeWidth->setEnabled(has_compose);
1744 m_composeLeft->setEnabled(has_compose);
1745 m_composeHeight->setEnabled(has_compose);
1746 m_composeTop->setEnabled(has_compose);
1747 }
1748 g_mw->updateLimRGBRange();
1749 }
1750
updateAudioInput()1751 void GeneralTab::updateAudioInput()
1752 {
1753 v4l2_audio audio;
1754 QString what;
1755
1756 g_audio(audio);
1757 m_audioInput->setCurrentIndex(audio.index);
1758 if (audio.capability & V4L2_AUDCAP_STEREO)
1759 what = "stereo input";
1760 else
1761 what = "mono input";
1762 if (audio.capability & V4L2_AUDCAP_AVL)
1763 what += ", has AVL";
1764 if (audio.mode & V4L2_AUDMODE_AVL)
1765 what += ", AVL is on";
1766 m_audioInput->setStatusTip(what);
1767 m_audioInput->setWhatsThis(what);
1768 }
1769
updateAudioOutput()1770 void GeneralTab::updateAudioOutput()
1771 {
1772 v4l2_audioout audio;
1773
1774 g_audout(audio);
1775 m_audioOutput->setCurrentIndex(audio.index);
1776 }
1777
refreshStandards()1778 void GeneralTab::refreshStandards()
1779 {
1780 v4l2_standard vs;
1781 m_tvStandard->clear();
1782 if (!enum_std(vs, true)) {
1783 do {
1784 m_tvStandard->addItem((char *)vs.name);
1785 } while (!enum_std(vs));
1786 }
1787 }
1788
updateStandard()1789 void GeneralTab::updateStandard()
1790 {
1791 v4l2_std_id std;
1792 v4l2_standard vs;
1793 QString what;
1794
1795 if (g_std(std))
1796 return;
1797 if (enum_std(vs, true))
1798 return;
1799 do {
1800 if (vs.id == std)
1801 break;
1802 } while (!enum_std(vs));
1803 if (vs.id != std) {
1804 if (!enum_std(vs, true)) {
1805 do {
1806 if (vs.id & std)
1807 break;
1808 } while (!enum_std(vs));
1809 }
1810 }
1811 if ((vs.id & std) == 0)
1812 return;
1813 m_tvStandard->setCurrentIndex(vs.index);
1814 #if QT_VERSION < 0x050500
1815 what.sprintf(
1816 #else
1817 what.asprintf(
1818 #endif
1819 "TV Standard (0x%llX)\n"
1820 "Frame period: %f (%d/%d)\n"
1821 "Frame lines: %d", (long long int)std,
1822 (double)vs.frameperiod.numerator / vs.frameperiod.denominator,
1823 vs.frameperiod.numerator, vs.frameperiod.denominator,
1824 vs.framelines);
1825 m_tvStandard->setStatusTip(what);
1826 m_tvStandard->setWhatsThis(what);
1827 updateVidFormat();
1828 if (!isVbi() && !isTouch() && !m_isOutput)
1829 changePixelAspectRatio();
1830 }
1831
qryStdClicked()1832 void GeneralTab::qryStdClicked()
1833 {
1834 v4l2_std_id std;
1835
1836 if (query_std(std))
1837 return;
1838
1839 if (std == V4L2_STD_UNKNOWN) {
1840 info("No standard detected\n");
1841 } else {
1842 s_std(std);
1843 updateStandard();
1844 }
1845 }
1846
refreshTimings()1847 void GeneralTab::refreshTimings()
1848 {
1849 v4l2_enum_dv_timings timings;
1850 m_videoTimings->clear();
1851 if (!enum_dv_timings(timings, true)) {
1852 do {
1853 v4l2_bt_timings &bt = timings.timings.bt;
1854 double tot_height = bt.height +
1855 bt.vfrontporch + bt.vsync + bt.vbackporch +
1856 bt.il_vfrontporch + bt.il_vsync + bt.il_vbackporch;
1857 double tot_width = bt.width +
1858 bt.hfrontporch + bt.hsync + bt.hbackporch;
1859 char buf[100];
1860
1861 if (bt.interlaced)
1862 sprintf(buf, "%dx%di%.2f", bt.width, bt.height,
1863 (double)bt.pixelclock / (tot_width * (tot_height / 2)));
1864 else
1865 sprintf(buf, "%dx%dp%.2f", bt.width, bt.height,
1866 (double)bt.pixelclock / (tot_width * tot_height));
1867 m_videoTimings->addItem(buf);
1868 } while (!enum_dv_timings(timings));
1869 }
1870 }
1871
updateTimings()1872 void GeneralTab::updateTimings()
1873 {
1874 v4l2_dv_timings timings;
1875 v4l2_enum_dv_timings p;
1876 QString what;
1877
1878 if (g_dv_timings(timings))
1879 return;
1880 if (enum_dv_timings(p, true))
1881 return;
1882 do {
1883 if (!memcmp(&timings, &p.timings, sizeof(timings)))
1884 break;
1885 } while (!enum_dv_timings(p));
1886 if (memcmp(&timings, &p.timings, sizeof(timings)))
1887 return;
1888 m_videoTimings->setCurrentIndex(p.index);
1889 #if QT_VERSION < 0x050500
1890 what.sprintf(
1891 #else
1892 what.asprintf(
1893 #endif
1894 "Video Timings (%u)\n"
1895 "Frame %ux%u\n",
1896 p.index, p.timings.bt.width, p.timings.bt.height);
1897 m_isSDTV = p.timings.bt.width <= 720 && p.timings.bt.height <= 576;
1898 m_videoTimings->setStatusTip(what);
1899 m_videoTimings->setWhatsThis(what);
1900 updateVidFormat();
1901 g_mw->updateLimRGBRange();
1902 }
1903
qryTimingsClicked()1904 void GeneralTab::qryTimingsClicked()
1905 {
1906 v4l2_dv_timings timings;
1907 int err = query_dv_timings(timings);
1908
1909 switch (err) {
1910 case ENOLINK:
1911 info("No signal found\n");
1912 break;
1913 case ENOLCK:
1914 info("Could not lock to signal\n");
1915 break;
1916 case ERANGE:
1917 info("Frequency out of range\n");
1918 break;
1919 case 0:
1920 s_dv_timings(timings);
1921 updateTimings();
1922 break;
1923 default:
1924 error(err);
1925 break;
1926 }
1927 }
1928
sourceChange(const v4l2_event & ev)1929 void GeneralTab::sourceChange(const v4l2_event &ev)
1930 {
1931 if (!m_videoInput || (int)ev.id != m_videoInput->currentIndex())
1932 return;
1933 if (m_qryStandard && m_qryStandard->isEnabled())
1934 m_qryStandard->click();
1935 else if (m_qryTimings && m_qryTimings->isEnabled())
1936 m_qryTimings->click();
1937 if (has_vid_cap() || has_vid_out())
1938 updateColorspace();
1939 }
1940
updateFreq()1941 void GeneralTab::updateFreq()
1942 {
1943 v4l2_frequency f;
1944
1945 g_frequency(f);
1946 /* m_freq listens to valueChanged block it to avoid recursion */
1947 m_freq->blockSignals(true);
1948 m_freq->setValue((double)f.frequency / m_freqFac);
1949 m_freq->blockSignals(false);
1950 }
1951
updateFreqChannel()1952 void GeneralTab::updateFreqChannel()
1953 {
1954 m_freqChannel->clear();
1955 int tbl = m_freqTable->currentIndex();
1956 const struct v4l2_channel_list *list = v4l2_channel_lists[tbl].list;
1957 for (unsigned i = 0; i < v4l2_channel_lists[tbl].count; i++)
1958 m_freqChannel->addItem(list[i].name);
1959 }
1960
updateFreqRf()1961 void GeneralTab::updateFreqRf()
1962 {
1963 v4l2_frequency f;
1964
1965 g_frequency(f, 1);
1966 /* m_freqRf listens to valueChanged block it to avoid recursion */
1967 m_freqRf->blockSignals(true);
1968 m_freqRf->setValue((double)f.frequency / m_freqRfFac);
1969 m_freqRf->blockSignals(false);
1970 }
1971
updateColorspace()1972 void GeneralTab::updateColorspace()
1973 {
1974 cv4l_fmt fmt;
1975 int idx;
1976
1977 g_fmt(fmt);
1978 idx = m_colorspace->findData(fmt.g_colorspace());
1979 if (m_colorspace->currentIndex())
1980 m_colorspace->setCurrentIndex(idx >= 0 ? idx : 0);
1981 idx = m_xferFunc->findData(fmt.g_xfer_func());
1982 if (m_xferFunc->currentIndex())
1983 m_xferFunc->setCurrentIndex(idx >= 0 ? idx : 0);
1984 idx = m_ycbcrEnc->findData(fmt.g_ycbcr_enc());
1985 if (m_ycbcrEnc->currentIndex())
1986 m_ycbcrEnc->setCurrentIndex(idx >= 0 ? idx : 0);
1987 idx = m_quantRange->findData(fmt.g_quantization());
1988 if (m_quantRange->currentIndex())
1989 m_quantRange->setCurrentIndex(idx >= 0 ? idx : 0);
1990 g_mw->updateColorspace();
1991 }
1992
updateVidCapFormat()1993 void GeneralTab::updateVidCapFormat()
1994 {
1995 v4l2_fmtdesc desc;
1996 cv4l_fmt fmt;
1997
1998 if (isVbi())
1999 return;
2000 g_fmt(fmt);
2001 m_pixelformat = fmt.g_pixelformat();
2002 m_width = fmt.g_width();
2003 m_height = fmt.g_height();
2004 updateColorspace();
2005 updateFrameSize();
2006 updateFrameInterval();
2007 if (!enum_fmt(desc, true)) {
2008 do {
2009 if (desc.pixelformat == m_pixelformat)
2010 break;
2011 } while (!enum_fmt(desc));
2012 }
2013 if (desc.pixelformat != m_pixelformat)
2014 return;
2015 m_vidCapFormats->setCurrentIndex(desc.index);
2016 updateVidFields();
2017 updateCrop();
2018 updateCompose();
2019 }
2020
updateVidOutFormat()2021 void GeneralTab::updateVidOutFormat()
2022 {
2023 v4l2_fmtdesc desc;
2024 cv4l_fmt fmt;
2025
2026 if (isVbi())
2027 return;
2028 g_fmt(fmt);
2029 m_pixelformat = fmt.g_pixelformat();
2030 m_width = fmt.g_width();
2031 m_height = fmt.g_height();
2032 updateColorspace();
2033 updateFrameSize();
2034 if (!enum_fmt(desc, true)) {
2035 do {
2036 if (desc.pixelformat == m_pixelformat)
2037 break;
2038 } while (!enum_fmt(desc));
2039 }
2040 if (desc.pixelformat != m_pixelformat)
2041 return;
2042 m_vidOutFormats->setCurrentIndex(desc.index);
2043 updateVidFields();
2044 updateCrop();
2045 updateCompose();
2046 }
2047
updateVidFields()2048 void GeneralTab::updateVidFields()
2049 {
2050 cv4l_fmt fmt;
2051 cv4l_fmt tmp;
2052 bool first = true;
2053
2054 g_fmt(fmt);
2055
2056 for (__u32 f = V4L2_FIELD_NONE; f <= V4L2_FIELD_INTERLACED_BT; f++) {
2057 tmp = fmt;
2058 tmp.s_field(f);
2059 if (try_fmt(tmp) || tmp.g_field() != f)
2060 continue;
2061 if (first) {
2062 m_vidFields->clear();
2063 first = false;
2064 }
2065 m_vidFields->addItem(field2s(f));
2066 if (fmt.g_field() == f)
2067 m_vidFields->setCurrentIndex(m_vidFields->count() - 1);
2068 }
2069 }
2070
updateCrop()2071 void GeneralTab::updateCrop()
2072 {
2073 if (m_cropWidth == NULL || !m_cropWidth->isEnabled())
2074 return;
2075
2076 v4l2_selection sel = { 0 };
2077 v4l2_rect &r = sel.r;
2078 v4l2_rect b = { 0, 0, m_width, m_height };
2079
2080 sel.type = g_selection_type();
2081 if (sel.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
2082 sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
2083 if (g_selection(sel))
2084 return;
2085 b = sel.r;
2086 }
2087 sel.target = V4L2_SEL_TGT_CROP;
2088 if (g_selection(sel))
2089 return;
2090
2091 m_cropWidth->blockSignals(true);
2092 m_cropLeft->blockSignals(true);
2093 m_cropHeight->blockSignals(true);
2094 m_cropTop->blockSignals(true);
2095
2096 m_cropWidth->setRange(8, b.width);
2097 m_cropWidth->setSliderPosition(r.width);
2098 if (b.width != r.width) {
2099 m_cropLeft->setRange(b.left, b.left + b.width - r.width);
2100 m_cropLeft->setSliderPosition(r.left);
2101 }
2102 m_cropHeight->setRange(8, b.height);
2103 m_cropHeight->setSliderPosition(r.height);
2104 if (b.height != r.height) {
2105 m_cropTop->setRange(b.top, b.top + b.height - r.height);
2106 m_cropTop->setSliderPosition(r.top);
2107 }
2108
2109 m_cropWidth->blockSignals(false);
2110 m_cropLeft->blockSignals(false);
2111 m_cropHeight->blockSignals(false);
2112 m_cropTop->blockSignals(false);
2113 }
2114
updateCompose()2115 void GeneralTab::updateCompose()
2116 {
2117 if (m_composeWidth == NULL || !m_composeWidth->isEnabled())
2118 return;
2119
2120 v4l2_selection sel = { 0 };
2121 v4l2_rect &r = sel.r;
2122 v4l2_rect b = { 0, 0, m_width, m_height };
2123
2124 sel.type = g_selection_type();
2125 if (sel.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
2126 sel.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
2127 if (g_selection(sel))
2128 return;
2129 b = sel.r;
2130 }
2131 sel.target = V4L2_SEL_TGT_COMPOSE;
2132 if (g_selection(sel))
2133 return;
2134
2135 m_composeWidth->blockSignals(true);
2136 m_composeLeft->blockSignals(true);
2137 m_composeHeight->blockSignals(true);
2138 m_composeTop->blockSignals(true);
2139
2140 m_composeWidth->setRange(8, b.width);
2141 m_composeWidth->setSliderPosition(r.width);
2142 if (b.width != r.width) {
2143 m_composeLeft->setRange(b.left, b.left + b.width - r.width);
2144 m_composeLeft->setSliderPosition(r.left);
2145 }
2146 m_composeHeight->setRange(8, b.height);
2147 m_composeHeight->setSliderPosition(r.height);
2148 if (b.height != r.height) {
2149 m_composeTop->setRange(b.top, b.top + b.height - r.height);
2150 m_composeTop->setSliderPosition(r.top);
2151 }
2152
2153 m_composeWidth->blockSignals(false);
2154 m_composeLeft->blockSignals(false);
2155 m_composeHeight->blockSignals(false);
2156 m_composeTop->blockSignals(false);
2157 emit clearBuffers();
2158 }
2159
updateFrameSize()2160 void GeneralTab::updateFrameSize()
2161 {
2162 v4l2_frmsizeenum frmsize;
2163 bool ok = false;
2164
2165 if (m_frameSize)
2166 m_frameSize->clear();
2167
2168 ok = !enum_framesizes(frmsize, m_pixelformat);
2169 if (ok && frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
2170 if (m_frameSize) {
2171 do {
2172 m_frameSize->addItem(QString("%1x%2")
2173 .arg(frmsize.discrete.width)
2174 .arg(frmsize.discrete.height));
2175 if (frmsize.discrete.width == m_width &&
2176 frmsize.discrete.height == m_height)
2177 m_frameSize->setCurrentIndex(frmsize.index);
2178 } while (!enum_framesizes(frmsize));
2179 }
2180
2181 m_discreteSizes = true;
2182 m_frameWidth->setEnabled(false);
2183 m_frameWidth->blockSignals(true);
2184 m_frameWidth->setMinimum(m_width);
2185 m_frameWidth->setMaximum(m_width);
2186 m_frameWidth->setValue(m_width);
2187 m_frameWidth->blockSignals(false);
2188
2189 m_frameHeight->setEnabled(false);
2190 m_frameHeight->blockSignals(true);
2191 m_frameHeight->setMinimum(m_height);
2192 m_frameHeight->setMaximum(m_height);
2193 m_frameHeight->setValue(m_height);
2194 m_frameHeight->blockSignals(false);
2195 if (m_frameSize)
2196 m_frameSize->setEnabled(!m_haveBuffers);
2197 updateFrameInterval();
2198 return;
2199 }
2200 if (!ok) {
2201 frmsize.stepwise.min_width = 8;
2202 frmsize.stepwise.max_width = 4096;
2203 frmsize.stepwise.step_width = 1;
2204 frmsize.stepwise.min_height = 8;
2205 frmsize.stepwise.max_height = 2160;
2206 frmsize.stepwise.step_height = 1;
2207 }
2208 m_discreteSizes = false;
2209 if (m_frameSize)
2210 m_frameSize->setEnabled(false);
2211 if (!m_frameWidth) {
2212 updateFrameInterval();
2213 return;
2214 }
2215
2216 m_frameWidth->setEnabled(!m_haveBuffers);
2217 m_frameWidth->blockSignals(true);
2218 m_frameWidth->setMinimum(frmsize.stepwise.min_width);
2219 m_frameWidth->setMaximum(frmsize.stepwise.max_width);
2220 m_frameWidth->setSingleStep(frmsize.stepwise.step_width);
2221 m_frameWidth->setValue(m_width);
2222 m_frameWidth->blockSignals(false);
2223
2224 m_frameHeight->setEnabled(!m_haveBuffers);
2225 m_frameHeight->blockSignals(true);
2226 m_frameHeight->setMinimum(frmsize.stepwise.min_height);
2227 m_frameHeight->setMaximum(frmsize.stepwise.max_height);
2228 m_frameHeight->setSingleStep(frmsize.stepwise.step_height);
2229 m_frameHeight->setValue(m_height);
2230 m_frameHeight->blockSignals(false);
2231 updateFrameInterval();
2232 }
2233
getCropMethod()2234 CropMethod GeneralTab::getCropMethod()
2235 {
2236 switch (m_cropping->currentIndex()) {
2237 case 1:
2238 return QV4L2_CROP_TB;
2239 case 2:
2240 return QV4L2_CROP_P43;
2241 case 3:
2242 return QV4L2_CROP_W149;
2243 case 4:
2244 return QV4L2_CROP_W169;
2245 case 5:
2246 return QV4L2_CROP_C185;
2247 case 6:
2248 return QV4L2_CROP_C239;
2249 default:
2250 return QV4L2_CROP_NONE;
2251 }
2252 }
2253
changePixelAspectRatio()2254 void GeneralTab::changePixelAspectRatio()
2255 {
2256 // Update hints by calling a get
2257 getPixelAspectRatio();
2258 info("");
2259 emit pixelAspectRatioChanged();
2260 }
2261
getPixelAspectRatio()2262 double GeneralTab::getPixelAspectRatio()
2263 {
2264 v4l2_fract ratio = { 1, 1 };
2265 unsigned w = 0, h = 0;
2266
2267 ratio = g_pixel_aspect(w, h);
2268 if (!m_pixelAspectRatio)
2269 return 1;
2270
2271 switch (m_pixelAspectRatio->currentIndex()) {
2272 // override ratio if hardcoded, but keep w and h
2273 case 1:
2274 ratio.numerator = 1;
2275 ratio.denominator = 1;
2276 break;
2277 case 2:
2278 ratio.numerator = 11;
2279 ratio.denominator = 10;
2280 break;
2281 case 3:
2282 ratio.numerator = 33;
2283 ratio.denominator = 40;
2284 break;
2285 case 4:
2286 ratio.numerator = 54;
2287 ratio.denominator = 59;
2288 break;
2289 case 5:
2290 ratio.numerator = 81;
2291 ratio.denominator = 118;
2292 break;
2293 default:
2294 break;
2295 }
2296
2297 m_pixelAspectRatio->setWhatsThis(QString("Pixel Aspect Ratio y:x = %1:%2")
2298 .arg(ratio.numerator).arg(ratio.denominator));
2299 m_pixelAspectRatio->setStatusTip(m_pixelAspectRatio->whatsThis());
2300
2301 cv4l_fmt fmt;
2302 unsigned cur_width, cur_height;
2303 unsigned cur_field;
2304
2305 g_fmt(fmt);
2306
2307 cur_width = fmt.g_width();
2308 cur_height = fmt.g_height();
2309 cur_field = fmt.g_field();
2310 if (w == 0)
2311 w = cur_width;
2312 if (cur_field == V4L2_FIELD_TOP ||
2313 cur_field == V4L2_FIELD_BOTTOM ||
2314 cur_field == V4L2_FIELD_ALTERNATE) {
2315 // If we only capture a single field, then each pixel is twice
2316 // as high and the default image height is half the reported
2317 // height.
2318 ratio.numerator *= 2;
2319 h /= 2;
2320 }
2321 if (h == 0)
2322 h = cur_height;
2323
2324 // Note: ratio is y / x, whereas we want x / y, so we return
2325 // denominator / numerator.
2326 // In addition, the ratio is for the unscaled image (i.e., the default
2327 // image rectangle as returned by VIDIOC_CROPCAP). So we have to
2328 // compensate for the current scaling factor.
2329 return (((double)ratio.denominator * w) / cur_width) /
2330 (((double)ratio.numerator * h) / cur_height);
2331 }
2332
updateFrameInterval()2333 void GeneralTab::updateFrameInterval()
2334 {
2335 v4l2_frmivalenum frmival = { 0 };
2336 v4l2_fract curr = { 1, 1 };
2337 bool curr_ok, ok;
2338
2339 if (m_frameInterval == NULL)
2340 return;
2341
2342 m_frameInterval->clear();
2343
2344 ok = !enum_frameintervals(frmival, m_pixelformat, m_width, m_height);
2345 m_has_interval = ok && frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE;
2346 m_frameInterval->setEnabled(m_has_interval);
2347 if (m_has_interval) {
2348 m_interval = frmival.discrete;
2349 curr_ok = !m_fd->get_interval(curr);
2350 do {
2351 m_frameInterval->addItem(QString("%1 fps")
2352 .arg((double)frmival.discrete.denominator / frmival.discrete.numerator));
2353 if (curr_ok &&
2354 frmival.discrete.numerator == curr.numerator &&
2355 frmival.discrete.denominator == curr.denominator) {
2356 m_frameInterval->setCurrentIndex(frmival.index);
2357 m_interval = frmival.discrete;
2358 }
2359 } while (!enum_frameintervals(frmival));
2360 }
2361 }
2362
get_interval(struct v4l2_fract & interval)2363 bool GeneralTab::get_interval(struct v4l2_fract &interval)
2364 {
2365 if (m_has_interval)
2366 interval = m_interval;
2367
2368 return m_has_interval;
2369 }
2370
getAudioInDevice()2371 QString GeneralTab::getAudioInDevice()
2372 {
2373 if (m_audioInDevice == NULL)
2374 return NULL;
2375
2376 return m_audioInDeviceMap[m_audioInDevice->currentIndex()];
2377 }
2378
getAudioOutDevice()2379 QString GeneralTab::getAudioOutDevice()
2380 {
2381 if (m_audioOutDevice == NULL)
2382 return NULL;
2383
2384 return m_audioOutDeviceMap[m_audioOutDevice->currentIndex()];
2385 }
2386
setAudioDeviceBufferSize(int size)2387 void GeneralTab::setAudioDeviceBufferSize(int size)
2388 {
2389 m_audioDeviceBufferSize = size;
2390 }
2391
getAudioDeviceBufferSize()2392 int GeneralTab::getAudioDeviceBufferSize()
2393 {
2394 return m_audioDeviceBufferSize;
2395 }
2396
2397 #ifdef HAVE_ALSA
checkMatchAudioDevice(void * md,const char * vid,enum device_type type)2398 int GeneralTab::checkMatchAudioDevice(void *md, const char *vid, enum device_type type)
2399 {
2400 const char *devname = NULL;
2401 enum device_type dtype = isRadio() ? MEDIA_V4L_RADIO : MEDIA_V4L_VIDEO;
2402
2403 while ((devname = get_associated_device(md, devname, type, vid, dtype)) != NULL) {
2404 if (type == MEDIA_SND_CAP) {
2405 #if QT_VERSION < 0x060000
2406 QStringList devAddr = QString(devname).split(QRegExp("[:,]"));
2407 return devAddr.value(1).toInt();
2408 #else
2409 QRegExp rx("[:,]");
2410 rx.indexIn(devname);
2411 return rx.cap(1).toInt();
2412 #endif
2413 }
2414 }
2415 return -1;
2416 }
2417
matchAudioDevice()2418 int GeneralTab::matchAudioDevice()
2419 {
2420 QStringList devPath = m_device.split("/");
2421 QString curDev = devPath.value(devPath.count() - 1);
2422 void *media;
2423 int match;
2424
2425 media = discover_media_devices();
2426
2427 if ((match = checkMatchAudioDevice(media, curDev.toLatin1(), MEDIA_SND_CAP)) != -1)
2428 return match;
2429 return -1;
2430 }
2431 #endif
2432
hasAlsaAudio()2433 bool GeneralTab::hasAlsaAudio()
2434 {
2435 #ifdef HAVE_ALSA
2436 return !isVbi() && !isTouch();
2437 #else
2438 return false;
2439 #endif
2440 }
2441