7#include <QtMultimedia/qmediaplayer.h>
8#include <QtMultimedia/qmediacapturesession.h>
9#include <QtMultimedia/private/qmultimediautils_p.h>
10#include <QtMultimedia/private/qvideoframetexturepool_p.h>
11#include <QtMultimedia/private/qvideooutputorientationhandler_p.h>
12#include <QtQuick/qquickwindow.h>
13#include <QtQuick/private/qquickwindow_p.h>
14#include <QtCore/qloggingcategory.h>
15#include <QtCore/qrunnable.h>
16#include <QtCore/private/qfactoryloader_p.h>
17#include <QtMultimediaQuick/private/qsgvideonode_p.h>
21Q_STATIC_LOGGING_CATEGORY(qLcVideo,
"qt.multimedia.video")
27 return (o % 180) == 0;
32 return qIsDefaultAspect(qToUnderlying(rotation));
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
92
93
94
95
97QQuickVideoOutput::QQuickVideoOutput(QQuickItem *parent) :
100 setFlag(ItemHasContents,
true);
102 m_sink =
new QQuickVideoSink(
this);
103 qRegisterMetaType<QVideoFrameFormat>();
106 connect(m_sink, &QVideoSink::videoFrameChanged,
this,
107 makeGuardedCall([
this](
const QVideoFrame &frame) {
108 if (frame.isValid() || m_endOfStreamPolicy == ClearOutput)
111 Qt::DirectConnection);
116QQuickVideoOutput::~QQuickVideoOutput()
119 QMutexLocker lock(&m_destructorGuard->m_mutex);
120 m_destructorGuard->m_isAlive =
false;
124 disconnectWindowConnections();
128
129
130
131
132
133
134
136QVideoSink *QQuickVideoOutput::videoSink()
const
142
143
144
145
146
147
148
149
150
151
152
153
155QQuickVideoOutput::FillMode QQuickVideoOutput::fillMode()
const
157 return FillMode(m_aspectRatioMode);
160void QQuickVideoOutput::setFillMode(FillMode mode)
162 if (mode == fillMode())
165 m_aspectRatioMode = Qt::AspectRatioMode(mode);
167 m_geometryDirty =
true;
170 emit fillModeChanged(mode);
173void QQuickVideoOutput::_q_newFrame(QSize size)
177 size = qRotatedFrameSize(size, m_frameDisplayingRotation);
179 if (m_nativeSize != size) {
182 m_geometryDirty =
true;
184 setImplicitWidth(size.width());
185 setImplicitHeight(size.height());
187 emit sourceRectChanged();
192void QQuickVideoOutput::_q_updateGeometry()
194 const QRectF rect(0, 0, width(), height());
195 const QRectF absoluteRect(x(), y(), width(), height());
197 if (!m_geometryDirty && m_lastRect == absoluteRect)
200 QRectF oldContentRect(m_contentRect);
202 m_geometryDirty =
false;
203 m_lastRect = absoluteRect;
205 const auto fill = m_aspectRatioMode;
206 if (m_nativeSize.isEmpty()) {
209 m_contentRect = rect;
210 }
else if (fill == Qt::IgnoreAspectRatio) {
211 m_contentRect = rect;
213 QSizeF scaled = m_nativeSize;
214 scaled.scale(rect.size(), fill);
216 m_contentRect = QRectF(QPointF(), scaled);
217 m_contentRect.moveCenter(rect.center());
222 if (m_contentRect != oldContentRect)
223 emit contentRectChanged();
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251int QQuickVideoOutput::orientation()
const
253 return m_orientation;
256void QQuickVideoOutput::setOrientation(
int orientation)
259 if (orientation % 90)
263 if (m_orientation == orientation)
268 if (qVideoRotationFromDegrees(orientation - m_orientation) == QtVideo::Rotation::None) {
269 m_orientation = orientation;
270 emit orientationChanged();
274 m_geometryDirty =
true;
278 bool oldAspect = qIsDefaultAspect(m_orientation);
279 bool newAspect = qIsDefaultAspect(orientation);
281 m_orientation = orientation;
284 QMutexLocker lock(&m_frameMutex);
285 m_frameDisplayingRotation = qNormalizedFrameTransformation(m_frame, m_orientation).rotation;
288 if (oldAspect != newAspect) {
289 m_nativeSize.transpose();
291 setImplicitWidth(m_nativeSize.width());
292 setImplicitHeight(m_nativeSize.height());
298 emit orientationChanged();
302
303
304
305
306
307
308
309
310
311
312
313
314bool QQuickVideoOutput::mirrored()
const
319void QQuickVideoOutput::setMirrored(
bool mirrored)
321 if (m_mirrored == mirrored)
323 m_mirrored = mirrored;
326 emit mirroredChanged();
330
331
332
333
334
335
336
337
338
339
340
341
342
343QRectF QQuickVideoOutput::contentRect()
const
345 return m_contentRect;
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364QQuickVideoOutput::EndOfStreamPolicy QQuickVideoOutput::endOfStreamPolicy()
const
366 return m_endOfStreamPolicy;
369void QQuickVideoOutput::setEndOfStreamPolicy(EndOfStreamPolicy policy)
371 if (m_endOfStreamPolicy == policy)
374 m_endOfStreamPolicy = policy;
375 emit endOfStreamPolicyChanged(policy);
379
380
381
382
383
384
385
386
387void QQuickVideoOutput::clearOutput()
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409QRectF QQuickVideoOutput::sourceRect()
const
412 QSizeF size = m_nativeSize;
416 if (!qIsDefaultAspect(m_frameDisplayingRotation))
423 const QRectF viewport = adjustedViewport();
424 Q_ASSERT(viewport.size() == size);
425 return QRectF(viewport.topLeft(), size);
428void QQuickVideoOutput::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
430 Q_UNUSED(newGeometry);
431 Q_UNUSED(oldGeometry);
433 QQuickItem::geometryChange(newGeometry, oldGeometry);
442void QQuickVideoOutput::releaseResources()
446 QQuickItem::releaseResources();
449void QQuickVideoOutput::initRhiForSink()
451 QRhi *rhi = m_window ? QQuickWindowPrivate::get(m_window)->rhi :
nullptr;
455void QQuickVideoOutput::itemChange(QQuickItem::ItemChange change,
456 const QQuickItem::ItemChangeData &changeData)
458 if (change != QQuickItem::ItemSceneChange)
461 if (changeData.window == m_window)
464 disconnectWindowConnections();
465 m_window = changeData.window;
468 auto connectToWindow = [&](
auto signal,
auto function) {
469 connect(m_window, signal,
this, makeGuardedCall(std::move(function)),
470 Qt::DirectConnection);
474 connectToWindow(&QQuickWindow::sceneGraphInitialized, [
this] {
478 connectToWindow(&QQuickWindow::sceneGraphInvalidated, [
this] {
479 if (
auto texturePool = m_texturePool.lock())
480 texturePool->clearTextures();
481 m_sink->setRhi(
nullptr);
484 connectToWindow(&QQuickWindow::afterFrameEnd, [
this] {
485 if (
auto texturePool = m_texturePool.lock())
486 texturePool->onFrameEndInvoked();
492QSize QQuickVideoOutput::nativeSize()
const
494 return m_videoFormat.viewport().size();
497void QQuickVideoOutput::updateGeometry()
499 const QRectF viewport = m_videoFormat.viewport();
500 const QSizeF frameSize = m_videoFormat.frameSize();
501 const QRectF normalizedViewport(viewport.x() / frameSize.width(),
502 viewport.y() / frameSize.height(),
503 viewport.width() / frameSize.width(),
504 viewport.height() / frameSize.height());
505 const QRectF rect(0, 0, width(), height());
506 if (nativeSize().isEmpty()) {
507 m_renderedRect = rect;
508 m_sourceTextureRect = normalizedViewport;
509 }
else if (m_aspectRatioMode == Qt::IgnoreAspectRatio) {
510 m_renderedRect = rect;
511 m_sourceTextureRect = normalizedViewport;
512 }
else if (m_aspectRatioMode == Qt::KeepAspectRatio) {
513 m_sourceTextureRect = normalizedViewport;
514 m_renderedRect = contentRect();
515 }
else if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
516 m_renderedRect = rect;
517 const qreal contentHeight = contentRect().height();
518 const qreal contentWidth = contentRect().width();
521 const qreal relativeOffsetLeft = -contentRect().left() / contentWidth;
522 const qreal relativeOffsetTop = -contentRect().top() / contentHeight;
523 const qreal relativeWidth = rect.width() / contentWidth;
524 const qreal relativeHeight = rect.height() / contentHeight;
527 const qreal totalOffsetLeft = normalizedViewport.x() + relativeOffsetLeft * normalizedViewport.width();
528 const qreal totalOffsetTop = normalizedViewport.y() + relativeOffsetTop * normalizedViewport.height();
529 const qreal totalWidth = normalizedViewport.width() * relativeWidth;
530 const qreal totalHeight = normalizedViewport.height() * relativeHeight;
532 if (qIsDefaultAspect(m_frameDisplayingRotation)) {
533 m_sourceTextureRect = QRectF(totalOffsetLeft, totalOffsetTop,
534 totalWidth, totalHeight);
536 m_sourceTextureRect = QRectF(totalOffsetTop, totalOffsetLeft,
537 totalHeight, totalWidth);
542QSGNode *QQuickVideoOutput::updatePaintNode(QSGNode *oldNode,
543 QQuickItem::UpdatePaintNodeData *data)
548 QSGVideoNode *videoNode =
static_cast<QSGVideoNode *>(oldNode);
550 QMutexLocker lock(&m_frameMutex);
552 if (m_frameChanged) {
553 if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) {
554 qCDebug(qLcVideo) <<
"updatePaintNode: deleting old video node because frame format changed";
559 if (!m_frame.isValid()) {
560 qCDebug(qLcVideo) <<
"updatePaintNode: no frames yet";
561 m_frameChanged =
false;
569 QRhi *rhi = m_window ? QQuickWindowPrivate::get(m_window)->rhi :
nullptr;
570 videoNode =
new QSGVideoNode(
this, m_videoFormat, rhi);
571 m_texturePool = videoNode->texturePool();
572 qCDebug(qLcVideo) <<
"updatePaintNode: Video node created. Handle type:" << m_frame.handleType();
577 m_frameChanged =
false;
578 m_frame = QVideoFrame();
582 if (m_frameChanged) {
583 videoNode->setCurrentFrame(m_frame);
585 updateHdr(videoNode);
588 m_frameChanged =
false;
589 m_frame = QVideoFrame();
592 videoNode->setTexturedRectGeometry(
593 m_renderedRect, m_sourceTextureRect,
594 VideoTransformation{ qVideoRotationFromDegrees(orientation()), m_mirrored });
599void QQuickVideoOutput::updateHdr(QSGVideoNode *videoNode)
601 auto *videoOutputWindow = window();
602 if (!videoOutputWindow)
605 auto *swapChain = videoOutputWindow->swapChain();
609 const auto requiredSwapChainFormat = qGetRequiredSwapChainFormat(m_frame.surfaceFormat());
610 if (qShouldUpdateSwapChainFormat(swapChain, requiredSwapChainFormat)) {
611 auto *recreateSwapChainJob = QRunnable::create([swapChain, requiredSwapChainFormat]() {
612 swapChain->destroy();
613 swapChain->setFormat(requiredSwapChainFormat);
614 swapChain->createOrResize();
620 videoOutputWindow->scheduleRenderJob(recreateSwapChainJob, QQuickWindow::AfterSwapStage);
623 videoNode->setSurfaceFormat(swapChain->format());
624 videoNode->setHdrInfo(swapChain->hdrInfo());
627void QQuickVideoOutput::disconnectWindowConnections()
632 m_window->disconnect(
this);
635QRectF QQuickVideoOutput::adjustedViewport()
const
637 return m_videoFormat.viewport();
640void QQuickVideoOutput::setFrame(
const QVideoFrame &frame)
643 QMutexLocker lock(&m_frameMutex);
645 m_videoFormat = frame.surfaceFormat();
647 m_frameDisplayingRotation = qNormalizedFrameTransformation(frame, m_orientation).rotation;
648 m_frameChanged =
true;
651 QMetaObject::invokeMethod(
this, &QQuickVideoOutput::_q_newFrame, frame.size());
654std::optional<std::chrono::nanoseconds> QQuickVideoOutput::g_signalBackoff;
655void QQuickVideoOutput::setSignalBackoff(std::optional<std::chrono::nanoseconds> ns)
657 g_signalBackoff = ns;
662#include "moc_qquickvideooutput_p.cpp"
Combined button and popup list for selecting options.
bool qIsDefaultAspect(int o)