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