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