| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part 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 |
| |
| #include <qtmultimediaglobal.h> |
| #include "qgraphicsvideoitem.h" |
| #include <QtTest/QtTest> |
| #include "qmediaobject.h" |
| #include "qmediaservice.h" |
| #include <private/qpaintervideosurface_p.h> |
| #include "qvideorenderercontrol.h" |
| |
| #include <qabstractvideosurface.h> |
| #include <qvideosurfaceformat.h> |
| |
| #include <QtWidgets/qapplication.h> |
| #include <QtWidgets/qgraphicsscene.h> |
| #include <QtWidgets/qgraphicsview.h> |
| |
| QT_USE_NAMESPACE |
| class tst_QGraphicsVideoItem : public QObject |
| { |
| Q_OBJECT |
| public slots: |
| void initTestCase(); |
| |
| private slots: |
| void nullObject(); |
| void nullService(); |
| void noOutputs(); |
| void serviceDestroyed(); |
| void mediaObjectDestroyed(); |
| void setMediaObject(); |
| |
| void show(); |
| |
| void aspectRatioMode(); |
| void offset(); |
| void size(); |
| void nativeSize_data(); |
| void nativeSize(); |
| |
| void boundingRect_data(); |
| void boundingRect(); |
| |
| void paint(); |
| }; |
| |
| Q_DECLARE_METATYPE(const uchar *) |
| Q_DECLARE_METATYPE(Qt::AspectRatioMode) |
| |
| class QtTestRendererControl : public QVideoRendererControl |
| { |
| public: |
| QtTestRendererControl() |
| : m_surface(0) |
| { |
| } |
| |
| QAbstractVideoSurface *surface() const { return m_surface; } |
| void setSurface(QAbstractVideoSurface *surface) { m_surface = surface; } |
| |
| private: |
| QAbstractVideoSurface *m_surface; |
| }; |
| |
| class QtTestVideoService : public QMediaService |
| { |
| Q_OBJECT |
| public: |
| QtTestVideoService( |
| QtTestRendererControl *renderer) |
| : QMediaService(0) |
| , rendererRef(0) |
| , rendererControl(renderer) |
| { |
| } |
| |
| ~QtTestVideoService() |
| { |
| delete rendererControl; |
| } |
| |
| QMediaControl *requestControl(const char *name) |
| { |
| if (qstrcmp(name, QVideoRendererControl_iid) == 0 && rendererControl) { |
| rendererRef += 1; |
| |
| return rendererControl; |
| } else { |
| return 0; |
| } |
| } |
| |
| void releaseControl(QMediaControl *control) |
| { |
| Q_ASSERT(control); |
| |
| if (control == rendererControl) { |
| rendererRef -= 1; |
| |
| if (rendererRef == 0) |
| rendererControl->setSurface(0); |
| } |
| } |
| |
| int rendererRef; |
| QtTestRendererControl *rendererControl; |
| }; |
| |
| class QtTestVideoObject : public QMediaObject |
| { |
| Q_OBJECT |
| public: |
| QtTestVideoObject(QtTestRendererControl *renderer) |
| : QMediaObject(0, new QtTestVideoService(renderer)) |
| { |
| testService = qobject_cast<QtTestVideoService*>(service()); |
| } |
| |
| QtTestVideoObject(QtTestVideoService *service): |
| QMediaObject(0, service), |
| testService(service) |
| { |
| } |
| |
| ~QtTestVideoObject() |
| { |
| delete testService; |
| } |
| |
| QtTestVideoService *testService; |
| }; |
| |
| class QtTestGraphicsVideoItem : public QGraphicsVideoItem |
| { |
| public: |
| QtTestGraphicsVideoItem(QGraphicsItem *parent = 0) |
| : QGraphicsVideoItem(parent) |
| , m_paintCount(0) |
| { |
| } |
| |
| void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
| { |
| ++m_paintCount; |
| |
| QTestEventLoop::instance().exitLoop(); |
| |
| QGraphicsVideoItem::paint(painter, option, widget); |
| } |
| |
| bool waitForPaint(int secs) |
| { |
| const int paintCount = m_paintCount; |
| |
| QTestEventLoop::instance().enterLoop(secs); |
| |
| return m_paintCount != paintCount; |
| } |
| |
| int paintCount() const |
| { |
| return m_paintCount; |
| } |
| |
| private: |
| int m_paintCount; |
| }; |
| |
| void tst_QGraphicsVideoItem::initTestCase() |
| { |
| qRegisterMetaType<Qt::AspectRatioMode>(); |
| } |
| |
| void tst_QGraphicsVideoItem::nullObject() |
| { |
| QGraphicsVideoItem item(0); |
| |
| QVERIFY(item.boundingRect().isEmpty()); |
| } |
| |
| void tst_QGraphicsVideoItem::nullService() |
| { |
| QtTestVideoService *service = 0; |
| |
| QtTestVideoObject object(service); |
| |
| QtTestGraphicsVideoItem *item = new QtTestGraphicsVideoItem; |
| object.bind(item); |
| |
| QVERIFY(item->boundingRect().isEmpty()); |
| |
| item->hide(); |
| item->show(); |
| |
| QGraphicsScene graphicsScene; |
| graphicsScene.addItem(item); |
| QGraphicsView graphicsView(&graphicsScene); |
| graphicsView.show(); |
| } |
| |
| void tst_QGraphicsVideoItem::noOutputs() |
| { |
| QtTestRendererControl *control = 0; |
| QtTestVideoObject object(control); |
| |
| QtTestGraphicsVideoItem *item = new QtTestGraphicsVideoItem; |
| object.bind(item); |
| |
| QVERIFY(item->boundingRect().isEmpty()); |
| |
| item->hide(); |
| item->show(); |
| |
| QGraphicsScene graphicsScene; |
| graphicsScene.addItem(item); |
| QGraphicsView graphicsView(&graphicsScene); |
| graphicsView.show(); |
| } |
| |
| void tst_QGraphicsVideoItem::serviceDestroyed() |
| { |
| QtTestVideoObject object(new QtTestRendererControl); |
| |
| QGraphicsVideoItem item; |
| object.bind(&item); |
| |
| QCOMPARE(object.testService->rendererRef, 1); |
| |
| QtTestVideoService *service = object.testService; |
| object.testService = 0; |
| |
| delete service; |
| |
| QCOMPARE(item.mediaObject(), static_cast<QMediaObject *>(&object)); |
| QVERIFY(item.boundingRect().isEmpty()); |
| } |
| |
| void tst_QGraphicsVideoItem::mediaObjectDestroyed() |
| { |
| QtTestVideoObject *object = new QtTestVideoObject(new QtTestRendererControl); |
| |
| QGraphicsVideoItem item; |
| object->bind(&item); |
| |
| QCOMPARE(object->testService->rendererRef, 1); |
| |
| delete object; |
| object = 0; |
| |
| QCOMPARE(item.mediaObject(), static_cast<QMediaObject *>(object)); |
| QVERIFY(item.boundingRect().isEmpty()); |
| } |
| |
| void tst_QGraphicsVideoItem::setMediaObject() |
| { |
| QMediaObject *nullObject = 0; |
| QtTestVideoObject object(new QtTestRendererControl); |
| |
| QGraphicsVideoItem item; |
| |
| QCOMPARE(item.mediaObject(), nullObject); |
| QCOMPARE(object.testService->rendererRef, 0); |
| |
| object.bind(&item); |
| QCOMPARE(item.mediaObject(), static_cast<QMediaObject *>(&object)); |
| QCOMPARE(object.testService->rendererRef, 1); |
| QVERIFY(object.testService->rendererControl->surface() == 0); |
| |
| { // Surface setup is deferred until after the first paint. |
| QImage image(320, 240, QImage::Format_RGB32); |
| QPainter painter(&image); |
| |
| item.paint(&painter, 0); |
| } |
| QVERIFY(object.testService->rendererControl->surface() != 0); |
| |
| object.unbind(&item); |
| QCOMPARE(item.mediaObject(), nullObject); |
| |
| QCOMPARE(object.testService->rendererRef, 0); |
| QVERIFY(object.testService->rendererControl->surface() == 0); |
| |
| item.setVisible(false); |
| |
| object.bind(&item); |
| QCOMPARE(item.mediaObject(), static_cast<QMediaObject *>(&object)); |
| QCOMPARE(object.testService->rendererRef, 1); |
| QVERIFY(object.testService->rendererControl->surface() != 0); |
| } |
| |
| void tst_QGraphicsVideoItem::show() |
| { |
| QtTestVideoObject object(new QtTestRendererControl); |
| QtTestGraphicsVideoItem *item = new QtTestGraphicsVideoItem; |
| object.bind(item); |
| |
| // Graphics items are visible by default |
| QCOMPARE(object.testService->rendererRef, 1); |
| QVERIFY(object.testService->rendererControl->surface() == 0); |
| |
| item->hide(); |
| QCOMPARE(object.testService->rendererRef, 1); |
| |
| item->show(); |
| QCOMPARE(object.testService->rendererRef, 1); |
| QVERIFY(object.testService->rendererControl->surface() == 0); |
| |
| QGraphicsScene graphicsScene; |
| graphicsScene.addItem(item); |
| QGraphicsView graphicsView(&graphicsScene); |
| graphicsView.show(); |
| |
| QVERIFY(item->paintCount() || item->waitForPaint(1)); |
| QVERIFY(object.testService->rendererControl->surface() != 0); |
| |
| QVERIFY(item->boundingRect().isEmpty()); |
| |
| QVideoSurfaceFormat format(QSize(320,240),QVideoFrame::Format_RGB32); |
| QVERIFY(object.testService->rendererControl->surface()->start(format)); |
| |
| QCoreApplication::processEvents(); |
| QVERIFY(!item->boundingRect().isEmpty()); |
| } |
| |
| void tst_QGraphicsVideoItem::aspectRatioMode() |
| { |
| QGraphicsVideoItem item; |
| |
| QCOMPARE(item.aspectRatioMode(), Qt::KeepAspectRatio); |
| |
| item.setAspectRatioMode(Qt::IgnoreAspectRatio); |
| QCOMPARE(item.aspectRatioMode(), Qt::IgnoreAspectRatio); |
| |
| item.setAspectRatioMode(Qt::KeepAspectRatioByExpanding); |
| QCOMPARE(item.aspectRatioMode(), Qt::KeepAspectRatioByExpanding); |
| |
| item.setAspectRatioMode(Qt::KeepAspectRatio); |
| QCOMPARE(item.aspectRatioMode(), Qt::KeepAspectRatio); |
| } |
| |
| void tst_QGraphicsVideoItem::offset() |
| { |
| QGraphicsVideoItem item; |
| |
| QCOMPARE(item.offset(), QPointF(0, 0)); |
| |
| item.setOffset(QPointF(-32.4, 43.0)); |
| QCOMPARE(item.offset(), QPointF(-32.4, 43.0)); |
| |
| item.setOffset(QPointF(1, 1)); |
| QCOMPARE(item.offset(), QPointF(1, 1)); |
| |
| item.setOffset(QPointF(12, -30.4)); |
| QCOMPARE(item.offset(), QPointF(12, -30.4)); |
| |
| item.setOffset(QPointF(-90.4, -75)); |
| QCOMPARE(item.offset(), QPointF(-90.4, -75)); |
| } |
| |
| void tst_QGraphicsVideoItem::size() |
| { |
| QGraphicsVideoItem item; |
| |
| QCOMPARE(item.size(), QSizeF(320, 240)); |
| |
| item.setSize(QSizeF(542.5, 436.3)); |
| QCOMPARE(item.size(), QSizeF(542.5, 436.3)); |
| |
| item.setSize(QSizeF(-43, 12)); |
| QCOMPARE(item.size(), QSizeF(0, 0)); |
| |
| item.setSize(QSizeF(54, -9)); |
| QCOMPARE(item.size(), QSizeF(0, 0)); |
| |
| item.setSize(QSizeF(-90, -65)); |
| QCOMPARE(item.size(), QSizeF(0, 0)); |
| |
| item.setSize(QSizeF(1000, 1000)); |
| QCOMPARE(item.size(), QSizeF(1000, 1000)); |
| } |
| |
| void tst_QGraphicsVideoItem::nativeSize_data() |
| { |
| QTest::addColumn<QSize>("frameSize"); |
| QTest::addColumn<QRect>("viewport"); |
| QTest::addColumn<QSize>("pixelAspectRatio"); |
| QTest::addColumn<QSizeF>("nativeSize"); |
| |
| QTest::newRow("640x480") |
| << QSize(640, 480) |
| << QRect(0, 0, 640, 480) |
| << QSize(1, 1) |
| << QSizeF(640, 480); |
| |
| QTest::newRow("800x600, (80,60, 640x480) viewport") |
| << QSize(800, 600) |
| << QRect(80, 60, 640, 480) |
| << QSize(1, 1) |
| << QSizeF(640, 480); |
| |
| QTest::newRow("800x600, (80,60, 640x480) viewport, 4:3") |
| << QSize(800, 600) |
| << QRect(80, 60, 640, 480) |
| << QSize(4, 3) |
| << QSizeF(853, 480); |
| } |
| |
| void tst_QGraphicsVideoItem::nativeSize() |
| { |
| QFETCH(QSize, frameSize); |
| QFETCH(QRect, viewport); |
| QFETCH(QSize, pixelAspectRatio); |
| QFETCH(QSizeF, nativeSize); |
| |
| QtTestVideoObject object(new QtTestRendererControl); |
| QGraphicsVideoItem item; |
| object.bind(&item); |
| |
| QCOMPARE(item.nativeSize(), QSizeF()); |
| |
| QSignalSpy spy(&item, SIGNAL(nativeSizeChanged(QSizeF))); |
| |
| QVideoSurfaceFormat format(frameSize, QVideoFrame::Format_ARGB32); |
| format.setViewport(viewport); |
| format.setPixelAspectRatio(pixelAspectRatio); |
| |
| { // Surface setup is deferred until after the first paint. |
| QImage image(320, 240, QImage::Format_RGB32); |
| QPainter painter(&image); |
| |
| item.paint(&painter, 0); |
| } |
| QVERIFY(object.testService->rendererControl->surface() != 0); |
| QVERIFY(object.testService->rendererControl->surface()->start(format)); |
| |
| QCoreApplication::processEvents(); |
| QCOMPARE(item.nativeSize(), nativeSize); |
| QCOMPARE(spy.count(), 1); |
| QCOMPARE(spy.last().first().toSizeF(), nativeSize); |
| |
| object.testService->rendererControl->surface()->stop(); |
| |
| QCoreApplication::processEvents(); |
| QVERIFY(item.nativeSize().isEmpty()); |
| QCOMPARE(spy.count(), 2); |
| QVERIFY(spy.last().first().toSizeF().isEmpty()); |
| } |
| |
| void tst_QGraphicsVideoItem::boundingRect_data() |
| { |
| QTest::addColumn<QSize>("frameSize"); |
| QTest::addColumn<QPointF>("offset"); |
| QTest::addColumn<QSizeF>("size"); |
| QTest::addColumn<Qt::AspectRatioMode>("aspectRatioMode"); |
| QTest::addColumn<QRectF>("expectedRect"); |
| |
| |
| QTest::newRow("640x480: (0,0 640x480), Keep") |
| << QSize(640, 480) |
| << QPointF(0, 0) |
| << QSizeF(640, 480) |
| << Qt::KeepAspectRatio |
| << QRectF(0, 0, 640, 480); |
| |
| QTest::newRow("800x600, (0,0, 640x480), Keep") |
| << QSize(800, 600) |
| << QPointF(0, 0) |
| << QSizeF(640, 480) |
| << Qt::KeepAspectRatio |
| << QRectF(0, 0, 640, 480); |
| |
| QTest::newRow("800x600, (0,0, 640x480), KeepByExpanding") |
| << QSize(800, 600) |
| << QPointF(0, 0) |
| << QSizeF(640, 480) |
| << Qt::KeepAspectRatioByExpanding |
| << QRectF(0, 0, 640, 480); |
| |
| QTest::newRow("800x600, (0,0, 640x480), Ignore") |
| << QSize(800, 600) |
| << QPointF(0, 0) |
| << QSizeF(640, 480) |
| << Qt::IgnoreAspectRatio |
| << QRectF(0, 0, 640, 480); |
| |
| QTest::newRow("800x600, (100,100, 640x480), Keep") |
| << QSize(800, 600) |
| << QPointF(100, 100) |
| << QSizeF(640, 480) |
| << Qt::KeepAspectRatio |
| << QRectF(100, 100, 640, 480); |
| |
| QTest::newRow("800x600, (100,-100, 640x480), KeepByExpanding") |
| << QSize(800, 600) |
| << QPointF(100, -100) |
| << QSizeF(640, 480) |
| << Qt::KeepAspectRatioByExpanding |
| << QRectF(100, -100, 640, 480); |
| |
| QTest::newRow("800x600, (-100,-100, 640x480), Ignore") |
| << QSize(800, 600) |
| << QPointF(-100, -100) |
| << QSizeF(640, 480) |
| << Qt::IgnoreAspectRatio |
| << QRectF(-100, -100, 640, 480); |
| |
| QTest::newRow("800x600, (0,0, 1920x1024), Keep") |
| << QSize(800, 600) |
| << QPointF(0, 0) |
| << QSizeF(1920, 1024) |
| << Qt::KeepAspectRatio |
| << QRectF(832.0 / 3, 0, 4096.0 / 3, 1024); |
| |
| QTest::newRow("800x600, (0,0, 1920x1024), KeepByExpanding") |
| << QSize(800, 600) |
| << QPointF(0, 0) |
| << QSizeF(1920, 1024) |
| << Qt::KeepAspectRatioByExpanding |
| << QRectF(0, 0, 1920, 1024); |
| |
| QTest::newRow("800x600, (0,0, 1920x1024), Ignore") |
| << QSize(800, 600) |
| << QPointF(0, 0) |
| << QSizeF(1920, 1024) |
| << Qt::IgnoreAspectRatio |
| << QRectF(0, 0, 1920, 1024); |
| |
| QTest::newRow("800x600, (100,100, 1920x1024), Keep") |
| << QSize(800, 600) |
| << QPointF(100, 100) |
| << QSizeF(1920, 1024) |
| << Qt::KeepAspectRatio |
| << QRectF(100 + 832.0 / 3, 100, 4096.0 / 3, 1024); |
| |
| QTest::newRow("800x600, (100,-100, 1920x1024), KeepByExpanding") |
| << QSize(800, 600) |
| << QPointF(100, -100) |
| << QSizeF(1920, 1024) |
| << Qt::KeepAspectRatioByExpanding |
| << QRectF(100, -100, 1920, 1024); |
| |
| QTest::newRow("800x600, (-100,-100, 1920x1024), Ignore") |
| << QSize(800, 600) |
| << QPointF(-100, -100) |
| << QSizeF(1920, 1024) |
| << Qt::IgnoreAspectRatio |
| << QRectF(-100, -100, 1920, 1024); |
| } |
| |
| void tst_QGraphicsVideoItem::boundingRect() |
| { |
| QFETCH(QSize, frameSize); |
| QFETCH(QPointF, offset); |
| QFETCH(QSizeF, size); |
| QFETCH(Qt::AspectRatioMode, aspectRatioMode); |
| QFETCH(QRectF, expectedRect); |
| |
| QtTestVideoObject object(new QtTestRendererControl); |
| QGraphicsVideoItem item; |
| object.bind(&item); |
| |
| item.setOffset(offset); |
| item.setSize(size); |
| item.setAspectRatioMode(aspectRatioMode); |
| |
| QVideoSurfaceFormat format(frameSize, QVideoFrame::Format_ARGB32); |
| |
| { // Surface setup is deferred until after the first paint. |
| QImage image(320, 240, QImage::Format_RGB32); |
| QPainter painter(&image); |
| |
| item.paint(&painter, 0); |
| } |
| QVERIFY(object.testService->rendererControl->surface() != 0); |
| QVERIFY(object.testService->rendererControl->surface()->start(format)); |
| |
| QCoreApplication::processEvents(); |
| QCOMPARE(item.boundingRect(), expectedRect); |
| } |
| |
| static const uchar rgb32ImageData[] = |
| { |
| 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, |
| 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00 |
| }; |
| |
| void tst_QGraphicsVideoItem::paint() |
| { |
| QtTestVideoObject object(new QtTestRendererControl); |
| QtTestGraphicsVideoItem *item = new QtTestGraphicsVideoItem; |
| object.bind(item); |
| |
| QGraphicsScene graphicsScene; |
| graphicsScene.addItem(item); |
| QGraphicsView graphicsView(&graphicsScene); |
| graphicsView.show(); |
| QVERIFY(item->waitForPaint(1)); |
| |
| QPainterVideoSurface *surface = qobject_cast<QPainterVideoSurface *>( |
| object.testService->rendererControl->surface()); |
| if (!surface) |
| QSKIP("QGraphicsVideoItem is not QPainterVideoSurface based"); |
| |
| QVideoSurfaceFormat format(QSize(2, 2), QVideoFrame::Format_RGB32); |
| |
| QVERIFY(surface->start(format)); |
| QCOMPARE(surface->isActive(), true); |
| QCOMPARE(surface->isReady(), true); |
| |
| QVERIFY(item->waitForPaint(1)); |
| |
| QCOMPARE(surface->isActive(), true); |
| QCOMPARE(surface->isReady(), true); |
| |
| QVideoFrame frame(sizeof(rgb32ImageData), QSize(2, 2), 8, QVideoFrame::Format_RGB32); |
| |
| frame.map(QAbstractVideoBuffer::WriteOnly); |
| memcpy(frame.bits(), rgb32ImageData, frame.mappedBytes()); |
| frame.unmap(); |
| |
| QVERIFY(surface->present(frame)); |
| QCOMPARE(surface->isActive(), true); |
| QCOMPARE(surface->isReady(), false); |
| |
| QVERIFY(item->waitForPaint(1)); |
| |
| QCOMPARE(surface->isActive(), true); |
| QCOMPARE(surface->isReady(), true); |
| } |
| |
| |
| QTEST_MAIN(tst_QGraphicsVideoItem) |
| |
| #include "tst_qgraphicsvideoitem.moc" |