• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* qv4l2: a control panel controlling v4l2 devices.
2  *
3  * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 
20 #include "capture-win.h"
21 
22 #undef min
23 #undef max
24 
25 #include <QCloseEvent>
26 #include <QLabel>
27 #include <QImage>
28 #include <QVBoxLayout>
29 #include <QApplication>
30 #if QT_VERSION < 0x050a00
31 #include <QDesktopWidget>
32 #endif
33 #include <QScreen>
34 #include <QWindow>
35 
36 #include <math.h>
37 
38 #define MIN_WIN_SIZE_WIDTH 16
39 #define MIN_WIN_SIZE_HEIGHT 12
40 
41 bool CaptureWin::m_enableScaling = true;
42 double CaptureWin::m_pixelAspectRatio = 1.0;
43 CropMethod CaptureWin::m_cropMethod = QV4L2_CROP_NONE;
44 
CaptureWin(ApplicationWindow * aw)45 CaptureWin::CaptureWin(ApplicationWindow *aw) :
46 	m_appWin(aw)
47 {
48 	setWindowTitle("V4L2 Capture");
49 	m_hotkeyScaleReset = new QShortcut(Qt::CTRL|Qt::Key_F, this);
50 	connect(m_hotkeyScaleReset, SIGNAL(activated()), this, SLOT(resetSize()));
51 	connect(aw->m_resetScalingAct, SIGNAL(triggered()), this, SLOT(resetSize()));
52 	m_hotkeyExitFullscreen = new QShortcut(Qt::Key_Escape, this);
53 	connect(m_hotkeyExitFullscreen, SIGNAL(activated()), this, SLOT(escape()));
54 	m_hotkeyToggleFullscreen = new QShortcut(Qt::Key_F, this);
55 	connect(m_hotkeyToggleFullscreen, SIGNAL(activated()), aw->m_makeFullScreenAct, SLOT(toggle()));
56 	m_exitFullScreen = new QAction(QIcon(":/fullscreenexit.png"), "Exit Fullscreen", this);
57 	m_exitFullScreen->setShortcut(m_hotkeyToggleFullscreen->key());
58 	connect(m_exitFullScreen, SIGNAL(triggered()), this, SLOT(escape()));
59 	m_enterFullScreen = new QAction(QIcon(":/fullscreen.png"), "Show Fullscreen", this);
60 	m_enterFullScreen ->setShortcut(m_hotkeyToggleFullscreen->key());
61 	connect(m_enterFullScreen, SIGNAL(triggered()), this, SLOT(fullScreen()));
62 	// Add the action to allow the hotkey to start/stop the stream
63 	addAction(m_appWin->m_capStartAct);
64 
65 	m_closeWindowAct = new QAction(QIcon(":/fileclose.png"), "&Close Window", this);
66 	m_closeWindowAct->setStatusTip("Close");
67 	QList<QKeySequence> shortcuts;
68 	// More standard close window shortcut
69 	shortcuts << (Qt::CTRL|Qt::Key_W);
70 	// Historic qv4l2 shortcut
71 	shortcuts << Qt::Key_Q;
72 	m_closeWindowAct->setShortcuts(shortcuts);
73 	addAction(m_closeWindowAct);
74 	connect(m_closeWindowAct, SIGNAL(triggered()), this, SLOT(close()));
75 
76 	m_frame.format = 0;
77 	m_frame.size.setWidth(0);
78 	m_frame.size.setHeight(0);
79 	m_frame.planeData[0] = NULL;
80 	m_frame.planeData[1] = NULL;
81 	m_frame.planeData[2] = NULL;
82 	m_crop.delta.setWidth(0);
83 	m_crop.delta.setHeight(0);
84 	m_crop.size.setWidth(0);
85 	m_crop.size.setHeight(0);
86 	m_crop.updated = 0;
87 	m_scaledSize.setWidth(0);
88 	m_scaledSize.setHeight(0);
89 	m_origFrameSize.setWidth(0);
90 	m_origFrameSize.setHeight(0);
91 	m_windowSize.setWidth(0);
92 	m_windowSize.setHeight(0);
93 }
94 
~CaptureWin()95 CaptureWin::~CaptureWin()
96 {
97 	if (layout() == NULL)
98 		return;
99 
100 	layout()->removeWidget(this);
101 	delete layout();
102 	delete m_hotkeyScaleReset;
103 }
104 
setFrame(int width,int height,__u32 format,unsigned char * data,unsigned char * data2,unsigned char * data3)105 void CaptureWin::setFrame(int width, int height, __u32 format,
106 	  unsigned char *data, unsigned char *data2, unsigned char *data3)
107 {
108 	m_frame.planeData[0] = data;
109 	m_frame.planeData[1] = data2;
110 	m_frame.planeData[2] = data3;
111 
112 	m_frame.updated = false;
113 	if (width != m_frame.size.width() || height != m_frame.size.height()
114 	    || format != m_frame.format) {
115 		m_frame.size.setHeight(height);
116 		m_frame.size.setWidth(width);
117 		m_frame.format       = format;
118 		m_frame.updated      = true;
119 		updateSize();
120 	}
121 
122 	setRenderFrame();
123 }
124 
buildWindow(QWidget * videoSurface)125 void CaptureWin::buildWindow(QWidget *videoSurface)
126 {
127 	int l, t, r, b;
128 	m_vboxLayout = new QVBoxLayout(this);
129 	m_vboxLayout->getContentsMargins(&l, &t, &r, &b);
130 #if QT_VERSION < 0x060000
131 	m_vboxLayout->setMargin(0);
132 #else
133 	m_vboxLayout->setContentsMargins(0, 0, 0, 0);
134 #endif
135 	m_vboxLayout->addWidget(videoSurface, 1000, Qt::AlignCenter);
136 
137 	setContextMenuPolicy(Qt::CustomContextMenu);
138 	connect(this, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
139 }
140 
resetSize()141 void CaptureWin::resetSize()
142 {
143         // Force resize even if no size change
144 	QSize resetFrameSize = m_origFrameSize;
145 	m_origFrameSize.setWidth(0);
146 	m_origFrameSize.setHeight(0);
147 
148 	setWindowSize(resetFrameSize);
149 }
150 
cropSize(QSize size)151 QSize CaptureWin::cropSize(QSize size)
152 {
153 	QSize croppedSize = size;
154 	double realWidth = size.width() * m_pixelAspectRatio;
155 	double realHeight = size.height() / m_pixelAspectRatio;
156 	double aspectRatio = 1;
157 
158 	switch (m_cropMethod) {
159 	case QV4L2_CROP_P43:
160 		aspectRatio = 4.0 / 3.0;
161 		break;
162 	case QV4L2_CROP_W149:
163 		aspectRatio = 14.0 / 9.0;
164 		break;
165 	case QV4L2_CROP_W169:
166 		aspectRatio = 16.0 / 9.0;
167 		break;
168 	case QV4L2_CROP_C185:
169 		aspectRatio = 1.85;
170 		break;
171 	case QV4L2_CROP_C239:
172 		aspectRatio = 2.39;
173 		break;
174 	case QV4L2_CROP_TB:
175 		croppedSize.setHeight(size.height() - 2);
176 		break;
177 	default:
178 		break;  // No cropping
179 	}
180 
181 	if ((m_cropMethod != QV4L2_CROP_TB) && (m_cropMethod != QV4L2_CROP_NONE)) {
182 		if (realWidth / size.height() < aspectRatio)
183 			croppedSize.setHeight(realWidth / aspectRatio);
184 		else
185 			croppedSize.setWidth(realHeight * aspectRatio);
186 	}
187 
188 	if (croppedSize.width() >= size.width())
189 		croppedSize.setWidth(size.width());
190 	if (croppedSize.width() < MIN_WIN_SIZE_WIDTH)
191 		croppedSize.setWidth(MIN_WIN_SIZE_WIDTH);
192 	if (croppedSize.height() >= size.height())
193 		croppedSize.setHeight(size.height());
194 	if (croppedSize.height() < MIN_WIN_SIZE_HEIGHT)
195 		croppedSize.setHeight(MIN_WIN_SIZE_HEIGHT);
196 
197 	return croppedSize;
198 }
199 
updateSize()200 void CaptureWin::updateSize()
201 {
202 	m_crop.updated = false;
203 	if (m_frame.updated) {
204 		m_scaledSize = scaleFrameSize(m_windowSize, m_frame.size);
205 		m_crop.size = cropSize(m_frame.size);
206 		m_crop.delta = (m_frame.size - m_crop.size) / 2;
207 		m_crop.updated = true;
208 	}
209 }
210 
setCropMethod(CropMethod crop)211 void CaptureWin::setCropMethod(CropMethod crop)
212 {
213 	m_cropMethod = crop;
214 	resetSize();
215 }
216 
pixelAspectFrameSize(QSize size)217 QSize CaptureWin::pixelAspectFrameSize(QSize size)
218 {
219 	if (!m_enableScaling)
220 		return size;
221 
222 	if (m_pixelAspectRatio > 1)
223 		size.rwidth() *= m_pixelAspectRatio;
224 
225 	if (m_pixelAspectRatio < 1)
226 		size.rheight() /= m_pixelAspectRatio;
227 
228 	return size;
229 }
230 
getMargins()231 QSize CaptureWin::getMargins()
232 {
233 	int l, t, r, b;
234 	layout()->getContentsMargins(&l, &t, &r, &b);
235 	return QSize(l + r, t + b);
236 }
237 
enableScaling(bool enable)238 void CaptureWin::enableScaling(bool enable)
239 {
240 	if (!enable) {
241 		QSize margins = getMargins();
242 		QWidget::setMinimumSize(m_origFrameSize.width() + margins.width(),
243 					m_origFrameSize.height() + margins.height());
244 	} else {
245 		QWidget::setMinimumSize(MIN_WIN_SIZE_WIDTH, MIN_WIN_SIZE_HEIGHT);
246 	}
247 	m_enableScaling = enable;
248 	resetSize();
249 }
250 
setWindowSize(QSize frameSize)251 void CaptureWin::setWindowSize(QSize frameSize)
252 {
253 	// Dont resize window if the frame size is the same in
254 	// the event the window has been paused when beeing resized.
255 	if (frameSize == m_origFrameSize)
256 		return;
257 
258 	m_origFrameSize = frameSize;
259 
260 	QSize margins = getMargins();
261 #if QT_VERSION < 0x050a00
262 	QDesktopWidget *screen = QApplication::desktop();
263 	QRect resolution = screen->screenGeometry();
264 #else
265 	QSize resolution(1920, 1080);
266 
267 	// window()->windowHandle() can be NULL, for example when
268 	// using X11 forwarding.
269 	if (window()->windowHandle()) {
270 		QScreen *screen = window()->windowHandle()->screen();
271 		resolution = screen->availableSize();
272 	}
273 #endif
274 
275 	QSize windowSize =  pixelAspectFrameSize(cropSize(frameSize)) + margins;
276 
277 	if (windowSize.width() > resolution.width())
278 		windowSize.setWidth(resolution.width());
279 	if (windowSize.width() < MIN_WIN_SIZE_WIDTH)
280 		windowSize.setWidth(MIN_WIN_SIZE_WIDTH);
281 
282 	if (windowSize.height() > resolution.height())
283 		windowSize.setHeight(resolution.height());
284 	if (windowSize.height() < MIN_WIN_SIZE_HEIGHT)
285 		windowSize.setHeight(MIN_WIN_SIZE_HEIGHT);
286 
287 	QWidget::setMinimumSize(MIN_WIN_SIZE_WIDTH, MIN_WIN_SIZE_HEIGHT);
288 
289 	m_frame.size = frameSize;
290 
291 	QWidget::resize(windowSize);
292 }
293 
scaleFrameSize(QSize window,QSize frame)294 QSize CaptureWin::scaleFrameSize(QSize window, QSize frame)
295 {
296 	QSize actualSize = pixelAspectFrameSize(cropSize(frame));
297 
298 	if (!m_enableScaling) {
299 		window.setWidth(actualSize.width());
300 		window.setHeight(actualSize.height());
301 	}
302 
303 	qreal newW, newH;
304 	if (window.width() >= window.height()) {
305 		newW = (qreal)window.width() / actualSize.width();
306 		newH = (qreal)window.height() / actualSize.height();
307 	} else {
308 		newH = (qreal)window.width() / actualSize.width();
309 		newW = (qreal)window.height() / actualSize.height();
310 	}
311 	qreal resized = (qreal)std::min(newW, newH);
312 
313 	return (actualSize * resized);
314 }
315 
setPixelAspectRatio(double ratio)316 void CaptureWin::setPixelAspectRatio(double ratio)
317 {
318 	m_pixelAspectRatio = ratio;
319 	resetSize();
320 }
321 
getHorScaleFactor()322 float CaptureWin::getHorScaleFactor()
323 {
324 	float ow, sw, wscale;
325 
326 	sw = m_scaledSize.width();
327 	ow = m_origFrameSize.width();
328 	wscale = floor(100 * (sw / ow)) / 100.0;
329 
330 	return wscale;
331 }
332 
getVertScaleFactor()333 float CaptureWin::getVertScaleFactor()
334 {
335 	float oh, sh, hscale;
336 
337 	sh = m_scaledSize.height();
338 	oh = m_origFrameSize.height();
339 	hscale = floor(100 * (sh / oh)) / 100.0;
340 
341 	return hscale;
342 }
343 
mouseDoubleClickEvent(QMouseEvent * e)344 void CaptureWin::mouseDoubleClickEvent(QMouseEvent *e)
345 {
346 	m_appWin->m_makeFullScreenAct->toggle();
347 }
348 
escape()349 void CaptureWin::escape()
350 {
351 	m_appWin->m_makeFullScreenAct->setChecked(false);
352 }
353 
fullScreen()354 void CaptureWin::fullScreen()
355 {
356 	m_appWin->m_makeFullScreenAct->setChecked(true);
357 }
358 
makeFullScreen(bool enable)359 void CaptureWin::makeFullScreen(bool enable)
360 {
361 	if (enable) {
362 		showFullScreen();
363 		setStyleSheet("background-color:#000000;");
364 	} else {
365 		showNormal();
366 		setStyleSheet("background-color:none;");
367 	}
368 	QSize resetFrameSize = m_origFrameSize;
369 	m_origFrameSize.setWidth(0);
370 	m_origFrameSize.setHeight(0);
371 
372 	setWindowSize(resetFrameSize);
373 }
374 
customMenuRequested(QPoint pos)375 void CaptureWin::customMenuRequested(QPoint pos)
376 {
377 	QMenu *menu = new QMenu(this);
378 
379 	if (isFullScreen()) {
380 		menu->addAction(m_exitFullScreen);
381 		menu->setStyleSheet("background-color:none;");
382 	} else {
383 		menu->addAction(m_enterFullScreen);
384 	}
385 
386 	menu->addAction(m_appWin->m_capStartAct);
387 	menu->addAction(m_appWin->m_capStepAct);
388 	menu->addAction(m_appWin->m_resetScalingAct);
389 	if (m_appWin->m_useBlendingAct)
390 		menu->addAction(m_appWin->m_useBlendingAct);
391 	if (m_appWin->m_useLinearAct)
392 		menu->addAction(m_appWin->m_useLinearAct);
393 	menu->addAction(m_appWin->m_snapshotAct);
394 	menu->addAction(m_appWin->m_showFramesAct);
395 	menu->addMenu(m_appWin->m_overrideColorspaceMenu);
396 	menu->addMenu(m_appWin->m_overrideXferFuncMenu);
397 	menu->addMenu(m_appWin->m_overrideYCbCrEncMenu);
398 	menu->addMenu(m_appWin->m_overrideQuantizationMenu);
399 	menu->addAction(m_closeWindowAct);
400 
401 	menu->popup(mapToGlobal(pos));
402 }
403 
closeEvent(QCloseEvent * event)404 void CaptureWin::closeEvent(QCloseEvent *event)
405 {
406 	QWidget::closeEvent(event);
407 	emit close();
408 }
409