/**************************************************************************** ** ** 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 #include "qgraphicsvideoitem.h" #include #include "qmediaobject.h" #include "qmediaservice.h" #include #include "qvideorenderercontrol.h" #include #include #include #include #include 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(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(); } 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(&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(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(&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(&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("frameSize"); QTest::addColumn("viewport"); QTest::addColumn("pixelAspectRatio"); QTest::addColumn("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("frameSize"); QTest::addColumn("offset"); QTest::addColumn("size"); QTest::addColumn("aspectRatioMode"); QTest::addColumn("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( 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"