6#include <private/qvideooutputorientationhandler_p.h>
7#include <private/qvideoframetexturepool_p.h>
8#include <QtMultimedia/qmediaplayer.h>
9#include <QtMultimedia/qmediacapturesession.h>
10#include <private/qfactoryloader_p.h>
11#include <QtCore/qloggingcategory.h>
12#include <QtQuick/QQuickWindow>
13#include <private/qquickwindow_p.h>
14#include <private/qmultimediautils_p.h>
15#include <qsgvideonode_p.h>
16#include <QtCore/qrunnable.h>
20Q_STATIC_LOGGING_CATEGORY(qLcVideo,
"qt.multimedia.video")
26 return (o % 180) == 0;
31 return qIsDefaultAspect(qToUnderlying(rotation));
36
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
91
92
93
94
96QQuickVideoOutput::QQuickVideoOutput(QQuickItem *parent) :
99 setFlag(ItemHasContents,
true);
101 m_sink =
new QQuickVideoSink(
this);
102 qRegisterMetaType<QVideoFrameFormat>();
105 connect(m_sink, &QVideoSink::videoFrameChanged,
this,
106 makeGuardedCall([
this](
const QVideoFrame &frame) {
107 if (frame.isValid() || m_endOfStreamPolicy == ClearOutput)
110 Qt::DirectConnection);
115QQuickVideoOutput::~QQuickVideoOutput()
118 QMutexLocker lock(&m_destructorGuard->m_mutex);
119 m_destructorGuard->m_isAlive =
false;
123 disconnectWindowConnections();
127
128
129
130
131
132
133
135QVideoSink *QQuickVideoOutput::videoSink()
const
141
142
143
144
145
146
147
148
149
150
151
152
154QQuickVideoOutput::FillMode QQuickVideoOutput::fillMode()
const
156 return FillMode(m_aspectRatioMode);
159void QQuickVideoOutput::setFillMode(FillMode mode)
161 if (mode == fillMode())
164 m_aspectRatioMode = Qt::AspectRatioMode(mode);
166 m_geometryDirty =
true;
169 emit fillModeChanged(mode);
172void QQuickVideoOutput::_q_newFrame(QSize size)
176 size = qRotatedFrameSize(size, m_frameDisplayingRotation);
178 if (m_nativeSize != size) {
181 m_geometryDirty =
true;
183 setImplicitWidth(size.width());
184 setImplicitHeight(size.height());
186 emit sourceRectChanged();
191void QQuickVideoOutput::_q_updateGeometry()
193 const QRectF rect(0, 0, width(), height());
194 const QRectF absoluteRect(x(), y(), width(), height());
196 if (!m_geometryDirty && m_lastRect == absoluteRect)
199 QRectF oldContentRect(m_contentRect);
201 m_geometryDirty =
false;
202 m_lastRect = absoluteRect;
204 const auto fill = m_aspectRatioMode;
205 if (m_nativeSize.isEmpty()) {
208 m_contentRect = rect;
209 }
else if (fill == Qt::IgnoreAspectRatio) {
210 m_contentRect = rect;
212 QSizeF scaled = m_nativeSize;
213 scaled.scale(rect.size(), fill);
215 m_contentRect = QRectF(QPointF(), scaled);
216 m_contentRect.moveCenter(rect.center());
221 if (m_contentRect != oldContentRect)
222 emit contentRectChanged();
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250int QQuickVideoOutput::orientation()
const
252 return m_orientation;
255void QQuickVideoOutput::setOrientation(
int orientation)
258 if (orientation % 90)
262 if (m_orientation == orientation)
267 if (qVideoRotationFromDegrees(orientation - m_orientation) == QtVideo::Rotation::None) {
268 m_orientation = orientation;
269 emit orientationChanged();
273 m_geometryDirty =
true;
277 bool oldAspect = qIsDefaultAspect(m_orientation);
278 bool newAspect = qIsDefaultAspect(orientation);
280 m_orientation = orientation;
283 QMutexLocker lock(&m_frameMutex);
284 m_frameDisplayingRotation = qNormalizedFrameTransformation(m_frame, m_orientation).rotation;
287 if (oldAspect != newAspect) {
288 m_nativeSize.transpose();
290 setImplicitWidth(m_nativeSize.width());
291 setImplicitHeight(m_nativeSize.height());
297 emit orientationChanged();
301
302
303
304
305
306
307
308
309
310
311
312
313bool QQuickVideoOutput::mirrored()
const
318void QQuickVideoOutput::setMirrored(
bool mirrored)
320 if (m_mirrored == mirrored)
322 m_mirrored = mirrored;
325 emit mirroredChanged();
329
330
331
332
333
334
335
336
337
338
339
340
341
342QRectF QQuickVideoOutput::contentRect()
const
344 return m_contentRect;
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363QQuickVideoOutput::EndOfStreamPolicy QQuickVideoOutput::endOfStreamPolicy()
const
365 return m_endOfStreamPolicy;
368void QQuickVideoOutput::setEndOfStreamPolicy(EndOfStreamPolicy policy)
370 if (m_endOfStreamPolicy == policy)
373 m_endOfStreamPolicy = policy;
374 emit endOfStreamPolicyChanged(policy);
378
379
380
381
382
383
384
385
386void QQuickVideoOutput::clearOutput()
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408QRectF QQuickVideoOutput::sourceRect()
const
411 QSizeF size = m_nativeSize;
415 if (!qIsDefaultAspect(m_frameDisplayingRotation))
422 const QRectF viewport = adjustedViewport();
423 Q_ASSERT(viewport.size() == size);
424 return QRectF(viewport.topLeft(), size);
427void QQuickVideoOutput::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
429 Q_UNUSED(newGeometry);
430 Q_UNUSED(oldGeometry);
432 QQuickItem::geometryChange(newGeometry, oldGeometry);
441void QQuickVideoOutput::releaseResources()
445 QQuickItem::releaseResources();
448void QQuickVideoOutput::initRhiForSink()
450 QRhi *rhi = m_window ? QQuickWindowPrivate::get(m_window)->rhi :
nullptr;
454void QQuickVideoOutput::itemChange(QQuickItem::ItemChange change,
455 const QQuickItem::ItemChangeData &changeData)
457 if (change != QQuickItem::ItemSceneChange)
460 if (changeData.window == m_window)
463 disconnectWindowConnections();
464 m_window = changeData.window;
467 auto connectToWindow = [&](
auto signal,
auto function) {
468 connect(m_window, signal,
this, makeGuardedCall(std::move(function)),
469 Qt::DirectConnection);
473 connectToWindow(&QQuickWindow::sceneGraphInitialized, [
this] {
477 connectToWindow(&QQuickWindow::sceneGraphInvalidated, [
this] {
478 if (
auto texturePool = m_texturePool.lock())
479 texturePool->clearTextures();
480 m_sink->setRhi(
nullptr);
483 connectToWindow(&QQuickWindow::afterFrameEnd, [
this] {
484 if (
auto texturePool = m_texturePool.lock())
485 texturePool->onFrameEndInvoked();
491QSize QQuickVideoOutput::nativeSize()
const
493 return m_videoFormat.viewport().size();
496void QQuickVideoOutput::updateGeometry()
498 const QRectF viewport = m_videoFormat.viewport();
499 const QSizeF frameSize = m_videoFormat.frameSize();
500 const QRectF normalizedViewport(viewport.x() / frameSize.width(),
501 viewport.y() / frameSize.height(),
502 viewport.width() / frameSize.width(),
503 viewport.height() / frameSize.height());
504 const QRectF rect(0, 0, width(), height());
505 if (nativeSize().isEmpty()) {
506 m_renderedRect = rect;
507 m_sourceTextureRect = normalizedViewport;
508 }
else if (m_aspectRatioMode == Qt::IgnoreAspectRatio) {
509 m_renderedRect = rect;
510 m_sourceTextureRect = normalizedViewport;
511 }
else if (m_aspectRatioMode == Qt::KeepAspectRatio) {
512 m_sourceTextureRect = normalizedViewport;
513 m_renderedRect = contentRect();
514 }
else if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
515 m_renderedRect = rect;
516 const qreal contentHeight = contentRect().height();
517 const qreal contentWidth = contentRect().width();
520 const qreal relativeOffsetLeft = -contentRect().left() / contentWidth;
521 const qreal relativeOffsetTop = -contentRect().top() / contentHeight;
522 const qreal relativeWidth = rect.width() / contentWidth;
523 const qreal relativeHeight = rect.height() / contentHeight;
526 const qreal totalOffsetLeft = normalizedViewport.x() + relativeOffsetLeft * normalizedViewport.width();
527 const qreal totalOffsetTop = normalizedViewport.y() + relativeOffsetTop * normalizedViewport.height();
528 const qreal totalWidth = normalizedViewport.width() * relativeWidth;
529 const qreal totalHeight = normalizedViewport.height() * relativeHeight;
531 if (qIsDefaultAspect(m_frameDisplayingRotation)) {
532 m_sourceTextureRect = QRectF(totalOffsetLeft, totalOffsetTop,
533 totalWidth, totalHeight);
535 m_sourceTextureRect = QRectF(totalOffsetTop, totalOffsetLeft,
536 totalHeight, totalWidth);
541QSGNode *QQuickVideoOutput::updatePaintNode(QSGNode *oldNode,
542 QQuickItem::UpdatePaintNodeData *data)
547 QSGVideoNode *videoNode =
static_cast<QSGVideoNode *>(oldNode);
549 QMutexLocker lock(&m_frameMutex);
551 if (m_frameChanged) {
552 if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) {
553 qCDebug(qLcVideo) <<
"updatePaintNode: deleting old video node because frame format changed";
558 if (!m_frame.isValid()) {
559 qCDebug(qLcVideo) <<
"updatePaintNode: no frames yet";
560 m_frameChanged =
false;
568 QRhi *rhi = m_window ? QQuickWindowPrivate::get(m_window)->rhi :
nullptr;
569 videoNode =
new QSGVideoNode(
this, m_videoFormat, rhi);
570 m_texturePool = videoNode->texturePool();
571 qCDebug(qLcVideo) <<
"updatePaintNode: Video node created. Handle type:" << m_frame.handleType();
576 m_frameChanged =
false;
577 m_frame = QVideoFrame();
581 if (m_frameChanged) {
582 videoNode->setCurrentFrame(m_frame);
584 updateHdr(videoNode);
587 m_frameChanged =
false;
588 m_frame = QVideoFrame();
591 videoNode->setTexturedRectGeometry(
592 m_renderedRect, m_sourceTextureRect,
593 VideoTransformation{ qVideoRotationFromDegrees(orientation()), m_mirrored });
598void QQuickVideoOutput::updateHdr(QSGVideoNode *videoNode)
600 auto *videoOutputWindow = window();
601 if (!videoOutputWindow)
604 auto *swapChain = videoOutputWindow->swapChain();
608 const auto requiredSwapChainFormat = qGetRequiredSwapChainFormat(m_frame.surfaceFormat());
609 if (qShouldUpdateSwapChainFormat(swapChain, requiredSwapChainFormat)) {
610 auto *recreateSwapChainJob = QRunnable::create([swapChain, requiredSwapChainFormat]() {
611 swapChain->destroy();
612 swapChain->setFormat(requiredSwapChainFormat);
613 swapChain->createOrResize();
619 videoOutputWindow->scheduleRenderJob(recreateSwapChainJob, QQuickWindow::AfterSwapStage);
622 videoNode->setSurfaceFormat(swapChain->format());
623 videoNode->setHdrInfo(swapChain->hdrInfo());
626void QQuickVideoOutput::disconnectWindowConnections()
631 m_window->disconnect(
this);
634QRectF QQuickVideoOutput::adjustedViewport()
const
636 return m_videoFormat.viewport();
639void QQuickVideoOutput::setFrame(
const QVideoFrame &frame)
642 QMutexLocker lock(&m_frameMutex);
644 m_videoFormat = frame.surfaceFormat();
646 m_frameDisplayingRotation = qNormalizedFrameTransformation(frame, m_orientation).rotation;
647 m_frameChanged =
true;
650 QMetaObject::invokeMethod(
this, &QQuickVideoOutput::_q_newFrame, frame.size());
653std::optional<std::chrono::nanoseconds> QQuickVideoOutput::g_signalBackoff;
654void QQuickVideoOutput::setSignalBackoff(std::optional<std::chrono::nanoseconds> ns)
656 g_signalBackoff = ns;
661#include "moc_qquickvideooutput_p.cpp"
bool qIsDefaultAspect(int o)