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