Qt 6.x
The Qt SDK
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
9#include "qvideoframeformat.h"
10#include "qpainter.h"
11#include <qtextlayout.h>
12
13#include <qimage.h>
14#include <qmutex.h>
15#include <qpair.h>
16#include <qsize.h>
17#include <qvariant.h>
18#include <rhi/qrhi.h>
19
20#include <QDebug>
21
23
25{
26public:
27 QVideoFramePrivate() = default;
29 : format(format)
30 {
31 }
32
34 {
35 delete buffer;
36 }
37
43 int mappedCount = 0;
47 bool mirrored = false;
49private:
50 Q_DISABLE_COPY(QVideoFramePrivate)
51};
52
54
96{
97}
98
107{
108 d->buffer = buffer;
109}
110
115{
116 return d ? d->buffer : nullptr;
117}
118
125{
126 auto *textureDescription = QVideoTextureHelper::textureDescription(format.pixelFormat());
127 qsizetype bytes = textureDescription->bytesForSize(format.frameSize());
128 if (bytes > 0) {
130 data.resize(bytes);
131
132 // Check the memory was successfully allocated.
133 if (!data.isEmpty())
134 d->buffer = new QMemoryVideoBuffer(data, textureDescription->strideForWidth(format.frameWidth()));
135 }
136}
137
144
169
174{
175 // Due to explicit sharing we just compare the QSharedData which in turn compares the pointers.
176 return d == other.d;
177}
178
183{
184 return d != other.d;
185}
186
190QVideoFrame::~QVideoFrame() = default;
191
200{
202}
203
208{
210}
211
216{
217 return d ? d->format : QVideoFrameFormat{};
218}
219
227{
228 return (d && d->buffer) ? d->buffer->handleType() : QVideoFrame::NoHandle;
229}
230
235{
236 return d ? d->format.frameSize() : QSize();
237}
238
243{
244 return size().width();
245}
246
251{
252 return size().height();
253}
254
268{
269 return d && d->buffer && d->buffer->mapMode() != QVideoFrame::NotMapped;
270}
271
287{
288 return d && d->buffer && (d->buffer->mapMode() & QVideoFrame::WriteOnly);
289}
290
303{
304 return d && d->buffer && (d->buffer->mapMode() & QVideoFrame::ReadOnly);
305}
306
313{
314 return (d && d->buffer) ? d->buffer->mapMode() : QVideoFrame::NotMapped;
315}
316
348{
349
350 if (!d || !d->buffer)
351 return false;
352
355 return false;
356
357 if (d->mappedCount > 0) {
358 //it's allowed to map the video frame multiple times in read only mode
361 d->mappedCount++;
362 return true;
363 }
364
365 return false;
366 }
367
368 Q_ASSERT(d->mapData.data[0] == nullptr);
369 Q_ASSERT(d->mapData.bytesPerLine[0] == 0);
370 Q_ASSERT(d->mapData.nPlanes == 0);
371 Q_ASSERT(d->mapData.size[0] == 0);
372
373 d->mapData = d->buffer->map(mode);
374 if (d->mapData.nPlanes == 0)
375 return false;
376
377 if (d->mapData.nPlanes == 1) {
378 auto pixelFmt = d->format.pixelFormat();
379 // If the plane count is 1 derive the additional planes for planar formats.
380 switch (pixelFmt) {
401 // Single plane or opaque format.
402 break;
407 // The UV stride is usually half the Y stride and is 32-bit aligned.
408 // However it's not always the case, at least on Windows where the
409 // UV planes are sometimes not aligned.
410 // We calculate the stride using the UV byte count to always
411 // have a correct stride.
412 const int height = this->height();
413 const int yStride = d->mapData.bytesPerLine[0];
414 const int uvHeight = pixelFmt == QVideoFrameFormat::Format_YUV422P ? height : height / 2;
415 const int uvStride = (d->mapData.size[0] - (yStride * height)) / uvHeight / 2;
416
417 // Three planes, the second and third vertically (and horizontally for other than Format_YUV422P formats) subsampled.
418 d->mapData.nPlanes = 3;
419 d->mapData.bytesPerLine[2] = d->mapData.bytesPerLine[1] = uvStride;
420 d->mapData.size[0] = yStride * height;
421 d->mapData.size[1] = uvStride * uvHeight;
422 d->mapData.size[2] = uvStride * uvHeight;
423 d->mapData.data[1] = d->mapData.data[0] + d->mapData.size[0];
424 d->mapData.data[2] = d->mapData.data[1] + d->mapData.size[1];
425 break;
426 }
433 // Semi planar, Full resolution Y plane with interleaved subsampled U and V planes.
434 d->mapData.nPlanes = 2;
436 int size = d->mapData.size[0];
437 d->mapData.size[0] = (d->mapData.bytesPerLine[0] * height());
438 d->mapData.size[1] = size - d->mapData.size[0];
439 d->mapData.data[1] = d->mapData.data[0] + d->mapData.size[0];
440 break;
441 }
444 // Three planes, the second and third vertically and horizontally subsumpled,
445 // but with lines padded to the width of the first plane.
446 d->mapData.nPlanes = 3;
448 d->mapData.size[0] = (d->mapData.bytesPerLine[0] * height());
449 d->mapData.size[1] = (d->mapData.bytesPerLine[0] * height() / 2);
450 d->mapData.size[2] = (d->mapData.bytesPerLine[0] * height() / 2);
451 d->mapData.data[1] = d->mapData.data[0] + d->mapData.size[0];
452 d->mapData.data[2] = d->mapData.data[1] + d->mapData.size[1];
453 break;
454 }
455 }
456 }
457
458 d->mappedCount++;
459 return true;
460}
461
473{
474 if (!d || !d->buffer)
475 return;
476
478
479 if (d->mappedCount == 0) {
480 qWarning() << "QVideoFrame::unmap() was called more times then QVideoFrame::map()";
481 return;
482 }
483
484 d->mappedCount--;
485
486 if (d->mappedCount == 0) {
487 d->mapData = {};
488 d->buffer->unmap();
489 }
490}
491
501int QVideoFrame::bytesPerLine(int plane) const
502{
503 if (!d)
504 return 0;
505 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.bytesPerLine[plane] : 0;
506}
507
521{
522 if (!d)
523 return nullptr;
524 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.data[plane] : nullptr;
525}
526
538const uchar *QVideoFrame::bits(int plane) const
539{
540 if (!d)
541 return nullptr;
542 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.data[plane] : nullptr;
543}
544
552int QVideoFrame::mappedBytes(int plane) const
553{
554 if (!d)
555 return 0;
556 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.size[plane] : 0;
557}
558
567{
568 if (!d)
569 return 0;
570 return d->format.planeCount();
571}
572
580{
581 if (!d)
582 return -1;
583 return d->startTime;
584}
585
593{
594 if (!d)
595 return;
596 d->startTime = time;
597}
598
606{
607 if (!d)
608 return -1;
609 return d->endTime;
610}
611
619{
620 if (!d)
621 return;
622 d->endTime = time;
623}
624
641{
642 if (d)
643 d->rotationAngle = angle;
644}
645
650{
651 return d ? d->rotationAngle : Rotation0;
652}
653
657void QVideoFrame::setMirrored(bool mirrored)
658{
659 if (d)
660 d->mirrored = mirrored;
661}
662
667{
668 return d && d->mirrored;
669}
670
676{
677 if (!isValid())
678 return {};
679 if (!d->image.isNull())
680 return d->image;
681
683 surfaceFormat().scanLineDirection() != QVideoFrameFormat::TopToBottom);
684 return d->image;
685}
686
691{
692 return d ? d->subtitleText : QString();
693}
694
699{
700 if (!d)
701 return;
702 d->subtitleText = text;
703}
704
714{
715 if (!isValid()) {
717 return;
718 }
719
720 QRectF targetRect = rect;
721 QSizeF size = this->size();
722 if (rotationAngle() % 180)
723 size.transpose();
724
725 size.scale(targetRect.size(), options.aspectRatioMode);
726
727 if (options.aspectRatioMode == Qt::KeepAspectRatio) {
728 targetRect = QRect(0, 0, size.width(), size.height());
729 targetRect.moveCenter(rect.center());
730 // we might not be drawing every pixel, fill the leftovers black
731 if (options.backgroundColor != Qt::transparent && rect != targetRect) {
732 if (targetRect.top() > rect.top()) {
733 QRectF top(rect.left(), rect.top(), rect.width(), targetRect.top() - rect.top());
735 }
736 if (targetRect.left() > rect.left()) {
737 QRectF top(rect.left(), targetRect.top(), targetRect.left() - rect.left(), targetRect.height());
739 }
740 if (targetRect.right() < rect.right()) {
741 QRectF top(targetRect.right(), targetRect.top(), rect.right() - targetRect.right(), targetRect.height());
743 }
744 if (targetRect.bottom() < rect.bottom()) {
745 QRectF top(rect.left(), targetRect.bottom(), rect.width(), rect.bottom() - targetRect.bottom());
747 }
748 }
749 }
750
752 const QTransform oldTransform = painter->transform();
753 QTransform transform = oldTransform;
754 transform.translate(targetRect.center().x() - size.width()/2,
755 targetRect.center().y() - size.height()/2);
757 QImage image = toImage();
758 painter->drawImage({{}, size}, image, {{},image.size()});
759 painter->setTransform(oldTransform);
760
761 unmap();
762 } else if (isValid()) {
763 // #### error handling
764 } else {
766 }
767
769 return;
770
771 // draw subtitles
772 auto text = d->subtitleText;
774
776 layout.update(targetRect.size().toSize(), this->subtitleText());
777 layout.draw(painter, targetRect.topLeft());
778}
779
780#ifndef QT_NO_DEBUG_STREAM
782{
783 // Early out for invalid.
784 if (start < 0)
785 return QLatin1String("[no timestamp]");
786
787 bool onlyOne = (start == end);
788
789 // [hh:]mm:ss.ms
790 const int s_millis = start % 1000000;
791 start /= 1000000;
792 const int s_seconds = start % 60;
793 start /= 60;
794 const int s_minutes = start % 60;
795 start /= 60;
796
797 if (onlyOne) {
798 if (start > 0)
799 return QString::fromLatin1("@%1:%2:%3.%4")
800 .arg(start, 1, 10, QLatin1Char('0'))
801 .arg(s_minutes, 2, 10, QLatin1Char('0'))
802 .arg(s_seconds, 2, 10, QLatin1Char('0'))
803 .arg(s_millis, 2, 10, QLatin1Char('0'));
804 return QString::fromLatin1("@%1:%2.%3")
805 .arg(s_minutes, 2, 10, QLatin1Char('0'))
806 .arg(s_seconds, 2, 10, QLatin1Char('0'))
807 .arg(s_millis, 2, 10, QLatin1Char('0'));
808 }
809
810 if (end == -1) {
811 // Similar to start-start, except it means keep displaying it?
812 if (start > 0)
813 return QString::fromLatin1("%1:%2:%3.%4 - forever")
814 .arg(start, 1, 10, QLatin1Char('0'))
815 .arg(s_minutes, 2, 10, QLatin1Char('0'))
816 .arg(s_seconds, 2, 10, QLatin1Char('0'))
817 .arg(s_millis, 2, 10, QLatin1Char('0'));
818 return QString::fromLatin1("%1:%2.%3 - forever")
819 .arg(s_minutes, 2, 10, QLatin1Char('0'))
820 .arg(s_seconds, 2, 10, QLatin1Char('0'))
821 .arg(s_millis, 2, 10, QLatin1Char('0'));
822 }
823
824 const int e_millis = end % 1000000;
825 end /= 1000000;
826 const int e_seconds = end % 60;
827 end /= 60;
828 const int e_minutes = end % 60;
829 end /= 60;
830
831 if (start > 0 || end > 0)
832 return QString::fromLatin1("%1:%2:%3.%4 - %5:%6:%7.%8")
833 .arg(start, 1, 10, QLatin1Char('0'))
834 .arg(s_minutes, 2, 10, QLatin1Char('0'))
835 .arg(s_seconds, 2, 10, QLatin1Char('0'))
836 .arg(s_millis, 2, 10, QLatin1Char('0'))
837 .arg(end, 1, 10, QLatin1Char('0'))
838 .arg(e_minutes, 2, 10, QLatin1Char('0'))
839 .arg(e_seconds, 2, 10, QLatin1Char('0'))
840 .arg(e_millis, 2, 10, QLatin1Char('0'));
841 return QString::fromLatin1("%1:%2.%3 - %4:%5.%6")
842 .arg(s_minutes, 2, 10, QLatin1Char('0'))
843 .arg(s_seconds, 2, 10, QLatin1Char('0'))
844 .arg(s_millis, 2, 10, QLatin1Char('0'))
845 .arg(e_minutes, 2, 10, QLatin1Char('0'))
846 .arg(e_seconds, 2, 10, QLatin1Char('0'))
847 .arg(e_millis, 2, 10, QLatin1Char('0'));
848}
849
851{
852 QDebugStateSaver saver(dbg);
853 dbg.nospace();
854 switch (type) {
856 return dbg << "NoHandle";
858 return dbg << "RhiTextureHandle";
859 }
860 return dbg;
861}
862
864{
865 QDebugStateSaver saver(dbg);
866 dbg.nospace();
867 dbg << "QVideoFrame(" << f.size() << ", "
868 << f.pixelFormat() << ", "
869 << f.handleType() << ", "
870 << f.mapMode() << ", "
871 << qFormatTimeStamps(f.startTime(), f.endTime()).toLatin1().constData();
872 dbg << ')';
873 return dbg;
874}
875#endif
876
878
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
virtual void unmap()=0
Releases the memory mapped by the map() function.
virtual MapData map(QVideoFrame::MapMode mode)=0
Independently maps the planes of a video buffer to memory.
virtual QVideoFrame::MapMode mapMode() const =0
QVideoFrame::HandleType handleType() const
Returns the type of a video buffer's handle.
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
@ LineSeparator
Definition qchar.h:64
\inmodule QtCore
\inmodule QtCore
\inmodule QtGui
Definition qimage.h:37
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1197
void update()
Updates the layout for parentWidget().
Definition qlayout.cpp:970
The QMemoryVideoBuffer class provides a system memory allocated video data buffer.
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags=Qt::AutoColor)
Draws the rectangular portion source of the given image into the target rectangle in the paint device...
const QTransform & transform() const
Alias for worldTransform().
void fillRect(const QRectF &, const QBrush &)
Fills the given rectangle with the brush specified.
void setTransform(const QTransform &transform, bool combine=false)
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr void moveCenter(const QPointF &p) noexcept
Moves the rectangle, leaving the center point at the given position.
Definition qrect.h:712
constexpr qreal bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:499
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:496
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:510
constexpr QPointF center() const noexcept
Returns the center point of the rectangle.
Definition qrect.h:685
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:721
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:497
constexpr qreal right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:498
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
Definition qshareddata.h:19
\inmodule QtCore
Definition qsize.h:207
constexpr QSize toSize() const noexcept
Returns an integer based copy of this size.
Definition qsize.h:390
void transpose() noexcept
Swaps the width and height values.
Definition qsize.cpp:581
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
PixelFormat
Enumerates video data types.
int planeCount() const
Returns the number of planes used.
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of frames in a video stream.
QSize frameSize() const
Returns the dimensions of frames in a video stream.
QVideoFrameFormat format
QVideoFramePrivate(const QVideoFrameFormat &format)
QAbstractVideoBuffer::MapData mapData
QVideoFramePrivate()=default
QAbstractVideoBuffer * buffer
QVideoFrame::RotationAngle rotationAngle
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:26
QVideoFrame::HandleType handleType() const
Returns the type of a video frame's handle.
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Definition qvideoframe.h:36
QVideoFrame()
Constructs a null video frame.
void setRotationAngle(RotationAngle)
Sets the angle the frame should be rotated clockwise before displaying.
QAbstractVideoBuffer * videoBuffer() const
RotationAngle
The angle of the clockwise rotation that should be applied to a video frame before displaying.
Definition qvideoframe.h:44
bool operator==(const QVideoFrame &other) const
qint64 startTime() const
Returns the presentation time (in microseconds) when the frame should be displayed.
void unmap()
Releases the memory mapped by the map() function.
void setMirrored(bool)
Sets the mirrored flag for the frame.
int planeCount() const
Returns the number of planes in the video frame.
bool mirrored() const
Returns whether the frame should be mirrored before displaying.
void paint(QPainter *painter, const QRectF &rect, const PaintOptions &options)
Uses a QPainter, {painter}, to render this QVideoFrame to rect.
QVideoFrameFormat surfaceFormat() const
Returns the surface format of this video frame.
QString subtitleText() const
Returns the subtitle text that should be rendered together with this video frame.
bool isMapped() const
Identifies if a video frame's contents are currently mapped to system memory.
QImage toImage() const
Based on the pixel format converts current video frame to image.
QSize size() const
Returns the dimensions of a video frame.
bool isReadable() const
Identifies if the mapped contents of a video frame were read from the frame when it was mapped.
int height() const
Returns the height of a video frame.
void setEndTime(qint64 time)
Sets the presentation time (in microseconds) when a frame should stop being displayed.
uchar * bits(int plane)
Returns a pointer to the start of the frame data buffer for a plane.
void setSubtitleText(const QString &text)
Sets the subtitle text that should be rendered together with this video frame to text.
bool map(QVideoFrame::MapMode mode)
Maps the contents of a video frame to system (CPU addressable) memory.
bool operator!=(const QVideoFrame &other) const
HandleType
Identifies the type of a video buffers handle.
Definition qvideoframe.h:30
QVideoFrame::MapMode mapMode() const
Returns the mode a video frame was mapped to system memory in.
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of this video frame.
QVideoFrame & operator=(const QVideoFrame &other)
Moves other into this QVideoFrame.
bool isWritable() const
Identifies if the mapped contents of a video frame will be persisted when the frame is unmapped.
bool isValid() const
Identifies whether a video frame is valid.
int width() const
Returns the width of a video frame.
RotationAngle rotationAngle() const
Returns the angle the frame should be rotated clockwise before displaying.
qint64 endTime() const
Returns the presentation time (in microseconds) when a frame should stop being displayed.
int mappedBytes(int plane) const
Returns the number of bytes occupied by plane plane of the mapped frame data.
void setStartTime(qint64 time)
Sets the presentation time (in microseconds) when the frame should initially be displayed.
~QVideoFrame()
Destroys a video frame.
int bytesPerLine(int plane) const
Returns the number of bytes in a scan line of a plane.
#define this
Definition dialogs.cpp:9
QMap< QString, QString > map
[6]
QString text
rect
[4]
Combined button and popup list for selecting options.
const TextureDescription * textureDescription(QVideoFrameFormat::PixelFormat format)
@ KeepAspectRatio
@ transparent
Definition qnamespace.h:46
@ black
Definition qnamespace.h:29
Definition image.cpp:4
#define qWarning
Definition qlogging.h:162
GLenum mode
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLdouble GLdouble GLdouble GLdouble top
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLfloat angle
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLint GLsizei GLsizei GLenum format
GLuint GLenum GLenum transform
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
unsigned char uchar
Definition qtypes.h:27
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
QDebug operator<<(QDebug dbg, QVideoFrame::HandleType type)
static QString qFormatTimeStamps(qint64 start, qint64 end)
QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
QVBoxLayout * layout
QReadWriteLock lock
[0]
QSharedPointer< T > other(t)
[5]
QPainter painter(this)
[7]
\inmodule QtCore \reentrant
Definition qchar.h:17
Qt::AspectRatioMode aspectRatioMode