Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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 "qvideoframe_p.h"
11#include "qimagevideobuffer_p.h"
12#include "qpainter.h"
13#include <qtextlayout.h>
14
15#include <qimage.h>
16#include <qpair.h>
17#include <qsize.h>
18#include <qvariant.h>
19#include <rhi/qrhi.h>
20
21#include <QDebug>
22
24
26
70
82
87{
88 return d ? d->buffer.get() : nullptr;
89}
90
97{
98 auto *textureDescription = QVideoTextureHelper::textureDescription(format.pixelFormat());
99 qsizetype bytes = textureDescription->bytesForSize(format.frameSize());
100 if (bytes > 0) {
102 data.resize(bytes);
103
104 // Check the memory was successfully allocated.
105 if (!data.isEmpty())
106 d->buffer = std::make_unique<QMemoryVideoBuffer>(data, textureDescription->strideForWidth(format.frameWidth()));
107 }
108}
109
132{
133 auto buffer = std::make_unique<QImageVideoBuffer>(image);
134
135 // If the QImage::Format is not convertible to QVideoFrameFormat,
136 // QImageVideoBuffer automatically converts image to a compatible
137 // (A)RGB format.
138 const QImage &bufferImage = buffer->underlyingImage();
139
140 if (bufferImage.isNull())
141 return;
142
143 // `bufferImage` is now supported by QVideoFrameFormat::pixelFormatFromImageFormat()
145 bufferImage.size(), QVideoFrameFormat::pixelFormatFromImageFormat(bufferImage.format())
146 };
147
148 Q_ASSERT(format.isValid());
149
150 d = new QVideoFramePrivate{ std::move(format), std::move(buffer) };
151}
152
159
184
189{
190 // Due to explicit sharing we just compare the QSharedData which in turn compares the pointers.
191 return d == other.d;
192}
193
198{
199 return d != other.d;
200}
201
205QVideoFrame::~QVideoFrame() = default;
206
215{
217}
218
226
234
242{
243 return (d && d->buffer) ? d->buffer->handleType() : QVideoFrame::NoHandle;
244}
245
250{
251 return d ? d->format.frameSize() : QSize();
252}
253
258{
259 return size().width();
260}
261
266{
267 return size().height();
268}
269
283{
284 return d && d->buffer && d->buffer->mapMode() != QVideoFrame::NotMapped;
285}
286
302{
303 return d && d->buffer && (d->buffer->mapMode() & QVideoFrame::WriteOnly);
304}
305
318{
319 return d && d->buffer && (d->buffer->mapMode() & QVideoFrame::ReadOnly);
320}
321
328{
329 return (d && d->buffer) ? d->buffer->mapMode() : QVideoFrame::NotMapped;
330}
331
363{
364
365 if (!d || !d->buffer)
366 return false;
367
370 return false;
371
372 if (d->mappedCount > 0) {
373 //it's allowed to map the video frame multiple times in read only mode
374 if (d->buffer->mapMode() == QVideoFrame::ReadOnly
376 d->mappedCount++;
377 return true;
378 }
379
380 return false;
381 }
382
383 Q_ASSERT(d->mapData.data[0] == nullptr);
384 Q_ASSERT(d->mapData.bytesPerLine[0] == 0);
385 Q_ASSERT(d->mapData.nPlanes == 0);
386 Q_ASSERT(d->mapData.size[0] == 0);
387
388 d->mapData = d->buffer->map(mode);
389 if (d->mapData.nPlanes == 0)
390 return false;
391
392 if (d->mapData.nPlanes == 1) {
393 auto pixelFmt = d->format.pixelFormat();
394 // If the plane count is 1 derive the additional planes for planar formats.
395 switch (pixelFmt) {
416 // Single plane or opaque format.
417 break;
422 // The UV stride is usually half the Y stride and is 32-bit aligned.
423 // However it's not always the case, at least on Windows where the
424 // UV planes are sometimes not aligned.
425 // We calculate the stride using the UV byte count to always
426 // have a correct stride.
427 const int height = this->height();
428 const int yStride = d->mapData.bytesPerLine[0];
429 const int uvHeight = pixelFmt == QVideoFrameFormat::Format_YUV422P ? height : height / 2;
430 const int uvStride = (d->mapData.size[0] - (yStride * height)) / uvHeight / 2;
431
432 // Three planes, the second and third vertically (and horizontally for other than Format_YUV422P formats) subsampled.
433 d->mapData.nPlanes = 3;
434 d->mapData.bytesPerLine[2] = d->mapData.bytesPerLine[1] = uvStride;
435 d->mapData.size[0] = yStride * height;
436 d->mapData.size[1] = uvStride * uvHeight;
437 d->mapData.size[2] = uvStride * uvHeight;
438 d->mapData.data[1] = d->mapData.data[0] + d->mapData.size[0];
439 d->mapData.data[2] = d->mapData.data[1] + d->mapData.size[1];
440 break;
441 }
448 // Semi planar, Full resolution Y plane with interleaved subsampled U and V planes.
449 d->mapData.nPlanes = 2;
451 int size = d->mapData.size[0];
452 d->mapData.size[0] = (d->mapData.bytesPerLine[0] * height());
453 d->mapData.size[1] = size - d->mapData.size[0];
454 d->mapData.data[1] = d->mapData.data[0] + d->mapData.size[0];
455 break;
456 }
459 // Three planes, the second and third vertically and horizontally subsumpled,
460 // but with lines padded to the width of the first plane.
461 d->mapData.nPlanes = 3;
463 d->mapData.size[0] = (d->mapData.bytesPerLine[0] * height());
464 d->mapData.size[1] = (d->mapData.bytesPerLine[0] * height() / 2);
465 d->mapData.size[2] = (d->mapData.bytesPerLine[0] * height() / 2);
466 d->mapData.data[1] = d->mapData.data[0] + d->mapData.size[0];
467 d->mapData.data[2] = d->mapData.data[1] + d->mapData.size[1];
468 break;
469 }
470 }
471 }
472
473 d->mappedCount++;
474
475 // unlock mapMutex to avoid potential deadlock imageMutex <--> mapMutex
476 lock.unlock();
477
478 if ((mode & QVideoFrame::WriteOnly) != 0) {
480 d->image = {};
481 }
482
483 return true;
484}
485
497{
498 if (!d || !d->buffer)
499 return;
500
502
503 if (d->mappedCount == 0) {
504 qWarning() << "QVideoFrame::unmap() was called more times then QVideoFrame::map()";
505 return;
506 }
507
508 d->mappedCount--;
509
510 if (d->mappedCount == 0) {
511 d->mapData = {};
512 d->buffer->unmap();
513 }
514}
515
525int QVideoFrame::bytesPerLine(int plane) const
526{
527 if (!d)
528 return 0;
529 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.bytesPerLine[plane] : 0;
530}
531
545{
546 if (!d)
547 return nullptr;
548 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.data[plane] : nullptr;
549}
550
562const uchar *QVideoFrame::bits(int plane) const
563{
564 if (!d)
565 return nullptr;
566 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.data[plane] : nullptr;
567}
568
576int QVideoFrame::mappedBytes(int plane) const
577{
578 if (!d)
579 return 0;
580 return plane >= 0 && plane < d->mapData.nPlanes ? d->mapData.size[plane] : 0;
581}
582
591{
592 if (!d)
593 return 0;
594 return d->format.planeCount();
595}
596
604{
605 if (!d)
606 return -1;
607 return d->startTime;
608}
609
617{
618 if (!d)
619 return;
620 d->startTime = time;
621}
622
630{
631 if (!d)
632 return -1;
633 return d->endTime;
634}
635
643{
644 if (!d)
645 return;
646 d->endTime = time;
647}
648
649#if QT_DEPRECATED_SINCE(6, 7)
677#endif
678
679
688
696
701void QVideoFrame::setMirrored(bool mirrored)
702{
703 if (d)
705}
706
711{
712 return d && d->format.isMirrored();
713}
714
723
728{
729 return d ? d->format.streamFrameRate() : 0.;
730}
731
737{
738 if (!isValid())
739 return {};
740
742
743 if (d->image.isNull()) {
745 d->image = qImageFromVideoFrame(*this, rotation(), mirrored(), mirrorY);
746 }
747
748 return d->image;
749}
750
755{
756 return d ? d->subtitleText : QString();
757}
758
763{
764 if (!d)
765 return;
766 d->subtitleText = text;
767}
768
778{
779 if (!isValid()) {
781 return;
782 }
783
784 QRectF targetRect = rect;
786
787 size.scale(targetRect.size(), options.aspectRatioMode);
788
789 if (options.aspectRatioMode == Qt::KeepAspectRatio) {
790 targetRect = QRect(0, 0, size.width(), size.height());
791 targetRect.moveCenter(rect.center());
792 // we might not be drawing every pixel, fill the leftovers black
793 if (options.backgroundColor != Qt::transparent && rect != targetRect) {
794 if (targetRect.top() > rect.top()) {
795 QRectF top(rect.left(), rect.top(), rect.width(), targetRect.top() - rect.top());
797 }
798 if (targetRect.left() > rect.left()) {
799 QRectF top(rect.left(), targetRect.top(), targetRect.left() - rect.left(), targetRect.height());
801 }
802 if (targetRect.right() < rect.right()) {
803 QRectF top(targetRect.right(), targetRect.top(), rect.right() - targetRect.right(), targetRect.height());
805 }
806 if (targetRect.bottom() < rect.bottom()) {
807 QRectF top(rect.left(), targetRect.bottom(), rect.width(), rect.bottom() - targetRect.bottom());
809 }
810 }
811 }
812
814 const QTransform oldTransform = painter->transform();
815 QTransform transform = oldTransform;
816 transform.translate(targetRect.center().x() - size.width()/2,
817 targetRect.center().y() - size.height()/2);
819 QImage image = toImage();
820 painter->drawImage({{}, size}, image, {{},image.size()});
821 painter->setTransform(oldTransform);
822
823 unmap();
824 } else if (isValid()) {
825 // #### error handling
826 } else {
828 }
829
831 return;
832
833 // draw subtitles
834 auto text = d->subtitleText;
835 text.replace(QLatin1Char('\n'), QChar::LineSeparator);
836
838 layout.update(targetRect.size().toSize(), this->subtitleText());
839 layout.draw(painter, targetRect.topLeft());
840}
841
842#ifndef QT_NO_DEBUG_STREAM
844{
845 // Early out for invalid.
846 if (start < 0)
847 return QLatin1String("[no timestamp]");
848
849 bool onlyOne = (start == end);
850
851 // [hh:]mm:ss.ms
852 const int s_millis = start % 1000000;
853 start /= 1000000;
854 const int s_seconds = start % 60;
855 start /= 60;
856 const int s_minutes = start % 60;
857 start /= 60;
858
859 if (onlyOne) {
860 if (start > 0)
861 return QStringLiteral("@%1:%2:%3.%4")
862 .arg(start, 1, 10, QLatin1Char('0'))
863 .arg(s_minutes, 2, 10, QLatin1Char('0'))
864 .arg(s_seconds, 2, 10, QLatin1Char('0'))
865 .arg(s_millis, 2, 10, QLatin1Char('0'));
866 return QStringLiteral("@%1:%2.%3")
867 .arg(s_minutes, 2, 10, QLatin1Char('0'))
868 .arg(s_seconds, 2, 10, QLatin1Char('0'))
869 .arg(s_millis, 2, 10, QLatin1Char('0'));
870 }
871
872 if (end == -1) {
873 // Similar to start-start, except it means keep displaying it?
874 if (start > 0)
875 return QStringLiteral("%1:%2:%3.%4 - forever")
876 .arg(start, 1, 10, QLatin1Char('0'))
877 .arg(s_minutes, 2, 10, QLatin1Char('0'))
878 .arg(s_seconds, 2, 10, QLatin1Char('0'))
879 .arg(s_millis, 2, 10, QLatin1Char('0'));
880 return QStringLiteral("%1:%2.%3 - forever")
881 .arg(s_minutes, 2, 10, QLatin1Char('0'))
882 .arg(s_seconds, 2, 10, QLatin1Char('0'))
883 .arg(s_millis, 2, 10, QLatin1Char('0'));
884 }
885
886 const int e_millis = end % 1000000;
887 end /= 1000000;
888 const int e_seconds = end % 60;
889 end /= 60;
890 const int e_minutes = end % 60;
891 end /= 60;
892
893 if (start > 0 || end > 0)
894 return QStringLiteral("%1:%2:%3.%4 - %5:%6:%7.%8")
895 .arg(start, 1, 10, QLatin1Char('0'))
896 .arg(s_minutes, 2, 10, QLatin1Char('0'))
897 .arg(s_seconds, 2, 10, QLatin1Char('0'))
898 .arg(s_millis, 2, 10, QLatin1Char('0'))
899 .arg(end, 1, 10, QLatin1Char('0'))
900 .arg(e_minutes, 2, 10, QLatin1Char('0'))
901 .arg(e_seconds, 2, 10, QLatin1Char('0'))
902 .arg(e_millis, 2, 10, QLatin1Char('0'));
903 return QStringLiteral("%1:%2.%3 - %4:%5.%6")
904 .arg(s_minutes, 2, 10, QLatin1Char('0'))
905 .arg(s_seconds, 2, 10, QLatin1Char('0'))
906 .arg(s_millis, 2, 10, QLatin1Char('0'))
907 .arg(e_minutes, 2, 10, QLatin1Char('0'))
908 .arg(e_seconds, 2, 10, QLatin1Char('0'))
909 .arg(e_millis, 2, 10, QLatin1Char('0'));
910}
911
913{
914 QDebugStateSaver saver(dbg);
915 dbg.nospace();
916 switch (type) {
918 return dbg << "NoHandle";
920 return dbg << "RhiTextureHandle";
921 }
922 return dbg;
923}
924
926{
927 QDebugStateSaver saver(dbg);
928 dbg.nospace();
929 dbg << "QVideoFrame(" << f.size() << ", "
930 << f.pixelFormat() << ", "
931 << f.handleType() << ", "
932 << f.mapMode() << ", "
933 << qFormatTimeStamps(f.startTime(), f.endTime()).toLatin1().constData();
934 dbg << ')';
935 return dbg;
936}
937#endif
938
940
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
\inmodule QtCore
Definition qbytearray.h:57
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
\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:1222
void update()
Updates the layout for parentWidget().
Definition qlayout.cpp:971
\inmodule QtCore
Definition qmutex.h:313
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)
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
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.
bool isMirrored() const
Returns true if the surface is mirrored around its vertical axis.
void setStreamFrameRate(qreal rate)
Sets the frame rate of a video stream in frames per second.
PixelFormat
Enumerates video data types.
Direction scanLineDirection() const
Returns the direction of scan lines.
int planeCount() const
Returns the number of planes used.
qreal streamFrameRate() const
Returns the frame rate of a video stream in frames per second.
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of frames in a video stream.
void setRotation(QtVideo::Rotation rotation)
Sets the rotation angle the matching video frame should be rotated clockwise before displaying.
void setMirrored(bool mirrored)
Sets if the surface is mirrored around its vertical axis.
QtVideo::Rotation rotation() const
Returns the rotation angle the matching video frame should be rotated clockwise before displaying.
QSize frameSize() const
Returns the dimensions of frames in a video stream.
static PixelFormat pixelFormatFromImageFormat(QImage::Format format)
Returns a video pixel format equivalent to an image format.
QVideoFrameFormat format
QAbstractVideoBuffer::MapData mapData
std::unique_ptr< QAbstractVideoBuffer > buffer
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
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:37
QVideoFrame()
Constructs a null video frame.
void setRotation(QtVideo::Rotation angle)
Sets the angle the frame should be rotated clockwise before displaying.
qreal streamFrameRate() const
Returns the frame rate of a video stream in frames per second.
QAbstractVideoBuffer * videoBuffer() const
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 and sets the flag to the underlying \l surfaceFormat.
QtVideo::Rotation rotation() const
Returns the angle the frame should be rotated clockwise before displaying.
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:31
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.
void setStreamFrameRate(qreal rate)
Sets the frame rate of a video stream in frames per second.
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.
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:47
@ black
Definition qnamespace.h:30
Definition image.cpp:4
#define qWarning
Definition qlogging.h:166
QSize qRotatedFrameSize(QSize size, int rotation)
GLenum mode
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLdouble GLdouble GLdouble GLdouble top
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLfloat angle
GLuint start
GLint GLsizei GLsizei GLenum format
GLuint GLenum GLenum transform
GLuint GLenum * rate
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QDebug operator<<(QDebug dbg, QVideoFrame::HandleType type)
static QString qFormatTimeStamps(qint64 start, qint64 end)
QImage qImageFromVideoFrame(const QVideoFrame &frame, QtVideo::Rotation 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:18
Qt::AspectRatioMode aspectRatioMode