1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * qvidcap: a control panel controlling v4l2 devices.
4  *
5  * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6  */
7 
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 
12 #include <QApplication>
13 #include <QScrollArea>
14 #include <QtMath>
15 
16 #include "qvidcap.h"
17 #include "capture.h"
18 
19 #include <libv4lconvert.h>
20 #include "v4l-stream.h"
21 #include "v4l2-info.h"
22 
usage()23 static void usage()
24 {
25 	printf("Usage: qvidcap <options>\n\n"
26 	       "Options:\n\n"
27 	       "  -d, --device=<dev>       use device <dev> as the video device\n"
28 	       "                           if <dev> is a number, then /dev/video<dev> is used\n"
29 	       "  -f, --file=<file>        read from the file <file> for the raw frame data\n"
30 	       "  -p, --port[=<port>]      listen for a network connection on the given port\n"
31 	       "                           The default port is %d\n"
32 	       "  -T, --tpg                use the test pattern generator\n"
33 	       "\n"
34 	       "  If neither -d, -f, -p nor -T is specified then use /dev/video0.\n"
35 	       "\n"
36 	       "  -c, --count=<cnt>        stop after <cnt> captured frames\n"
37 	       "  -b, --buffers=<bufs>     request <bufs> buffers (default 4) when streaming\n"
38 	       "                           from a video device\n"
39 	       "  -s, --single-step[=<frm>] starting with frame <frm> (default 1), pause after\n"
40 	       "                           displaying each frame until Space is pressed.\n"
41 	       "  -C, --colorspace=<c>     override colorspace\n"
42 	       "                           <c> can be one of the following colorspaces:\n"
43 	       "                               smpte170m, smpte240m, rec709, 470m, 470bg, jpeg, srgb,\n"
44 	       "                               oprgb, bt2020, dcip3\n"
45 	       "  -X, --xfer-func=<x>      override transfer function\n"
46 	       "                           <x> can be one of the following transfer functions:\n"
47 	       "                               default, 709, srgb, oprgb, smpte240m, smpte2084, dcip3, none\n"
48 	       "  -Y, --ycbcr-enc=<y>      override Y'CbCr encoding\n"
49 	       "                           <y> can be one of the following Y'CbCr encodings:\n"
50 	       "                               default, 601, 709, xv601, xv709, bt2020, bt2020c, smpte240m\n"
51 	       "  -H, --hsv-enc=<hsv>      override HSV encoding\n"
52 	       "                           <hsv> can be one of the following HSV encodings:\n"
53 	       "                               default, 180, 256\n"
54 	       "  -Q, --quant=<q>          override quantization\n"
55 	       "                           <q> can be one of the following quantization methods:\n"
56 	       "                               default, full-range, lim-range\n"
57 	       "  -P, --pixelformat=<p>    For video devices: set the format to this pixel format.\n"
58 	       "                           For reading from a file: interpret the data using this\n"
59 	       "                           pixel format setting.\n"
60 	       "                           Use -l to see the list of supported pixel formats.\n"
61 	       "\n"
62 	       "  -l, --list-formats       display all supported formats\n"
63 	       "  -h, --help               display this help message\n"
64 	       "  -t, --timings            report frame render timings\n"
65 	       "  -v, --verbose            be more verbose\n"
66 	       "  -R, --raw                open device in raw mode\n"
67 	       "\n"
68 	       "  --opengl                 force openGL to display the video\n"
69 	       "  --opengles               force openGL ES to display the video\n"
70 	       "\n"
71 	       "  The following options are ignored when capturing from a video device:\n"
72 	       "\n"
73 	       "  -W, --width=<width>      set width\n"
74 	       "  -H, --height=<height>    set frame (not field!) height\n"
75 	       "  -A, --padding=<bytes>    set additional horizontal padding (after width)\n"
76 	       "  --fps=<fps>              set frames-per-second (default is 30)\n"
77 	       "\n"
78 	       "  The following option is only valid when reading from a file:\n"
79 	       "\n"
80 	       "  -F, --field=<f>          override field setting\n"
81 	       "                           <f> can be one of the following field layouts:\n"
82 	       "                               any, none, top, bottom, interlaced, seq_tb, seq_bt,\n"
83 	       "                               alternate, interlaced_tb, interlaced_bt\n"
84 	       "  The following options are specific to the test pattern generator:\n"
85 	       "\n"
86 	       "  --list-patterns          list available patterns for use with --pattern\n"
87 	       "  --pattern=<pat>          choose output test pattern, the default is 0\n"
88 	       "  --square                 show a square in the middle of the output test pattern\n"
89 	       "  --border                 show a border around the pillar/letterboxed video\n"
90 	       "  --sav                    insert an SAV code in every line\n"
91 	       "  --eav                    insert an EAV code in every line\n"
92 	       "  --pixel-aspect=<aspect>  select a pixel aspect ratio, the default is to autodetect\n"
93 	       "                           <aspect> can be one of: square, ntsc, pal\n"
94 	       "  --video-aspect=<aspect>  select a video aspect ratio, the default is to use the frame ratio\n"
95 	       "                           <aspect> can be one of: 4x3, 14x9, 16x9, anamorphic\n"
96 	       "  --alpha=<alpha-value>    value to use for the alpha component, range 0-255, the default is 0\n"
97 	       "  --alpha-red-only         only use the --alpha value for the red colors, for all others use 0\n"
98 	       "  --rgb-lim-range          encode RGB values as limited [16-235] instead of full range\n"
99 	       "  --hor-speed=<speed>      choose speed for horizontal movement, the default is 0\n"
100 	       "                           and the range is [-3...3]\n"
101 	       "  --vert-speed=<speed>     choose speed for vertical movement, the default is 0\n"
102 	       "                           and the range is [-3...3]\n"
103 	       "  --perc-fill=<percentage> percentage of the frame to actually fill. the default is 100%%\n"
104 	       "\n"
105 	       "  These options use the test pattern generator to test the OpenGL backend:\n"
106 	       "\n"
107 	       "  --test=<count>           test all formats, each test generates <count> frames.\n"
108 	       "  --test-mask=<mask>       mask which tests are performed.\n"
109 	       "                           <mask> is a bit mask with these values:\n"
110 	       "                           0x01: mask iterating over pixel formats\n"
111 	       "                           0x02: mask iterating over fields\n"
112 	       "                           0x04: mask iterating over colorspaces\n"
113 	       "                           0x08: mask iterating over transfer functions\n"
114 	       "                           0x10: mask iterating over Y'CbCr/HSV encodings\n"
115 	       "                           0x20: mask iterating over quantization ranges\n",
116 		V4L_STREAM_PORT);
117 }
118 
usageError(const char * msg)119 static void usageError(const char *msg)
120 {
121 	printf("Missing parameter for %s\n", msg);
122 	usage();
123 }
124 
usageInvParm(const char * msg)125 static void usageInvParm(const char *msg)
126 {
127 	printf("Invalid parameter for %s\n", msg);
128 	usage();
129 }
130 
getDeviceName(QString dev,QString & name)131 static QString getDeviceName(QString dev, QString &name)
132 {
133 	bool ok;
134 	name.toInt(&ok);
135 	return ok ? QString("%1%2").arg(dev).arg(name) : name;
136 }
137 
parse_field(const QString & s)138 static uint32_t parse_field(const QString &s)
139 {
140 	if (s == "any") return V4L2_FIELD_ANY;
141 	if (s == "none") return V4L2_FIELD_NONE;
142 	if (s == "top") return V4L2_FIELD_TOP;
143 	if (s == "bottom") return V4L2_FIELD_BOTTOM;
144 	if (s == "interlaced") return V4L2_FIELD_INTERLACED;
145 	if (s == "seq_tb") return V4L2_FIELD_SEQ_TB;
146 	if (s == "seq_bt") return V4L2_FIELD_SEQ_BT;
147 	if (s == "alternate") return V4L2_FIELD_ALTERNATE;
148 	if (s == "interlaced_tb") return V4L2_FIELD_INTERLACED_TB;
149 	if (s == "interlaced_bt") return V4L2_FIELD_INTERLACED_BT;
150 	return V4L2_FIELD_ANY;
151 }
152 
parse_colorspace(const QString & s)153 static uint32_t parse_colorspace(const QString &s)
154 {
155 	if (s == "smpte170m") return V4L2_COLORSPACE_SMPTE170M;
156 	if (s == "smpte240m") return V4L2_COLORSPACE_SMPTE240M;
157 	if (s == "rec709") return V4L2_COLORSPACE_REC709;
158 	if (s == "470m") return V4L2_COLORSPACE_470_SYSTEM_M;
159 	if (s == "470bg") return V4L2_COLORSPACE_470_SYSTEM_BG;
160 	if (s == "jpeg") return V4L2_COLORSPACE_JPEG;
161 	if (s == "srgb") return V4L2_COLORSPACE_SRGB;
162 	if (s == "oprgb") return V4L2_COLORSPACE_OPRGB;
163 	if (s == "bt2020") return V4L2_COLORSPACE_BT2020;
164 	if (s == "dcip3") return V4L2_COLORSPACE_DCI_P3;
165 	return 0;
166 }
167 
parse_xfer_func(const QString & s)168 static uint32_t parse_xfer_func(const QString &s)
169 {
170 	if (s == "default") return V4L2_XFER_FUNC_DEFAULT;
171 	if (s == "smpte240m") return V4L2_XFER_FUNC_SMPTE240M;
172 	if (s == "rec709") return V4L2_XFER_FUNC_709;
173 	if (s == "srgb") return V4L2_XFER_FUNC_SRGB;
174 	if (s == "oprgb") return V4L2_XFER_FUNC_OPRGB;
175 	if (s == "dcip3") return V4L2_XFER_FUNC_DCI_P3;
176 	if (s == "smpte2084") return V4L2_XFER_FUNC_SMPTE2084;
177 	if (s == "none") return V4L2_XFER_FUNC_NONE;
178 	return 0;
179 }
180 
parse_ycbcr(const QString & s)181 static uint32_t parse_ycbcr(const QString &s)
182 {
183 	if (s == "default") return V4L2_YCBCR_ENC_DEFAULT;
184 	if (s == "601") return V4L2_YCBCR_ENC_601;
185 	if (s == "709") return V4L2_YCBCR_ENC_709;
186 	if (s == "xv601") return V4L2_YCBCR_ENC_XV601;
187 	if (s == "xv709") return V4L2_YCBCR_ENC_XV709;
188 	if (s == "bt2020") return V4L2_YCBCR_ENC_BT2020;
189 	if (s == "bt2020c") return V4L2_YCBCR_ENC_BT2020_CONST_LUM;
190 	if (s == "smpte240m") return V4L2_YCBCR_ENC_SMPTE240M;
191 	return V4L2_YCBCR_ENC_DEFAULT;
192 }
193 
parse_hsv(const QString & s)194 static uint32_t parse_hsv(const QString &s)
195 {
196 	if (s == "default") return V4L2_YCBCR_ENC_DEFAULT;
197 	if (s == "180") return V4L2_HSV_ENC_180;
198 	if (s == "256") return V4L2_HSV_ENC_256;
199 	return V4L2_YCBCR_ENC_DEFAULT;
200 }
201 
parse_quantization(const QString & s)202 static uint32_t parse_quantization(const QString &s)
203 {
204 	if (s == "default") return V4L2_QUANTIZATION_DEFAULT;
205 	if (s == "full-range") return V4L2_QUANTIZATION_FULL_RANGE;
206 	if (s == "lim-range") return V4L2_QUANTIZATION_LIM_RANGE;
207 	return V4L2_QUANTIZATION_DEFAULT;
208 }
209 
parse_pixel_format(const QString & s)210 static uint32_t parse_pixel_format(const QString &s)
211 {
212 	for (unsigned i = 0; formats[i]; i++)
213 		if (s == fcc2s(formats[i]).c_str())
214 			return formats[i];
215 	return 0;
216 }
217 
findVal(const uint32_t * values,uint32_t & val)218 static unsigned findVal(const uint32_t *values, uint32_t &val)
219 {
220 	unsigned idx = 0;
221 	uint32_t first = *values;
222 
223 	while (*values) {
224 		if (*values == val)
225 			return idx;
226 		values++;
227 		idx++;
228 	}
229 	val = first;
230 	return 0;
231 }
232 
list_formats()233 static void list_formats()
234 {
235 	for (unsigned i = 0; formats[i]; i++)
236 		printf("'%s':\r\t\t%s\n", fcc2s(formats[i]).c_str(),
237 		       pixfmt2s(formats[i]).c_str());
238 }
239 
processOption(const QStringList & args,int & i,QString & s)240 static bool processOption(const QStringList &args, int &i, QString &s)
241 {
242 	int index = -1;
243 
244 	if (args[i][1] == '-')
245 		index = args[i].indexOf('=');
246 	else if (args[i].length() > 2)
247 		index = 1;
248 
249 	if (index >= 0) {
250 		s = args[i].mid(index + 1);
251 		if (s.length() == 0) {
252 			usageError(args[i].toUtf8());
253 			return false;
254 		}
255 		return true;
256 	}
257 	if (i + 1 >= args.size()) {
258 		usageError(args[i].toUtf8());
259 		return false;
260 	}
261 	s = args[++i];
262 	return true;
263 }
264 
processOption(const QStringList & args,int & i,unsigned & u)265 static bool processOption(const QStringList &args, int &i, unsigned &u)
266 {
267 	QString s;
268 	bool ok = processOption(args, i, s);
269 
270 	if (!ok)
271 		return ok;
272 	u = s.toUInt(&ok, 0);
273 	if (!ok)
274 		usageInvParm(s.toUtf8());
275 	return ok;
276 }
277 
processOption(const QStringList & args,int & i,int & v)278 static bool processOption(const QStringList &args, int &i, int &v)
279 {
280 	QString s;
281 	bool ok = processOption(args, i, s);
282 
283 	if (!ok)
284 		return ok;
285 	v = s.toInt(&ok, 0);
286 	if (!ok)
287 		usageInvParm(s.toUtf8());
288 	return ok;
289 }
290 
processOption(const QStringList & args,int & i,double & v)291 static bool processOption(const QStringList &args, int &i, double &v)
292 {
293 	QString s;
294 	bool ok = processOption(args, i, s);
295 
296 	if (!ok)
297 		return ok;
298 	v = s.toDouble(&ok);
299 	if (!ok)
300 		usageInvParm(s.toUtf8());
301 	return ok;
302 }
303 
isOptArg(const QString & opt,const char * longOpt,const char * shortOpt=NULL)304 static bool isOptArg(const QString &opt, const char *longOpt, const char *shortOpt = NULL)
305 {
306 	return opt.startsWith(longOpt) || (shortOpt && opt.startsWith(shortOpt));
307 }
308 
isOption(const QString & opt,const char * longOpt,const char * shortOpt=NULL)309 static bool isOption(const QString &opt, const char *longOpt, const char *shortOpt = NULL)
310 {
311 	return opt == longOpt || opt == shortOpt;
312 }
313 
read_u32(int fd)314 uint32_t read_u32(int fd)
315 {
316 	uint32_t v;
317 	int n;
318 
319 	n = read(fd, &v, sizeof(v));
320 	if (n != sizeof(v)) {
321 		fprintf(stderr, "could not read uint32_t\n");
322 		std::exit(EXIT_FAILURE);
323 	}
324 	return ntohl(v);
325 }
326 
initSocket(int port,cv4l_fmt & fmt,v4l2_fract & pixelaspect)327 int initSocket(int port, cv4l_fmt &fmt, v4l2_fract &pixelaspect)
328 {
329 	static int listen_fd = -1;
330 	int sock_fd;
331 	socklen_t clilen;
332 	struct sockaddr_in serv_addr = {}, cli_addr;
333 	int val = 1;
334 
335 	if (listen_fd < 0) {
336 		listen_fd = socket(AF_INET, SOCK_STREAM, 0);
337 		if (listen_fd < 0) {
338 			fprintf(stderr, "could not opening socket\n");
339 			std::exit(EXIT_FAILURE);
340 		}
341 		setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int));
342 
343 		serv_addr.sin_family = AF_INET;
344 		serv_addr.sin_addr.s_addr = INADDR_ANY;
345 		serv_addr.sin_port = htons(port);
346 		if (bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
347 			fprintf(stderr, "could not bind: %s\n", strerror(errno));
348 			std::exit(EXIT_FAILURE);
349 		}
350 	}
351 	listen(listen_fd, 1);
352 	clilen = sizeof(cli_addr);
353 	sock_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &clilen);
354 	if (sock_fd < 0) {
355 		fprintf(stderr, "could not accept\n");
356 		std::exit(EXIT_FAILURE);
357 	}
358 	if (read_u32(sock_fd) != V4L_STREAM_ID) {
359 		fprintf(stderr, "unknown protocol ID\n");
360 		std::exit(EXIT_FAILURE);
361 	}
362 	uint32_t version = read_u32(sock_fd);
363 
364 	if (!version || version > V4L_STREAM_VERSION) {
365 		fprintf(stderr, "unknown protocol version %u\n", version);
366 		std::exit(EXIT_FAILURE);
367 	}
368 	for (;;) {
369 		uint32_t packet = read_u32(sock_fd);
370 		char buf[1024];
371 
372 		if (packet == V4L_STREAM_PACKET_END) {
373 			fprintf(stderr, "END packet read\n");
374 			std::exit(EXIT_FAILURE);
375 		}
376 
377 		if (packet == V4L_STREAM_PACKET_FMT_VIDEO)
378 			break;
379 
380 		unsigned sz = read_u32(sock_fd);
381 		while (sz) {
382 			unsigned rdsize = sz > sizeof(buf) ? sizeof(buf) : sz;
383 			int n;
384 
385 			n = read(sock_fd, buf, rdsize);
386 			if (n < 0) {
387 				fprintf(stderr, "error reading %d bytes\n", sz);
388 				std::exit(EXIT_FAILURE);
389 			}
390 			sz -= n;
391 		}
392 	}
393 	read_u32(sock_fd);
394 	fmt.s_type(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
395 	unsigned sz = read_u32(sock_fd);
396 
397 	if (sz != V4L_STREAM_PACKET_FMT_VIDEO_SIZE_FMT) {
398 		fprintf(stderr, "unsupported FMT_VIDEO size\n");
399 		std::exit(EXIT_FAILURE);
400 	}
401 	fmt.s_num_planes(read_u32(sock_fd));
402 	fmt.s_pixelformat(read_u32(sock_fd));
403 	fmt.s_width(read_u32(sock_fd));
404 	fmt.s_height(read_u32(sock_fd));
405 	fmt.s_field(read_u32(sock_fd));
406 	fmt.s_colorspace(read_u32(sock_fd));
407 	fmt.s_ycbcr_enc(read_u32(sock_fd));
408 	fmt.s_quantization(read_u32(sock_fd));
409 	fmt.s_xfer_func(read_u32(sock_fd));
410 	fmt.s_flags(read_u32(sock_fd));
411 	pixelaspect.numerator = read_u32(sock_fd);
412 	pixelaspect.denominator = read_u32(sock_fd);
413 
414 	for (unsigned i = 0; i < fmt.g_num_planes(); i++) {
415 		unsigned sz = read_u32(sock_fd);
416 
417 		if (sz != V4L_STREAM_PACKET_FMT_VIDEO_SIZE_FMT_PLANE) {
418 			fprintf(stderr, "unsupported FMT_VIDEO plane size\n");
419 			std::exit(EXIT_FAILURE);
420 		}
421 		fmt.s_sizeimage(read_u32(sock_fd), i);
422 		fmt.s_bytesperline(read_u32(sock_fd), i);
423 	}
424 	return sock_fd;
425 }
426 
main(int argc,char ** argv)427 int main(int argc, char **argv)
428 {
429 	QApplication disp(argc, argv);
430 	QScrollArea *sa = new QScrollArea; // Automatically freed on window close
431 	QSurfaceFormat format;
432 	QString video_device = "0";
433 	QString filename;
434 	cv4l_fd fd;
435 	enum AppMode mode = AppModeV4L2;
436 	int sock_fd = -1;
437 	cv4l_fmt fmt;
438 	v4l2_fract pixelaspect = { 1, 1 };
439 	unsigned cnt = 0;
440 	unsigned v4l2_bufs = 4;
441 	bool single_step = false;
442 	unsigned single_step_start = 1;
443 	int port = 0;
444 	bool info_option = false;
445 	bool report_timings = false;
446 	bool verbose = false;
447 	uint32_t overridePixelFormat = 0;
448 	uint32_t overrideWidth = 0;
449 	uint32_t overrideHeight = 0;
450 	uint32_t overrideHorPadding = 0;
451 	uint32_t overrideField = 0xffffffff;
452 	uint32_t overrideColorspace = 0xffffffff;
453 	uint32_t overrideYCbCrEnc = 0xffffffff;
454 	uint32_t overrideHSVEnc = 0xffffffff;
455 	uint32_t overrideXferFunc = 0xffffffff;
456 	uint32_t overrideQuantization = 0xffffffff;
457 	double fps = 30;
458 	unsigned pattern = 0;
459 	unsigned test = 0;
460 	unsigned test_mask = 0;
461 	bool square = false;
462 	bool border = false;
463 	bool sav = false;
464 	bool eav = false;
465 	int tpg_pixelaspect = -1;
466 	tpg_video_aspect video_aspect = TPG_VIDEO_ASPECT_IMAGE;
467 	unsigned alpha = 0;
468 	bool alpha_red_only = false;
469 	bool rgb_lim_range = false;
470 	unsigned perc_fill = 100;
471 	tpg_move_mode hor_mode = TPG_MOVE_NONE;
472 	tpg_move_mode vert_mode = TPG_MOVE_NONE;
473 	bool force_opengl = false;
474 	bool force_opengles = false;
475 
476 	disp.setWindowIcon(QIcon(":/qvidcap.png"));
477 	disp.setApplicationDisplayName("V4L2 Viewer");
478 	QStringList args = disp.arguments();
479 	for (int i = 1; i < args.size(); i++) {
480 		QString s;
481 
482 		if (isOptArg(args[i], "--device", "-d")) {
483 			if (!processOption(args, i, video_device))
484 				return 0;
485 			mode = AppModeV4L2;
486 		} else if (isOptArg(args[i], "--file", "-f")) {
487 			if (!processOption(args, i, filename))
488 				return 0;
489 			mode = AppModeFile;
490 		} else if (isOption(args[i], "--port", "-p")) {
491 			mode = AppModeSocket;
492 			port = V4L_STREAM_PORT;
493 		} else if (isOptArg(args[i], "--port", "-p")) {
494 			if (!processOption(args, i, port))
495 				return 0;
496 			mode = AppModeSocket;
497 		} else if (isOption(args[i], "--tpg", "-T")) {
498 			mode = AppModeTPG;
499 		} else if (isOptArg(args[i], "--test-mask")) {
500 			if (!processOption(args, i, test_mask))
501 				return 0;
502 		} else if (isOptArg(args[i], "--test")) {
503 			if (!processOption(args, i, test))
504 				return 0;
505 			mode = AppModeTest;
506 			test &= ~1;
507 			if (test == 0)
508 				test = 2;
509 			if (!pattern)
510 				pattern = TPG_PAT_CSC_COLORBAR;
511 		} else if (isOptArg(args[i], "--pixelformat", "-P")) {
512 			if (!processOption(args, i, s))
513 				return 0;
514 			overridePixelFormat = parse_pixel_format(s);
515 		} else if (isOptArg(args[i], "--width", "-W")) {
516 			if (!processOption(args, i, overrideWidth))
517 				return 0;
518 		} else if (isOptArg(args[i], "--height", "-H")) {
519 			if (!processOption(args, i, overrideHeight))
520 				return 0;
521 		} else if (isOptArg(args[i], "--padding", "-A")) {
522 			if (!processOption(args, i, overrideHorPadding))
523 				return 0;
524 		} else if (isOptArg(args[i], "--field", "-F")) {
525 			if (!processOption(args, i, s))
526 				return 0;
527 			overrideField = parse_field(s);
528 		} else if (isOptArg(args[i], "--colorspace", "-C")) {
529 			if (!processOption(args, i, s))
530 				return 0;
531 			overrideColorspace = parse_colorspace(s);
532 		} else if (isOptArg(args[i], "--ycbcr-enc", "-Y")) {
533 			if (!processOption(args, i, s))
534 				return 0;
535 			overrideYCbCrEnc = parse_ycbcr(s);
536 		} else if (isOptArg(args[i], "--hsv-enc", "-H")) {
537 			if (!processOption(args, i, s))
538 				return 0;
539 			overrideHSVEnc = parse_hsv(s);
540 		} else if (isOptArg(args[i], "--xfer-func", "-X")) {
541 			if (!processOption(args, i, s))
542 				return 0;
543 			overrideXferFunc = parse_xfer_func(s);
544 		} else if (isOptArg(args[i], "--quant", "-Q")) {
545 			if (!processOption(args, i, s))
546 				return 0;
547 			overrideQuantization = parse_quantization(s);
548 		} else if (isOption(args[i], "--list-patterns")) {
549 			printf("List of available patterns:\n");
550 			for (unsigned i = 0; tpg_pattern_strings[i]; i++)
551 				printf("\t%2d: %s\n", i, tpg_pattern_strings[i]);
552 			info_option = true;
553 		} else if (isOptArg(args[i], "--fps")) {
554 			if (!processOption(args, i, fps))
555 				return 0;
556 			if (fps <= 0)
557 				fps = 30;
558 		} else if (isOptArg(args[i], "--pattern")) {
559 			if (!processOption(args, i, pattern))
560 				return 0;
561 		} else if (isOption(args[i], "--square")) {
562 		} else if (args[i] == "--square") {
563 			square = true;
564 		} else if (isOption(args[i], "--border")) {
565 			border = true;
566 		} else if (isOption(args[i], "--sav")) {
567 			sav = true;
568 		} else if (isOption(args[i], "--eav")) {
569 			eav = true;
570 		} else if (isOptArg(args[i], "--pixel-aspect")) {
571 			if (!processOption(args, i, s))
572 				return 0;
573 			if (s == "square") {
574 				tpg_pixelaspect = TPG_PIXEL_ASPECT_SQUARE;
575 			} else if (s == "ntsc") {
576 				static const v4l2_fract hz50 = { 11, 12 };
577 				pixelaspect = hz50;
578 				tpg_pixelaspect = TPG_PIXEL_ASPECT_NTSC;
579 			} else if (s == "pal") {
580 				static const v4l2_fract hz60 = { 11, 10 };
581 				pixelaspect = hz60;
582 				tpg_pixelaspect = TPG_PIXEL_ASPECT_PAL;
583 			} else {
584 				usage();
585 				return 0;
586 			}
587 		} else if (isOptArg(args[i], "--video-aspect")) {
588 			if (!processOption(args, i, s))
589 				return 0;
590 			if (s == "4x3")
591 				video_aspect = TPG_VIDEO_ASPECT_4X3;
592 			else if (s == "14x9")
593 				video_aspect = TPG_VIDEO_ASPECT_14X9_CENTRE;
594 			else if (s == "16x9")
595 				video_aspect = TPG_VIDEO_ASPECT_16X9_CENTRE;
596 			else if (s == "anamorphic")
597 				video_aspect = TPG_VIDEO_ASPECT_16X9_ANAMORPHIC;
598 			else {
599 				usage();
600 				return 0;
601 			}
602 		} else if (isOption(args[i], "--alpha-red-only")) {
603 			alpha_red_only = true;
604 		} else if (isOptArg(args[i], "--alpha")) {
605 			if (!processOption(args, i, alpha))
606 				return 0;
607 			if (alpha > 255)
608 				alpha = 255;
609 		} else if (isOption(args[i], "--rgb-lim-range")) {
610 			rgb_lim_range = true;
611 		} else if (isOptArg(args[i], "--hor-speed")) {
612 			int speed;
613 
614 			if (!processOption(args, i, speed))
615 				return 0;
616 			if (speed < -3)
617 				speed = -3;
618 			if (speed > 3)
619 				speed = 3;
620 			hor_mode = (tpg_move_mode)(speed + 3);
621 		} else if (isOptArg(args[i], "--vert-speed")) {
622 			int speed;
623 
624 			if (!processOption(args, i, speed))
625 				return 0;
626 			if (speed < -3)
627 				speed = -3;
628 			if (speed > 3)
629 				speed = 3;
630 			vert_mode = (tpg_move_mode)(speed + 3);
631 		} else if (isOptArg(args[i], "--perc-fill")) {
632 			if (!processOption(args, i, perc_fill))
633 				return 0;
634 			if (perc_fill > 100)
635 				perc_fill = 100;
636 		} else if (isOption(args[i], "--help", "-h")) {
637 			usage();
638 			info_option = true;
639 		} else if (isOption(args[i], "--list-formats", "-l")) {
640 			list_formats();
641 			info_option = true;
642 		} else if (isOption(args[i], "--timings", "-t")) {
643 			report_timings = true;
644 		} else if (isOptArg(args[i], "--opengles")) {
645 			force_opengles = true;
646 		} else if (isOptArg(args[i], "--opengl")) {
647 			force_opengl = true;
648 		} else if (isOption(args[i], "--verbose", "-v")) {
649 			verbose = true;
650 		} else if (isOption(args[i], "--raw", "-R")) {
651 			fd.s_direct(true);
652 		} else if (isOptArg(args[i], "--count", "-c")) {
653 			if (!processOption(args, i, cnt))
654 				return 0;
655 		} else if (isOptArg(args[i], "--buffers", "-b")) {
656 			if (!processOption(args, i, v4l2_bufs))
657 				return 0;
658 		} else if (isOption(args[i], "--single-step", "-s")) {
659 			single_step = true;
660 			single_step_start = 1;
661 		} else if (isOptArg(args[i], "--single-step", "-s")) {
662 			if (!processOption(args, i, single_step_start))
663 				return 0;
664 			single_step = true;
665 		} else {
666 			printf("Invalid argument %s\n", args[i].toUtf8().data());
667 			return 0;
668 		}
669 	}
670 	if (info_option)
671 		return 0;
672 
673 	if (mode == AppModeV4L2) {
674 		fps = 0;
675 		video_device = getDeviceName("/dev/video", video_device);
676 		if (fd.open(video_device.toUtf8().data(), true) < 0) {
677 			perror((QString("could not open ") + video_device).toUtf8().data());
678 			std::exit(EXIT_FAILURE);
679 		}
680 		if (!fd.has_vid_cap()) {
681 			fprintf(stderr, "%s is not a video capture device\n", video_device.toUtf8().data());
682 			std::exit(EXIT_FAILURE);
683 		}
684 		fd.g_fmt(fmt);
685 
686 		if (!overridePixelFormat) {
687 			bool found = false;
688 
689 			for (unsigned i = 0; formats[i]; i++) {
690 				if (fmt.g_pixelformat() == formats[i]) {
691 					found = true;
692 					break;
693 				}
694 			}
695 			if (!found)
696 				overridePixelFormat = V4L2_PIX_FMT_RGB24;
697 		}
698 
699 		if (overridePixelFormat) {
700 			fmt.s_pixelformat(overridePixelFormat);
701 			fd.s_fmt(fmt);
702 			fd.g_fmt(fmt);
703 			if (fmt.g_pixelformat() != overridePixelFormat) {
704 				fprintf(stderr, "Could not set format: '%s' %s\n",
705 					fcc2s(overridePixelFormat).c_str(),
706 					pixfmt2s(overridePixelFormat).c_str());
707 				fprintf(stderr, "Fall back to format: '%s' %s\n",
708 					fcc2s(fmt.g_pixelformat()).c_str(),
709 					pixfmt2s(fmt.g_pixelformat()).c_str());
710 			}
711 		}
712 
713 		unsigned tmp_w, tmp_h;
714 
715 		pixelaspect = fd.g_pixel_aspect(tmp_w, tmp_h);
716 	} else if (mode == AppModeSocket) {
717 		fps = 0;
718 		sock_fd = initSocket(port, fmt, pixelaspect);
719 	} else {
720 		fmt.s_type(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
721 		fmt.s_num_planes(1);
722 		fmt.s_pixelformat(V4L2_PIX_FMT_RGB24);
723 		fmt.s_width(640);
724 		fmt.s_height(360);
725 		fmt.s_field(V4L2_FIELD_NONE);
726 		fmt.s_colorspace(V4L2_COLORSPACE_SRGB);
727 		fmt.s_xfer_func(V4L2_XFER_FUNC_DEFAULT);
728 		fmt.s_ycbcr_enc(V4L2_YCBCR_ENC_DEFAULT);
729 		fmt.s_quantization(V4L2_QUANTIZATION_DEFAULT);
730 		fmt.s_bytesperline(3 * fmt.g_width());
731 		fmt.s_sizeimage(fmt.g_bytesperline() * fmt.g_height());
732 	}
733 
734 	format.setDepthBufferSize(24);
735 
736 	if (force_opengles)
737 		format.setRenderableType(QSurfaceFormat::OpenGLES);
738 	else if (force_opengl)
739 		format.setRenderableType(QSurfaceFormat::OpenGL);
740 	format.setProfile(QSurfaceFormat::CoreProfile);
741 	format.setVersion(3, 3);
742 
743 	QSurfaceFormat::setDefaultFormat(format);
744 	CaptureWin win(sa);
745 	win.setVerbose(verbose);
746 	if (mode == AppModeFile) {
747 		win.setModeFile(filename);
748 		if (single_step_start)
749 			single_step_start--;
750 	} else if (mode == AppModeV4L2) {
751 		win.setModeV4L2(&fd);
752 	} else if (mode == AppModeTPG) {
753 		win.setModeTPG();
754 	}
755 	win.setOverrideWidth(overrideWidth);
756 	win.setOverrideHeight(overrideHeight);
757 	win.setOverrideHorPadding(overrideHorPadding);
758 	win.setFps(fps);
759 	win.setFormat(format);
760 	win.setReportTimings(report_timings);
761 	win.setCount(test ? test : cnt);
762 	if (mode == AppModeTest) {
763 		win.setModeTest(test);
764 
765 		TestState state = { };
766 
767 		state.fmt_idx = findVal(formats, overridePixelFormat);
768 		state.field_idx = findVal(fields, overrideField);
769 		state.colorspace_idx = findVal(colorspaces, overrideColorspace);
770 		state.xfer_func_idx = findVal(xfer_funcs, overrideXferFunc);
771 		state.ycbcr_enc_idx = findVal(ycbcr_encs, overrideYCbCrEnc);
772 		state.hsv_enc_idx = findVal(hsv_encs, overrideHSVEnc);
773 		state.quant_idx = findVal(quantizations, overrideQuantization);
774 		state.mask = test_mask;
775 		win.setTestState(state);
776 	}
777 
778 	win.setOverridePixelFormat(overridePixelFormat);
779 	win.setOverrideField(overrideField);
780 	win.setOverrideColorspace(overrideColorspace);
781 	win.setOverrideYCbCrEnc(overrideYCbCrEnc);
782 	win.setOverrideHSVEnc(overrideHSVEnc);
783 	win.setOverrideXferFunc(overrideXferFunc);
784 	win.setOverrideQuantization(overrideQuantization);
785 	while (!win.setV4LFormat(fmt)) {
786 		fprintf(stderr, "Unsupported format: '%s' %s\n",
787 			fcc2s(fmt.g_pixelformat()).c_str(),
788 			pixfmt2s(fmt.g_pixelformat()).c_str());
789 		if (mode != AppModeSocket)
790 			std::exit(EXIT_FAILURE);
791 		sock_fd = initSocket(port, fmt, pixelaspect);
792 	}
793 	win.setPixelAspect(pixelaspect);
794 	win.setMinimumSize(16, 16);
795 	win.setSizeIncrement(2, 2);
796 	win.resize(fmt.g_width(), fmt.g_frame_height());
797 	win.setFocusPolicy(Qt::StrongFocus);
798 	if (single_step && mode != AppModeTest)
799 		win.setSingleStepStart(single_step_start);
800 
801 	sa->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
802 	sa->setWidget(win.window());
803 	sa->setFrameShape(QFrame::NoFrame);
804 	sa->resize(win.correctAspect(QSize(fmt.g_width(), fmt.g_frame_height())));
805 	sa->setWidgetResizable(true);
806 
807 	if (mode == AppModeSocket)
808 		win.setModeSocket(sock_fd, port);
809 	else if (mode == AppModeV4L2) {
810 		cv4l_queue q(fd.g_type(), V4L2_MEMORY_MMAP);
811 		q.reqbufs(&fd, v4l2_bufs);
812 		q.obtain_bufs(&fd);
813 		q.queue_all(&fd);
814 		win.setQueue(&q);
815 		if (fd.streamon())
816 			std::exit(EXIT_FAILURE);
817 	} else {
818 		struct tpg_data *tpg = win.getTPG();
819 
820 		tpg_init(tpg, fmt.g_width(), fmt.g_height());
821 		tpg_alloc(tpg, fmt.g_width());
822 		tpg_s_pattern(tpg, (tpg_pattern)pattern);
823 		tpg_s_mv_hor_mode(tpg, hor_mode);
824 		tpg_s_mv_vert_mode(tpg, vert_mode);
825 		tpg_s_show_square(tpg, square);
826 		tpg_s_show_border(tpg, border);
827 		tpg_s_insert_sav(tpg, sav);
828 		tpg_s_insert_eav(tpg, eav);
829 		tpg_s_perc_fill(tpg, perc_fill);
830 		if (rgb_lim_range)
831 			tpg_s_real_rgb_range(tpg, V4L2_DV_RGB_RANGE_LIMITED);
832 		tpg_s_alpha_component(tpg, alpha);
833 		tpg_s_alpha_mode(tpg, alpha_red_only);
834 		tpg_s_video_aspect(tpg, video_aspect);
835 		switch (tpg_pixelaspect) {
836 		case -1:
837 			break;
838 		default:
839 			tpg_s_pixel_aspect(tpg, (tpg_pixel_aspect)tpg_pixelaspect);
840 			break;
841 		}
842 		win.startTimer();
843 	}
844 	sa->show();
845 	return disp.exec();
846 }
847