| 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 | |