7#include <QtCore/qloggingcategory.h>
8#include <QtCore/qrunnable.h>
9#include <QtCore/private/qfactoryloader_p.h>
11#include <QtMultimedia/qmediaplayer.h>
12#include <QtMultimedia/qmediacapturesession.h>
13#include <QtMultimedia/private/qmultimediautils_p.h>
14#include <QtMultimedia/private/qvideoframetexturepool_p.h>
15#include <QtMultimedia/private/qvideooutputorientationhandler_p.h>
17#include <QtMultimediaQuick/private/qsgvideonode_p.h>
19#include <QtQuick/qquickwindow.h>
20#include <QtQuick/private/qquickwindow_p.h>
24Q_STATIC_LOGGING_CATEGORY(qLcVideo,
"qt.multimedia.video")
30 return (o % 180) == 0;
35 return qIsDefaultAspect(qToUnderlying(rotation));
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
88
89
90
95
96
97
98
100QQuickVideoOutput::QQuickVideoOutput(QQuickItem *parent) :
103 setFlag(ItemHasContents,
true);
105 m_sink =
new QQuickVideoSink(
this);
106 qRegisterMetaType<QVideoFrameFormat>();
109 connect(m_sink, &QVideoSink::videoFrameChanged,
this,
110 makeGuardedCall([
this](
const QVideoFrame &frame) {
111 if (frame.isValid() || m_endOfStreamPolicy == ClearOutput)
114 Qt::DirectConnection);
119QQuickVideoOutput::~QQuickVideoOutput()
122 QMutexLocker lock(&m_destructorGuard->m_mutex);
123 m_destructorGuard->m_isAlive =
false;
127 disconnectWindowConnections();
131
132
133
134
135
136
137
139QVideoSink *QQuickVideoOutput::videoSink()
const
145
146
147
148
149
150
151
152
153
154
155
156
158QQuickVideoOutput::FillMode QQuickVideoOutput::fillMode()
const
160 return FillMode(m_aspectRatioMode);
163void QQuickVideoOutput::setFillMode(FillMode mode)
165 if (mode == fillMode())
168 m_aspectRatioMode = Qt::AspectRatioMode(mode);
170 m_geometryDirty =
true;
173 emit fillModeChanged(mode);
176void QQuickVideoOutput::_q_newFrame(QSize size)
180 size = qRotatedFrameSize(size, m_frameDisplayingRotation);
182 if (m_nativeSize != size) {
185 m_geometryDirty =
true;
187 setImplicitWidth(size.width());
188 setImplicitHeight(size.height());
190 emit sourceRectChanged();
195void QQuickVideoOutput::_q_updateGeometry()
197 const QRectF rect(0, 0, width(), height());
198 const QRectF absoluteRect(x(), y(), width(), height());
200 if (!m_geometryDirty && m_lastRect == absoluteRect)
203 QRectF oldContentRect(m_contentRect);
205 m_geometryDirty =
false;
206 m_lastRect = absoluteRect;
208 const auto fill = m_aspectRatioMode;
209 if (m_nativeSize.isEmpty()) {
212 m_contentRect = rect;
213 }
else if (fill == Qt::IgnoreAspectRatio) {
214 m_contentRect = rect;
216 QSizeF scaled = m_nativeSize;
217 scaled.scale(rect.size(), fill);
219 m_contentRect = QRectF(QPointF(), scaled);
220 m_contentRect.moveCenter(rect.center());
225 if (m_contentRect != oldContentRect)
226 emit contentRectChanged();
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254int QQuickVideoOutput::orientation()
const
256 return m_orientation;
259void QQuickVideoOutput::setOrientation(
int orientation)
262 if (orientation % 90)
266 if (m_orientation == orientation)
271 if (qVideoRotationFromDegrees(orientation - m_orientation) == QtVideo::Rotation::None) {
272 m_orientation = orientation;
273 emit orientationChanged();
277 m_geometryDirty =
true;
281 bool oldAspect = qIsDefaultAspect(m_orientation);
282 bool newAspect = qIsDefaultAspect(orientation);
284 m_orientation = orientation;
287 QMutexLocker lock(&m_frameMutex);
288 m_frameDisplayingRotation = qNormalizedFrameTransformation(m_frame, m_orientation).rotation;
291 if (oldAspect != newAspect) {
292 m_nativeSize.transpose();
294 setImplicitWidth(m_nativeSize.width());
295 setImplicitHeight(m_nativeSize.height());
301 emit orientationChanged();
305
306
307
308
309
310
311
312
313
314
315
316
317bool QQuickVideoOutput::mirrored()
const
322void QQuickVideoOutput::setMirrored(
bool mirrored)
324 if (m_mirrored == mirrored)
326 m_mirrored = mirrored;
329 emit mirroredChanged();
333
334
335
336
337
338
339
340
341
342
343
344
345
346QRectF QQuickVideoOutput::contentRect()
const
348 return m_contentRect;
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367QQuickVideoOutput::EndOfStreamPolicy QQuickVideoOutput::endOfStreamPolicy()
const
369 return m_endOfStreamPolicy;
372void QQuickVideoOutput::setEndOfStreamPolicy(EndOfStreamPolicy policy)
374 if (m_endOfStreamPolicy == policy)
377 m_endOfStreamPolicy = policy;
378 emit endOfStreamPolicyChanged(policy);
382
383
384
385
386
387
388
389
390void QQuickVideoOutput::clearOutput()
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412QRectF QQuickVideoOutput::sourceRect()
const
415 QSizeF size = m_nativeSize;
419 if (!qIsDefaultAspect(m_frameDisplayingRotation))
426 const QRectF viewport = adjustedViewport();
427 Q_ASSERT(viewport.size() == size);
428 return QRectF(viewport.topLeft(), size);
431void QQuickVideoOutput::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
433 Q_UNUSED(newGeometry);
434 Q_UNUSED(oldGeometry);
436 QQuickItem::geometryChange(newGeometry, oldGeometry);
445void QQuickVideoOutput::releaseResources()
449 QQuickItem::releaseResources();
452void QQuickVideoOutput::initRhiForSink()
454 QRhi *rhi = m_window ? QQuickWindowPrivate::get(m_window)->rhi :
nullptr;
458void QQuickVideoOutput::itemChange(QQuickItem::ItemChange change,
459 const QQuickItem::ItemChangeData &changeData)
461 if (change != QQuickItem::ItemSceneChange)
464 if (changeData.window == m_window)
467 disconnectWindowConnections();
468 m_window = changeData.window;
471 auto connectToWindow = [&](
auto signal,
auto function) {
472 connect(m_window, signal,
this, makeGuardedCall(std::move(function)),
473 Qt::DirectConnection);
477 connectToWindow(&QQuickWindow::sceneGraphInitialized, [
this] {
481 connectToWindow(&QQuickWindow::sceneGraphInvalidated, [
this] {
482 if (
auto texturePool = m_texturePool.lock())
483 texturePool->clearTextures();
484 m_sink->setRhi(
nullptr);
487 connectToWindow(&QQuickWindow::afterFrameEnd, [
this] {
488 if (
auto texturePool = m_texturePool.lock())
489 texturePool->onFrameEndInvoked();
495QSize QQuickVideoOutput::nativeSize()
const
497 return m_videoFormat.viewport().size();
500void QQuickVideoOutput::updateGeometry()
502 const QRectF viewport = m_videoFormat.viewport();
503 const QSizeF frameSize = m_videoFormat.frameSize();
504 const QRectF normalizedViewport(viewport.x() / frameSize.width(),
505 viewport.y() / frameSize.height(),
506 viewport.width() / frameSize.width(),
507 viewport.height() / frameSize.height());
508 const QRectF rect(0, 0, width(), height());
509 if (nativeSize().isEmpty()) {
510 m_renderedRect = rect;
511 m_sourceTextureRect = normalizedViewport;
512 }
else if (m_aspectRatioMode == Qt::IgnoreAspectRatio) {
513 m_renderedRect = rect;
514 m_sourceTextureRect = normalizedViewport;
515 }
else if (m_aspectRatioMode == Qt::KeepAspectRatio) {
516 m_sourceTextureRect = normalizedViewport;
517 m_renderedRect = contentRect();
518 }
else if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
519 m_renderedRect = rect;
520 const qreal contentHeight = contentRect().height();
521 const qreal contentWidth = contentRect().width();
524 const qreal relativeOffsetLeft = -contentRect().left() / contentWidth;
525 const qreal relativeOffsetTop = -contentRect().top() / contentHeight;
526 const qreal relativeWidth = rect.width() / contentWidth;
527 const qreal relativeHeight = rect.height() / contentHeight;
530 const qreal totalOffsetLeft = normalizedViewport.x() + relativeOffsetLeft * normalizedViewport.width();
531 const qreal totalOffsetTop = normalizedViewport.y() + relativeOffsetTop * normalizedViewport.height();
532 const qreal totalWidth = normalizedViewport.width() * relativeWidth;
533 const qreal totalHeight = normalizedViewport.height() * relativeHeight;
535 if (qIsDefaultAspect(m_frameDisplayingRotation)) {
536 m_sourceTextureRect = QRectF(totalOffsetLeft, totalOffsetTop,
537 totalWidth, totalHeight);
539 m_sourceTextureRect = QRectF(totalOffsetTop, totalOffsetLeft,
540 totalHeight, totalWidth);
545QSGNode *QQuickVideoOutput::updatePaintNode(QSGNode *oldNode,
546 QQuickItem::UpdatePaintNodeData *data)
551 QSGVideoNode *videoNode =
static_cast<QSGVideoNode *>(oldNode);
553 QMutexLocker lock(&m_frameMutex);
555 if (m_frameChanged) {
556 if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) {
557 qCDebug(qLcVideo) <<
"updatePaintNode: deleting old video node because frame format changed";
562 if (!m_frame.isValid()) {
563 qCDebug(qLcVideo) <<
"updatePaintNode: no frames yet";
564 m_frameChanged =
false;
572 QRhi *rhi = m_window ? QQuickWindowPrivate::get(m_window)->rhi :
nullptr;
573 videoNode =
new QSGVideoNode(
this, m_videoFormat, rhi);
574 m_texturePool = videoNode->texturePool();
575 qCDebug(qLcVideo) <<
"updatePaintNode: Video node created. Handle type:" << m_frame.handleType();
580 m_frameChanged =
false;
581 m_frame = QVideoFrame();
585 if (m_frameChanged) {
586 videoNode->setCurrentFrame(m_frame);
588 updateHdr(videoNode);
591 m_frameChanged =
false;
592 m_frame = QVideoFrame();
595 videoNode->setTexturedRectGeometry(
596 m_renderedRect, m_sourceTextureRect,
597 VideoTransformation{ qVideoRotationFromDegrees(orientation()), m_mirrored });
602void QQuickVideoOutput::updateHdr(QSGVideoNode *videoNode)
604 auto *videoOutputWindow = window();
605 if (!videoOutputWindow)
608 auto *swapChain = videoOutputWindow->swapChain();
612 const auto requiredSwapChainFormat = qGetRequiredSwapChainFormat(m_frame.surfaceFormat());
613 if (qShouldUpdateSwapChainFormat(swapChain, requiredSwapChainFormat)) {
614 auto *recreateSwapChainJob = QRunnable::create([swapChain, requiredSwapChainFormat]() {
615 swapChain->destroy();
616 swapChain->setFormat(requiredSwapChainFormat);
617 swapChain->createOrResize();
623 videoOutputWindow->scheduleRenderJob(recreateSwapChainJob, QQuickWindow::AfterSwapStage);
626 videoNode->setSurfaceFormat(swapChain->format());
627 videoNode->setHdrInfo(swapChain->hdrInfo());
630void QQuickVideoOutput::disconnectWindowConnections()
635 m_window->disconnect(
this);
638QRectF QQuickVideoOutput::adjustedViewport()
const
640 return m_videoFormat.viewport();
643void QQuickVideoOutput::setFrame(
const QVideoFrame &frame)
646 QMutexLocker lock(&m_frameMutex);
648 m_videoFormat = frame.surfaceFormat();
650 m_frameDisplayingRotation = qNormalizedFrameTransformation(frame, m_orientation).rotation;
651 m_frameChanged =
true;
654 QMetaObject::invokeMethod(
this, &QQuickVideoOutput::_q_newFrame, frame.size());
657std::optional<std::chrono::nanoseconds> QQuickVideoOutput::g_signalBackoff;
658void QQuickVideoOutput::setSignalBackoff(std::optional<std::chrono::nanoseconds> ns)
660 g_signalBackoff = ns;
665#include "moc_qquickvideooutput_p.cpp"
Combined button and popup list for selecting options.
bool qIsDefaultAspect(int o)