1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * The YUY2 shader code is based on face-responder. The code is under public domain:
4 * https://bitbucket.org/nateharward/face-responder/src/0c3b4b957039d9f4bf1da09b9471371942de2601/yuv42201_laplace.frag?at=master
5 *
6 * All other OpenGL code:
7 *
8 * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
9 */
10
11 #include "capture.h"
12
13 #include <QtCore/QTextStream>
14 #include <QtCore/QCoreApplication>
15 #include <QtGui/QOpenGLContext>
16 #if QT_VERSION < 0x060000
17 #include <QtGui/QOpenGLPaintDevice>
18 #else
19 #include <QOpenGLPaintDevice>
20 #endif
21 #include <QtGui/QContextMenuEvent>
22 #include <QtGui/QKeyEvent>
23 #include <QtGui/QPainter>
24 #include <QtCore/QSocketNotifier>
25 #include <QtMath>
26 #include <QTimer>
27 #include <QApplication>
28
29 #include <netinet/in.h>
30 #include "v4l2-info.h"
31
32 const __u32 formats[] = {
33 V4L2_PIX_FMT_YUYV,
34 V4L2_PIX_FMT_YVYU,
35 V4L2_PIX_FMT_UYVY,
36 V4L2_PIX_FMT_VYUY,
37 V4L2_PIX_FMT_YUV422P,
38 V4L2_PIX_FMT_YVU420,
39 V4L2_PIX_FMT_YUV420,
40 V4L2_PIX_FMT_NV12,
41 V4L2_PIX_FMT_NV21,
42 V4L2_PIX_FMT_NV16,
43 V4L2_PIX_FMT_NV61,
44 V4L2_PIX_FMT_NV24,
45 V4L2_PIX_FMT_NV42,
46 V4L2_PIX_FMT_NV16M,
47 V4L2_PIX_FMT_NV61M,
48 V4L2_PIX_FMT_YVU420M,
49 V4L2_PIX_FMT_YUV420M,
50 V4L2_PIX_FMT_YVU422M,
51 V4L2_PIX_FMT_YUV422M,
52 V4L2_PIX_FMT_YVU444M,
53 V4L2_PIX_FMT_YUV444M,
54 V4L2_PIX_FMT_NV12M,
55 V4L2_PIX_FMT_NV21M,
56 V4L2_PIX_FMT_YUV444,
57 V4L2_PIX_FMT_YUV555,
58 V4L2_PIX_FMT_YUV565,
59 V4L2_PIX_FMT_YUV32,
60 V4L2_PIX_FMT_AYUV32,
61 V4L2_PIX_FMT_XYUV32,
62 V4L2_PIX_FMT_VUYA32,
63 V4L2_PIX_FMT_VUYX32,
64 V4L2_PIX_FMT_YUVA32,
65 V4L2_PIX_FMT_YUVX32,
66 V4L2_PIX_FMT_RGB32,
67 V4L2_PIX_FMT_XRGB32,
68 V4L2_PIX_FMT_ARGB32,
69 V4L2_PIX_FMT_RGBX32,
70 V4L2_PIX_FMT_RGBA32,
71 V4L2_PIX_FMT_BGR32,
72 V4L2_PIX_FMT_XBGR32,
73 V4L2_PIX_FMT_ABGR32,
74 V4L2_PIX_FMT_BGRX32,
75 V4L2_PIX_FMT_BGRA32,
76 V4L2_PIX_FMT_RGB24,
77 V4L2_PIX_FMT_BGR24,
78 V4L2_PIX_FMT_RGB565,
79 V4L2_PIX_FMT_RGB565X,
80 V4L2_PIX_FMT_RGB444,
81 V4L2_PIX_FMT_XRGB444,
82 V4L2_PIX_FMT_ARGB444,
83 V4L2_PIX_FMT_XBGR444,
84 V4L2_PIX_FMT_ABGR444,
85 V4L2_PIX_FMT_RGBX444,
86 V4L2_PIX_FMT_RGBA444,
87 V4L2_PIX_FMT_BGRX444,
88 V4L2_PIX_FMT_BGRA444,
89 V4L2_PIX_FMT_RGB555,
90 V4L2_PIX_FMT_XRGB555,
91 V4L2_PIX_FMT_ARGB555,
92 V4L2_PIX_FMT_RGB555X,
93 V4L2_PIX_FMT_XRGB555X,
94 V4L2_PIX_FMT_ARGB555X,
95 V4L2_PIX_FMT_RGBX555,
96 V4L2_PIX_FMT_RGBA555,
97 V4L2_PIX_FMT_XBGR555,
98 V4L2_PIX_FMT_ABGR555,
99 V4L2_PIX_FMT_BGRX555,
100 V4L2_PIX_FMT_BGRA555,
101 V4L2_PIX_FMT_RGB332,
102 V4L2_PIX_FMT_BGR666,
103 V4L2_PIX_FMT_SBGGR8,
104 V4L2_PIX_FMT_SGBRG8,
105 V4L2_PIX_FMT_SGRBG8,
106 V4L2_PIX_FMT_SRGGB8,
107 V4L2_PIX_FMT_SBGGR10,
108 V4L2_PIX_FMT_SGBRG10,
109 V4L2_PIX_FMT_SGRBG10,
110 V4L2_PIX_FMT_SRGGB10,
111 V4L2_PIX_FMT_SBGGR12,
112 V4L2_PIX_FMT_SGBRG12,
113 V4L2_PIX_FMT_SGRBG12,
114 V4L2_PIX_FMT_SRGGB12,
115 V4L2_PIX_FMT_SBGGR16,
116 V4L2_PIX_FMT_SGBRG16,
117 V4L2_PIX_FMT_SGRBG16,
118 V4L2_PIX_FMT_SRGGB16,
119 V4L2_PIX_FMT_HSV24,
120 V4L2_PIX_FMT_HSV32,
121 V4L2_PIX_FMT_GREY,
122 V4L2_PIX_FMT_Y10,
123 V4L2_PIX_FMT_Y12,
124 V4L2_PIX_FMT_Y16,
125 V4L2_PIX_FMT_Y16_BE,
126 V4L2_PIX_FMT_Z16,
127 0
128 };
129
130 const __u32 fields[] = {
131 V4L2_FIELD_NONE,
132 V4L2_FIELD_TOP,
133 V4L2_FIELD_BOTTOM,
134 V4L2_FIELD_INTERLACED,
135 V4L2_FIELD_SEQ_TB,
136 V4L2_FIELD_SEQ_BT,
137 V4L2_FIELD_ALTERNATE,
138 V4L2_FIELD_INTERLACED_TB,
139 V4L2_FIELD_INTERLACED_BT,
140 0
141 };
142
143 const __u32 colorspaces[] = {
144 V4L2_COLORSPACE_SMPTE170M,
145 V4L2_COLORSPACE_SMPTE240M,
146 V4L2_COLORSPACE_REC709,
147 V4L2_COLORSPACE_470_SYSTEM_M,
148 V4L2_COLORSPACE_470_SYSTEM_BG,
149 V4L2_COLORSPACE_SRGB,
150 V4L2_COLORSPACE_OPRGB,
151 V4L2_COLORSPACE_DCI_P3,
152 V4L2_COLORSPACE_BT2020,
153 0
154 };
155
156 const __u32 xfer_funcs[] = {
157 V4L2_XFER_FUNC_709,
158 V4L2_XFER_FUNC_SRGB,
159 V4L2_XFER_FUNC_OPRGB,
160 V4L2_XFER_FUNC_DCI_P3,
161 V4L2_XFER_FUNC_SMPTE2084,
162 V4L2_XFER_FUNC_SMPTE240M,
163 V4L2_XFER_FUNC_NONE,
164 0
165 };
166
167 const __u32 ycbcr_encs[] = {
168 V4L2_YCBCR_ENC_601,
169 V4L2_YCBCR_ENC_709,
170 V4L2_YCBCR_ENC_XV601,
171 V4L2_YCBCR_ENC_XV709,
172 V4L2_YCBCR_ENC_BT2020,
173 V4L2_YCBCR_ENC_BT2020_CONST_LUM,
174 V4L2_YCBCR_ENC_SMPTE240M,
175 0
176 };
177
178 const __u32 hsv_encs[] = {
179 V4L2_HSV_ENC_180,
180 V4L2_HSV_ENC_256,
181 0
182 };
183
184 const __u32 quantizations[] = {
185 V4L2_QUANTIZATION_FULL_RANGE,
186 V4L2_QUANTIZATION_LIM_RANGE,
187 0
188 };
189
190 enum {
191 CAPTURE_GL_WIN_RESIZE,
192 CAPTURE_GL_WIN_SCROLLBAR,
193 };
194
checkSubMenuItem(QMenu * menu,__u32 value)195 static void checkSubMenuItem(QMenu *menu, __u32 value)
196 {
197 for (auto &action : menu->actions()) {
198 if (action->data() == value) {
199 action->setChecked(true);
200 break;
201 }
202 }
203 }
204
addSubMenuItem(QActionGroup * grp,QMenu * menu,const QString & text,int val)205 static QAction *addSubMenuItem(QActionGroup *grp, QMenu *menu, const QString &text, int val)
206 {
207 QAction *a = grp->addAction(menu->addAction(text));
208
209 a->setData(QVariant(val));
210 a->setCheckable(true);
211 return a;
212 }
213
CaptureWin(QScrollArea * sa,QWidget * parent)214 CaptureWin::CaptureWin(QScrollArea *sa, QWidget *parent) :
215 QOpenGLWidget(parent),
216 m_fd(0),
217 m_sock(0),
218 m_v4l_queue(0),
219 m_frame(0),
220 m_ctx(0),
221 m_origPixelFormat(0),
222 m_fps(0),
223 m_singleStep(false),
224 m_singleStepStart(0),
225 m_singleStepNext(false),
226 m_screenTextureCount(0),
227 m_program(0),
228 m_curIndex(-1),
229 m_nextIndex(-1),
230 m_scrollArea(sa)
231 {
232 m_curSize[0] = 0;
233 m_curData[0] = 0;
234 m_canOverrideResolution = false;
235 m_pixelaspect.numerator = 1;
236 m_pixelaspect.denominator = 1;
237 QMenu *menu = new QMenu("Override Pixel Format (P)");
238 m_fmtMenu = menu;
239 QActionGroup *grp = new QActionGroup(menu);
240 addSubMenuItem(grp, menu, "No Override", 0)->setChecked(true);
241 for (unsigned i = 0; formats[i]; i++) {
242 std::string fmt = "'" + fcc2s(formats[i]) + "' " + pixfmt2s(formats[i]);
243
244 addSubMenuItem(grp, menu, fmt.c_str(), formats[i]);
245 }
246 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(fmtChanged(QAction *)));
247
248 menu = new QMenu("Override Field (I)");
249 m_fieldMenu = menu;
250 grp = new QActionGroup(menu);
251 addSubMenuItem(grp, menu, "No Override", -1)->setChecked(true);
252 for (unsigned i = 0; fields[i]; i++)
253 addSubMenuItem(grp, menu,
254 field2s(fields[i]).c_str(), fields[i]);
255 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(fieldChanged(QAction *)));
256
257 menu = new QMenu("Override Colorspace (C)");
258 m_colorspaceMenu = menu;
259 grp = new QActionGroup(menu);
260 addSubMenuItem(grp, menu, "No Override", -1)->setChecked(true);
261 for (unsigned i = 0; colorspaces[i]; i++)
262 addSubMenuItem(grp, menu,
263 colorspace2s(colorspaces[i]).c_str(), colorspaces[i]);
264 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(colorspaceChanged(QAction *)));
265
266 menu = new QMenu("Override Transfer Function (X)");
267 m_xferFuncMenu = menu;
268 grp = new QActionGroup(menu);
269 addSubMenuItem(grp, menu, "No Override", -1)->setChecked(true);
270 for (unsigned i = 0; xfer_funcs[i]; i++)
271 addSubMenuItem(grp, menu,
272 xfer_func2s(xfer_funcs[i]).c_str(), xfer_funcs[i]);
273 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(xferFuncChanged(QAction *)));
274
275 menu = new QMenu("Override Y'CbCr Encoding (Y)");
276 m_ycbcrEncMenu = menu;
277 grp = new QActionGroup(menu);
278 addSubMenuItem(grp, menu, "No Override", -1)->setChecked(true);
279 for (unsigned i = 0; ycbcr_encs[i]; i++)
280 addSubMenuItem(grp, menu,
281 ycbcr_enc2s(ycbcr_encs[i]).c_str(), ycbcr_encs[i]);
282 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(ycbcrEncChanged(QAction *)));
283
284 menu = new QMenu("Override HSV Encoding (H)");
285 m_hsvEncMenu = menu;
286 grp = new QActionGroup(menu);
287 addSubMenuItem(grp, menu, "No Override", -1)->setChecked(true);
288 for (unsigned i = 0; hsv_encs[i]; i++)
289 addSubMenuItem(grp, menu,
290 ycbcr_enc2s(hsv_encs[i]).c_str(), hsv_encs[i]);
291 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(hsvEncChanged(QAction *)));
292
293 menu = new QMenu("Override Quantization (R)");
294 m_quantMenu = menu;
295 grp = new QActionGroup(menu);
296 addSubMenuItem(grp, menu, "No Override", -1)->setChecked(true);
297 for (unsigned i = 0; quantizations[i]; i++)
298 addSubMenuItem(grp, menu,
299 quantization2s(quantizations[i]).c_str(), quantizations[i]);
300 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(quantChanged(QAction *)));
301
302 menu = new QMenu("Display Options");
303 m_displayMenu = menu;
304 grp = new QActionGroup(menu);
305 addSubMenuItem(grp, menu, "Frame Resize", CAPTURE_GL_WIN_RESIZE)->setChecked(true);
306 addSubMenuItem(grp, menu, "Window Scrollbars", CAPTURE_GL_WIN_SCROLLBAR)->setChecked(false);
307 connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(windowScalingChanged(QAction *)));
308
309 m_resolutionOverride = new QAction("Override resolution", this);
310 m_resolutionOverride->setCheckable(true);
311 connect(m_resolutionOverride, SIGNAL(triggered(bool)),
312 this, SLOT(resolutionOverrideChanged(bool)));
313
314 m_enterFullScreen = new QAction("Enter fullscreen (F)", this);
315 connect(m_enterFullScreen, SIGNAL(triggered(bool)),
316 this, SLOT(toggleFullScreen(bool)));
317
318 m_exitFullScreen = new QAction("Exit fullscreen (F or Esc)", this);
319 connect(m_exitFullScreen, SIGNAL(triggered(bool)),
320 this, SLOT(toggleFullScreen(bool)));
321 }
322
~CaptureWin()323 CaptureWin::~CaptureWin()
324 {
325 makeCurrent();
326 delete m_program;
327 }
328
resizeEvent(QResizeEvent * event)329 void CaptureWin::resizeEvent(QResizeEvent *event)
330 {
331 QSize origSize = correctAspect(QSize(m_origWidth, m_origHeight));
332 QSize window = size();
333 qreal scale;
334
335 if ((qreal)window.width() / (qreal)origSize.width() >
336 (qreal)window.height() / (qreal)origSize.height()) {
337 // Horizontal scale factor > vert. scale factor
338 scale = (qreal)window.height() / (qreal)origSize.height();
339 } else {
340 scale = (qreal)window.width() / (qreal)origSize.width();
341 }
342 m_viewSize = QSize((qreal)m_origWidth * scale, (qreal)m_origHeight * scale);
343 QOpenGLWidget::resizeEvent(event);
344 }
345
updateShader()346 void CaptureWin::updateShader()
347 {
348 setV4LFormat(m_v4l_fmt);
349 if (m_mode == AppModeTest || m_mode == AppModeTPG || m_mode == AppModeFile) {
350 delete m_timer;
351 m_timer = NULL;
352 startTimer();
353 }
354 m_updateShader = true;
355 }
356
showCurrentOverrides()357 void CaptureWin::showCurrentOverrides()
358 {
359 static bool firstTime = true;
360 const char *prefix = firstTime ? "" : "New ";
361
362 if (m_mode == AppModeTest)
363 return;
364
365 if (m_canOverrideResolution || firstTime) {
366 printf("%sPixel Format: '%s' %s\n", prefix,
367 fcc2s(m_origPixelFormat).c_str(),
368 pixfmt2s(m_origPixelFormat).c_str());
369 printf("%sField: %s\n", prefix, field2s(m_origField).c_str());
370 }
371 printf("%sColorspace: %s\n", prefix, colorspace2s(m_origColorspace).c_str());
372 printf("%sTransfer Function: %s\n", prefix, xfer_func2s(m_origXferFunc).c_str());
373 if (m_is_hsv)
374 printf("%sHSV Encoding: %s\n", prefix, ycbcr_enc2s(m_origHSVEnc).c_str());
375 else if (!m_is_rgb)
376 printf("%sY'CbCr Encoding: %s\n", prefix, ycbcr_enc2s(m_origYCbCrEnc).c_str());
377 printf("%sQuantization: %s\n", prefix, quantization2s(m_origQuantization).c_str());
378 firstTime = false;
379 }
380
restoreAll(bool checked)381 void CaptureWin::restoreAll(bool checked)
382 {
383 m_overridePixelFormat = m_origPixelFormat;
384 m_overrideField = m_origField;
385 m_overrideColorspace = m_origColorspace;
386 m_overrideXferFunc = m_origXferFunc;
387 m_overrideYCbCrEnc = m_origYCbCrEnc;
388 m_overrideHSVEnc = m_origHSVEnc;
389 m_overrideQuantization = m_origQuantization;
390 m_overrideWidth = m_overrideHeight = 0;
391 showCurrentOverrides();
392 restoreSize();
393 }
394
fmtChanged(QAction * a)395 void CaptureWin::fmtChanged(QAction *a)
396 {
397 m_overridePixelFormat = a->data().toInt();
398 if (m_overridePixelFormat == 0)
399 m_overridePixelFormat = m_origPixelFormat;
400 printf("New Pixel Format: '%s' %s\n",
401 fcc2s(m_overridePixelFormat).c_str(),
402 pixfmt2s(m_overridePixelFormat).c_str());
403 updateShader();
404 }
405
fieldChanged(QAction * a)406 void CaptureWin::fieldChanged(QAction *a)
407 {
408 m_overrideField = a->data().toInt();
409 if (m_overrideField == 0xffffffff)
410 m_overrideField = m_origField;
411 printf("New Field: %s\n", field2s(m_overrideField).c_str());
412 updateShader();
413 }
414
colorspaceChanged(QAction * a)415 void CaptureWin::colorspaceChanged(QAction *a)
416 {
417 m_overrideColorspace = a->data().toInt();
418 if (m_overrideColorspace == 0xffffffff)
419 m_overrideColorspace = m_origColorspace;
420 printf("New Colorspace: %s\n", colorspace2s(m_overrideColorspace).c_str());
421 updateShader();
422 }
423
xferFuncChanged(QAction * a)424 void CaptureWin::xferFuncChanged(QAction *a)
425 {
426 m_overrideXferFunc = a->data().toInt();
427 if (m_overrideXferFunc == 0xffffffff)
428 m_overrideXferFunc = m_origXferFunc;
429 printf("New Transfer Function: %s\n", xfer_func2s(m_overrideXferFunc).c_str());
430 updateShader();
431 }
432
ycbcrEncChanged(QAction * a)433 void CaptureWin::ycbcrEncChanged(QAction *a)
434 {
435 m_overrideYCbCrEnc = a->data().toInt();
436 if (m_overrideYCbCrEnc == 0xffffffff)
437 m_overrideYCbCrEnc = m_origYCbCrEnc;
438 printf("New Y'CbCr Encoding: %s\n", ycbcr_enc2s(m_overrideYCbCrEnc).c_str());
439 updateShader();
440 }
441
hsvEncChanged(QAction * a)442 void CaptureWin::hsvEncChanged(QAction *a)
443 {
444 m_overrideHSVEnc = a->data().toInt();
445 if (m_overrideHSVEnc == 0xffffffff)
446 m_overrideHSVEnc = m_origHSVEnc;
447 printf("New HSV Encoding: %s\n", ycbcr_enc2s(m_overrideHSVEnc).c_str());
448 updateShader();
449 }
450
quantChanged(QAction * a)451 void CaptureWin::quantChanged(QAction *a)
452 {
453 m_overrideQuantization = a->data().toInt();
454 if (m_overrideQuantization == 0xffffffff)
455 m_overrideQuantization = m_origQuantization;
456 printf("New Quantization Range: %s\n", quantization2s(m_overrideQuantization).c_str());
457 updateShader();
458 }
459
restoreSize(bool)460 void CaptureWin::restoreSize(bool)
461 {
462 QSize s = correctAspect(QSize(m_origWidth, m_origHeight));
463
464 m_scrollArea->resize(s);
465 resize(s);
466 updateShader();
467 }
468
correctAspect(const QSize & s) const469 QSize CaptureWin::correctAspect(const QSize &s) const
470 {
471 qreal aspect
472 = (qreal)m_pixelaspect.denominator
473 / (qreal)m_pixelaspect.numerator;
474
475 qreal w = s.width();
476 qreal h = s.height();
477
478 if (aspect < 1.0)
479 h *= 1.0 / aspect;
480 else
481 w *= aspect;
482 return QSize(qFloor(w), qFloor(h));
483 }
484
windowScalingChanged(QAction * a)485 void CaptureWin::windowScalingChanged(QAction *a)
486 {
487 m_scrollArea->setWidgetResizable(a->data().toInt() == CAPTURE_GL_WIN_RESIZE);
488 resize(correctAspect(QSize(m_origWidth, m_origHeight)));
489 updateShader();
490 }
491
resolutionOverrideChanged(bool checked)492 void CaptureWin::resolutionOverrideChanged(bool checked)
493 {
494 if (m_overrideWidth)
495 m_origWidth = m_overrideWidth;
496 if (m_overrideHeight)
497 m_origHeight = m_overrideHeight;
498
499 restoreSize();
500 }
501
toggleFullScreen(bool)502 void CaptureWin::toggleFullScreen(bool)
503 {
504 if (m_scrollArea->isFullScreen())
505 m_scrollArea->showNormal();
506 else
507 m_scrollArea->showFullScreen();
508 }
509
contextMenuEvent(QContextMenuEvent * event)510 void CaptureWin::contextMenuEvent(QContextMenuEvent *event)
511 {
512 QMenu menu(this);
513
514 QAction *act = menu.addAction("Restore All");
515 connect(act, SIGNAL(triggered(bool)), this, SLOT(restoreAll(bool)));
516
517 act = menu.addAction("Reset window");
518 connect(act, SIGNAL(triggered(bool)), this, SLOT(restoreSize(bool)));
519
520 if (m_scrollArea->isFullScreen())
521 menu.addAction(m_exitFullScreen);
522 else
523 menu.addAction(m_enterFullScreen);
524
525 if (m_canOverrideResolution) {
526 menu.addAction(m_resolutionOverride);
527 menu.addMenu(m_fmtMenu);
528 }
529 menu.addMenu(m_fieldMenu);
530 menu.addMenu(m_colorspaceMenu);
531 menu.addMenu(m_xferFuncMenu);
532 if (m_is_hsv)
533 menu.addMenu(m_hsvEncMenu);
534 else if (!m_is_rgb)
535 menu.addMenu(m_ycbcrEncMenu);
536 menu.addMenu(m_quantMenu);
537 menu.addMenu(m_displayMenu);
538
539 menu.exec(event->globalPos());
540 }
541
mouseDoubleClickEvent(QMouseEvent * e)542 void CaptureWin::mouseDoubleClickEvent(QMouseEvent * e)
543 {
544 if (e->button() != Qt::LeftButton)
545 return;
546
547 toggleFullScreen();
548 }
549
cycleMenu(__u32 & overrideVal,__u32 origVal,const __u32 values[],bool hasShift,bool hasCtrl)550 void CaptureWin::cycleMenu(__u32 &overrideVal, __u32 origVal,
551 const __u32 values[], bool hasShift, bool hasCtrl)
552 {
553 unsigned i;
554
555 if (overrideVal == 0xffffffff || hasCtrl)
556 overrideVal = origVal;
557 if (hasCtrl)
558 return;
559 for (i = 0; values[i] && values[i] != overrideVal; i++);
560 if (!values[i])
561 overrideVal = values[0];
562 else if (hasShift) {
563 if (i)
564 overrideVal = values[i - 1];
565 else for (i = 0; values[i]; i++)
566 overrideVal = values[i];
567 } else {
568 if (!values[i + 1])
569 overrideVal = values[0];
570 else
571 overrideVal = values[i + 1];
572 }
573 }
574
keyPressEvent(QKeyEvent * event)575 void CaptureWin::keyPressEvent(QKeyEvent *event)
576 {
577 unsigned w = m_v4l_fmt.g_width();
578 unsigned h = m_v4l_fmt.g_frame_height();
579 unsigned p = m_overrideHorPadding;
580 bool hasShift = event->modifiers() & Qt::ShiftModifier;
581 bool hasCtrl = event->modifiers() & Qt::ControlModifier;
582 bool scalingEnabled = m_canOverrideResolution &&
583 m_resolutionOverride->isChecked();
584
585 switch (event->key()) {
586 case Qt::Key_Space:
587 if (m_mode == AppModeTest)
588 m_cnt = 1;
589 else if (m_singleStep && m_frame > m_singleStepStart)
590 m_singleStepNext = true;
591 return;
592 case Qt::Key_Escape:
593 if (!m_scrollArea->isFullScreen())
594 return;
595 case Qt::Key_Left:
596 if (hasShift) {
597 if (scalingEnabled && p >= 2)
598 p -= 2;
599 } else {
600 if (scalingEnabled && w > 16)
601 w -= 2;
602 }
603 break;
604 case Qt::Key_Right:
605 if (hasShift) {
606 if (scalingEnabled && p < 10240)
607 p += 2;
608 } else {
609 if (scalingEnabled && w < 10240)
610 w += 2;
611 }
612 break;
613 case Qt::Key_Up:
614 if (scalingEnabled && h > 16)
615 h -= 2;
616 break;
617 case Qt::Key_Down:
618 if (scalingEnabled && h < 10240)
619 h += 2;
620 break;
621
622 case Qt::Key_C:
623 cycleMenu(m_overrideColorspace, m_origColorspace,
624 colorspaces, hasShift, hasCtrl);
625 printf("New Colorspace: %s\n", colorspace2s(m_overrideColorspace).c_str());
626 checkSubMenuItem(m_colorspaceMenu, m_overrideColorspace);
627 updateShader();
628 return;
629 case Qt::Key_F:
630 toggleFullScreen();
631 return;
632 case Qt::Key_H:
633 if (!m_is_hsv)
634 return;
635 cycleMenu(m_overrideHSVEnc, m_origHSVEnc,
636 hsv_encs, hasShift, hasCtrl);
637 printf("New HSV Encoding: %s\n", ycbcr_enc2s(m_overrideHSVEnc).c_str());
638 checkSubMenuItem(m_hsvEncMenu, m_overrideHSVEnc);
639 updateShader();
640 return;
641 case Qt::Key_I:
642 cycleMenu(m_overrideField, m_origField,
643 fields, hasShift, hasCtrl);
644 printf("New Field: %s\n", field2s(m_overrideField).c_str());
645 checkSubMenuItem(m_fieldMenu, m_overrideField);
646 updateShader();
647 return;
648 case Qt::Key_P:
649 if (!m_canOverrideResolution)
650 return;
651 cycleMenu(m_overridePixelFormat, m_origPixelFormat,
652 formats, hasShift, hasCtrl);
653 printf("New Pixel Format: '%s' %s\n", fcc2s(m_overridePixelFormat).c_str(),
654 pixfmt2s(m_overridePixelFormat).c_str());
655 checkSubMenuItem(m_fmtMenu, m_overridePixelFormat);
656 updateShader();
657 return;
658 case Qt::Key_Q:
659 QApplication::quit();
660 return;
661 case Qt::Key_R:
662 cycleMenu(m_overrideQuantization, m_origQuantization,
663 quantizations, hasShift, hasCtrl);
664 printf("New Quantization Range: %s\n", quantization2s(m_overrideQuantization).c_str());
665 checkSubMenuItem(m_quantMenu, m_overrideQuantization);
666 updateShader();
667 return;
668 case Qt::Key_X:
669 cycleMenu(m_overrideXferFunc, m_origXferFunc,
670 xfer_funcs, hasShift, hasCtrl);
671 printf("New Transfer Function: %s\n", xfer_func2s(m_overrideXferFunc).c_str());
672 checkSubMenuItem(m_xferFuncMenu, m_overrideXferFunc);
673 updateShader();
674 return;
675 case Qt::Key_Y:
676 if (m_is_rgb || m_is_hsv)
677 return;
678 cycleMenu(m_overrideYCbCrEnc, m_origYCbCrEnc,
679 ycbcr_encs, hasShift, hasCtrl);
680 printf("New Y'CbCr Encoding: %s\n", ycbcr_enc2s(m_overrideYCbCrEnc).c_str());
681 checkSubMenuItem(m_ycbcrEncMenu, m_overrideYCbCrEnc);
682 updateShader();
683 return;
684 default:
685 QOpenGLWidget::keyPressEvent(event);
686 return;
687 }
688
689 if (scalingEnabled) {
690 if (m_scrollArea->widgetResizable())
691 m_scrollArea->resize(w, h);
692 else
693 resize(w, h);
694
695 if ((w + p) != m_v4l_fmt.g_bytesperline()) {
696 printf("New horizontal resolution: %u + %u (%u)\n", w, p, w + p);
697 setOverrideHorPadding(p);
698 updateShader();
699 }
700 }
701 }
702
supportedFmt(__u32 fmt)703 bool CaptureWin::supportedFmt(__u32 fmt)
704 {
705 switch (fmt) {
706 case V4L2_PIX_FMT_RGB565X:
707 case V4L2_PIX_FMT_Y16_BE:
708 return m_haveSwapBytes;
709
710 /*
711 * Note for RGB555(X) formats:
712 * openGL ES doesn't support GL_UNSIGNED_SHORT_1_5_5_5_REV
713 */
714 case V4L2_PIX_FMT_RGB555:
715 case V4L2_PIX_FMT_XRGB555:
716 case V4L2_PIX_FMT_ARGB555:
717 case V4L2_PIX_FMT_RGB555X:
718 case V4L2_PIX_FMT_XRGB555X:
719 case V4L2_PIX_FMT_ARGB555X:
720 case V4L2_PIX_FMT_RGBX555:
721 case V4L2_PIX_FMT_RGBA555:
722 case V4L2_PIX_FMT_XBGR555:
723 case V4L2_PIX_FMT_ABGR555:
724 case V4L2_PIX_FMT_BGRX555:
725 case V4L2_PIX_FMT_BGRA555:
726 case V4L2_PIX_FMT_YUV555:
727 case V4L2_PIX_FMT_RGB332:
728 case V4L2_PIX_FMT_BGR666:
729 return !context()->isOpenGLES();
730 }
731
732 return true;
733 }
734
checkError(const char * msg)735 void CaptureWin::checkError(const char *msg)
736 {
737 int err;
738 unsigned errNo = 0;
739
740 while ((err = glGetError()) != GL_NO_ERROR)
741 fprintf(stderr, "OpenGL Error (no: %u) code 0x%x: %s.\n", errNo++, err, msg);
742
743 if (errNo)
744 std::exit(errNo);
745 }
746
configureTexture(size_t idx)747 void CaptureWin::configureTexture(size_t idx)
748 {
749 glBindTexture(GL_TEXTURE_2D, m_screenTexture[idx]);
750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
751 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
752 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
753 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
754 }
755
setOverrideWidth(__u32 w)756 void CaptureWin::setOverrideWidth(__u32 w)
757 {
758 m_overrideWidth = w;
759
760 if (!m_overrideWidth && m_canOverrideResolution)
761 m_resolutionOverride->setChecked(true);
762 }
763
setOverrideHeight(__u32 h)764 void CaptureWin::setOverrideHeight(__u32 h)
765 {
766 m_overrideHeight = h;
767
768 if (!m_overrideHeight && m_canOverrideResolution)
769 m_resolutionOverride->setChecked(true);
770 }
771
setOverrideHorPadding(__u32 p)772 void CaptureWin::setOverrideHorPadding(__u32 p)
773 {
774 m_overrideHorPadding = p;
775
776 if (!m_overrideHorPadding && m_canOverrideResolution)
777 m_resolutionOverride->setChecked(true);
778 }
779
setModeV4L2(cv4l_fd * fd)780 void CaptureWin::setModeV4L2(cv4l_fd *fd)
781 {
782 m_mode = AppModeV4L2;
783 m_fd = fd;
784 QSocketNotifier *readSock = new QSocketNotifier(fd->g_fd(),
785 QSocketNotifier::Read, this);
786 QSocketNotifier *excepSock = new QSocketNotifier(fd->g_fd(),
787 QSocketNotifier::Exception, this);
788
789 v4l2_event_subscription sub = { };
790
791 sub.type = V4L2_EVENT_SOURCE_CHANGE;
792 m_fd->subscribe_event(sub);
793 connect(readSock, SIGNAL(activated(int)), this, SLOT(v4l2ReadEvent()));
794 connect(excepSock, SIGNAL(activated(int)), this, SLOT(v4l2ExceptionEvent()));
795
796 if (m_verbose && m_fd->g_direct())
797 printf("using libv4l2\n");
798 }
799
setModeSocket(int socket,int port)800 void CaptureWin::setModeSocket(int socket, int port)
801 {
802 m_mode = AppModeSocket;
803 m_sock = socket;
804 m_port = port;
805 if (m_ctx)
806 free(m_ctx);
807 m_ctx = fwht_alloc(m_v4l_fmt.g_pixelformat(), m_v4l_fmt.g_width(), m_v4l_fmt.g_height(),
808 m_v4l_fmt.g_width(), m_v4l_fmt.g_height(),
809 m_v4l_fmt.g_field(), m_v4l_fmt.g_colorspace(), m_v4l_fmt.g_xfer_func(),
810 m_v4l_fmt.g_ycbcr_enc(), m_v4l_fmt.g_quantization());
811
812 QSocketNotifier *readSock = new QSocketNotifier(m_sock,
813 QSocketNotifier::Read, this);
814
815 connect(readSock, SIGNAL(activated(int)), this, SLOT(sockReadEvent()));
816 }
817
setModeFile(const QString & filename)818 void CaptureWin::setModeFile(const QString &filename)
819 {
820 m_mode = AppModeFile;
821 m_file.setFileName(filename);
822 if (!m_file.open(QIODevice::ReadOnly)) {
823 fprintf(stderr, "could not open %s\n", filename.toUtf8().data());
824 std::exit(EXIT_FAILURE);
825 }
826 m_canOverrideResolution = true;
827 }
828
setModeTPG()829 void CaptureWin::setModeTPG()
830 {
831 m_mode = AppModeTPG;
832 }
833
setModeTest(unsigned cnt)834 void CaptureWin::setModeTest(unsigned cnt)
835 {
836 m_mode = AppModeTest;
837 m_test = cnt;
838 }
839
setQueue(cv4l_queue * q)840 void CaptureWin::setQueue(cv4l_queue *q)
841 {
842 m_v4l_queue = q;
843 if (m_origPixelFormat == 0)
844 updateOrigValues();
845 }
846
updateV4LFormat(const cv4l_fmt & fmt)847 bool CaptureWin::updateV4LFormat(const cv4l_fmt &fmt)
848 {
849 m_is_rgb = true;
850 m_is_hsv = false;
851 m_uses_gl_red = false;
852 m_accepts_srgb = true;
853 m_is_bayer = false;
854
855 switch (fmt.g_pixelformat()) {
856 case V4L2_PIX_FMT_YUV422P:
857 case V4L2_PIX_FMT_YUV420:
858 case V4L2_PIX_FMT_YVU420:
859 case V4L2_PIX_FMT_YUV420M:
860 case V4L2_PIX_FMT_YVU420M:
861 case V4L2_PIX_FMT_YUV422M:
862 case V4L2_PIX_FMT_YVU422M:
863 case V4L2_PIX_FMT_YUV444M:
864 case V4L2_PIX_FMT_YVU444M:
865 case V4L2_PIX_FMT_NV12:
866 case V4L2_PIX_FMT_NV21:
867 case V4L2_PIX_FMT_NV16:
868 case V4L2_PIX_FMT_NV61:
869 case V4L2_PIX_FMT_NV24:
870 case V4L2_PIX_FMT_NV42:
871 case V4L2_PIX_FMT_NV16M:
872 case V4L2_PIX_FMT_NV61M:
873 case V4L2_PIX_FMT_NV12M:
874 case V4L2_PIX_FMT_NV21M:
875 m_uses_gl_red = true;
876 /* fall through */
877 case V4L2_PIX_FMT_YUYV:
878 case V4L2_PIX_FMT_YVYU:
879 case V4L2_PIX_FMT_UYVY:
880 case V4L2_PIX_FMT_VYUY:
881 case V4L2_PIX_FMT_YUV444:
882 case V4L2_PIX_FMT_YUV555:
883 case V4L2_PIX_FMT_YUV565:
884 case V4L2_PIX_FMT_YUV32:
885 case V4L2_PIX_FMT_AYUV32:
886 case V4L2_PIX_FMT_XYUV32:
887 case V4L2_PIX_FMT_VUYA32:
888 case V4L2_PIX_FMT_VUYX32:
889 case V4L2_PIX_FMT_YUVA32:
890 case V4L2_PIX_FMT_YUVX32:
891 m_is_rgb = false;
892 m_accepts_srgb = false;
893 break;
894 case V4L2_PIX_FMT_HSV24:
895 case V4L2_PIX_FMT_HSV32:
896 m_is_rgb = false;
897 m_is_hsv = true;
898 m_accepts_srgb = false;
899 break;
900 case V4L2_PIX_FMT_SBGGR8:
901 case V4L2_PIX_FMT_SGBRG8:
902 case V4L2_PIX_FMT_SGRBG8:
903 case V4L2_PIX_FMT_SRGGB8:
904 case V4L2_PIX_FMT_SBGGR10:
905 case V4L2_PIX_FMT_SGBRG10:
906 case V4L2_PIX_FMT_SGRBG10:
907 case V4L2_PIX_FMT_SRGGB10:
908 case V4L2_PIX_FMT_SBGGR12:
909 case V4L2_PIX_FMT_SGBRG12:
910 case V4L2_PIX_FMT_SGRBG12:
911 case V4L2_PIX_FMT_SRGGB12:
912 case V4L2_PIX_FMT_SBGGR16:
913 case V4L2_PIX_FMT_SGBRG16:
914 case V4L2_PIX_FMT_SGRBG16:
915 case V4L2_PIX_FMT_SRGGB16:
916 m_is_bayer = true;
917 /* fall through */
918 case V4L2_PIX_FMT_GREY:
919 case V4L2_PIX_FMT_Y10:
920 case V4L2_PIX_FMT_Y12:
921 case V4L2_PIX_FMT_Y16:
922 case V4L2_PIX_FMT_Y16_BE:
923 case V4L2_PIX_FMT_Z16:
924 m_uses_gl_red = true;
925 /* fall through */
926 case V4L2_PIX_FMT_BGR666:
927 case V4L2_PIX_FMT_RGB565:
928 case V4L2_PIX_FMT_RGB565X:
929 case V4L2_PIX_FMT_RGB444:
930 case V4L2_PIX_FMT_XRGB444:
931 case V4L2_PIX_FMT_ARGB444:
932 case V4L2_PIX_FMT_XBGR444:
933 case V4L2_PIX_FMT_ABGR444:
934 case V4L2_PIX_FMT_RGBX444:
935 case V4L2_PIX_FMT_RGBA444:
936 case V4L2_PIX_FMT_BGRX444:
937 case V4L2_PIX_FMT_BGRA444:
938 case V4L2_PIX_FMT_RGB32:
939 case V4L2_PIX_FMT_XRGB32:
940 case V4L2_PIX_FMT_ARGB32:
941 case V4L2_PIX_FMT_RGBX32:
942 case V4L2_PIX_FMT_RGBA32:
943 case V4L2_PIX_FMT_BGR32:
944 case V4L2_PIX_FMT_XBGR32:
945 case V4L2_PIX_FMT_ABGR32:
946 case V4L2_PIX_FMT_BGRX32:
947 case V4L2_PIX_FMT_BGRA32:
948 m_accepts_srgb = false;
949 /* fall through */
950 case V4L2_PIX_FMT_RGB24:
951 case V4L2_PIX_FMT_BGR24:
952 case V4L2_PIX_FMT_RGB555:
953 case V4L2_PIX_FMT_XRGB555:
954 case V4L2_PIX_FMT_ARGB555:
955 case V4L2_PIX_FMT_RGB555X:
956 case V4L2_PIX_FMT_XRGB555X:
957 case V4L2_PIX_FMT_ARGB555X:
958 case V4L2_PIX_FMT_RGBX555:
959 case V4L2_PIX_FMT_RGBA555:
960 case V4L2_PIX_FMT_XBGR555:
961 case V4L2_PIX_FMT_ABGR555:
962 case V4L2_PIX_FMT_BGRX555:
963 case V4L2_PIX_FMT_BGRA555:
964 case V4L2_PIX_FMT_RGB332:
965 break;
966 default:
967 return false;
968 }
969 return true;
970 }
971
setV4LFormat(cv4l_fmt & fmt)972 bool CaptureWin::setV4LFormat(cv4l_fmt &fmt)
973 {
974 m_is_sdtv = false;
975
976 if (m_mode == AppModeFile && m_overridePixelFormat)
977 fmt.s_pixelformat(m_overridePixelFormat);
978
979 if (!updateV4LFormat(fmt))
980 return false;
981
982 if (m_is_bayer) {
983 m_v4l_fmt.s_field(V4L2_FIELD_NONE);
984 m_overrideField = 0;
985 }
986 if (m_mode == AppModeFile && m_overrideWidth)
987 fmt.s_width(m_overrideWidth);
988 if (m_mode == AppModeFile && m_overrideHorPadding)
989 fmt.s_bytesperline(fmt.g_bytesperline() + m_overrideHorPadding);
990 if (m_mode == AppModeFile && m_overrideField != 0xffffffff)
991 fmt.s_field(m_overrideField);
992 if (m_mode == AppModeFile && m_overrideHeight)
993 fmt.s_frame_height(m_overrideHeight);
994 if (m_overrideColorspace != 0xffffffff)
995 fmt.s_colorspace(m_overrideColorspace);
996 if (m_is_hsv && m_overrideHSVEnc != 0xffffffff)
997 fmt.s_ycbcr_enc(m_overrideHSVEnc);
998 else if (!m_is_rgb && m_overrideYCbCrEnc != 0xffffffff)
999 fmt.s_ycbcr_enc(m_overrideYCbCrEnc);
1000 if (m_overrideXferFunc != 0xffffffff)
1001 fmt.s_xfer_func(m_overrideXferFunc);
1002 if (m_overrideQuantization != 0xffffffff)
1003 fmt.s_quantization(m_overrideQuantization);
1004
1005 m_v4l_fmt = fmt;
1006
1007 v4l2_input in;
1008
1009 m_std = 0;
1010 if (m_fd && !m_fd->g_input(in.index) && !m_fd->enum_input(in, true, in.index)) {
1011 if (in.capabilities & V4L2_IN_CAP_STD) {
1012 m_is_sdtv = true;
1013 if (m_fd->g_std(m_std))
1014 m_std = fmt.g_frame_height() <= 480 ?
1015 V4L2_STD_525_60 : V4L2_STD_625_50;
1016 } else if (in.capabilities & V4L2_IN_CAP_DV_TIMINGS) {
1017 v4l2_dv_timings timings;
1018 if (m_fd->g_dv_timings(timings) == 0) {
1019 m_is_sdtv = timings.bt.width <= 720 && timings.bt.height <= 576;
1020 if (m_is_sdtv)
1021 m_std = timings.bt.height <= 480 ?
1022 V4L2_STD_525_60 : V4L2_STD_625_50;
1023 }
1024 }
1025 }
1026
1027 switch (fmt.g_colorspace()) {
1028 case V4L2_COLORSPACE_SMPTE170M:
1029 case V4L2_COLORSPACE_SMPTE240M:
1030 case V4L2_COLORSPACE_REC709:
1031 case V4L2_COLORSPACE_470_SYSTEM_M:
1032 case V4L2_COLORSPACE_470_SYSTEM_BG:
1033 case V4L2_COLORSPACE_SRGB:
1034 case V4L2_COLORSPACE_OPRGB:
1035 case V4L2_COLORSPACE_BT2020:
1036 case V4L2_COLORSPACE_DCI_P3:
1037 break;
1038 default:
1039 // If the colorspace was not specified, then guess
1040 // based on the pixel format.
1041 if (m_is_rgb)
1042 m_v4l_fmt.s_colorspace(V4L2_COLORSPACE_SRGB);
1043 else if (m_is_sdtv)
1044 m_v4l_fmt.s_colorspace(V4L2_COLORSPACE_SMPTE170M);
1045 else
1046 m_v4l_fmt.s_colorspace(V4L2_COLORSPACE_REC709);
1047 break;
1048 }
1049 if (fmt.g_xfer_func() == V4L2_XFER_FUNC_DEFAULT)
1050 m_v4l_fmt.s_xfer_func(V4L2_MAP_XFER_FUNC_DEFAULT(m_v4l_fmt.g_colorspace()));
1051 if (m_is_hsv)
1052 m_v4l_fmt.s_ycbcr_enc(fmt.g_hsv_enc());
1053 else if (fmt.g_ycbcr_enc() == V4L2_YCBCR_ENC_DEFAULT)
1054 m_v4l_fmt.s_ycbcr_enc(V4L2_MAP_YCBCR_ENC_DEFAULT(m_v4l_fmt.g_colorspace()));
1055 if (fmt.g_quantization() == V4L2_QUANTIZATION_DEFAULT)
1056 m_v4l_fmt.s_quantization(V4L2_MAP_QUANTIZATION_DEFAULT(m_is_rgb,
1057 m_v4l_fmt.g_colorspace(), m_v4l_fmt.g_ycbcr_enc()));
1058
1059 if (m_accepts_srgb &&
1060 (m_v4l_fmt.g_quantization() == V4L2_QUANTIZATION_LIM_RANGE ||
1061 m_v4l_fmt.g_xfer_func() != V4L2_XFER_FUNC_SRGB)) {
1062 /* Can't let openGL convert from non-linear to linear */
1063 m_accepts_srgb = false;
1064 }
1065
1066 if (m_verbose) {
1067 v4l2_fmtdesc fmt;
1068
1069 strcpy((char *)fmt.description, fcc2s(m_v4l_fmt.g_pixelformat()).c_str());
1070 if (m_fd) {
1071 m_fd->enum_fmt(fmt, true);
1072 while (fmt.pixelformat != m_v4l_fmt.g_pixelformat()) {
1073 if (m_fd->enum_fmt(fmt))
1074 break;
1075 }
1076 }
1077
1078 printf("\n");
1079 switch (m_mode) {
1080 case AppModeSocket:
1081 printf("Mode: Capture from socket\n");
1082 break;
1083 case AppModeFile:
1084 printf("Mode: Read from file\n");
1085 break;
1086 case AppModeTest:
1087 printf("Mode: Test Formats\n");
1088 break;
1089 case AppModeTPG:
1090 printf("Mode: Test Pattern Generator\n");
1091 break;
1092 case AppModeV4L2:
1093 default:
1094 printf("Mode: Capture\n");
1095 break;
1096 }
1097 printf("Width x Height: %ux%u\n", m_v4l_fmt.g_width(), m_v4l_fmt.g_height());
1098 printf("Field: %s\n", field2s(m_v4l_fmt.g_field()).c_str());
1099 printf("Pixel Format: %s ('%s')\n", fmt.description, fcc2s(m_v4l_fmt.g_pixelformat()).c_str());
1100 printf("Colorspace: %s\n", colorspace2s(m_v4l_fmt.g_colorspace()).c_str());
1101 if (m_is_hsv)
1102 printf("HSV Encoding: %s\n", ycbcr_enc2s(m_v4l_fmt.g_hsv_enc()).c_str());
1103 else if (!m_is_rgb)
1104 printf("Y'CbCr Encoding: %s\n", ycbcr_enc2s(m_v4l_fmt.g_ycbcr_enc()).c_str());
1105 printf("Transfer Function: %s\n", xfer_func2s(m_v4l_fmt.g_xfer_func()).c_str());
1106 printf("Quantization: %s\n", quantization2s(m_v4l_fmt.g_quantization()).c_str());
1107 for (unsigned i = 0; i < m_v4l_fmt.g_num_planes(); i++) {
1108 printf("Plane %d Image Size: %u\n", i, m_v4l_fmt.g_sizeimage(i));
1109 printf("Plane %d Bytes per Line: %u\n", i, m_v4l_fmt.g_bytesperline(i));
1110 }
1111 }
1112 return true;
1113 }
1114
setPixelAspect(const v4l2_fract & pixelaspect)1115 void CaptureWin::setPixelAspect(const v4l2_fract &pixelaspect)
1116 {
1117 m_pixelaspect = pixelaspect;
1118 }
1119
v4l2ReadEvent()1120 void CaptureWin::v4l2ReadEvent()
1121 {
1122 cv4l_buffer buf(m_fd->g_type());
1123
1124 if (m_singleStep && m_frame > m_singleStepStart && !m_singleStepNext)
1125 return;
1126
1127 m_singleStepNext = false;
1128 if (m_fd->dqbuf(buf))
1129 return;
1130
1131 for (unsigned i = 0; i < m_v4l_queue->g_num_planes(); i++) {
1132 m_nextData[i] = (__u8 *)m_v4l_queue->g_dataptr(buf.g_index(), i);
1133 m_nextSize[i] = buf.g_bytesused(i);
1134 }
1135 int next = m_nextIndex;
1136 m_nextIndex = buf.g_index();
1137 if (next != -1) {
1138 buf.s_index(next);
1139 m_fd->qbuf(buf);
1140 }
1141 m_frame++;
1142 update();
1143 if (m_cnt == 0)
1144 return;
1145 if (--m_cnt == 0)
1146 std::exit(EXIT_SUCCESS);
1147 }
1148
v4l2ExceptionEvent()1149 void CaptureWin::v4l2ExceptionEvent()
1150 {
1151 v4l2_event ev;
1152 cv4l_fmt fmt;
1153
1154 while (m_fd->dqevent(ev) == 0) {
1155 switch (ev.type) {
1156 case V4L2_EVENT_SOURCE_CHANGE:
1157 m_fd->g_fmt(fmt);
1158 if (!setV4LFormat(fmt)) {
1159 fprintf(stderr, "Unsupported format: '%s' %s\n",
1160 fcc2s(fmt.g_pixelformat()).c_str(),
1161 pixfmt2s(fmt.g_pixelformat()).c_str());
1162 std::exit(EXIT_FAILURE);
1163 }
1164 updateOrigValues();
1165 m_updateShader = true;
1166 break;
1167 }
1168 }
1169 }
1170
listenForNewConnection()1171 void CaptureWin::listenForNewConnection()
1172 {
1173 cv4l_fmt fmt;
1174 v4l2_fract pixelaspect = { 1, 1 };
1175
1176 ::close(m_sock);
1177
1178 for (unsigned p = 0; p < m_v4l_fmt.g_num_planes(); p++) {
1179 m_curSize[p] = 0;
1180 delete [] m_curData[p];
1181 m_curData[p] = NULL;
1182 }
1183
1184 int sock_fd;
1185
1186 for (;;) {
1187 sock_fd = initSocket(m_port, fmt, pixelaspect);
1188 if (setV4LFormat(fmt))
1189 break;
1190 fprintf(stderr, "Unsupported format: '%s' %s\n",
1191 fcc2s(fmt.g_pixelformat()).c_str(),
1192 pixfmt2s(fmt.g_pixelformat()).c_str());
1193 ::close(sock_fd);
1194 }
1195 if (m_ctx)
1196 free(m_ctx);
1197 m_ctx = fwht_alloc(fmt.g_pixelformat(), fmt.g_width(), fmt.g_height(),
1198 fmt.g_width(), fmt.g_height(),
1199 fmt.g_field(), fmt.g_colorspace(), fmt.g_xfer_func(),
1200 fmt.g_ycbcr_enc(), fmt.g_quantization());
1201 setPixelAspect(pixelaspect);
1202 updateOrigValues();
1203 setModeSocket(sock_fd, m_port);
1204 restoreSize();
1205 }
1206
read_u32(__u32 & v)1207 int CaptureWin::read_u32(__u32 &v)
1208 {
1209 int n;
1210
1211 v = 0;
1212 n = read(m_sock, &v, sizeof(v));
1213 if (n != sizeof(v)) {
1214 fprintf(stderr, "could not read __u32\n");
1215 return -1;
1216 }
1217 v = ntohl(v);
1218 return 0;
1219 }
1220
sockReadEvent()1221 void CaptureWin::sockReadEvent()
1222 {
1223 int n;
1224
1225 if (m_singleStep && m_frame > m_singleStepStart && !m_singleStepNext)
1226 return;
1227 m_singleStepNext = false;
1228
1229 if (m_origPixelFormat == 0)
1230 updateOrigValues();
1231
1232 if (m_curSize[0] == 0) {
1233 for (unsigned p = 0; p < m_v4l_fmt.g_num_planes(); p++) {
1234 m_curSize[p] = m_v4l_fmt.g_sizeimage(p);
1235 m_curData[p] = new __u8[m_curSize[p]];
1236 }
1237 }
1238
1239 unsigned packet, sz;
1240 bool is_fwht;
1241
1242 if (read_u32(packet))
1243 goto new_conn;
1244
1245 if (packet == V4L_STREAM_PACKET_END) {
1246 fprintf(stderr, "END packet read\n");
1247 goto new_conn;
1248 }
1249
1250 if (read_u32(sz))
1251 goto new_conn;
1252
1253 if (packet != V4L_STREAM_PACKET_FRAME_VIDEO_RLE &&
1254 packet != V4L_STREAM_PACKET_FRAME_VIDEO_FWHT) {
1255 char buf[1024];
1256
1257 fprintf(stderr, "expected FRAME_VIDEO, got 0x%08x\n", packet);
1258 while (sz) {
1259 unsigned rdsize = sz > sizeof(buf) ? sizeof(buf) : sz;
1260
1261 n = read(m_sock, buf, rdsize);
1262 if (n < 0) {
1263 fprintf(stderr, "error reading %d bytes\n", sz);
1264 goto new_conn;
1265 }
1266 sz -= n;
1267 }
1268 return;
1269 }
1270
1271 is_fwht = m_ctx && packet == V4L_STREAM_PACKET_FRAME_VIDEO_FWHT;
1272
1273 if (read_u32(sz))
1274 goto new_conn;
1275
1276 if (sz != V4L_STREAM_PACKET_FRAME_VIDEO_SIZE_HDR) {
1277 fprintf(stderr, "unsupported FRAME_VIDEO size\n");
1278 goto new_conn;
1279 }
1280 if (read_u32(sz) || // ignore field
1281 read_u32(sz)) // ignore flags
1282 goto new_conn;
1283
1284 for (unsigned p = 0; p < m_v4l_fmt.g_num_planes(); p++) {
1285 __u32 max_size = is_fwht ? m_ctx->comp_max_size : m_curSize[p];
1286 __u8 *dst = is_fwht ? m_ctx->state.compressed_frame : m_curData[p];
1287 __u32 data_size;
1288 __u32 offset;
1289 __u32 size;
1290
1291 if (read_u32(sz))
1292 goto new_conn;
1293 if (sz != V4L_STREAM_PACKET_FRAME_VIDEO_SIZE_PLANE_HDR) {
1294 fprintf(stderr, "unsupported FRAME_VIDEO plane size\n");
1295 goto new_conn;
1296 }
1297 if (read_u32(size) || read_u32(data_size))
1298 goto new_conn;
1299 offset = is_fwht ? 0 : size - data_size;
1300 sz = data_size;
1301
1302 if (data_size > max_size) {
1303 fprintf(stderr, "data size is too large (%u > %u)\n",
1304 data_size, max_size);
1305 goto new_conn;
1306 }
1307 while (sz) {
1308 n = read(m_sock, dst + offset, sz);
1309 if (n < 0) {
1310 fprintf(stderr, "error reading %d bytes\n", sz);
1311 goto new_conn;
1312 }
1313 if ((__u32)n == sz)
1314 break;
1315 offset += n;
1316 sz -= n;
1317 }
1318 if (is_fwht)
1319 fwht_decompress(m_ctx, dst, data_size, m_curData[p], m_curSize[p]);
1320 else
1321 rle_decompress(dst, size, data_size,
1322 rle_calc_bpl(m_v4l_fmt.g_bytesperline(p), m_v4l_fmt.g_pixelformat()));
1323 }
1324 m_frame++;
1325 update();
1326 if (m_cnt == 0)
1327 return;
1328 if (--m_cnt == 0)
1329 std::exit(EXIT_SUCCESS);
1330 return;
1331
1332 new_conn:
1333 listenForNewConnection();
1334 }
1335
resizeGL(int w,int h)1336 void CaptureWin::resizeGL(int w, int h)
1337 {
1338 if (!m_canOverrideResolution || !m_resolutionOverride->isChecked())
1339 return;
1340 w &= ~1;
1341 h &= ~1;
1342 m_overrideWidth = w;
1343 m_overrideHeight = h;
1344 m_viewSize = QSize(m_overrideWidth, m_overrideHeight);
1345 updateShader();
1346 printf("New resolution: %ux%u\n", w, h);
1347 }
1348
updateOrigValues()1349 void CaptureWin::updateOrigValues()
1350 {
1351 m_origWidth = m_v4l_fmt.g_width();
1352 m_origHeight = m_v4l_fmt.g_frame_height();
1353 m_overrideWidth = m_overrideHeight = 0;
1354 m_origPixelFormat = m_v4l_fmt.g_pixelformat();
1355 m_origField = m_v4l_fmt.g_field();
1356 m_origColorspace = m_v4l_fmt.g_colorspace();
1357 m_origXferFunc = m_v4l_fmt.g_xfer_func();
1358 if (m_is_rgb)
1359 m_origXferFunc = V4L2_YCBCR_ENC_601;
1360 else if (m_is_hsv)
1361 m_origHSVEnc = m_v4l_fmt.g_hsv_enc();
1362 else
1363 m_origYCbCrEnc = m_v4l_fmt.g_ycbcr_enc();
1364 m_origQuantization = m_v4l_fmt.g_quantization();
1365 showCurrentOverrides();
1366 m_viewSize = QSize(m_origWidth, m_origHeight);
1367 }
1368
initImageFormat()1369 void CaptureWin::initImageFormat()
1370 {
1371 updateV4LFormat(m_v4l_fmt);
1372 tpg_s_fourcc(&m_tpg, m_v4l_fmt.g_pixelformat());
1373 bool is_alt = m_v4l_fmt.g_field() == V4L2_FIELD_ALTERNATE;
1374
1375 tpg_reset_source(&m_tpg, m_v4l_fmt.g_width(),
1376 m_v4l_fmt.g_frame_height(), m_v4l_fmt.g_field());
1377 tpg_s_field(&m_tpg, m_v4l_fmt.g_first_field(m_std), is_alt);
1378 tpg_s_colorspace(&m_tpg, m_v4l_fmt.g_colorspace());
1379 tpg_s_xfer_func(&m_tpg, m_v4l_fmt.g_xfer_func());
1380 if (m_is_hsv)
1381 tpg_s_hsv_enc(&m_tpg, m_v4l_fmt.g_hsv_enc());
1382 else if (!m_is_rgb)
1383 tpg_s_ycbcr_enc(&m_tpg, m_v4l_fmt.g_ycbcr_enc());
1384 tpg_s_quantization(&m_tpg, m_v4l_fmt.g_quantization());
1385 m_v4l_fmt.s_num_planes(tpg_g_buffers(&m_tpg));
1386 for (unsigned p = 0; p < m_v4l_fmt.g_num_planes(); p++) {
1387 if (m_mode == AppModeFile && m_overrideHorPadding)
1388 tpg_s_bytesperline(&m_tpg, p, tpg_g_bytesperline(&m_tpg, p) + m_overrideHorPadding);
1389
1390 m_v4l_fmt.s_bytesperline(tpg_g_bytesperline(&m_tpg, p), p);
1391 m_v4l_fmt.s_sizeimage(tpg_calc_plane_size(&m_tpg, p), p);
1392 }
1393 if (tpg_g_buffers(&m_tpg) == 1) {
1394 unsigned size = 0;
1395
1396 for (unsigned p = 0; p < tpg_g_planes(&m_tpg); p++)
1397 size += tpg_calc_plane_size(&m_tpg, p);
1398 m_v4l_fmt.s_sizeimage(size, 0);
1399 }
1400 if (m_verbose)
1401 tpg_log_status(&m_tpg);
1402 }
1403
startTimer()1404 void CaptureWin::startTimer()
1405 {
1406 if (m_origPixelFormat == 0)
1407 updateOrigValues();
1408 initImageFormat();
1409
1410 m_timer = new QTimer(this);
1411 connect(m_timer, SIGNAL(timeout()), this, SLOT(tpgUpdateFrame()));
1412
1413 m_imageSize = 0;
1414 for (unsigned p = 0; p < m_v4l_fmt.g_num_planes(); p++)
1415 m_imageSize += m_v4l_fmt.g_sizeimage(p);
1416
1417 if (m_file.isOpen())
1418 m_file.seek(0);
1419
1420 if (m_file.isOpen() && m_imageSize > m_file.size()) {
1421 fprintf(stderr, "the file size is too small (expect at least %u, got %llu)\n",
1422 m_imageSize, m_file.size());
1423 }
1424
1425 for (unsigned p = 0; p < m_v4l_fmt.g_num_planes(); p++) {
1426 m_curSize[p] = m_v4l_fmt.g_sizeimage(p);
1427 delete [] m_curData[p];
1428 if (m_canOverrideResolution)
1429 m_curData[p] = new __u8[4096 * 2160 * (p ? 2 : 4)];
1430 else
1431 m_curData[p] = new __u8[m_curSize[p]];
1432 if (m_file.isOpen())
1433 m_file.read((char *)m_curData[p], m_curSize[p]);
1434 else
1435 tpg_fillbuffer(&m_tpg, 0, p, m_curData[p]);
1436 }
1437 bool is_alt = m_v4l_fmt.g_field() == V4L2_FIELD_ALTERNATE;
1438 tpg_update_mv_count(&m_tpg, is_alt);
1439 m_timer->setTimerType(Qt::PreciseTimer);
1440 m_timer->setSingleShot(false);
1441 m_timer->setInterval(1000.0 / (m_fps * (is_alt ? 2 : 1)));
1442 m_timer->start(1000.0 / (m_fps * (is_alt ? 2 : 1)));
1443 if (is_alt && m_cnt)
1444 m_cnt *= 2;
1445 if (m_mode == AppModeTest) {
1446 fprintf(stderr, "test %s ('%s'), %s, %s, %s, ",
1447 pixfmt2s(formats[m_testState.fmt_idx]).c_str(),
1448 fcc2s(formats[m_testState.fmt_idx]).c_str(),
1449 field2s(fields[m_testState.field_idx]).c_str(),
1450 colorspace2s(colorspaces[m_testState.colorspace_idx]).c_str(),
1451 xfer_func2s(xfer_funcs[m_testState.xfer_func_idx]).c_str());
1452 if (m_is_rgb)
1453 fprintf(stderr, "%s\n",
1454 quantization2s(quantizations[m_testState.quant_idx]).c_str());
1455 else if (m_is_hsv)
1456 fprintf(stderr, "%s, %s\n",
1457 ycbcr_enc2s(ycbcr_encs[m_testState.hsv_enc_idx]).c_str(),
1458 quantization2s(quantizations[m_testState.quant_idx]).c_str());
1459 else
1460 fprintf(stderr, "%s, %s\n",
1461 ycbcr_enc2s(ycbcr_encs[m_testState.ycbcr_enc_idx]).c_str(),
1462 quantization2s(quantizations[m_testState.quant_idx]).c_str());
1463 }
1464 }
1465
tpgUpdateFrame()1466 void CaptureWin::tpgUpdateFrame()
1467 {
1468 bool is_alt = m_v4l_fmt.g_field() == V4L2_FIELD_ALTERNATE;
1469
1470 if (m_mode != AppModeTest && m_singleStep && m_frame > m_singleStepStart &&
1471 !m_singleStepNext)
1472 return;
1473 m_singleStepNext = false;
1474
1475 if (m_mode == AppModeFile && m_file.pos() + m_imageSize > m_file.size())
1476 m_file.seek(0);
1477
1478 if (m_mode != AppModeFile && is_alt) {
1479 if (m_tpg.field == V4L2_FIELD_TOP)
1480 tpg_s_field(&m_tpg, V4L2_FIELD_BOTTOM, true);
1481 else
1482 tpg_s_field(&m_tpg, V4L2_FIELD_TOP, true);
1483 }
1484
1485 for (unsigned p = 0; p < m_v4l_fmt.g_num_planes(); p++) {
1486 if (m_mode == AppModeFile)
1487 m_file.read((char *)m_curData[p], m_curSize[p]);
1488 else
1489 tpg_fillbuffer(&m_tpg, 0, p, m_curData[p]);
1490 }
1491 m_frame++;
1492 update();
1493 if (m_cnt != 1)
1494 tpg_update_mv_count(&m_tpg, is_alt);
1495
1496 if (m_cnt == 0)
1497 return;
1498 if (--m_cnt)
1499 return;
1500
1501 delete m_timer;
1502 m_timer = NULL;
1503 if (!m_test)
1504 std::exit(EXIT_SUCCESS);
1505
1506 m_cnt = m_test;
1507
1508 bool mask_quant = m_testState.mask & QUANT_MASK;
1509 bool mask_ycbcr_enc = m_is_rgb || m_is_hsv || (m_testState.mask & YCBCR_HSV_ENC_MASK);
1510 bool mask_hsv_enc = !m_is_hsv || (m_testState.mask & YCBCR_HSV_ENC_MASK);
1511 bool mask_xfer_func = m_testState.mask & XFER_FUNC_MASK;
1512 bool mask_colorspace = m_testState.mask & COLORSPACE_MASK;
1513 bool mask_field = m_is_bayer || (m_testState.mask & FIELD_MASK);
1514 bool mask_fmt = m_testState.mask & FMT_MASK;
1515
1516 if (mask_quant ||
1517 quantizations[++m_testState.quant_idx] == 0) {
1518 if (!mask_quant)
1519 m_testState.quant_idx = 0;
1520 if (mask_ycbcr_enc ||
1521 ycbcr_encs[++m_testState.ycbcr_enc_idx] == 0) {
1522 if (!mask_ycbcr_enc)
1523 m_testState.ycbcr_enc_idx = 0;
1524 if (mask_hsv_enc ||
1525 hsv_encs[++m_testState.hsv_enc_idx] == 0) {
1526 if (!mask_hsv_enc)
1527 m_testState.hsv_enc_idx = 0;
1528 if (mask_xfer_func ||
1529 xfer_funcs[++m_testState.xfer_func_idx] == 0) {
1530 if (!mask_xfer_func)
1531 m_testState.xfer_func_idx = 0;
1532 if (mask_colorspace ||
1533 colorspaces[++m_testState.colorspace_idx] == 0) {
1534 if (!mask_colorspace)
1535 m_testState.colorspace_idx = 0;
1536 if (mask_field ||
1537 fields[++m_testState.field_idx] == 0) {
1538 if (!mask_field)
1539 m_testState.field_idx = 0;
1540 if (mask_fmt ||
1541 formats[++m_testState.fmt_idx] == 0)
1542 std::exit(EXIT_SUCCESS);
1543 }
1544 }
1545 }
1546 }
1547 }
1548 }
1549
1550 while (!supportedFmt(formats[m_testState.fmt_idx]))
1551 if (formats[++m_testState.fmt_idx] == 0)
1552 std::exit(EXIT_SUCCESS);
1553
1554 m_v4l_fmt.s_pixelformat(formats[m_testState.fmt_idx]);
1555 updateV4LFormat(m_v4l_fmt);
1556 m_v4l_fmt.s_field(fields[m_testState.field_idx]);
1557 m_v4l_fmt.s_colorspace(colorspaces[m_testState.colorspace_idx]);
1558 m_v4l_fmt.s_xfer_func(xfer_funcs[m_testState.xfer_func_idx]);
1559 if (m_is_hsv)
1560 m_v4l_fmt.s_ycbcr_enc(hsv_encs[m_testState.hsv_enc_idx]);
1561 else
1562 m_v4l_fmt.s_ycbcr_enc(ycbcr_encs[m_testState.ycbcr_enc_idx]);
1563 m_v4l_fmt.s_quantization(quantizations[m_testState.quant_idx]);
1564
1565 if (m_accepts_srgb &&
1566 (m_v4l_fmt.g_quantization() == V4L2_QUANTIZATION_LIM_RANGE ||
1567 m_v4l_fmt.g_xfer_func() != V4L2_XFER_FUNC_SRGB)) {
1568 /* Can't let openGL convert from non-linear to linear */
1569 m_accepts_srgb = false;
1570 }
1571 startTimer();
1572 m_updateShader = true;
1573 }
1574