summaryrefslogtreecommitdiffstats
path: root/src/multimedia/video/qvideoframe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/video/qvideoframe.cpp')
-rw-r--r--src/multimedia/video/qvideoframe.cpp256
1 files changed, 133 insertions, 123 deletions
diff --git a/src/multimedia/video/qvideoframe.cpp b/src/multimedia/video/qvideoframe.cpp
index 0894cc466..45b3ad85d 100644
--- a/src/multimedia/video/qvideoframe.cpp
+++ b/src/multimedia/video/qvideoframe.cpp
@@ -1,91 +1,27 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframe.h"
+#include "qvideoframe_p.h"
#include "qvideotexturehelper_p.h"
+#include "qmultimediautils_p.h"
#include "qmemoryvideobuffer_p.h"
#include "qvideoframeconverter_p.h"
-#include "qvideoframeformat.h"
+#include "qimagevideobuffer_p.h"
#include "qpainter.h"
#include <qtextlayout.h>
#include <qimage.h>
-#include <qmutex.h>
#include <qpair.h>
#include <qsize.h>
#include <qvariant.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
-class QVideoFramePrivate : public QSharedData
-{
-public:
- QVideoFramePrivate() = default;
- QVideoFramePrivate(const QVideoFrameFormat &format)
- : format(format)
- {
- }
-
- ~QVideoFramePrivate()
- {
- delete buffer;
- }
-
- qint64 startTime = -1;
- qint64 endTime = -1;
- QAbstractVideoBuffer::MapData mapData;
- QVideoFrameFormat format;
- QAbstractVideoBuffer *buffer = nullptr;
- int mappedCount = 0;
- QMutex mapMutex;
- QString subtitleText;
- QVideoFrame::RotationAngle rotationAngle = QVideoFrame::Rotation0;
- bool mirrored = false;
- QImage image;
-private:
- Q_DISABLE_COPY(QVideoFramePrivate)
-};
-
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QVideoFramePrivate);
/*!
@@ -141,7 +77,7 @@ QVideoFrame::QVideoFrame()
QVideoFrame::QVideoFrame(QAbstractVideoBuffer *buffer, const QVideoFrameFormat &format)
: d(new QVideoFramePrivate(format))
{
- d->buffer = buffer;
+ d->buffer.reset(buffer);
}
/*!
@@ -149,7 +85,7 @@ QVideoFrame::QVideoFrame(QAbstractVideoBuffer *buffer, const QVideoFrameFormat &
*/
QAbstractVideoBuffer *QVideoFrame::videoBuffer() const
{
- return d ? d->buffer : nullptr;
+ return d ? d->buffer.get() : nullptr;
}
/*!
@@ -167,11 +103,54 @@ QVideoFrame::QVideoFrame(const QVideoFrameFormat &format)
// Check the memory was successfully allocated.
if (!data.isEmpty())
- d->buffer = new QMemoryVideoBuffer(data, textureDescription->strideForWidth(format.frameWidth()));
+ d->buffer = std::make_unique<QMemoryVideoBuffer>(data, textureDescription->strideForWidth(format.frameWidth()));
}
}
/*!
+ Constructs a QVideoFrame from a QImage. The QImage pixels are copied
+ into the QVideoFrame's memory buffer. The resulting frame has the
+ same size as the QImage, but the number of bytes per line may
+ differ.
+ \since 6.8
+
+ If the QImage::Format matches one of the formats in
+ QVideoFrameFormat::PixelFormat, the QVideoFrame will use that format
+ without any pixel format conversion. Otherwise, the image is first
+ converted to a supported (A)RGB format using QImage::convertedTo()
+ with the Qt::AutoColor flag. This may incur a performance penalty.
+
+ If QImage::isNull() evaluates to true for the input QImage, the
+ QVideoFrame will be invalid and QVideoFrameFormat::isValid() will
+ return false.
+
+ \sa QVideoFrameFormat::pixelFormatFromImageFormat()
+ \sa QImage::convertedTo()
+ \sa QImage::isNull()
+*/
+QVideoFrame::QVideoFrame(const QImage &image)
+{
+ auto buffer = std::make_unique<QImageVideoBuffer>(image);
+
+ // If the QImage::Format is not convertible to QVideoFrameFormat,
+ // QImageVideoBuffer automatically converts image to a compatible
+ // (A)RGB format.
+ const QImage &bufferImage = buffer->underlyingImage();
+
+ if (bufferImage.isNull())
+ return;
+
+ // `bufferImage` is now supported by QVideoFrameFormat::pixelFormatFromImageFormat()
+ QVideoFrameFormat format = {
+ bufferImage.size(), QVideoFrameFormat::pixelFormatFromImageFormat(bufferImage.format())
+ };
+
+ Q_ASSERT(format.isValid());
+
+ d = new QVideoFramePrivate{ std::move(format), std::move(buffer) };
+}
+
+/*!
Constructs a shallow copy of \a other. Since QVideoFrame is
explicitly shared, these two instances will reflect the same frame.
@@ -185,6 +164,12 @@ QVideoFrame::QVideoFrame(const QVideoFrame &other) = default;
*/
/*!
+ \fn void QVideoFrame::swap(QVideoFrame &other) noexcept
+
+ Swaps the current video frame with \a other.
+*/
+
+/*!
\fn QVideoFrame &QVideoFrame::operator=(QVideoFrame &&other)
Moves \a other into this QVideoFrame.
@@ -296,7 +281,7 @@ int QVideoFrame::height() const
bool QVideoFrame::isMapped() const
{
- return d && d->buffer && d->buffer->mapMode() != QVideoFrame::NotMapped;
+ return d && d->mapMode != QVideoFrame::NotMapped;
}
/*!
@@ -315,7 +300,7 @@ bool QVideoFrame::isMapped() const
*/
bool QVideoFrame::isWritable() const
{
- return d && d->buffer && (d->buffer->mapMode() & QVideoFrame::WriteOnly);
+ return d && (d->mapMode & QVideoFrame::WriteOnly);
}
/*!
@@ -331,7 +316,7 @@ bool QVideoFrame::isWritable() const
*/
bool QVideoFrame::isReadable() const
{
- return d && d->buffer && (d->buffer->mapMode() & QVideoFrame::ReadOnly);
+ return d && (d->mapMode & QVideoFrame::ReadOnly);
}
/*!
@@ -341,7 +326,7 @@ bool QVideoFrame::isReadable() const
*/
QVideoFrame::MapMode QVideoFrame::mapMode() const
{
- return (d && d->buffer) ? d->buffer->mapMode() : QVideoFrame::NotMapped;
+ return d ? d->mapMode : QVideoFrame::NotMapped;
}
/*!
@@ -386,8 +371,7 @@ bool QVideoFrame::map(QVideoFrame::MapMode mode)
if (d->mappedCount > 0) {
//it's allowed to map the video frame multiple times in read only mode
- if (d->buffer->mapMode() == QVideoFrame::ReadOnly
- && mode == QVideoFrame::ReadOnly) {
+ if (d->mapMode == QVideoFrame::ReadOnly && mode == QVideoFrame::ReadOnly) {
d->mappedCount++;
return true;
}
@@ -404,6 +388,8 @@ bool QVideoFrame::map(QVideoFrame::MapMode mode)
if (d->mapData.nPlanes == 0)
return false;
+ d->mapMode = mode;
+
if (d->mapData.nPlanes == 1) {
auto pixelFmt = d->format.pixelFormat();
// If the plane count is 1 derive the additional planes for planar formats.
@@ -486,6 +472,15 @@ bool QVideoFrame::map(QVideoFrame::MapMode mode)
}
d->mappedCount++;
+
+ // unlock mapMutex to avoid potential deadlock imageMutex <--> mapMutex
+ lock.unlock();
+
+ if ((mode & QVideoFrame::WriteOnly) != 0) {
+ QMutexLocker lock(&d->imageMutex);
+ d->image = {};
+ }
+
return true;
}
@@ -515,6 +510,7 @@ void QVideoFrame::unmap()
if (d->mappedCount == 0) {
d->mapData = {};
+ d->mapMode = QVideoFrame::NotMapped;
d->buffer->unmap();
}
}
@@ -601,30 +597,6 @@ int QVideoFrame::planeCount() const
}
/*!
- \internal
- Returns a texture id to the video frame's buffers.
-*/
-quint64 QVideoFrame::textureHandle(int plane) const
-{
- if (!d || !d->buffer)
- return 0;
- d->buffer->mapTextures();
- return d->buffer->textureHandle(plane);
-}
-
-/*!
- \internal
- Returns a QRhiTexture of the video frame
-*/
-std::unique_ptr<QRhiTexture> QVideoFrame::rhiTexture(int plane) const
-{
- if (!d || !d->buffer)
- return {};
- d->buffer->mapTextures();
- return d->buffer->texture(plane);
-}
-
-/*!
Returns the presentation time (in microseconds) when the frame should be displayed.
An invalid time is represented as -1.
@@ -676,8 +648,10 @@ void QVideoFrame::setEndTime(qint64 time)
d->endTime = time;
}
+#if QT_DEPRECATED_SINCE(6, 7)
/*!
\enum QVideoFrame::RotationAngle
+ \deprecated [6.7] Use QtVideo::Rotation instead.
The angle of the clockwise rotation that should be applied to a video
frame before displaying.
@@ -689,29 +663,47 @@ void QVideoFrame::setEndTime(qint64 time)
*/
/*!
+ \fn void QVideoFrame::setRotationAngle(RotationAngle)
+ \deprecated [6.7] Use \c QVideoFrame::setRotation instead.
+
+ Sets the \a angle the frame should be rotated clockwise before displaying.
+*/
+
+/*!
+ \fn QVideoFrame::RotationAngle QVideoFrame::rotationAngle() const
+ \deprecated [6.7] Use \c QVideoFrame::rotation instead.
+
+ Returns the angle the frame should be rotated clockwise before displaying.
+*/
+
+#endif
+
+
+/*!
Sets the \a angle the frame should be rotated clockwise before displaying.
*/
-void QVideoFrame::setRotationAngle(QVideoFrame::RotationAngle angle)
+void QVideoFrame::setRotation(QtVideo::Rotation angle)
{
if (d)
- d->rotationAngle = angle;
+ d->format.setRotation(angle);
}
/*!
Returns the angle the frame should be rotated clockwise before displaying.
*/
-QVideoFrame::RotationAngle QVideoFrame::rotationAngle() const
+QtVideo::Rotation QVideoFrame::rotation() const
{
- return d ? d->rotationAngle : Rotation0;
+ return d ? d->format.rotation() : QtVideo::Rotation::None;
}
/*!
- Sets the \a mirrored flag for the frame.
+ Sets the \a mirrored flag for the frame and
+ sets the flag to the underlying \l surfaceFormat.
*/
void QVideoFrame::setMirrored(bool mirrored)
{
if (d)
- d->mirrored = mirrored;
+ d->format.setMirrored(mirrored);
}
/*!
@@ -719,7 +711,24 @@ void QVideoFrame::setMirrored(bool mirrored)
*/
bool QVideoFrame::mirrored() const
{
- return d && d->mirrored;
+ return d && d->format.isMirrored();
+}
+
+/*!
+ Sets the frame \a rate of a video stream in frames per second.
+*/
+void QVideoFrame::setStreamFrameRate(qreal rate)
+{
+ if (d)
+ d->format.setStreamFrameRate(rate);
+}
+
+/*!
+ Returns the frame rate of a video stream in frames per second.
+*/
+qreal QVideoFrame::streamFrameRate() const
+{
+ return d ? d->format.streamFrameRate() : 0.;
}
/*!
@@ -730,11 +739,14 @@ QImage QVideoFrame::toImage() const
{
if (!isValid())
return {};
- if (!d->image.isNull())
- return d->image;
- d->image = qImageFromVideoFrame(*this, rotationAngle(), mirrored(),
- surfaceFormat().scanLineDirection() != QVideoFrameFormat::TopToBottom);
+ QMutexLocker lock(&d->imageMutex);
+
+ if (d->image.isNull()) {
+ const bool mirrorY = surfaceFormat().scanLineDirection() != QVideoFrameFormat::TopToBottom;
+ d->image = qImageFromVideoFrame(*this, rotation(), mirrored(), mirrorY);
+ }
+
return d->image;
}
@@ -772,9 +784,7 @@ void QVideoFrame::paint(QPainter *painter, const QRectF &rect, const PaintOption
}
QRectF targetRect = rect;
- QSizeF size = this->size();
- if (rotationAngle() % 180)
- size.transpose();
+ QSizeF size = qRotatedFrameSize(*this);
size.scale(targetRect.size(), options.aspectRatioMode);
@@ -850,12 +860,12 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
if (onlyOne) {
if (start > 0)
- return QString::fromLatin1("@%1:%2:%3.%4")
+ return QStringLiteral("@%1:%2:%3.%4")
.arg(start, 1, 10, QLatin1Char('0'))
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
- return QString::fromLatin1("@%1:%2.%3")
+ return QStringLiteral("@%1:%2.%3")
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
@@ -864,12 +874,12 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
if (end == -1) {
// Similar to start-start, except it means keep displaying it?
if (start > 0)
- return QString::fromLatin1("%1:%2:%3.%4 - forever")
+ return QStringLiteral("%1:%2:%3.%4 - forever")
.arg(start, 1, 10, QLatin1Char('0'))
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
- return QString::fromLatin1("%1:%2.%3 - forever")
+ return QStringLiteral("%1:%2.%3 - forever")
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
@@ -883,7 +893,7 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
end /= 60;
if (start > 0 || end > 0)
- return QString::fromLatin1("%1:%2:%3.%4 - %5:%6:%7.%8")
+ return QStringLiteral("%1:%2:%3.%4 - %5:%6:%7.%8")
.arg(start, 1, 10, QLatin1Char('0'))
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
@@ -892,7 +902,7 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
.arg(e_minutes, 2, 10, QLatin1Char('0'))
.arg(e_seconds, 2, 10, QLatin1Char('0'))
.arg(e_millis, 2, 10, QLatin1Char('0'));
- return QString::fromLatin1("%1:%2.%3 - %4:%5.%6")
+ return QStringLiteral("%1:%2.%3 - %4:%5.%6")
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'))