Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qvideoframe.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qvideoframe.h"
5
6#include <QtCore/qdebug.h>
7#include <QtCore/qsize.h>
8#include <QtCore/qvariant.h>
9
10#include <QtGui/qimage.h>
11#include <QtGui/qtextlayout.h>
12#include <QtGui/qpainter.h>
13
14#include <QtMultimedia/private/qimagevideobuffer_p.h>
15#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
16#include <QtMultimedia/private/qmultimediautils_p.h>
17#include <QtMultimedia/private/qvideoframe_p.h>
18#include <QtMultimedia/private/qvideoframeconverter_p.h>
19#include <QtMultimedia/private/qvideotexturehelper_p.h>
20
21#include <rhi/qrhi.h>
22
24
25QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QVideoFramePrivate);
26
27/*!
28 \class QVideoFrame
29 \brief The QVideoFrame class represents a frame of video data.
30 \inmodule QtMultimedia
31
32 \ingroup multimedia
33 \ingroup multimedia_video
34
35 A QVideoFrame encapsulates the pixel data of a video frame, and information about the frame.
36
37 Video frames can come from several places - decoded \l {QMediaPlayer}{media}, a
38 \l {QCamera}{camera}, or generated programmatically. The way pixels are described in these
39 frames can vary greatly, and some pixel formats offer greater compression opportunities at
40 the expense of ease of use.
41
42 The pixel contents of a video frame can be mapped to memory using the map() function. After
43 a successful call to map(), the video data can be accessed through various functions. Some of
44 the YUV pixel formats provide the data in several planes. The planeCount() method will return
45 the amount of planes that being used.
46
47 While mapped, the video data of each plane can accessed using the bits() function, which
48 returns a pointer to a buffer. The size of this buffer is given by the mappedBytes() function,
49 and the size of each line is given by bytesPerLine(). The return value of the handle()
50 function may also be used to access frame data using the internal buffer's native APIs
51 (for example - an OpenGL texture handle).
52
53 A video frame can also have timestamp information associated with it. These timestamps can be
54 used to determine when to start and stop displaying the frame.
55
56 QVideoFrame objects can consume a significant amount of memory or system resources and
57 should not be held for longer than required by the application.
58
59 \note Since video frames can be expensive to copy, QVideoFrame is explicitly shared, so any
60 change made to a video frame will also apply to any copies.
61
62 \sa QAbstractVideoBuffer, QVideoFrameFormat, QVideoFrame::MapMode
63*/
64
65/*!
66 \enum QVideoFrame::HandleType
67
68 Identifies the type of a video buffers handle.
69
70 \value NoHandle
71 The buffer has no handle, its data can only be accessed by mapping the buffer.
72 \value RhiTextureHandle
73 The handle of the buffer is defined by The Qt Rendering Hardware Interface
74 (RHI). RHI is Qt's internal graphics abstraction for 3D APIs, such as
75 OpenGL, Vulkan, Metal, and Direct 3D.
76
77 \sa handleType()
78*/
79
80
81/*!
82 Constructs a null video frame.
83*/
84QVideoFrame::QVideoFrame()
85{
86}
87
88#if QT_DEPRECATED_SINCE(6, 8)
89
90/*!
91 \internal
92 Constructs a video frame from a \a buffer with the given pixel \a format and \a size in pixels.
93
94 \note This doesn't increment the reference count of the video buffer.
95*/
96QVideoFrame::QVideoFrame(QAbstractVideoBuffer *buffer, const QVideoFrameFormat &format)
97 : d(new QVideoFramePrivate(format, std::unique_ptr<QAbstractVideoBuffer>(buffer)))
98{
99}
100
101/*!
102 \internal
103*/
104QAbstractVideoBuffer *QVideoFrame::videoBuffer() const
105{
106 return d ? d->videoBuffer.get() : nullptr;
107}
108
109#endif
110
111/*!
112 Constructs a video frame of the given pixel \a format.
113
114*/
115QVideoFrame::QVideoFrame(const QVideoFrameFormat &format)
116 : d(new QVideoFramePrivate(format))
117{
118 auto *textureDescription = QVideoTextureHelper::textureDescription(format.pixelFormat());
119 qsizetype bytes = textureDescription->bytesForSize(format.frameSize());
120 if (bytes > 0) {
121 QByteArray data;
122 data.resize(bytes);
123
124 // Check the memory was successfully allocated.
125 if (!data.isEmpty())
126 d->videoBuffer = std::make_unique<QMemoryVideoBuffer>(
127 data, textureDescription->strideForWidth(format.frameWidth()));
128 }
129}
130
131/*!
132 Constructs a QVideoFrame from a QImage.
133 \since 6.8
134
135 If the QImage::Format matches one of the formats in
136 QVideoFrameFormat::PixelFormat, the QVideoFrame will hold an instance of
137 the \a image and use that format without any pixel format conversion.
138 In this case, pixel data will be copied only if you call \l{QVideoFrame::map}
139 with \c WriteOnly flag while keeping the original image.
140
141 Otherwise, if the QImage::Format matches none of video formats,
142 the image is first converted to a supported (A)RGB format using
143 QImage::convertedTo() with the Qt::AutoColor flag.
144 This may incur a performance penalty.
145
146 If QImage::isNull() evaluates to true for the input QImage, the
147 QVideoFrame will be invalid and QVideoFrameFormat::isValid() will
148 return false.
149
150 \sa QVideoFrameFormat::pixelFormatFromImageFormat()
151 \sa QImage::convertedTo()
152 \sa QImage::isNull()
153*/
154QVideoFrame::QVideoFrame(const QImage &image)
155{
156 auto buffer = std::make_unique<QImageVideoBuffer>(image);
157
158 // If the QImage::Format is not convertible to QVideoFrameFormat,
159 // QImageVideoBuffer automatically converts image to a compatible
160 // (A)RGB format.
161 const QImage &bufferImage = buffer->underlyingImage();
162
163 if (bufferImage.isNull())
164 return;
165
166 // `bufferImage` is now supported by QVideoFrameFormat::pixelFormatFromImageFormat()
167 QVideoFrameFormat format = {
168 bufferImage.size(), QVideoFrameFormat::pixelFormatFromImageFormat(bufferImage.format())
169 };
170
171 Q_ASSERT(format.isValid());
172
173 d = new QVideoFramePrivate{ std::move(format), std::move(buffer) };
174}
175
176/*!
177 Constructs a QVideoFrame from a \l QAbstractVideoBuffer.
178
179 \since 6.8
180
181 The specified \a videoBuffer refers to an instance a reimplemented
182 \l QAbstractVideoBuffer. The instance is expected to contain a preallocated custom
183 video buffer and must implement \l QAbstractVideoBuffer::format,
184 \l QAbstractVideoBuffer::map, and \l QAbstractVideoBuffer::unmap for GPU content.
185
186 If \a videoBuffer is null or gets an invalid \l QVideoFrameFormat,
187 the constructors creates an invalid video frame.
188
189 The created frame will hold ownership of the specified video buffer for its lifetime.
190 Considering that QVideoFrame is implemented via a shared private object,
191 the specified video buffer will be destroyed upon destruction of the last copy
192 of the created video frame.
193
194 Note, if a video frame has been passed to \l QMediaRecorder or a rendering pipeline,
195 the lifetime of the frame is undefined, and the media recorder can destroy it
196 in a different thread.
197
198 QVideoFrame will contain own instance of QVideoFrameFormat.
199 Upon invoking \l setStreamFrameRate, \l setMirrored, or \l setRotation,
200 the inner format can be modified, and \l surfaceFormat will return
201 a detached instance.
202
203 \sa QAbstractVideoBuffer, QVideoFrameFormat
204*/
205QVideoFrame::QVideoFrame(std::unique_ptr<QAbstractVideoBuffer> videoBuffer)
206{
207 if (!videoBuffer)
208 return;
209
210 QVideoFrameFormat format = videoBuffer->format();
211 if (!format.isValid())
212 return;
213
214 d = new QVideoFramePrivate{ std::move(format), std::move(videoBuffer) };
215}
216
217/*!
218 Constructs a shallow copy of \a other. Since QVideoFrame is
219 explicitly shared, these two instances will reflect the same frame.
220
221*/
222QVideoFrame::QVideoFrame(const QVideoFrame &other) = default;
223
224/*!
225 \fn QVideoFrame::QVideoFrame(QVideoFrame &&other)
226
227 Constructs a QVideoFrame by moving from \a other.
228*/
229
230/*!
231 \fn void QVideoFrame::swap(QVideoFrame &other) noexcept
232
233 Swaps the current video frame with \a other.
234*/
235
236/*!
237 \fn QVideoFrame &QVideoFrame::operator=(QVideoFrame &&other)
238
239 Moves \a other into this QVideoFrame.
240*/
241
242/*!
243 Assigns the contents of \a other to this video frame. Since QVideoFrame is
244 explicitly shared, these two instances will reflect the same frame.
245
246*/
247QVideoFrame &QVideoFrame::operator =(const QVideoFrame &other) = default;
248
249/*!
250 \return \c true if this QVideoFrame and \a other reflect the same frame.
251 */
252bool QVideoFrame::operator==(const QVideoFrame &other) const
253{
254 // Due to explicit sharing we just compare the QSharedData which in turn compares the pointers.
255 return d == other.d;
256}
257
258/*!
259 \return \c true if this QVideoFrame and \a other do not reflect the same frame.
260 */
261bool QVideoFrame::operator!=(const QVideoFrame &other) const
262{
263 return d != other.d;
264}
265
266/*!
267 Destroys a video frame.
268*/
269QVideoFrame::~QVideoFrame() = default;
270
271/*!
272 Identifies whether a video frame is valid.
273
274 An invalid frame has no video buffer associated with it.
275
276 Returns true if the frame is valid, and false if it is not.
277*/
278bool QVideoFrame::isValid() const
279{
280 return d && d->videoBuffer && d->format.pixelFormat() != QVideoFrameFormat::Format_Invalid;
281}
282
283/*!
284 Returns the pixel format of this video frame.
285*/
286QVideoFrameFormat::PixelFormat QVideoFrame::pixelFormat() const
287{
288 return d ? d->format.pixelFormat() : QVideoFrameFormat::Format_Invalid;
289}
290
291/*!
292 Returns the surface format of this video frame.
293*/
294QVideoFrameFormat QVideoFrame::surfaceFormat() const
295{
296 return d ? d->format : QVideoFrameFormat{};
297}
298
299/*!
300 Returns the type of a video frame's handle.
301
302 The handle type could either be NoHandle, meaning that the frame is memory
303 based, or a RHI texture.
304*/
305QVideoFrame::HandleType QVideoFrame::handleType() const
306{
307 return (d && d->hwVideoBuffer) ? d->hwVideoBuffer->handleType() : QVideoFrame::NoHandle;
308}
309
310/*!
311 Returns the dimensions of a video frame.
312*/
313QSize QVideoFrame::size() const
314{
315 return d ? d->format.frameSize() : QSize();
316}
317
318/*!
319 Returns the width of a video frame.
320*/
321int QVideoFrame::width() const
322{
323 return size().width();
324}
325
326/*!
327 Returns the height of a video frame.
328*/
329int QVideoFrame::height() const
330{
331 return size().height();
332}
333
334/*!
335 Identifies if a video frame's contents are currently mapped to system memory.
336
337 This is a convenience function which checks that the \l {QVideoFrame::MapMode}{MapMode}
338 of the frame is not equal to QVideoFrame::NotMapped.
339
340 Returns true if the contents of the video frame are mapped to system memory, and false
341 otherwise.
342
343 \sa mapMode(), QVideoFrame::MapMode
344*/
345
346bool QVideoFrame::isMapped() const
347{
348 return d && d->mapMode != QVideoFrame::NotMapped;
349}
350
351/*!
352 Identifies if the mapped contents of a video frame will be persisted when the frame is unmapped.
353
354 This is a convenience function which checks if the \l {QVideoFrame::MapMode}{MapMode}
355 contains the QVideoFrame::WriteOnly flag.
356
357 Returns true if the video frame will be updated when unmapped, and false otherwise.
358
359 \note The result of altering the data of a frame that is mapped in read-only mode is undefined.
360 Depending on the buffer implementation the changes may be persisted, or worse alter a shared
361 buffer.
362
363 \sa mapMode(), QVideoFrame::MapMode
364*/
365bool QVideoFrame::isWritable() const
366{
367 return d && (d->mapMode & QVideoFrame::WriteOnly);
368}
369
370/*!
371 Identifies if the mapped contents of a video frame were read from the frame when it was mapped.
372
373 This is a convenience function which checks if the \l {QVideoFrame::MapMode}{MapMode}
374 contains the QVideoFrame::WriteOnly flag.
375
376 Returns true if the contents of the mapped memory were read from the video frame, and false
377 otherwise.
378
379 \sa mapMode(), QVideoFrame::MapMode
380*/
381bool QVideoFrame::isReadable() const
382{
383 return d && (d->mapMode & QVideoFrame::ReadOnly);
384}
385
386/*!
387 \enum QVideoFrame::MapMode
388
389 Enumerates how a video buffer's data is mapped to system memory.
390
391 \value NotMapped
392 The video buffer is not mapped to memory.
393 \value ReadOnly
394 The mapped memory is populated with data from the video buffer when mapped,
395 but the content of the mapped memory may be discarded when unmapped.
396 \value WriteOnly
397 The mapped memory is uninitialized when mapped, but the possibly modified
398 content will be used to populate the video buffer when unmapped.
399 \value ReadWrite
400 The mapped memory is populated with data from the video
401 buffer, and the video buffer is repopulated with the content of the mapped
402 memory when it is unmapped.
403
404 \sa mapMode(), map()
405*/
406
407/*!
408 Returns the mode a video frame was mapped to system memory in.
409
410 \sa map(), QVideoFrame::MapMode
411*/
412QVideoFrame::MapMode QVideoFrame::mapMode() const
413{
414 return d ? d->mapMode : QVideoFrame::NotMapped;
415}
416
417/*!
418 Maps the contents of a video frame to system (CPU addressable) memory.
419
420 In some cases the video frame data might be stored in video memory or otherwise inaccessible
421 memory, so it is necessary to map a frame before accessing the pixel data. This may involve
422 copying the contents around, so avoid mapping and unmapping unless required.
423
424 The map \a mode indicates whether the contents of the mapped memory should be read from and/or
425 written to the frame. If the map mode includes the \c QVideoFrame::ReadOnly flag the
426 mapped memory will be populated with the content of the video frame when initially mapped. If the map
427 mode includes the \c QVideoFrame::WriteOnly flag the content of the possibly modified
428 mapped memory will be written back to the frame when unmapped.
429
430 While mapped the contents of a video frame can be accessed directly through the pointer returned
431 by the bits() function.
432
433 When access to the data is no longer needed, be sure to call the unmap() function to release the
434 mapped memory and possibly update the video frame contents.
435
436 If the video frame has been mapped in read only mode, it is permissible to map it
437 multiple times in read only mode (and unmap it a corresponding number of times). In all
438 other cases it is necessary to unmap the frame first before mapping a second time.
439
440 \note Writing to memory that is mapped as read-only is undefined, and may result in changes
441 to shared data or crashes.
442
443 Returns true if the frame was mapped to memory in the given \a mode and false otherwise.
444
445 \sa unmap(), mapMode(), bits()
446*/
447bool QVideoFrame::map(QVideoFrame::MapMode mode)
448{
449 if (!d || !d->videoBuffer || d->format.frameSize().isEmpty())
450 return false;
451
452 QMutexLocker lock(&d->mapMutex);
453 if (mode == QVideoFrame::NotMapped)
454 return false;
455
456 if (d->mappedCount > 0) {
457 //it's allowed to map the video frame multiple times in read only mode
458 if (d->mapMode == QVideoFrame::ReadOnly && mode == QVideoFrame::ReadOnly) {
459 d->mappedCount++;
460 return true;
461 }
462
463 return false;
464 }
465
466 Q_ASSERT(d->mapData.data[0] == nullptr);
467 Q_ASSERT(d->mapData.bytesPerLine[0] == 0);
468 Q_ASSERT(d->mapData.planeCount == 0);
469 Q_ASSERT(d->mapData.dataSize[0] == 0);
470
471 d->mapData = d->videoBuffer->map(mode);
472 if (d->mapData.planeCount == 0)
473 return false;
474
475 d->mapMode = mode;
476
477 if (d->mapData.planeCount == 1) {
478 auto pixelFmt = d->format.pixelFormat();
479 // If the plane count is 1 derive the additional planes for planar formats.
480 switch (pixelFmt) {
481 case QVideoFrameFormat::Format_Invalid:
482 case QVideoFrameFormat::Format_ARGB8888:
483 case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
484 case QVideoFrameFormat::Format_XRGB8888:
485 case QVideoFrameFormat::Format_BGRA8888:
486 case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
487 case QVideoFrameFormat::Format_BGRX8888:
488 case QVideoFrameFormat::Format_ABGR8888:
489 case QVideoFrameFormat::Format_XBGR8888:
490 case QVideoFrameFormat::Format_RGBA8888:
491 case QVideoFrameFormat::Format_RGBX8888:
492 case QVideoFrameFormat::Format_AYUV:
493 case QVideoFrameFormat::Format_AYUV_Premultiplied:
494 case QVideoFrameFormat::Format_UYVY:
495 case QVideoFrameFormat::Format_YUYV:
496 case QVideoFrameFormat::Format_Y8:
497 case QVideoFrameFormat::Format_Y16:
498 case QVideoFrameFormat::Format_Jpeg:
499 case QVideoFrameFormat::Format_SamplerExternalOES:
500 case QVideoFrameFormat::Format_SamplerRect:
501 // Single plane or opaque format.
502 break;
503 case QVideoFrameFormat::Format_YUV420P:
504 case QVideoFrameFormat::Format_YUV420P10:
505 case QVideoFrameFormat::Format_YUV422P:
506 case QVideoFrameFormat::Format_YV12: {
507 // The UV stride is usually half the Y stride and is 32-bit aligned.
508 // However it's not always the case, at least on Windows where the
509 // UV planes are sometimes not aligned.
510 // We calculate the stride using the UV byte count to always
511 // have a correct stride.
512 const int height = this->height();
513 const int yStride = d->mapData.bytesPerLine[0];
514 const int uvHeight = pixelFmt == QVideoFrameFormat::Format_YUV422P ? height : height / 2;
515 Q_ASSERT(uvHeight > 0);
516 const int uvSize = d->mapData.dataSize[0] - yStride * height;
517 const int uvStride = uvSize / 2 / uvHeight;
518
519 // Three planes, the second and third vertically (and horizontally for other than Format_YUV422P formats) subsampled.
520 d->mapData.planeCount = 3;
521 d->mapData.bytesPerLine[2] = d->mapData.bytesPerLine[1] = uvStride;
522 d->mapData.dataSize[0] = yStride * height;
523 d->mapData.dataSize[2] = d->mapData.dataSize[1] = uvStride * uvHeight;
524 d->mapData.data[1] = d->mapData.data[0] + d->mapData.dataSize[0];
525 d->mapData.data[2] = d->mapData.data[1] + d->mapData.dataSize[1];
526 break;
527 }
528 case QVideoFrameFormat::Format_NV12:
529 case QVideoFrameFormat::Format_NV21:
530 case QVideoFrameFormat::Format_IMC2:
531 case QVideoFrameFormat::Format_IMC4:
532 case QVideoFrameFormat::Format_P010:
533 case QVideoFrameFormat::Format_P016: {
534 // Semi planar, Full resolution Y plane with interleaved subsampled U and V planes.
535 d->mapData.planeCount = 2;
536 d->mapData.bytesPerLine[1] = d->mapData.bytesPerLine[0];
537 int size = d->mapData.dataSize[0];
538 d->mapData.dataSize[0] = (d->mapData.bytesPerLine[0] * height());
539 d->mapData.dataSize[1] = size - d->mapData.dataSize[0];
540 d->mapData.data[1] = d->mapData.data[0] + d->mapData.dataSize[0];
541 break;
542 }
543 case QVideoFrameFormat::Format_IMC1:
544 case QVideoFrameFormat::Format_IMC3: {
545 // Three planes, the second and third vertically and horizontally subsumpled,
546 // but with lines padded to the width of the first plane.
547 d->mapData.planeCount = 3;
548 d->mapData.bytesPerLine[2] = d->mapData.bytesPerLine[1] = d->mapData.bytesPerLine[0];
549 d->mapData.dataSize[0] = (d->mapData.bytesPerLine[0] * height());
550 d->mapData.dataSize[1] = (d->mapData.bytesPerLine[0] * height() / 2);
551 d->mapData.dataSize[2] = (d->mapData.bytesPerLine[0] * height() / 2);
552 d->mapData.data[1] = d->mapData.data[0] + d->mapData.dataSize[0];
553 d->mapData.data[2] = d->mapData.data[1] + d->mapData.dataSize[1];
554 break;
555 }
556 }
557 }
558
559 d->mappedCount++;
560
561 // unlock mapMutex to avoid potential deadlock imageMutex <--> mapMutex
562 lock.unlock();
563
564 if ((mode & QVideoFrame::WriteOnly) != 0) {
565 QMutexLocker lock(&d->imageMutex);
566 d->image = {};
567 }
568
569 return true;
570}
571
572/*!
573 Releases the memory mapped by the map() function.
574
575 If the \l {QVideoFrame::MapMode}{MapMode} included the QVideoFrame::WriteOnly
576 flag this will persist the current content of the mapped memory to the video frame.
577
578 unmap() should not be called if map() function failed.
579
580 \sa map()
581*/
582void QVideoFrame::unmap()
583{
584 if (!d || !d->videoBuffer)
585 return;
586
587 QMutexLocker lock(&d->mapMutex);
588
589 if (d->mappedCount == 0) {
590 qWarning() << "QVideoFrame::unmap() was called more times then QVideoFrame::map()";
591 return;
592 }
593
594 d->mappedCount--;
595
596 if (d->mappedCount == 0) {
597 d->mapData = {};
598 d->mapMode = QVideoFrame::NotMapped;
599 d->videoBuffer->unmap();
600 }
601}
602
603/*!
604 Returns the number of bytes in a scan line of a \a plane.
605
606 This value is only valid while the frame data is \l {map()}{mapped}.
607
608 \sa bits(), map(), mappedBytes(), planeCount()
609 \since 5.4
610*/
611
612int QVideoFrame::bytesPerLine(int plane) const
613{
614 if (!d)
615 return 0;
616 return plane >= 0 && plane < d->mapData.planeCount ? d->mapData.bytesPerLine[plane] : 0;
617}
618
619/*!
620 Returns a pointer to the start of the frame data buffer for a \a plane.
621
622 This value is only valid while the frame data is \l {map()}{mapped}.
623
624 Changes made to data accessed via this pointer (when mapped with write access)
625 are only guaranteed to have been persisted when unmap() is called and when the
626 buffer has been mapped for writing.
627
628 \sa map(), mappedBytes(), bytesPerLine(), planeCount()
629 \since 5.4
630*/
631uchar *QVideoFrame::bits(int plane)
632{
633 if (!d)
634 return nullptr;
635 return plane >= 0 && plane < d->mapData.planeCount ? d->mapData.data[plane] : nullptr;
636}
637
638/*!
639 Returns a pointer to the start of the frame data buffer for a \a plane.
640
641 This value is only valid while the frame data is \l {map()}{mapped}.
642
643 If the buffer was not mapped with read access, the contents of this
644 buffer will initially be uninitialized.
645
646 \sa map(), mappedBytes(), bytesPerLine(), planeCount()
647 \since 5.4
648*/
649const uchar *QVideoFrame::bits(int plane) const
650{
651 if (!d)
652 return nullptr;
653 return plane >= 0 && plane < d->mapData.planeCount ? d->mapData.data[plane] : nullptr;
654}
655
656/*!
657 Returns the number of bytes occupied by plane \a plane of the mapped frame data.
658
659 This value is only valid while the frame data is \l {map()}{mapped}.
660
661 \sa map()
662*/
663int QVideoFrame::mappedBytes(int plane) const
664{
665 if (!d)
666 return 0;
667 return plane >= 0 && plane < d->mapData.planeCount ? d->mapData.dataSize[plane] : 0;
668}
669
670/*!
671 Returns the number of planes in the video frame.
672
673 \sa map()
674 \since 5.4
675*/
676
677int QVideoFrame::planeCount() const
678{
679 if (!d)
680 return 0;
681 return d->format.planeCount();
682}
683
684/*!
685 Returns the presentation time (in microseconds) when the frame should be displayed.
686
687 An invalid time is represented as -1.
688
689*/
690qint64 QVideoFrame::startTime() const
691{
692 if (!d)
693 return -1;
694 return d->startTime;
695}
696
697/*!
698 Sets the presentation \a time (in microseconds) when the frame should initially be displayed.
699
700 An invalid time is represented as -1.
701
702*/
703void QVideoFrame::setStartTime(qint64 time)
704{
705 if (!d)
706 return;
707 d->startTime = time;
708}
709
710/*!
711 Returns the presentation time (in microseconds) when a frame should stop being displayed.
712
713 An invalid time is represented as -1.
714
715*/
716qint64 QVideoFrame::endTime() const
717{
718 if (!d)
719 return -1;
720 return d->endTime;
721}
722
723/*!
724 Sets the presentation \a time (in microseconds) when a frame should stop being displayed.
725
726 An invalid time is represented as -1.
727
728*/
729void QVideoFrame::setEndTime(qint64 time)
730{
731 if (!d)
732 return;
733 d->endTime = time;
734}
735
736#if QT_DEPRECATED_SINCE(6, 7)
737/*!
738 \enum QVideoFrame::RotationAngle
739 \deprecated [6.7] Use QtVideo::Rotation instead.
740
741 The angle of the clockwise rotation that should be applied to a video
742 frame before displaying.
743
744 \value Rotation0 No rotation required, the frame has correct orientation
745 \value Rotation90 The frame should be rotated by 90 degrees
746 \value Rotation180 The frame should be rotated by 180 degrees
747 \value Rotation270 The frame should be rotated by 270 degrees
748*/
749
750/*!
751 \fn void QVideoFrame::setRotationAngle(RotationAngle)
752 \deprecated [6.7] Use \c QVideoFrame::setRotation instead.
753
754 Sets the \a angle the frame should be rotated clockwise before displaying.
755*/
756
757/*!
758 \fn QVideoFrame::RotationAngle QVideoFrame::rotationAngle() const
759 \deprecated [6.7] Use \c QVideoFrame::rotation instead.
760
761 Returns the angle the frame should be rotated clockwise before displaying.
762*/
763
764#endif
765
766
767/*!
768 Sets the \a angle the frame should be rotated clockwise before displaying.
769
770 Transformations of \c QVideoFrame, specifically rotation and mirroring,
771 are used only for displaying the video frame and are applied on top
772 of the surface transformation, which is determined by \l QVideoFrameFormat.
773 Rotation is applied before mirroring.
774
775 Default value is \c QtVideo::Rotation::None.
776*/
777void QVideoFrame::setRotation(QtVideo::Rotation angle)
778{
779 if (d)
780 d->presentationTransformation.rotation = angle;
781}
782
783/*!
784 Returns the angle the frame should be rotated clockwise before displaying.
785
786 Transformations of \c QVideoFrame, specifically rotation and mirroring,
787 are used only for displaying the video frame and are applied on top
788 of the surface transformation, which is determined by \l QVideoFrameFormat.
789 Rotation is applied before mirroring.
790 */
791QtVideo::Rotation QVideoFrame::rotation() const
792{
793 return d ? d->presentationTransformation.rotation : QtVideo::Rotation::None;
794}
795
796/*!
797 Sets whether the frame should be \a mirrored around its vertical axis before displaying.
798
799 Transformations of \c QVideoFrame, specifically rotation and mirroring,
800 are used only for displaying the video frame and are applied on top
801 of the surface transformation, which is determined by \l QVideoFrameFormat.
802 Mirroring is applied after rotation.
803
804 Mirroring is typically needed for video frames coming from a front camera of a mobile device.
805
806 Default value is \c false.
807*/
808void QVideoFrame::setMirrored(bool mirrored)
809{
810 if (d)
811 d->presentationTransformation.mirroredHorizontallyAfterRotation = mirrored;
812}
813
814/*!
815 Returns whether the frame should be mirrored around its vertical axis before displaying.
816
817 Transformations of \c QVideoFrame, specifically rotation and mirroring,
818 are used only for displaying the video frame and are applied on top
819 of the surface transformation, which is determined by \l QVideoFrameFormat.
820 Mirroring is applied after rotation.
821
822 Mirroring is typically needed for video frames coming from a front camera of a mobile device.
823*/
824bool QVideoFrame::mirrored() const
825{
826 return d && d->presentationTransformation.mirroredHorizontallyAfterRotation;
827}
828
829/*!
830 Sets the frame \a rate of a video stream in frames per second.
831*/
832void QVideoFrame::setStreamFrameRate(qreal rate)
833{
834 if (d)
835 d->format.setStreamFrameRate(rate);
836}
837
838/*!
839 Returns the frame rate of a video stream in frames per second.
840*/
841qreal QVideoFrame::streamFrameRate() const
842{
843 return d ? d->format.streamFrameRate() : 0.;
844}
845
846/*!
847 Converts current video frame to image.
848
849 The conversion is based on the current pixel data and
850 the \l {QVideoFrame::surfaceFormat}{surface format}.
851 Transformations of the frame don't impact the result
852 since they are applied for presentation only.
853
854 \since 5.15
855*/
856QImage QVideoFrame::toImage() const
857{
858 if (!isValid())
859 return {};
860
861 QMutexLocker lock(&d->imageMutex);
862
863 if (d->image.isNull())
864 d->image = qImageFromVideoFrame(*this, qNormalizedSurfaceTransformation(d->format));
865
866 return d->image;
867}
868
869/*!
870 Returns the subtitle text that should be rendered together with this video frame.
871*/
872QString QVideoFrame::subtitleText() const
873{
874 return d ? d->subtitleText : QString();
875}
876
877/*!
878 Sets the subtitle text that should be rendered together with this video frame to \a text.
879*/
880void QVideoFrame::setSubtitleText(const QString &text)
881{
882 if (!d)
883 return;
884 d->subtitleText = text;
885}
886
887/*!
888 Uses a QPainter, \a{painter}, to render this QVideoFrame to \a rect.
889 The PaintOptions \a options can be used to specify a background color and
890 how \a rect should be filled with the video.
891
892 \note that rendering will usually happen without hardware acceleration when
893 using this method.
894*/
895void QVideoFrame::paint(QPainter *painter, const QRectF &rect, const PaintOptions &options)
896{
897 if (!isValid()) {
898 painter->fillRect(rect, options.backgroundColor);
899 return;
900 }
901
902 QRectF targetRect = rect;
903 QSizeF size = qRotatedFramePresentationSize(*this);
904
905 size.scale(targetRect.size(), options.aspectRatioMode);
906
907 if (options.aspectRatioMode == Qt::KeepAspectRatio) {
908 targetRect = QRect(0, 0, size.width(), size.height());
909 targetRect.moveCenter(rect.center());
910 // we might not be drawing every pixel, fill the leftovers black
911 if (options.backgroundColor != Qt::transparent && rect != targetRect) {
912 if (targetRect.top() > rect.top()) {
913 QRectF top(rect.left(), rect.top(), rect.width(), targetRect.top() - rect.top());
914 painter->fillRect(top, Qt::black);
915 }
916 if (targetRect.left() > rect.left()) {
917 QRectF top(rect.left(), targetRect.top(), targetRect.left() - rect.left(), targetRect.height());
918 painter->fillRect(top, Qt::black);
919 }
920 if (targetRect.right() < rect.right()) {
921 QRectF top(targetRect.right(), targetRect.top(), rect.right() - targetRect.right(), targetRect.height());
922 painter->fillRect(top, Qt::black);
923 }
924 if (targetRect.bottom() < rect.bottom()) {
925 QRectF top(rect.left(), targetRect.bottom(), rect.width(), rect.bottom() - targetRect.bottom());
926 painter->fillRect(top, Qt::black);
927 }
928 }
929 }
930
931 if (map(QVideoFrame::ReadOnly)) {
932 const QTransform oldTransform = painter->transform();
933 QTransform transform = oldTransform;
934 transform.translate(targetRect.center().x() - size.width()/2,
935 targetRect.center().y() - size.height()/2);
936 painter->setTransform(transform);
937
938 const bool hasPresentationTransformation =
939 d->presentationTransformation != VideoTransformation{};
940
941 // Use cache for images without presentation transform
942 const QImage image = hasPresentationTransformation
943 ? qImageFromVideoFrame(*this, qNormalizedFrameTransformation(*this))
944 : toImage();
945
946 painter->drawImage({{}, size}, image, {{},image.size()});
947 painter->setTransform(oldTransform);
948
949 unmap();
950 } else if (isValid()) {
951 // #### error handling
952 } else {
953 painter->fillRect(rect, Qt::black);
954 }
955
956 if ((options.paintFlags & PaintOptions::DontDrawSubtitles) || d->subtitleText.isEmpty())
957 return;
958
959 // draw subtitles
960 auto text = d->subtitleText;
961 text.replace(QLatin1Char('\n'), QChar::LineSeparator);
962
963 QVideoTextureHelper::SubtitleLayout layout;
964 layout.update(targetRect.size().toSize(), this->subtitleText());
965 layout.draw(painter, targetRect.topLeft());
966}
967
968#ifndef QT_NO_DEBUG_STREAM
969static QString qFormatTimeStamps(qint64 start, qint64 end)
970{
971 // Early out for invalid.
972 if (start < 0)
973 return QLatin1String("[no timestamp]");
974
975 bool onlyOne = (start == end);
976
977 // [hh:]mm:ss.ms
978 const int s_millis = start % 1000000;
979 start /= 1000000;
980 const int s_seconds = start % 60;
981 start /= 60;
982 const int s_minutes = start % 60;
983 start /= 60;
984
985 if (onlyOne) {
986 if (start > 0)
987 return QStringLiteral("@%1:%2:%3.%4")
988 .arg(start, 1, 10, QLatin1Char('0'))
989 .arg(s_minutes, 2, 10, QLatin1Char('0'))
990 .arg(s_seconds, 2, 10, QLatin1Char('0'))
991 .arg(s_millis, 2, 10, QLatin1Char('0'));
992 return QStringLiteral("@%1:%2.%3")
993 .arg(s_minutes, 2, 10, QLatin1Char('0'))
994 .arg(s_seconds, 2, 10, QLatin1Char('0'))
995 .arg(s_millis, 2, 10, QLatin1Char('0'));
996 }
997
998 if (end == -1) {
999 // Similar to start-start, except it means keep displaying it?
1000 if (start > 0)
1001 return QStringLiteral("%1:%2:%3.%4 - forever")
1002 .arg(start, 1, 10, QLatin1Char('0'))
1003 .arg(s_minutes, 2, 10, QLatin1Char('0'))
1004 .arg(s_seconds, 2, 10, QLatin1Char('0'))
1005 .arg(s_millis, 2, 10, QLatin1Char('0'));
1006 return QStringLiteral("%1:%2.%3 - forever")
1007 .arg(s_minutes, 2, 10, QLatin1Char('0'))
1008 .arg(s_seconds, 2, 10, QLatin1Char('0'))
1009 .arg(s_millis, 2, 10, QLatin1Char('0'));
1010 }
1011
1012 const int e_millis = end % 1000000;
1013 end /= 1000000;
1014 const int e_seconds = end % 60;
1015 end /= 60;
1016 const int e_minutes = end % 60;
1017 end /= 60;
1018
1019 if (start > 0 || end > 0)
1020 return QStringLiteral("%1:%2:%3.%4 - %5:%6:%7.%8")
1021 .arg(start, 1, 10, QLatin1Char('0'))
1022 .arg(s_minutes, 2, 10, QLatin1Char('0'))
1023 .arg(s_seconds, 2, 10, QLatin1Char('0'))
1024 .arg(s_millis, 2, 10, QLatin1Char('0'))
1025 .arg(end, 1, 10, QLatin1Char('0'))
1026 .arg(e_minutes, 2, 10, QLatin1Char('0'))
1027 .arg(e_seconds, 2, 10, QLatin1Char('0'))
1028 .arg(e_millis, 2, 10, QLatin1Char('0'));
1029 return QStringLiteral("%1:%2.%3 - %4:%5.%6")
1030 .arg(s_minutes, 2, 10, QLatin1Char('0'))
1031 .arg(s_seconds, 2, 10, QLatin1Char('0'))
1032 .arg(s_millis, 2, 10, QLatin1Char('0'))
1033 .arg(e_minutes, 2, 10, QLatin1Char('0'))
1034 .arg(e_seconds, 2, 10, QLatin1Char('0'))
1035 .arg(e_millis, 2, 10, QLatin1Char('0'));
1036}
1037
1038QDebug operator<<(QDebug dbg, QVideoFrame::HandleType type)
1039{
1040 QDebugStateSaver saver(dbg);
1041 dbg.nospace();
1042 switch (type) {
1043 case QVideoFrame::NoHandle:
1044 return dbg << "NoHandle";
1045 case QVideoFrame::RhiTextureHandle:
1046 return dbg << "RhiTextureHandle";
1047 }
1048 return dbg;
1049}
1050
1051QDebug operator<<(QDebug dbg, const QVideoFrame& f)
1052{
1053 QDebugStateSaver saver(dbg);
1054 dbg.nospace();
1055 dbg << "QVideoFrame(" << f.size() << ", "
1056 << f.pixelFormat() << ", "
1057 << f.handleType() << ", "
1058 << f.mapMode() << ", "
1059 << qFormatTimeStamps(f.startTime(), f.endTime()).toLatin1().constData();
1060 dbg << ')';
1061 return dbg;
1062}
1063#endif
1064
1065QT_END_NAMESPACE
Combined button and popup list for selecting options.
QDebug operator<<(QDebug debug, QDir::Filters filters)
Definition qdir.cpp:2598
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
static QString qFormatTimeStamps(qint64 start, qint64 end)