diff options
Diffstat (limited to 'tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp')
-rw-r--r-- | tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp | 568 |
1 files changed, 495 insertions, 73 deletions
diff --git a/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp b/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp index cb94c102e..a0cc96d02 100644 --- a/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp +++ b/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp @@ -1,41 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//TESTED_COMPONENT=src/multimedia +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <qvideoframe.h> #include <qvideoframeformat.h> +#include "QtTest/qtestcase.h" #include "private/qmemoryvideobuffer_p.h" #include <QtGui/QImage> #include <QtCore/QPointer> #include <QtMultimedia/private/qtmultimedia-config_p.h> +#include "private/qvideoframeconverter_p.h" // Adds an enum, and the stringized version #define ADD_ENUM_TEST(x) \ @@ -44,6 +19,160 @@ << QString(QLatin1String(#x)); +// Image used for testing conversion from QImage to QVideoFrame +QImage createTestImage(QImage::Format format) +{ + // +---+---+---+ + // | r | g | b | + // | b | r | g | + // +---+---+---+ + QImage image{ { 3, 2 }, QImage::Format_ARGB32 }; + image.setPixelColor(0, 0, QColor(Qt::red)); + image.setPixelColor(1, 0, QColor(Qt::green)); + image.setPixelColor(2, 0, QColor(Qt::blue)); + image.setPixelColor(0, 1, QColor(Qt::blue)); + image.setPixelColor(1, 1, QColor(Qt::red)); + image.setPixelColor(2, 1, QColor(Qt::green)); + return image.convertToFormat(format); +} + +// clang-format off + +// Convert a QVideoFrame pixel value from raw format to QRgb +// Only works with little-endian byte ordering +QRgb swizzle(uint value, QVideoFrameFormat::PixelFormat format) +{ + switch (format) { + case QVideoFrameFormat::Format_ARGB8888: + case QVideoFrameFormat::Format_ARGB8888_Premultiplied: + case QVideoFrameFormat::Format_XRGB8888: + Q_ASSERT(false); // not implemented + return 0; + case QVideoFrameFormat::Format_BGRA8888: + case QVideoFrameFormat::Format_BGRA8888_Premultiplied: + case QVideoFrameFormat::Format_BGRX8888: + return value; + case QVideoFrameFormat::Format_ABGR8888: + case QVideoFrameFormat::Format_XBGR8888: + Q_ASSERT(false); // not implemented + return 0; + case QVideoFrameFormat::Format_RGBA8888: + case QVideoFrameFormat::Format_RGBX8888: + return (((value >> 24) & 0xff) << 24) // a -> a + | ((value & 0xff) << 16) // b -> r + | (((value >> 8) & 0xff) << 8) // g -> g + | ((value >> 16) & 0xff); // r -> b + default: + qWarning() << "Unsupported format"; + return 0; + } +} + +std::vector<QRgb> swizzle(const std::vector<uint> &pixels, QVideoFrameFormat::PixelFormat format) +{ + std::vector<QRgb> rgba(pixels.size()); + std::transform(pixels.begin(), pixels.end(), rgba.begin(), + [format](uint value) { + return swizzle(value, format); + }); + return rgba; +} + +// clang-format on + +std::optional<std::vector<QRgb>> getPixels(QVideoFrame &frame) +{ + if (!frame.map(QVideoFrame::ReadOnly)) + return std::nullopt; + + const uint *mappedPixels = reinterpret_cast<const uint *>(frame.bits(0)); + const unsigned long long stride = frame.bytesPerLine(0) / sizeof(QRgb); + + std::vector<uint> pixels; + for (int j = 0; j < frame.size().height(); ++j) { + for (int i = 0; i < frame.size().width(); ++i) { + pixels.push_back(mappedPixels[i + j * stride]); + } + } + + frame.unmap(); + + return swizzle(pixels, frame.pixelFormat()); +} + +bool compareEq(QVideoFrame &frame, const QImage &image) +{ + if (frame.size() != image.size()) { + qDebug() << "Size mismatch"; + return false; + } + + const std::vector<QRgb> expectedPixels = { image.pixel(0, 0), image.pixel(1, 0), image.pixel(2, 0), + image.pixel(0, 1), image.pixel(1, 1), image.pixel(2, 1) }; + + const std::optional<std::vector<QRgb>> actualPixels = getPixels(frame); + if (!actualPixels) { + qDebug() << "Failed to read pixels from frame"; + return false; + } + + for (size_t i = 0; i < expectedPixels.size(); ++i) { + if (expectedPixels[i] != actualPixels->at(i)) { + qDebug() << "Pixel difference at element" << i << ":" << Qt::hex << expectedPixels[i] + << "vs" << actualPixels->at(i); + return false; + } + } + return true; +} + +QSet s_pixelFormats{ QVideoFrameFormat::Format_ARGB8888, + QVideoFrameFormat::Format_ARGB8888_Premultiplied, + QVideoFrameFormat::Format_XRGB8888, + QVideoFrameFormat::Format_BGRA8888, + QVideoFrameFormat::Format_BGRA8888_Premultiplied, + QVideoFrameFormat::Format_BGRX8888, + QVideoFrameFormat::Format_ABGR8888, + QVideoFrameFormat::Format_XBGR8888, + QVideoFrameFormat::Format_RGBA8888, + QVideoFrameFormat::Format_RGBX8888, + QVideoFrameFormat::Format_NV12, + QVideoFrameFormat::Format_NV21, + QVideoFrameFormat::Format_IMC1, + QVideoFrameFormat::Format_IMC2, + QVideoFrameFormat::Format_IMC3, + QVideoFrameFormat::Format_IMC4, + QVideoFrameFormat::Format_AYUV, + QVideoFrameFormat::Format_AYUV_Premultiplied, + QVideoFrameFormat::Format_YV12, + QVideoFrameFormat::Format_YUV420P, + QVideoFrameFormat::Format_YUV422P, + QVideoFrameFormat::Format_UYVY, + QVideoFrameFormat::Format_YUYV, + QVideoFrameFormat::Format_Y8, + QVideoFrameFormat::Format_Y16, + QVideoFrameFormat::Format_P010, + QVideoFrameFormat::Format_P016, + QVideoFrameFormat::Format_YUV420P10 }; + +bool isSupportedPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat) +{ +#ifdef Q_OS_ANDROID + // TODO: QTBUG-125238 + switch (pixelFormat) { + case QVideoFrameFormat::Format_Y16: + case QVideoFrameFormat::Format_P010: + case QVideoFrameFormat::Format_P016: + case QVideoFrameFormat::Format_YUV420P10: + return false; + default: + return true; + } +#else + return true; +#endif +} + class tst_QVideoFrame : public QObject { Q_OBJECT @@ -78,6 +207,9 @@ private slots: void formatConversion_data(); void formatConversion(); + void qImageFromVideoFrame_doesNotCrash_whenCalledWithEvenAndOddSizedFrames_data(); + void qImageFromVideoFrame_doesNotCrash_whenCalledWithEvenAndOddSizedFrames(); + void isMapped(); void isReadable(); void isWritable(); @@ -86,6 +218,18 @@ private slots: void image(); void emptyData(); + + void mirrored_takesValue_fromVideoFrameFormat(); + void rotation_takesValue_fromVideoFrameFormat(); + void streamFrameRate_takesValue_fromVideoFrameFormat(); + + void constructor_createsInvalidFrame_whenCalledWithNullImage(); + void constructor_createsInvalidFrame_whenCalledWithEmptyImage(); + void constructor_createsInvalidFrame_whenCalledWithInvalidImageFormat(); + void constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats_data(); + void constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats(); + void constructor_copiesImageData_whenCalledWithRGBFormats_data(); + void constructor_copiesImageData_whenCalledWithRGBFormats(); }; class QtTestDummyVideoBuffer : public QObject, public QAbstractVideoBuffer @@ -97,8 +241,6 @@ public: explicit QtTestDummyVideoBuffer(QVideoFrame::HandleType type) : QAbstractVideoBuffer(type) {} - [[nodiscard]] QVideoFrame::MapMode mapMode() const override { return QVideoFrame::NotMapped; } - MapData map(QVideoFrame::MapMode) override { return {}; } void unmap() override {} }; @@ -113,8 +255,6 @@ public: : QAbstractVideoBuffer(type) {} - [[nodiscard]] QVideoFrame::MapMode mapMode() const override { return m_mapMode; } - MapData map(QVideoFrame::MapMode mode) override { m_mapMode = mode; @@ -169,33 +309,43 @@ void tst_QVideoFrame::create_data() { QTest::addColumn<QSize>("size"); QTest::addColumn<QVideoFrameFormat::PixelFormat>("pixelFormat"); - QTest::addColumn<int>("bytes"); QTest::addColumn<int>("bytesPerLine"); QTest::newRow("64x64 ARGB32") << QSize(64, 64) - << QVideoFrameFormat::Format_ARGB8888; + << QVideoFrameFormat::Format_ARGB8888 + << 64*4; QTest::newRow("32x256 YUV420P") << QSize(32, 256) - << QVideoFrameFormat::Format_YUV420P; + << QVideoFrameFormat::Format_YUV420P + << 32; + QTest::newRow("32x256 UYVY") + << QSize(32, 256) + << QVideoFrameFormat::Format_UYVY + << 32*2; } void tst_QVideoFrame::create() { QFETCH(QSize, size); QFETCH(QVideoFrameFormat::PixelFormat, pixelFormat); + QFETCH(int, bytesPerLine); QVideoFrame frame(QVideoFrameFormat(size, pixelFormat)); QVERIFY(frame.isValid()); QCOMPARE(frame.handleType(), QVideoFrame::NoHandle); - QCOMPARE(frame.textureHandle(0), 0u); + QVERIFY(frame.videoBuffer() != nullptr); + QCOMPARE(frame.videoBuffer()->textureHandle(nullptr, 0), 0u); QCOMPARE(frame.pixelFormat(), pixelFormat); QCOMPARE(frame.size(), size); QCOMPARE(frame.width(), size.width()); QCOMPARE(frame.height(), size.height()); QCOMPARE(frame.startTime(), qint64(-1)); QCOMPARE(frame.endTime(), qint64(-1)); + frame.map(QVideoFrame::ReadOnly); + QCOMPARE(frame.bytesPerLine(0), bytesPerLine); + frame.unmap(); } void tst_QVideoFrame::createInvalid_data() @@ -220,7 +370,7 @@ void tst_QVideoFrame::createInvalid() QVERIFY(!frame.isValid()); QCOMPARE(frame.handleType(), QVideoFrame::NoHandle); - QCOMPARE(frame.textureHandle(0), 0u); + QCOMPARE(frame.videoBuffer(), nullptr); QCOMPARE(frame.pixelFormat(), pixelFormat); QCOMPARE(frame.size(), size); QCOMPARE(frame.width(), size.width()); @@ -606,7 +756,12 @@ void tst_QVideoFrame::map() void tst_QVideoFrame::mapPlanes_data() { QTest::addColumn<QVideoFrame>("frame"); + + // Distance between subsequent lines within a color plane in bytes QTest::addColumn<QList<int> >("strides"); + + // Distance from first pixel of first color plane to first pixel + // of n'th plane in bytes QTest::addColumn<QList<int> >("offsets"); static uchar bufferData[1024]; @@ -629,6 +784,10 @@ void tst_QVideoFrame::mapPlanes_data() << QVideoFrame(QVideoFrameFormat(QSize(60, 64), QVideoFrameFormat::Format_YUV420P)) << (QList<int>() << 64 << 32 << 32) << (QList<int>() << 4096 << 5120); + QTest::newRow("Format_YUV422P") + << QVideoFrame(QVideoFrameFormat(QSize(60, 64), QVideoFrameFormat::Format_YUV422P)) + << (QList<int>() << 64 << 64 / 2 << 64 / 2) + << (QList<int>() << 64 * 64 << 64 * 64 + 64 / 2 * 64); QTest::newRow("Format_YV12") << QVideoFrame(QVideoFrameFormat(QSize(60, 64), QVideoFrameFormat::Format_YV12)) << (QList<int>() << 64 << 32 << 32) @@ -669,24 +828,24 @@ void tst_QVideoFrame::mapPlanes() QFETCH(QList<int>, strides); QFETCH(QList<int>, offsets); - QCOMPARE(strides.count(), offsets.count() + 1); + QCOMPARE(strides.size(), offsets.size() + 1); QCOMPARE(frame.map(QVideoFrame::ReadOnly), true); - QCOMPARE(frame.planeCount(), strides.count()); + QCOMPARE(frame.planeCount(), strides.size()); - QVERIFY(strides.count() > 0); + QVERIFY(strides.size() > 0); QCOMPARE(frame.bytesPerLine(0), strides.at(0)); QVERIFY(frame.bits(0)); - if (strides.count() > 1) { + if (strides.size() > 1) { QCOMPARE(frame.bytesPerLine(1), strides.at(1)); QCOMPARE(int(frame.bits(1) - frame.bits(0)), offsets.at(0)); } - if (strides.count() > 2) { + if (strides.size() > 2) { QCOMPARE(frame.bytesPerLine(2), strides.at(2)); QCOMPARE(int(frame.bits(2) - frame.bits(0)), offsets.at(1)); } - if (strides.count() > 3) { + if (strides.size() > 3) { QCOMPARE(frame.bytesPerLine(3), strides.at(3)); QCOMPARE(int(frame.bits(3) - frame.bits(0)), offsets.at(0)); } @@ -803,6 +962,11 @@ void tst_QVideoFrame::formatConversion_data() QTest::newRow("QVideoFrameFormat::Format_Jpeg") << QImage::Format_Invalid << QVideoFrameFormat::Format_Jpeg; + QTest::newRow("QVideoFrameFormat::Format_RGBX8888") + << QImage::Format_RGBX8888 << QVideoFrameFormat::Format_RGBX8888; + QTest::newRow("QImage::Format_RGBA8888_Premultiplied => QVideoFrameFormat::Format_RGBX8888 " + "(workaround)") + << QImage::Format_RGBA8888_Premultiplied << QVideoFrameFormat::Format_RGBX8888; } void tst_QVideoFrame::formatConversion() @@ -813,10 +977,79 @@ void tst_QVideoFrame::formatConversion() if (imageFormat != QImage::Format_Invalid) QCOMPARE(QVideoFrameFormat::pixelFormatFromImageFormat(imageFormat), pixelFormat); + if (imageFormat == QImage::Format_RGBA8888_Premultiplied) { + qWarning() << "Workaround: convert QImage::Format_RGBA8888_Premultiplied to " + "QVideoFrameFormat::Format_RGBX8888; to be removed in 6.8"; + return; + } + if (pixelFormat != QVideoFrameFormat::Format_Invalid) QCOMPARE(QVideoFrameFormat::imageFormatFromPixelFormat(pixelFormat), imageFormat); } +void tst_QVideoFrame::qImageFromVideoFrame_doesNotCrash_whenCalledWithEvenAndOddSizedFrames_data() { + QTest::addColumn<QSize>("size"); + QTest::addColumn<QVideoFrameFormat::PixelFormat>("pixelFormat"); + QTest::addColumn<bool>("forceCpuConversion"); + QTest::addColumn<bool>("supportedOnPlatform"); + + const std::vector<QSize> sizes{ + // Even sized + { 2, 2 }, + { 2, 10 }, + { 10, 2 }, + { 640, 480 }, + { 4096, 2160 }, + // Odd sized + { 0, 0 }, + { 3, 3 }, + { 2, 3 }, + { 3, 2 }, + { 641, 480 }, + { 640, 481 }, + // TODO: Crashes + // { 1, 1 } // TODO: Division by zero in QVideoFrame::map (Debug) + // { 1, 2 } // TODO: D3D validation error in QRhiD3D11::executeCommandBuffer + // { 2, 1 } // TODO: D3D validation error in QRhiD3D11::executeCommandBuffer + }; + + for (const QSize &size : sizes) { + for (const QVideoFrameFormat::PixelFormat pixelFormat : s_pixelFormats) { + for (const bool forceCpu : { false, true }) { + + if (pixelFormat == QVideoFrameFormat::Format_YUV420P10 && forceCpu) + continue; // TODO: Cpu conversion not implemented + + QString name = QStringLiteral("%1x%2_%3%4") + .arg(size.width()) + .arg(size.height()) + .arg(QVideoFrameFormat::pixelFormatToString(pixelFormat)) + .arg(forceCpu ? "_cpu" : ""); + + QTest::addRow("%s", name.toLatin1().data()) + << size << pixelFormat << forceCpu << isSupportedPixelFormat(pixelFormat); + } + } + } +} + +void tst_QVideoFrame::qImageFromVideoFrame_doesNotCrash_whenCalledWithEvenAndOddSizedFrames() { + QFETCH(const QSize, size); + QFETCH(const QVideoFrameFormat::PixelFormat, pixelFormat); + QFETCH(const bool, forceCpuConversion); + QFETCH(const bool, supportedOnPlatform); + + const QVideoFrameFormat format{ size, pixelFormat }; + const QVideoFrame frame{ format }; + const QImage actual = qImageFromVideoFrame(frame, QtVideo::Rotation::None, false, false, + forceCpuConversion); + + if (supportedOnPlatform) + QCOMPARE_EQ(actual.isNull(), size.isEmpty()); + // Otherwise, we don't expect an image being produced, although it might. + // TODO: Investigate why 16 bit formats fail on some Android flavors. +} + #define TEST_MAPPED(frame, mode) \ do { \ QVERIFY(frame.bits(0)); \ @@ -915,85 +1148,69 @@ void tst_QVideoFrame::image_data() { QTest::addColumn<QSize>("size"); QTest::addColumn<QVideoFrameFormat::PixelFormat>("pixelFormat"); - QTest::addColumn<QImage::Format>("imageFormat"); QTest::newRow("64x64 ARGB32") << QSize(64, 64) - << QVideoFrameFormat::Format_ARGB8888 - << QImage::Format_ARGB32_Premultiplied; + << QVideoFrameFormat::Format_ARGB8888; QTest::newRow("64x64 ARGB32_Premultiplied") << QSize(64, 64) - << QVideoFrameFormat::Format_ARGB8888_Premultiplied - << QImage::Format_ARGB32_Premultiplied; + << QVideoFrameFormat::Format_ARGB8888_Premultiplied; QTest::newRow("64x64 RGB32") << QSize(64, 64) - << QVideoFrameFormat::Format_XRGB8888 - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_XRGB8888; QTest::newRow("64x64 BGRA32") << QSize(64, 64) - << QVideoFrameFormat::Format_BGRA8888 - << QImage::Format_ARGB32_Premultiplied; + << QVideoFrameFormat::Format_BGRA8888; QTest::newRow("64x64 BGRA32_Premultiplied") << QSize(64, 64) - << QVideoFrameFormat::Format_BGRA8888_Premultiplied - << QImage::Format_ARGB32_Premultiplied; + << QVideoFrameFormat::Format_BGRA8888_Premultiplied; QTest::newRow("64x64 BGR32") << QSize(64, 64) - << QVideoFrameFormat::Format_XBGR8888 - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_XBGR8888; QTest::newRow("64x64 AYUV") << QSize(64, 64) - << QVideoFrameFormat::Format_AYUV - << QImage::Format_ARGB32_Premultiplied; + << QVideoFrameFormat::Format_AYUV; QTest::newRow("64x64 YUV420P") << QSize(64, 64) - << QVideoFrameFormat::Format_YUV420P - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_YUV420P; QTest::newRow("64x64 YV12") << QSize(64, 64) - << QVideoFrameFormat::Format_YV12 - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_YV12; QTest::newRow("64x64 UYVY") << QSize(64, 64) - << QVideoFrameFormat::Format_UYVY - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_UYVY; QTest::newRow("64x64 YUYV") << QSize(64, 64) - << QVideoFrameFormat::Format_YUYV - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_YUYV; QTest::newRow("64x64 NV12") << QSize(64, 64) - << QVideoFrameFormat::Format_NV12 - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_NV12; QTest::newRow("64x64 NV21") << QSize(64, 64) - << QVideoFrameFormat::Format_NV21 - << QImage::Format_RGB32; + << QVideoFrameFormat::Format_NV21; } void tst_QVideoFrame::image() { QFETCH(QSize, size); QFETCH(QVideoFrameFormat::PixelFormat, pixelFormat); - QFETCH(QImage::Format, imageFormat); QVideoFrame frame(QVideoFrameFormat(size, pixelFormat)); QImage img = frame.toImage(); QVERIFY(!img.isNull()); - QCOMPARE(img.format(), imageFormat); QCOMPARE(img.size(), size); } @@ -1005,6 +1222,211 @@ void tst_QVideoFrame::emptyData() QVERIFY(!f.map(QVideoFrame::ReadOnly)); } +void tst_QVideoFrame::mirrored_takesValue_fromVideoFrameFormat() +{ + QVideoFrameFormat format(QSize(10, 20), QVideoFrameFormat::Format_ARGB8888); + format.setMirrored(true); + + QVideoFrame frame(format); + QVERIFY(frame.mirrored()); + + frame.setMirrored(false); + frame.setRotation(QtVideo::Rotation::Clockwise180); + QVERIFY(!frame.mirrored()); + QVERIFY(!frame.surfaceFormat().isMirrored()); +} + +void tst_QVideoFrame::rotation_takesValue_fromVideoFrameFormat() +{ + QVideoFrameFormat format(QSize(10, 20), QVideoFrameFormat::Format_ARGB8888); + format.setRotation(QtVideo::Rotation::Clockwise270); + + QVideoFrame frame(format); + QCOMPARE(frame.rotation(), QtVideo::Rotation::Clockwise270); + + frame.setRotation(QtVideo::Rotation::Clockwise180); + + QCOMPARE(frame.rotation(), QtVideo::Rotation::Clockwise180); + QCOMPARE(frame.surfaceFormat().rotation(), QtVideo::Rotation::Clockwise180); +} + +void tst_QVideoFrame::streamFrameRate_takesValue_fromVideoFrameFormat() +{ + QVideoFrameFormat format(QSize(10, 20), QVideoFrameFormat::Format_ARGB8888); + format.setStreamFrameRate(20.); + + QVideoFrame frame(format); + QCOMPARE(frame.streamFrameRate(), 20.); + + frame.setStreamFrameRate(25.); + + QCOMPARE(frame.streamFrameRate(), 25.); + QCOMPARE(frame.surfaceFormat().streamFrameRate(), 25.); +} + +void tst_QVideoFrame::constructor_createsInvalidFrame_whenCalledWithNullImage() +{ + const QVideoFrame frame{ QImage{} }; + QVERIFY(!frame.isValid()); +} + +void tst_QVideoFrame::constructor_createsInvalidFrame_whenCalledWithEmptyImage() +{ + { + const QImage image{ QSize{}, QImage::Format_RGB32 }; + const QVideoFrame frame{ image }; + + QVERIFY(!frame.isValid()); + } + + { + const QImage image{ { 0, 0 }, QImage::Format_RGB32 }; + const QVideoFrame frame{ image }; + + QVERIFY(!frame.isValid()); + } + + { + const QImage image{ { 1, 0 }, QImage::Format_RGB32 }; + const QVideoFrame frame{ image }; + + QVERIFY(!frame.isValid()); + } + + { + const QImage image{ { 0, 1 }, QImage::Format_RGB32 }; + const QVideoFrame frame{ image }; + + QVERIFY(!frame.isValid()); + } +} + +void tst_QVideoFrame::constructor_createsInvalidFrame_whenCalledWithInvalidImageFormat() +{ + const QImage image{ { 1, 1 }, QImage::Format_Invalid}; + const QVideoFrame frame{ image }; + + QVERIFY(!frame.isValid()); +} + +// clang-format off +void tst_QVideoFrame::constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats_data() +{ + QTest::addColumn<QImage::Format>("imageFormat"); + QTest::addColumn<QVideoFrameFormat::PixelFormat>("expectedFrameFormat"); + + // Formats that do not require conversion + QTest::newRow("Format_RGB32") << QImage::Format_RGB32 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_ARGB32") << QImage::Format_ARGB32 << QVideoFrameFormat::Format_BGRA8888; + QTest::newRow("Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_RGBA8888") << QImage::Format_RGBA8888 << QVideoFrameFormat::Format_RGBA8888; + QTest::newRow("Format_RGBA8888_Premultiplied") << QImage::Format_RGBA8888_Premultiplied << QVideoFrameFormat::Format_RGBX8888; + QTest::newRow("Format_RGBX8888") << QImage::Format_RGBX8888 << QVideoFrameFormat::Format_RGBX8888; + QTest::newRow("Format_Grayscale8") << QImage::Format_Grayscale8 << QVideoFrameFormat::Format_Y8; + QTest::newRow("Format_Grayscale16") << QImage::Format_Grayscale16 << QVideoFrameFormat::Format_Y16; + + // Formats that require conversion of input image + QTest::newRow("Format_Mono") << QImage::Format_Mono << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_MonoLSB") << QImage::Format_MonoLSB << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_Indexed8") << QImage::Format_Indexed8 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_RGB16") << QImage::Format_RGB16 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_RGB666") << QImage::Format_RGB666 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_ARGB6666_Premultiplied") << QImage::Format_ARGB6666_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_RGB555") << QImage::Format_RGB555 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_RGB888") << QImage::Format_RGB888 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_RGB444") << QImage::Format_RGB444 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_ARGB4444_Premultiplied") << QImage::Format_ARGB4444_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_BGR30") << QImage::Format_BGR30 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_A2BGR30_Premultiplied") << QImage::Format_A2BGR30_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_RGB30") << QImage::Format_RGB30 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_A2RGB30_Premultiplied") << QImage::Format_A2RGB30_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_Alpha8") << QImage::Format_Alpha8 << QVideoFrameFormat::Format_BGRA8888; + QTest::newRow("Format_RGBX64") << QImage::Format_RGBX64 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_RGBA64") << QImage::Format_RGBA64 << QVideoFrameFormat::Format_BGRA8888; + QTest::newRow("Format_RGBA64_Premultiplied") << QImage::Format_RGBA64_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_BGR888") << QImage::Format_BGR888 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_RGBX16FPx4") << QImage::Format_RGBX16FPx4 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_RGBA16FPx4") << QImage::Format_RGBA16FPx4 << QVideoFrameFormat::Format_BGRA8888; + QTest::newRow("Format_RGBA16FPx4_Premultiplied") << QImage::Format_RGBA16FPx4_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; + QTest::newRow("Format_RGBX32FPx4") << QImage::Format_RGBX32FPx4 << QVideoFrameFormat::Format_BGRX8888; + QTest::newRow("Format_RGBA32FPx4") << QImage::Format_RGBA32FPx4 << QVideoFrameFormat::Format_BGRA8888; + QTest::newRow("Format_RGBA32FPx4_Premultiplied") << QImage::Format_RGBA32FPx4_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied; +} +// clang-format on + +void tst_QVideoFrame::constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats() +{ + QFETCH(const QImage::Format, imageFormat); + QFETCH(QVideoFrameFormat::PixelFormat, expectedFrameFormat); + + const QImage image{ { 1, 1 }, imageFormat }; + const QVideoFrame frame{ image }; + + QVERIFY(frame.isValid()); + QCOMPARE_EQ(frame.pixelFormat(), expectedFrameFormat); +} + +// clang-format off +void tst_QVideoFrame::constructor_copiesImageData_whenCalledWithRGBFormats_data() +{ + QTest::addColumn<QImage::Format>("imageFormat"); + + // Formats that do not require image conversion + QTest::newRow("Format_RGB32") << QImage::Format_RGB32; + QTest::newRow("Format_RGBX8888") << QImage::Format_RGBX8888; + QTest::newRow("Format_ARGB32") << QImage::Format_ARGB32; + QTest::newRow("Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied; + QTest::newRow("Format_RGBA8888") << QImage::Format_RGBA8888; + QTest::newRow("Format_RGBA8888_Premultiplied") << QImage::Format_RGBA8888_Premultiplied; + + // Formats that require image conversion + QTest::newRow("Format_Mono") << QImage::Format_Mono; + QTest::newRow("Format_MonoLSB") << QImage::Format_MonoLSB; + QTest::newRow("Format_Indexed8") << QImage::Format_Indexed8; + QTest::newRow("Format_RGB16") << QImage::Format_RGB16; + QTest::newRow("Format_ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied; + QTest::newRow("Format_RGB666") << QImage::Format_RGB666; + QTest::newRow("Format_ARGB6666_Premultiplied") << QImage::Format_ARGB6666_Premultiplied; + QTest::newRow("Format_RGB555") << QImage::Format_RGB555; + QTest::newRow("Format_ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied; + QTest::newRow("Format_RGB888") << QImage::Format_RGB888; + QTest::newRow("Format_RGB444") << QImage::Format_RGB444; + QTest::newRow("Format_ARGB4444_Premultiplied") << QImage::Format_ARGB4444_Premultiplied; + QTest::newRow("Format_BGR30") << QImage::Format_BGR30; + QTest::newRow("Format_A2BGR30_Premultiplied") << QImage::Format_A2BGR30_Premultiplied; + QTest::newRow("Format_RGB30") << QImage::Format_RGB30; + QTest::newRow("Format_A2RGB30_Premultiplied") << QImage::Format_A2RGB30_Premultiplied; + QTest::newRow("Format_Alpha8") << QImage::Format_Alpha8; + QTest::newRow("Format_RGBX64") << QImage::Format_RGBX64; + QTest::newRow("Format_RGBA64") << QImage::Format_RGBA64; + QTest::newRow("Format_RGBA64_Premultiplied") << QImage::Format_RGBA64_Premultiplied; + QTest::newRow("Format_BGR888") << QImage::Format_BGR888; + QTest::newRow("Format_RGBX16FPx4") << QImage::Format_RGBX16FPx4; + QTest::newRow("Format_RGBA16FPx4") << QImage::Format_RGBA16FPx4; + QTest::newRow("Format_RGBA16FPx4_Premultiplied") << QImage::Format_RGBA16FPx4_Premultiplied; + QTest::newRow("Format_RGBX32FPx4") << QImage::Format_RGBX32FPx4; + QTest::newRow("Format_RGBA32FPx4") << QImage::Format_RGBA32FPx4; + QTest::newRow("Format_RGBA32FPx4_Premultiplied") << QImage::Format_RGBA32FPx4_Premultiplied; +} + +// clang-format on + +void tst_QVideoFrame::constructor_copiesImageData_whenCalledWithRGBFormats() +{ + QFETCH(const QImage::Format, imageFormat); + + // Arrange + const QImage image{ createTestImage(imageFormat) }; + + // Act + QVideoFrame frame{ image }; + + // Assert + QVERIFY(compareEq(frame, image)); +} + QTEST_MAIN(tst_QVideoFrame) #include "tst_qvideoframe.moc" |