• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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