diff options
Diffstat (limited to 'src/multimedia/video/qvideoframe.cpp')
-rw-r--r-- | src/multimedia/video/qvideoframe.cpp | 256 |
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')) |