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