1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qvideowidget_p.h" |
41 | #include "qpaintervideosurface_p.h" |
42 | |
43 | #include <qmediaobject.h> |
44 | #include <qmediaservice.h> |
45 | #include <qvideowindowcontrol.h> |
46 | #include <qvideowidgetcontrol.h> |
47 | |
48 | #include <qvideorenderercontrol.h> |
49 | #include <qvideosurfaceformat.h> |
50 | #include <qpainter.h> |
51 | |
52 | #include <qapplication.h> |
53 | #include <qevent.h> |
54 | #include <qboxlayout.h> |
55 | #include <qnamespace.h> |
56 | |
57 | #include <qwindow.h> |
58 | #include <private/qhighdpiscaling_p.h> |
59 | |
60 | #ifdef Q_OS_WIN |
61 | #include <QtCore/qt_windows.h> |
62 | #endif |
63 | |
64 | using namespace Qt; |
65 | |
66 | QT_BEGIN_NAMESPACE |
67 | |
68 | QVideoWidgetControlBackend::QVideoWidgetControlBackend( |
69 | QMediaService *service, QVideoWidgetControl *control, QWidget *widget) |
70 | : m_service(service) |
71 | , m_widgetControl(control) |
72 | { |
73 | connect(sender: control, SIGNAL(brightnessChanged(int)), receiver: widget, SLOT(_q_brightnessChanged(int))); |
74 | connect(sender: control, SIGNAL(contrastChanged(int)), receiver: widget, SLOT(_q_contrastChanged(int))); |
75 | connect(sender: control, SIGNAL(hueChanged(int)), receiver: widget, SLOT(_q_hueChanged(int))); |
76 | connect(sender: control, SIGNAL(saturationChanged(int)), receiver: widget, SLOT(_q_saturationChanged(int))); |
77 | connect(sender: control, SIGNAL(fullScreenChanged(bool)), receiver: widget, SLOT(_q_fullScreenChanged(bool))); |
78 | |
79 | QBoxLayout *layout = new QVBoxLayout; |
80 | layout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0); |
81 | layout->setSpacing(0); |
82 | |
83 | QWidget *videoWidget = control->videoWidget(); |
84 | videoWidget->setMouseTracking(widget->hasMouseTracking()); |
85 | layout->addWidget(videoWidget); |
86 | |
87 | widget->setLayout(layout); |
88 | } |
89 | |
90 | void QVideoWidgetControlBackend::releaseControl() |
91 | { |
92 | m_service->releaseControl(control: m_widgetControl); |
93 | } |
94 | |
95 | void QVideoWidgetControlBackend::setBrightness(int brightness) |
96 | { |
97 | m_widgetControl->setBrightness(brightness); |
98 | } |
99 | |
100 | void QVideoWidgetControlBackend::setContrast(int contrast) |
101 | { |
102 | m_widgetControl->setContrast(contrast); |
103 | } |
104 | |
105 | void QVideoWidgetControlBackend::setHue(int hue) |
106 | { |
107 | m_widgetControl->setHue(hue); |
108 | } |
109 | |
110 | void QVideoWidgetControlBackend::setSaturation(int saturation) |
111 | { |
112 | m_widgetControl->setSaturation(saturation); |
113 | } |
114 | |
115 | void QVideoWidgetControlBackend::setFullScreen(bool fullScreen) |
116 | { |
117 | m_widgetControl->setFullScreen(fullScreen); |
118 | } |
119 | |
120 | |
121 | Qt::AspectRatioMode QVideoWidgetControlBackend::aspectRatioMode() const |
122 | { |
123 | return m_widgetControl->aspectRatioMode(); |
124 | } |
125 | |
126 | void QVideoWidgetControlBackend::setAspectRatioMode(Qt::AspectRatioMode mode) |
127 | { |
128 | m_widgetControl->setAspectRatioMode(mode); |
129 | } |
130 | |
131 | QRendererVideoWidgetBackend::QRendererVideoWidgetBackend( |
132 | QMediaService *service, QVideoRendererControl *control, QWidget *widget) |
133 | : m_service(service) |
134 | , m_rendererControl(control) |
135 | , m_widget(widget) |
136 | , m_surface(new QPainterVideoSurface) |
137 | , m_aspectRatioMode(Qt::KeepAspectRatio) |
138 | , m_updatePaintDevice(true) |
139 | { |
140 | connect(sender: this, SIGNAL(brightnessChanged(int)), receiver: m_widget, SLOT(_q_brightnessChanged(int))); |
141 | connect(sender: this, SIGNAL(contrastChanged(int)), receiver: m_widget, SLOT(_q_contrastChanged(int))); |
142 | connect(sender: this, SIGNAL(hueChanged(int)), receiver: m_widget, SLOT(_q_hueChanged(int))); |
143 | connect(sender: this, SIGNAL(saturationChanged(int)), receiver: m_widget, SLOT(_q_saturationChanged(int))); |
144 | connect(sender: m_surface, SIGNAL(frameChanged()), receiver: this, SLOT(frameChanged())); |
145 | connect(sender: m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)), |
146 | receiver: this, SLOT(formatChanged(QVideoSurfaceFormat))); |
147 | |
148 | if (m_rendererControl) |
149 | m_rendererControl->setSurface(m_surface); |
150 | } |
151 | |
152 | QRendererVideoWidgetBackend::~QRendererVideoWidgetBackend() |
153 | { |
154 | delete m_surface; |
155 | } |
156 | |
157 | QAbstractVideoSurface *QRendererVideoWidgetBackend::videoSurface() const |
158 | { |
159 | return m_surface; |
160 | } |
161 | |
162 | void QRendererVideoWidgetBackend::releaseControl() |
163 | { |
164 | if (m_service && m_rendererControl) |
165 | m_service->releaseControl(control: m_rendererControl); |
166 | } |
167 | |
168 | void QRendererVideoWidgetBackend::clearSurface() |
169 | { |
170 | if (m_rendererControl) |
171 | m_rendererControl->setSurface(0); |
172 | } |
173 | |
174 | void QRendererVideoWidgetBackend::setBrightness(int brightness) |
175 | { |
176 | m_surface->setBrightness(brightness); |
177 | |
178 | emit brightnessChanged(brightness); |
179 | } |
180 | |
181 | void QRendererVideoWidgetBackend::setContrast(int contrast) |
182 | { |
183 | m_surface->setContrast(contrast); |
184 | |
185 | emit contrastChanged(contrast); |
186 | } |
187 | |
188 | void QRendererVideoWidgetBackend::setHue(int hue) |
189 | { |
190 | m_surface->setHue(hue); |
191 | |
192 | emit hueChanged(hue); |
193 | } |
194 | |
195 | void QRendererVideoWidgetBackend::setSaturation(int saturation) |
196 | { |
197 | m_surface->setSaturation(saturation); |
198 | |
199 | emit saturationChanged(saturation); |
200 | } |
201 | |
202 | Qt::AspectRatioMode QRendererVideoWidgetBackend::aspectRatioMode() const |
203 | { |
204 | return m_aspectRatioMode; |
205 | } |
206 | |
207 | void QRendererVideoWidgetBackend::setAspectRatioMode(Qt::AspectRatioMode mode) |
208 | { |
209 | m_aspectRatioMode = mode; |
210 | |
211 | m_widget->updateGeometry(); |
212 | } |
213 | |
214 | void QRendererVideoWidgetBackend::setFullScreen(bool) |
215 | { |
216 | } |
217 | |
218 | QSize QRendererVideoWidgetBackend::sizeHint() const |
219 | { |
220 | return m_surface->surfaceFormat().sizeHint(); |
221 | } |
222 | |
223 | void QRendererVideoWidgetBackend::showEvent() |
224 | { |
225 | } |
226 | |
227 | void QRendererVideoWidgetBackend::hideEvent(QHideEvent *) |
228 | { |
229 | #if QT_CONFIG(opengl) |
230 | m_updatePaintDevice = true; |
231 | #endif |
232 | } |
233 | |
234 | void QRendererVideoWidgetBackend::resizeEvent(QResizeEvent *) |
235 | { |
236 | updateRects(); |
237 | } |
238 | |
239 | void QRendererVideoWidgetBackend::moveEvent(QMoveEvent *) |
240 | { |
241 | } |
242 | |
243 | void QRendererVideoWidgetBackend::paintEvent(QPaintEvent *event) |
244 | { |
245 | QPainter painter(m_widget); |
246 | |
247 | if (m_widget->testAttribute(attribute: Qt::WA_OpaquePaintEvent)) { |
248 | QRegion borderRegion = event->region(); |
249 | borderRegion = borderRegion.subtracted(r: m_boundingRect); |
250 | |
251 | QBrush brush = m_widget->palette().window(); |
252 | |
253 | for (const QRect &r : borderRegion) |
254 | painter.fillRect(r, brush); |
255 | } |
256 | |
257 | if (m_surface->isActive() && m_boundingRect.intersects(r: event->rect())) { |
258 | m_surface->paint(painter: &painter, target: m_boundingRect, source: m_sourceRect); |
259 | |
260 | m_surface->setReady(true); |
261 | } else { |
262 | #if QT_CONFIG(opengl) |
263 | if (m_updatePaintDevice && (painter.paintEngine()->type() == QPaintEngine::OpenGL |
264 | || painter.paintEngine()->type() == QPaintEngine::OpenGL2)) { |
265 | m_updatePaintDevice = false; |
266 | |
267 | m_surface->updateGLContext(); |
268 | if (m_surface->supportedShaderTypes() & QPainterVideoSurface::GlslShader) { |
269 | m_surface->setShaderType(QPainterVideoSurface::GlslShader); |
270 | } else { |
271 | m_surface->setShaderType(QPainterVideoSurface::FragmentProgramShader); |
272 | } |
273 | } |
274 | #endif |
275 | } |
276 | |
277 | } |
278 | |
279 | void QRendererVideoWidgetBackend::formatChanged(const QVideoSurfaceFormat &format) |
280 | { |
281 | m_nativeSize = format.sizeHint(); |
282 | |
283 | updateRects(); |
284 | |
285 | m_widget->updateGeometry(); |
286 | m_widget->update(); |
287 | } |
288 | |
289 | void QRendererVideoWidgetBackend::frameChanged() |
290 | { |
291 | m_widget->update(m_boundingRect); |
292 | } |
293 | |
294 | void QRendererVideoWidgetBackend::updateRects() |
295 | { |
296 | QRect rect = m_widget->rect(); |
297 | |
298 | if (m_nativeSize.isEmpty()) { |
299 | m_boundingRect = QRect(); |
300 | } else if (m_aspectRatioMode == Qt::IgnoreAspectRatio) { |
301 | m_boundingRect = rect; |
302 | m_sourceRect = QRectF(0, 0, 1, 1); |
303 | } else if (m_aspectRatioMode == Qt::KeepAspectRatio) { |
304 | QSize size = m_nativeSize; |
305 | size.scale(s: rect.size(), mode: Qt::KeepAspectRatio); |
306 | |
307 | m_boundingRect = QRect(0, 0, size.width(), size.height()); |
308 | m_boundingRect.moveCenter(p: rect.center()); |
309 | |
310 | m_sourceRect = QRectF(0, 0, 1, 1); |
311 | } else if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { |
312 | m_boundingRect = rect; |
313 | |
314 | QSizeF size = rect.size(); |
315 | size.scale(s: m_nativeSize, mode: Qt::KeepAspectRatio); |
316 | |
317 | m_sourceRect = QRectF( |
318 | 0, 0, size.width() / m_nativeSize.width(), size.height() / m_nativeSize.height()); |
319 | m_sourceRect.moveCenter(p: QPointF(0.5, 0.5)); |
320 | } |
321 | } |
322 | |
323 | QWindowVideoWidgetBackend::QWindowVideoWidgetBackend( |
324 | QMediaService *service, QVideoWindowControl *control, QWidget *widget) |
325 | : m_service(service) |
326 | , m_windowControl(control) |
327 | , m_widget(widget) |
328 | { |
329 | connect(sender: control, SIGNAL(brightnessChanged(int)), receiver: m_widget, SLOT(_q_brightnessChanged(int))); |
330 | connect(sender: control, SIGNAL(contrastChanged(int)), receiver: m_widget, SLOT(_q_contrastChanged(int))); |
331 | connect(sender: control, SIGNAL(hueChanged(int)), receiver: m_widget, SLOT(_q_hueChanged(int))); |
332 | connect(sender: control, SIGNAL(saturationChanged(int)), receiver: m_widget, SLOT(_q_saturationChanged(int))); |
333 | connect(sender: control, SIGNAL(fullScreenChanged(bool)), receiver: m_widget, SLOT(_q_fullScreenChanged(bool))); |
334 | connect(sender: control, SIGNAL(nativeSizeChanged()), receiver: m_widget, SLOT(_q_dimensionsChanged())); |
335 | |
336 | control->setWinId(widget->winId()); |
337 | #if defined(Q_OS_WIN) |
338 | // Disable updates to avoid flickering while resizing/moving. |
339 | m_widget->setUpdatesEnabled(false); |
340 | #endif |
341 | } |
342 | |
343 | QWindowVideoWidgetBackend::~QWindowVideoWidgetBackend() |
344 | { |
345 | } |
346 | |
347 | void QWindowVideoWidgetBackend::releaseControl() |
348 | { |
349 | m_service->releaseControl(control: m_windowControl); |
350 | } |
351 | |
352 | void QWindowVideoWidgetBackend::setBrightness(int brightness) |
353 | { |
354 | m_windowControl->setBrightness(brightness); |
355 | } |
356 | |
357 | void QWindowVideoWidgetBackend::setContrast(int contrast) |
358 | { |
359 | m_windowControl->setContrast(contrast); |
360 | } |
361 | |
362 | void QWindowVideoWidgetBackend::setHue(int hue) |
363 | { |
364 | m_windowControl->setHue(hue); |
365 | } |
366 | |
367 | void QWindowVideoWidgetBackend::setSaturation(int saturation) |
368 | { |
369 | m_windowControl->setSaturation(saturation); |
370 | } |
371 | |
372 | void QWindowVideoWidgetBackend::setFullScreen(bool fullScreen) |
373 | { |
374 | m_windowControl->setFullScreen(fullScreen); |
375 | } |
376 | |
377 | Qt::AspectRatioMode QWindowVideoWidgetBackend::aspectRatioMode() const |
378 | { |
379 | return m_windowControl->aspectRatioMode(); |
380 | } |
381 | |
382 | void QWindowVideoWidgetBackend::setAspectRatioMode(Qt::AspectRatioMode mode) |
383 | { |
384 | m_windowControl->setAspectRatioMode(mode); |
385 | } |
386 | |
387 | QSize QWindowVideoWidgetBackend::sizeHint() const |
388 | { |
389 | return m_windowControl->nativeSize(); |
390 | } |
391 | |
392 | void QWindowVideoWidgetBackend::updateDisplayRect() |
393 | { |
394 | QRect rect = m_widget->rect(); |
395 | if (QHighDpiScaling::isActive()) { |
396 | const qreal factor = QHighDpiScaling::factor(context: m_widget->windowHandle()); |
397 | if (!qFuzzyCompare(p1: factor, p2: qreal(1))) { |
398 | rect = QRectF(QPointF(rect.topLeft()) * factor, |
399 | QSizeF(rect.size()) * factor).toRect(); |
400 | } |
401 | } |
402 | m_windowControl->setDisplayRect(rect); |
403 | } |
404 | |
405 | void QWindowVideoWidgetBackend::showEvent() |
406 | { |
407 | m_windowControl->setWinId(m_widget->winId()); |
408 | updateDisplayRect(); |
409 | |
410 | #if defined(Q_OS_WIN) |
411 | m_windowControl->repaint(); |
412 | #endif |
413 | } |
414 | |
415 | void QWindowVideoWidgetBackend::hideEvent(QHideEvent *) |
416 | { |
417 | } |
418 | |
419 | void QWindowVideoWidgetBackend::moveEvent(QMoveEvent *) |
420 | { |
421 | updateDisplayRect(); |
422 | } |
423 | |
424 | void QWindowVideoWidgetBackend::resizeEvent(QResizeEvent *) |
425 | { |
426 | updateDisplayRect(); |
427 | } |
428 | |
429 | void QWindowVideoWidgetBackend::paintEvent(QPaintEvent *event) |
430 | { |
431 | if (m_widget->testAttribute(attribute: Qt::WA_OpaquePaintEvent)) { |
432 | QPainter painter(m_widget); |
433 | |
434 | painter.fillRect(event->rect(), m_widget->palette().window()); |
435 | } |
436 | |
437 | m_windowControl->repaint(); |
438 | |
439 | event->accept(); |
440 | } |
441 | |
442 | void QVideoWidgetPrivate::setCurrentControl(QVideoWidgetControlInterface *control) |
443 | { |
444 | if (currentControl != control) { |
445 | currentControl = control; |
446 | |
447 | currentControl->setBrightness(brightness); |
448 | currentControl->setContrast(contrast); |
449 | currentControl->setHue(hue); |
450 | currentControl->setSaturation(saturation); |
451 | currentControl->setAspectRatioMode(aspectRatioMode); |
452 | } |
453 | } |
454 | |
455 | void QVideoWidgetPrivate::clearService() |
456 | { |
457 | if (service) { |
458 | QObject::disconnect(sender: service, SIGNAL(destroyed()), receiver: q_func(), SLOT(_q_serviceDestroyed())); |
459 | |
460 | if (widgetBackend) { |
461 | QLayout *layout = q_func()->layout(); |
462 | |
463 | for (QLayoutItem *item = layout->takeAt(index: 0); item; item = layout->takeAt(index: 0)) { |
464 | item->widget()->setParent(0); |
465 | delete item; |
466 | } |
467 | delete layout; |
468 | |
469 | widgetBackend->releaseControl(); |
470 | |
471 | delete widgetBackend; |
472 | widgetBackend = 0; |
473 | } else if (rendererBackend) { |
474 | rendererBackend->clearSurface(); |
475 | rendererBackend->releaseControl(); |
476 | |
477 | delete rendererBackend; |
478 | rendererBackend = 0; |
479 | } else if (windowBackend) { |
480 | windowBackend->releaseControl(); |
481 | |
482 | delete windowBackend; |
483 | windowBackend = 0; |
484 | } |
485 | |
486 | currentBackend = 0; |
487 | currentControl = 0; |
488 | service = 0; |
489 | } |
490 | } |
491 | |
492 | bool QVideoWidgetPrivate::createWidgetBackend() |
493 | { |
494 | if (QMediaControl *control = service->requestControl(QVideoWidgetControl_iid)) { |
495 | if (QVideoWidgetControl *widgetControl = qobject_cast<QVideoWidgetControl *>(object: control)) { |
496 | widgetBackend = new QVideoWidgetControlBackend(service, widgetControl, q_func()); |
497 | |
498 | setCurrentControl(widgetBackend); |
499 | |
500 | return true; |
501 | } |
502 | service->releaseControl(control); |
503 | } |
504 | return false; |
505 | } |
506 | |
507 | bool QVideoWidgetPrivate::createWindowBackend() |
508 | { |
509 | if (QMediaControl *control = service->requestControl(QVideoWindowControl_iid)) { |
510 | if (QVideoWindowControl *windowControl = qobject_cast<QVideoWindowControl *>(object: control)) { |
511 | windowBackend = new QWindowVideoWidgetBackend(service, windowControl, q_func()); |
512 | currentBackend = windowBackend; |
513 | |
514 | setCurrentControl(windowBackend); |
515 | |
516 | return true; |
517 | } |
518 | service->releaseControl(control); |
519 | } |
520 | return false; |
521 | } |
522 | |
523 | bool QVideoWidgetPrivate::createRendererBackend() |
524 | { |
525 | QMediaControl *control = service |
526 | ? service->requestControl(QVideoRendererControl_iid) |
527 | : nullptr; |
528 | rendererBackend = new QRendererVideoWidgetBackend(service, |
529 | qobject_cast<QVideoRendererControl *>(object: control), q_func()); |
530 | currentBackend = rendererBackend; |
531 | setCurrentControl(rendererBackend); |
532 | |
533 | return !service || (service && control); |
534 | } |
535 | |
536 | void QVideoWidgetPrivate::_q_serviceDestroyed() |
537 | { |
538 | if (widgetBackend) |
539 | delete q_func()->layout(); |
540 | |
541 | delete widgetBackend; |
542 | delete windowBackend; |
543 | delete rendererBackend; |
544 | |
545 | widgetBackend = 0; |
546 | windowBackend = 0; |
547 | rendererBackend = 0; |
548 | currentControl = 0; |
549 | currentBackend = 0; |
550 | service = 0; |
551 | } |
552 | |
553 | void QVideoWidgetPrivate::_q_brightnessChanged(int b) |
554 | { |
555 | if (b != brightness) |
556 | emit q_func()->brightnessChanged(brightness: brightness = b); |
557 | } |
558 | |
559 | void QVideoWidgetPrivate::_q_contrastChanged(int c) |
560 | { |
561 | if (c != contrast) |
562 | emit q_func()->contrastChanged(contrast: contrast = c); |
563 | } |
564 | |
565 | void QVideoWidgetPrivate::_q_hueChanged(int h) |
566 | { |
567 | if (h != hue) |
568 | emit q_func()->hueChanged(hue: hue = h); |
569 | } |
570 | |
571 | void QVideoWidgetPrivate::_q_saturationChanged(int s) |
572 | { |
573 | if (s != saturation) |
574 | emit q_func()->saturationChanged(saturation: saturation = s); |
575 | } |
576 | |
577 | |
578 | void QVideoWidgetPrivate::_q_fullScreenChanged(bool fullScreen) |
579 | { |
580 | if (!fullScreen && q_func()->isFullScreen()) |
581 | q_func()->showNormal(); |
582 | } |
583 | |
584 | void QVideoWidgetPrivate::_q_dimensionsChanged() |
585 | { |
586 | q_func()->updateGeometry(); |
587 | q_func()->update(); |
588 | } |
589 | |
590 | /*! |
591 | \class QVideoWidget |
592 | |
593 | |
594 | \brief The QVideoWidget class provides a widget which presents video |
595 | produced by a media object. |
596 | \ingroup multimedia |
597 | \inmodule QtMultimediaWidgets |
598 | |
599 | Attaching a QVideoWidget to a QMediaObject allows it to display the |
600 | video or image output of that media object. A QVideoWidget is attached |
601 | to media object by passing a pointer to the QMediaObject in its |
602 | constructor, and detached by destroying the QVideoWidget. |
603 | |
604 | \snippet multimedia-snippets/video.cpp Video widget |
605 | |
606 | \b {Note}: Only a single display output can be attached to a media |
607 | object at one time. |
608 | |
609 | \sa QMediaObject, QMediaPlayer, QGraphicsVideoItem |
610 | */ |
611 | |
612 | /*! |
613 | Constructs a new video widget. |
614 | |
615 | The \a parent is passed to QWidget. |
616 | */ |
617 | QVideoWidget::QVideoWidget(QWidget *parent) |
618 | : QWidget(parent, {}) |
619 | , d_ptr(new QVideoWidgetPrivate) |
620 | { |
621 | d_ptr->q_ptr = this; |
622 | } |
623 | |
624 | /*! |
625 | \internal |
626 | */ |
627 | QVideoWidget::QVideoWidget(QVideoWidgetPrivate &dd, QWidget *parent) |
628 | : QWidget(parent, {}) |
629 | , d_ptr(&dd) |
630 | { |
631 | d_ptr->q_ptr = this; |
632 | |
633 | QPalette palette = QWidget::palette(); |
634 | palette.setColor(acr: QPalette::Window, acolor: Qt::black); |
635 | setPalette(palette); |
636 | } |
637 | |
638 | /*! |
639 | Destroys a video widget. |
640 | */ |
641 | QVideoWidget::~QVideoWidget() |
642 | { |
643 | d_ptr->clearService(); |
644 | |
645 | delete d_ptr; |
646 | } |
647 | |
648 | /*! |
649 | \property QVideoWidget::mediaObject |
650 | \brief the media object which provides the video displayed by a widget. |
651 | */ |
652 | |
653 | QMediaObject *QVideoWidget::mediaObject() const |
654 | { |
655 | return d_func()->mediaObject; |
656 | } |
657 | |
658 | /*! |
659 | \internal |
660 | */ |
661 | bool QVideoWidget::setMediaObject(QMediaObject *object) |
662 | { |
663 | Q_D(QVideoWidget); |
664 | |
665 | if (object == d->mediaObject) |
666 | return true; |
667 | |
668 | d->clearService(); |
669 | |
670 | d->mediaObject = object; |
671 | |
672 | if (d->mediaObject) |
673 | d->service = d->mediaObject->service(); |
674 | |
675 | if (d->service) { |
676 | if (d->createWidgetBackend()) { |
677 | // Nothing to do here. |
678 | } else if ((!window() || !window()->testAttribute(attribute: Qt::WA_DontShowOnScreen)) |
679 | && d->createWindowBackend()) { |
680 | if (isVisible()) |
681 | d->windowBackend->showEvent(); |
682 | } else if (d->createRendererBackend()) { |
683 | if (isVisible()) |
684 | d->rendererBackend->showEvent(); |
685 | } else { |
686 | d->service = 0; |
687 | d->mediaObject = 0; |
688 | |
689 | return false; |
690 | } |
691 | |
692 | connect(asender: d->service, SIGNAL(destroyed()), SLOT(_q_serviceDestroyed())); |
693 | } else { |
694 | d->mediaObject = 0; |
695 | |
696 | return false; |
697 | } |
698 | |
699 | return true; |
700 | } |
701 | |
702 | /*! |
703 | \since 5.15 |
704 | \property QVideoWidget::videoSurface |
705 | \brief Returns the underlaying video surface that can render video frames |
706 | to the current widget. |
707 | This property is never \c nullptr. |
708 | Example of how to render video frames to QVideoWidget: |
709 | \snippet multimedia-snippets/video.cpp Widget Surface |
710 | \sa QMediaPlayer::setVideoOutput |
711 | */ |
712 | |
713 | QAbstractVideoSurface *QVideoWidget::videoSurface() const |
714 | { |
715 | auto d = const_cast<QVideoWidgetPrivate *>(d_func()); |
716 | |
717 | if (!d->rendererBackend) { |
718 | d->clearService(); |
719 | d->createRendererBackend(); |
720 | } |
721 | |
722 | return d->rendererBackend->videoSurface(); |
723 | } |
724 | |
725 | /*! |
726 | \property QVideoWidget::aspectRatioMode |
727 | \brief how video is scaled with respect to its aspect ratio. |
728 | */ |
729 | |
730 | Qt::AspectRatioMode QVideoWidget::aspectRatioMode() const |
731 | { |
732 | return d_func()->aspectRatioMode; |
733 | } |
734 | |
735 | void QVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode) |
736 | { |
737 | Q_D(QVideoWidget); |
738 | |
739 | if (d->currentControl) { |
740 | d->currentControl->setAspectRatioMode(mode); |
741 | d->aspectRatioMode = d->currentControl->aspectRatioMode(); |
742 | } else { |
743 | d->aspectRatioMode = mode; |
744 | } |
745 | } |
746 | |
747 | /*! |
748 | \property QVideoWidget::fullScreen |
749 | \brief whether video display is confined to a window or is fullScreen. |
750 | */ |
751 | |
752 | void QVideoWidget::setFullScreen(bool fullScreen) |
753 | { |
754 | Q_D(QVideoWidget); |
755 | |
756 | Qt::WindowFlags flags = windowFlags(); |
757 | |
758 | if (fullScreen) { |
759 | d->nonFullScreenFlags = flags & (Qt::Window | Qt::SubWindow); |
760 | flags |= Qt::Window; |
761 | flags &= ~Qt::SubWindow; |
762 | setWindowFlags(flags); |
763 | |
764 | showFullScreen(); |
765 | } else { |
766 | flags &= ~(Qt::Window | Qt::SubWindow); //clear the flags... |
767 | flags |= d->nonFullScreenFlags; //then we reset the flags (window and subwindow) |
768 | setWindowFlags(flags); |
769 | |
770 | showNormal(); |
771 | } |
772 | } |
773 | |
774 | /*! |
775 | \fn QVideoWidget::fullScreenChanged(bool fullScreen) |
776 | |
777 | Signals that the \a fullScreen mode of a video widget has changed. |
778 | |
779 | \sa isFullScreen() |
780 | */ |
781 | |
782 | /*! |
783 | \property QVideoWidget::brightness |
784 | \brief an adjustment to the brightness of displayed video. |
785 | |
786 | Valid brightness values range between -100 and 100, the default is 0. |
787 | */ |
788 | |
789 | int QVideoWidget::brightness() const |
790 | { |
791 | return d_func()->brightness; |
792 | } |
793 | |
794 | void QVideoWidget::setBrightness(int brightness) |
795 | { |
796 | Q_D(QVideoWidget); |
797 | |
798 | int boundedBrightness = qBound(min: -100, val: brightness, max: 100); |
799 | |
800 | if (d->currentControl) |
801 | d->currentControl->setBrightness(boundedBrightness); |
802 | else if (d->brightness != boundedBrightness) |
803 | emit brightnessChanged(brightness: d->brightness = boundedBrightness); |
804 | } |
805 | |
806 | /*! |
807 | \fn QVideoWidget::brightnessChanged(int brightness) |
808 | |
809 | Signals that a video widgets's \a brightness adjustment has changed. |
810 | |
811 | \sa brightness() |
812 | */ |
813 | |
814 | /*! |
815 | \property QVideoWidget::contrast |
816 | \brief an adjustment to the contrast of displayed video. |
817 | |
818 | Valid contrast values range between -100 and 100, the default is 0. |
819 | |
820 | */ |
821 | |
822 | int QVideoWidget::contrast() const |
823 | { |
824 | return d_func()->contrast; |
825 | } |
826 | |
827 | void QVideoWidget::setContrast(int contrast) |
828 | { |
829 | Q_D(QVideoWidget); |
830 | |
831 | int boundedContrast = qBound(min: -100, val: contrast, max: 100); |
832 | |
833 | if (d->currentControl) |
834 | d->currentControl->setContrast(boundedContrast); |
835 | else if (d->contrast != boundedContrast) |
836 | emit contrastChanged(contrast: d->contrast = boundedContrast); |
837 | } |
838 | |
839 | /*! |
840 | \fn QVideoWidget::contrastChanged(int contrast) |
841 | |
842 | Signals that a video widgets's \a contrast adjustment has changed. |
843 | |
844 | \sa contrast() |
845 | */ |
846 | |
847 | /*! |
848 | \property QVideoWidget::hue |
849 | \brief an adjustment to the hue of displayed video. |
850 | |
851 | Valid hue values range between -100 and 100, the default is 0. |
852 | */ |
853 | |
854 | int QVideoWidget::hue() const |
855 | { |
856 | return d_func()->hue; |
857 | } |
858 | |
859 | void QVideoWidget::setHue(int hue) |
860 | { |
861 | Q_D(QVideoWidget); |
862 | |
863 | int boundedHue = qBound(min: -100, val: hue, max: 100); |
864 | |
865 | if (d->currentControl) |
866 | d->currentControl->setHue(boundedHue); |
867 | else if (d->hue != boundedHue) |
868 | emit hueChanged(hue: d->hue = boundedHue); |
869 | } |
870 | |
871 | /*! |
872 | \fn QVideoWidget::hueChanged(int hue) |
873 | |
874 | Signals that a video widgets's \a hue has changed. |
875 | |
876 | \sa hue() |
877 | */ |
878 | |
879 | /*! |
880 | \property QVideoWidget::saturation |
881 | \brief an adjustment to the saturation of displayed video. |
882 | |
883 | Valid saturation values range between -100 and 100, the default is 0. |
884 | */ |
885 | |
886 | int QVideoWidget::saturation() const |
887 | { |
888 | return d_func()->saturation; |
889 | } |
890 | |
891 | void QVideoWidget::setSaturation(int saturation) |
892 | { |
893 | Q_D(QVideoWidget); |
894 | |
895 | int boundedSaturation = qBound(min: -100, val: saturation, max: 100); |
896 | |
897 | if (d->currentControl) |
898 | d->currentControl->setSaturation(boundedSaturation); |
899 | else if (d->saturation != boundedSaturation) |
900 | emit saturationChanged(saturation: d->saturation = boundedSaturation); |
901 | |
902 | } |
903 | |
904 | /*! |
905 | \fn QVideoWidget::saturationChanged(int saturation) |
906 | |
907 | Signals that a video widgets's \a saturation has changed. |
908 | |
909 | \sa saturation() |
910 | */ |
911 | |
912 | /*! |
913 | Returns the size hint for the current back end, |
914 | if there is one, or else the size hint from QWidget. |
915 | */ |
916 | QSize QVideoWidget::sizeHint() const |
917 | { |
918 | Q_D(const QVideoWidget); |
919 | |
920 | if (d->currentBackend) |
921 | return d->currentBackend->sizeHint(); |
922 | else |
923 | return QWidget::sizeHint(); |
924 | |
925 | |
926 | } |
927 | |
928 | /*! |
929 | \reimp |
930 | Current event \a event. |
931 | Returns the value of the baseclass QWidget::event(QEvent *event) function. |
932 | */ |
933 | bool QVideoWidget::event(QEvent *event) |
934 | { |
935 | Q_D(QVideoWidget); |
936 | |
937 | if (event->type() == QEvent::WindowStateChange) { |
938 | if (windowState() & Qt::WindowFullScreen) { |
939 | if (d->currentControl) |
940 | d->currentControl->setFullScreen(true); |
941 | |
942 | if (!d->wasFullScreen) |
943 | emit fullScreenChanged(fullScreen: d->wasFullScreen = true); |
944 | } else { |
945 | if (d->currentControl) |
946 | d->currentControl->setFullScreen(false); |
947 | |
948 | if (d->wasFullScreen) |
949 | emit fullScreenChanged(fullScreen: d->wasFullScreen = false); |
950 | } |
951 | } |
952 | return QWidget::event(event); |
953 | } |
954 | |
955 | /*! |
956 | \reimp |
957 | Handles the show \a event. |
958 | */ |
959 | void QVideoWidget::showEvent(QShowEvent *event) |
960 | { |
961 | Q_D(QVideoWidget); |
962 | |
963 | QWidget::showEvent(event); |
964 | |
965 | // The window backend won't work for re-directed windows so use the renderer backend instead. |
966 | if (d->windowBackend && window()->testAttribute(attribute: Qt::WA_DontShowOnScreen)) { |
967 | d->windowBackend->releaseControl(); |
968 | |
969 | delete d->windowBackend; |
970 | d->windowBackend = 0; |
971 | |
972 | d->createRendererBackend(); |
973 | } |
974 | |
975 | if (d->currentBackend) |
976 | d->currentBackend->showEvent(); |
977 | } |
978 | |
979 | /*! |
980 | \reimp |
981 | Handles the hide \a event. |
982 | */ |
983 | void QVideoWidget::hideEvent(QHideEvent *event) |
984 | { |
985 | Q_D(QVideoWidget); |
986 | |
987 | if (d->currentBackend) |
988 | d->currentBackend->hideEvent(event); |
989 | |
990 | QWidget::hideEvent(event); |
991 | } |
992 | |
993 | /*! |
994 | \reimp |
995 | Handles the resize \a event. |
996 | */ |
997 | void QVideoWidget::resizeEvent(QResizeEvent *event) |
998 | { |
999 | Q_D(QVideoWidget); |
1000 | |
1001 | QWidget::resizeEvent(event); |
1002 | |
1003 | if (d->currentBackend) |
1004 | d->currentBackend->resizeEvent(event); |
1005 | } |
1006 | |
1007 | /*! |
1008 | \reimp |
1009 | Handles the move \a event. |
1010 | */ |
1011 | void QVideoWidget::moveEvent(QMoveEvent *event) |
1012 | { |
1013 | Q_D(QVideoWidget); |
1014 | |
1015 | if (d->currentBackend) |
1016 | d->currentBackend->moveEvent(event); |
1017 | } |
1018 | |
1019 | /*! |
1020 | \reimp |
1021 | Handles the paint \a event. |
1022 | */ |
1023 | void QVideoWidget::paintEvent(QPaintEvent *event) |
1024 | { |
1025 | Q_D(QVideoWidget); |
1026 | |
1027 | if (d->currentBackend) { |
1028 | d->currentBackend->paintEvent(event); |
1029 | } else if (testAttribute(attribute: Qt::WA_OpaquePaintEvent)) { |
1030 | QPainter painter(this); |
1031 | |
1032 | painter.fillRect(event->rect(), palette().window()); |
1033 | } |
1034 | } |
1035 | |
1036 | #if defined(Q_OS_WIN) |
1037 | # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
1038 | bool QVideoWidget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) |
1039 | # else |
1040 | bool QVideoWidget::nativeEvent(const QByteArray &eventType, void *message, long *result) |
1041 | # endif |
1042 | { |
1043 | Q_D(QVideoWidget); |
1044 | Q_UNUSED(eventType); |
1045 | Q_UNUSED(result); |
1046 | |
1047 | MSG *mes = reinterpret_cast<MSG *>(message); |
1048 | if (mes->message == WM_PAINT || mes->message == WM_ERASEBKGND) { |
1049 | if (d->windowBackend) |
1050 | d->windowBackend->showEvent(); |
1051 | } |
1052 | |
1053 | return false; |
1054 | } |
1055 | #endif |
1056 | |
1057 | QT_END_NAMESPACE |
1058 | |
1059 | #include "moc_qvideowidget.cpp" |
1060 | #include "moc_qvideowidget_p.cpp" |
1061 | |