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 #include <QDesktopWidget>
31
32 #include <math.h>
33
34 #define MIN_WIN_SIZE_WIDTH 16
35 #define MIN_WIN_SIZE_HEIGHT 12
36
37 bool CaptureWin::m_enableScaling = true;
38 double CaptureWin::m_pixelAspectRatio = 1.0;
39 CropMethod CaptureWin::m_cropMethod = QV4L2_CROP_NONE;
40
CaptureWin(ApplicationWindow * aw)41 CaptureWin::CaptureWin(ApplicationWindow *aw) :
42 m_appWin(aw)
43 {
44 setWindowTitle("V4L2 Capture");
45 m_hotkeyClose = new QShortcut(Qt::CTRL+Qt::Key_W, this);
46 connect(m_hotkeyClose, SIGNAL(activated()), this, SLOT(close()));
47 connect(new QShortcut(Qt::Key_Q, this), SIGNAL(activated()), this, SLOT(close()));
48 m_hotkeyScaleReset = new QShortcut(Qt::CTRL+Qt::Key_F, this);
49 connect(m_hotkeyScaleReset, SIGNAL(activated()), this, SLOT(resetSize()));
50 connect(aw->m_resetScalingAct, SIGNAL(triggered()), this, SLOT(resetSize()));
51 m_hotkeyExitFullscreen = new QShortcut(Qt::Key_Escape, this);
52 connect(m_hotkeyExitFullscreen, SIGNAL(activated()), this, SLOT(escape()));
53 m_hotkeyToggleFullscreen = new QShortcut(Qt::Key_F, this);
54 connect(m_hotkeyToggleFullscreen, SIGNAL(activated()), aw->m_makeFullScreenAct, SLOT(toggle()));
55 m_exitFullScreen = new QAction(QIcon(":/fullscreenexit.png"), "Exit Fullscreen", this);
56 connect(m_exitFullScreen, SIGNAL(triggered()), this, SLOT(escape()));
57 m_enterFullScreen = new QAction(QIcon(":/fullscreen.png"), "Show Fullscreen", this);
58 connect(m_enterFullScreen, SIGNAL(triggered()), this, SLOT(fullScreen()));
59 m_frame.format = 0;
60 m_frame.size.setWidth(0);
61 m_frame.size.setHeight(0);
62 m_frame.planeData[0] = NULL;
63 m_frame.planeData[1] = NULL;
64 m_frame.planeData[2] = NULL;
65 m_crop.delta.setWidth(0);
66 m_crop.delta.setHeight(0);
67 m_crop.size.setWidth(0);
68 m_crop.size.setHeight(0);
69 m_crop.updated = 0;
70 m_scaledSize.setWidth(0);
71 m_scaledSize.setHeight(0);
72 m_origFrameSize.setWidth(0);
73 m_origFrameSize.setHeight(0);
74 m_windowSize.setWidth(0);
75 m_windowSize.setHeight(0);
76 }
77
~CaptureWin()78 CaptureWin::~CaptureWin()
79 {
80 if (layout() == NULL)
81 return;
82
83 layout()->removeWidget(this);
84 delete layout();
85 delete m_hotkeyClose;
86 delete m_hotkeyScaleReset;
87 }
88
setFrame(int width,int height,uint32_t format,unsigned char * data,unsigned char * data2,unsigned char * data3)89 void CaptureWin::setFrame(int width, int height, uint32_t format,
90 unsigned char *data, unsigned char *data2, unsigned char *data3)
91 {
92 m_frame.planeData[0] = data;
93 m_frame.planeData[1] = data2;
94 m_frame.planeData[2] = data3;
95
96 m_frame.updated = false;
97 if (width != m_frame.size.width() || height != m_frame.size.height()
98 || format != m_frame.format) {
99 m_frame.size.setHeight(height);
100 m_frame.size.setWidth(width);
101 m_frame.format = format;
102 m_frame.updated = true;
103 updateSize();
104 }
105
106 setRenderFrame();
107 }
108
buildWindow(QWidget * videoSurface)109 void CaptureWin::buildWindow(QWidget *videoSurface)
110 {
111 int l, t, r, b;
112 m_vboxLayout = new QVBoxLayout(this);
113 m_vboxLayout->getContentsMargins(&l, &t, &r, &b);
114 m_vboxLayout->setMargin(0);
115 m_vboxLayout->addWidget(videoSurface, 1000, Qt::AlignCenter);
116
117 setContextMenuPolicy(Qt::CustomContextMenu);
118 connect(this, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
119 }
120
resetSize()121 void CaptureWin::resetSize()
122 {
123 // Force resize even if no size change
124 QSize resetFrameSize = m_origFrameSize;
125 m_origFrameSize.setWidth(0);
126 m_origFrameSize.setHeight(0);
127
128 setWindowSize(resetFrameSize);
129 }
130
cropSize(QSize size)131 QSize CaptureWin::cropSize(QSize size)
132 {
133 QSize croppedSize = size;
134 double realWidth = size.width() * m_pixelAspectRatio;
135 double realHeight = size.height() / m_pixelAspectRatio;
136 double aspectRatio = 1;
137
138 switch (m_cropMethod) {
139 case QV4L2_CROP_P43:
140 aspectRatio = 4.0 / 3.0;
141 break;
142 case QV4L2_CROP_W149:
143 aspectRatio = 14.0 / 9.0;
144 break;
145 case QV4L2_CROP_W169:
146 aspectRatio = 16.0 / 9.0;
147 break;
148 case QV4L2_CROP_C185:
149 aspectRatio = 1.85;
150 break;
151 case QV4L2_CROP_C239:
152 aspectRatio = 2.39;
153 break;
154 case QV4L2_CROP_TB:
155 croppedSize.setHeight(size.height() - 2);
156 break;
157 default:
158 break; // No cropping
159 }
160
161 if ((m_cropMethod != QV4L2_CROP_TB) && (m_cropMethod != QV4L2_CROP_NONE)) {
162 if (realWidth / size.height() < aspectRatio)
163 croppedSize.setHeight(realWidth / aspectRatio);
164 else
165 croppedSize.setWidth(realHeight * aspectRatio);
166 }
167
168 if (croppedSize.width() >= size.width())
169 croppedSize.setWidth(size.width());
170 if (croppedSize.width() < MIN_WIN_SIZE_WIDTH)
171 croppedSize.setWidth(MIN_WIN_SIZE_WIDTH);
172 if (croppedSize.height() >= size.height())
173 croppedSize.setHeight(size.height());
174 if (croppedSize.height() < MIN_WIN_SIZE_HEIGHT)
175 croppedSize.setHeight(MIN_WIN_SIZE_HEIGHT);
176
177 return croppedSize;
178 }
179
updateSize()180 void CaptureWin::updateSize()
181 {
182 m_crop.updated = false;
183 if (m_frame.updated) {
184 m_scaledSize = scaleFrameSize(m_windowSize, m_frame.size);
185 m_crop.size = cropSize(m_frame.size);
186 m_crop.delta = (m_frame.size - m_crop.size) / 2;
187 m_crop.updated = true;
188 }
189 }
190
setCropMethod(CropMethod crop)191 void CaptureWin::setCropMethod(CropMethod crop)
192 {
193 m_cropMethod = crop;
194 resetSize();
195 }
196
pixelAspectFrameSize(QSize size)197 QSize CaptureWin::pixelAspectFrameSize(QSize size)
198 {
199 if (!m_enableScaling)
200 return size;
201
202 if (m_pixelAspectRatio > 1)
203 size.rwidth() *= m_pixelAspectRatio;
204
205 if (m_pixelAspectRatio < 1)
206 size.rheight() /= m_pixelAspectRatio;
207
208 return size;
209 }
210
getMargins()211 QSize CaptureWin::getMargins()
212 {
213 int l, t, r, b;
214 layout()->getContentsMargins(&l, &t, &r, &b);
215 return QSize(l + r, t + b);
216 }
217
enableScaling(bool enable)218 void CaptureWin::enableScaling(bool enable)
219 {
220 if (!enable) {
221 QSize margins = getMargins();
222 QWidget::setMinimumSize(m_origFrameSize.width() + margins.width(),
223 m_origFrameSize.height() + margins.height());
224 } else {
225 QWidget::setMinimumSize(MIN_WIN_SIZE_WIDTH, MIN_WIN_SIZE_HEIGHT);
226 }
227 m_enableScaling = enable;
228 resetSize();
229 }
230
setWindowSize(QSize frameSize)231 void CaptureWin::setWindowSize(QSize frameSize)
232 {
233 // Dont resize window if the frame size is the same in
234 // the event the window has been paused when beeing resized.
235 if (frameSize == m_origFrameSize)
236 return;
237
238 m_origFrameSize = frameSize;
239
240 QSize margins = getMargins();
241 QDesktopWidget *screen = QApplication::desktop();
242 QRect resolution = screen->screenGeometry();
243
244 QSize windowSize = pixelAspectFrameSize(cropSize(frameSize)) + margins;
245
246 if (windowSize.width() > resolution.width())
247 windowSize.setWidth(resolution.width());
248 if (windowSize.width() < MIN_WIN_SIZE_WIDTH)
249 windowSize.setWidth(MIN_WIN_SIZE_WIDTH);
250
251 if (windowSize.height() > resolution.height())
252 windowSize.setHeight(resolution.height());
253 if (windowSize.height() < MIN_WIN_SIZE_HEIGHT)
254 windowSize.setHeight(MIN_WIN_SIZE_HEIGHT);
255
256 QWidget::setMinimumSize(MIN_WIN_SIZE_WIDTH, MIN_WIN_SIZE_HEIGHT);
257
258 m_frame.size = frameSize;
259
260 QWidget::resize(windowSize);
261 }
262
scaleFrameSize(QSize window,QSize frame)263 QSize CaptureWin::scaleFrameSize(QSize window, QSize frame)
264 {
265 QSize actualSize = pixelAspectFrameSize(cropSize(frame));
266
267 if (!m_enableScaling) {
268 window.setWidth(actualSize.width());
269 window.setHeight(actualSize.height());
270 }
271
272 qreal newW, newH;
273 if (window.width() >= window.height()) {
274 newW = (qreal)window.width() / actualSize.width();
275 newH = (qreal)window.height() / actualSize.height();
276 } else {
277 newH = (qreal)window.width() / actualSize.width();
278 newW = (qreal)window.height() / actualSize.height();
279 }
280 qreal resized = (qreal)std::min(newW, newH);
281
282 return (actualSize * resized);
283 }
284
setPixelAspectRatio(double ratio)285 void CaptureWin::setPixelAspectRatio(double ratio)
286 {
287 m_pixelAspectRatio = ratio;
288 resetSize();
289 }
290
getHorScaleFactor()291 float CaptureWin::getHorScaleFactor()
292 {
293 float ow, sw, wscale;
294
295 sw = m_scaledSize.width();
296 ow = m_origFrameSize.width();
297 wscale = floor(100 * (sw / ow)) / 100.0;
298
299 return wscale;
300 }
301
getVertScaleFactor()302 float CaptureWin::getVertScaleFactor()
303 {
304 float oh, sh, hscale;
305
306 sh = m_scaledSize.height();
307 oh = m_origFrameSize.height();
308 hscale = floor(100 * (sh / oh)) / 100.0;
309
310 return hscale;
311 }
312
mouseDoubleClickEvent(QMouseEvent * e)313 void CaptureWin::mouseDoubleClickEvent(QMouseEvent *e)
314 {
315 m_appWin->m_makeFullScreenAct->toggle();
316 }
317
escape()318 void CaptureWin::escape()
319 {
320 m_appWin->m_makeFullScreenAct->setChecked(false);
321 }
322
fullScreen()323 void CaptureWin::fullScreen()
324 {
325 m_appWin->m_makeFullScreenAct->setChecked(true);
326 }
327
makeFullScreen(bool enable)328 void CaptureWin::makeFullScreen(bool enable)
329 {
330 if (enable) {
331 showFullScreen();
332 setStyleSheet("background-color:#000000;");
333 } else {
334 showNormal();
335 setStyleSheet("background-color:none;");
336 }
337 QSize resetFrameSize = m_origFrameSize;
338 m_origFrameSize.setWidth(0);
339 m_origFrameSize.setHeight(0);
340
341 setWindowSize(resetFrameSize);
342 }
343
customMenuRequested(QPoint pos)344 void CaptureWin::customMenuRequested(QPoint pos)
345 {
346 QMenu *menu = new QMenu(this);
347
348 if (isFullScreen()) {
349 menu->addAction(m_exitFullScreen);
350 menu->setStyleSheet("background-color:none;");
351 } else {
352 menu->addAction(m_enterFullScreen);
353 }
354
355 menu->addAction(m_appWin->m_resetScalingAct);
356 if (m_appWin->m_useBlendingAct)
357 menu->addAction(m_appWin->m_useBlendingAct);
358 if (m_appWin->m_useLinearAct)
359 menu->addAction(m_appWin->m_useLinearAct);
360 menu->addAction(m_appWin->m_snapshotAct);
361 menu->addAction(m_appWin->m_showFramesAct);
362 menu->addMenu(m_appWin->m_overrideColorspaceMenu);
363 menu->addMenu(m_appWin->m_overrideXferFuncMenu);
364 menu->addMenu(m_appWin->m_overrideYCbCrEncMenu);
365 menu->addMenu(m_appWin->m_overrideQuantizationMenu);
366
367 menu->popup(mapToGlobal(pos));
368 }
369
closeEvent(QCloseEvent * event)370 void CaptureWin::closeEvent(QCloseEvent *event)
371 {
372 QWidget::closeEvent(event);
373 emit close();
374 }
375