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