Move QDeclarativeVideoOutput to the private QtMultimediaQuickTools library to make the QDeclarativeVideoOutputBackend interface implementable by a plugin. Change-Id: I763c483a1fc9ec56dc7b8be0bc71523f029a36ee Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
714 lines
29 KiB
C++
714 lines
29 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** 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 Digia. For licensing terms and
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
//TESTED_COMPONENT=plugins/declarative/multimedia
|
|
|
|
#include <QtTest/QtTest>
|
|
|
|
#include <QtQml/qqmlengine.h>
|
|
#include <QtQml/qqmlcomponent.h>
|
|
|
|
#include "private/qdeclarativevideooutput_p.h"
|
|
|
|
#include <qabstractvideosurface.h>
|
|
#include <qvideorenderercontrol.h>
|
|
#include <qvideosurfaceformat.h>
|
|
|
|
#include <qmediaobject.h>
|
|
|
|
class SurfaceHolder : public QObject
|
|
{
|
|
Q_OBJECT
|
|
Q_PROPERTY(QAbstractVideoSurface *videoSurface READ videoSurface WRITE setVideoSurface)
|
|
public:
|
|
SurfaceHolder(QObject *parent)
|
|
: QObject(parent)
|
|
, m_surface(0)
|
|
{
|
|
}
|
|
|
|
QAbstractVideoSurface *videoSurface() const
|
|
{
|
|
return m_surface;
|
|
}
|
|
void setVideoSurface(QAbstractVideoSurface *surface)
|
|
{
|
|
if (m_surface != surface && m_surface && m_surface->isActive()) {
|
|
m_surface->stop();
|
|
}
|
|
m_surface = surface;
|
|
}
|
|
|
|
void presentDummyFrame(const QSize &size);
|
|
|
|
private:
|
|
QAbstractVideoSurface *m_surface;
|
|
|
|
};
|
|
|
|
// Starts the surface and puts a frame
|
|
void SurfaceHolder::presentDummyFrame(const QSize &size)
|
|
{
|
|
if (m_surface && m_surface->supportedPixelFormats().count() > 0) {
|
|
QVideoFrame::PixelFormat pixelFormat = m_surface->supportedPixelFormats().value(0);
|
|
QVideoSurfaceFormat format(size, pixelFormat);
|
|
QVideoFrame frame(size.width() * size.height() * 4, size, size.width() * 4, pixelFormat);
|
|
|
|
if (!m_surface->isActive())
|
|
m_surface->start(format);
|
|
m_surface->present(frame);
|
|
|
|
// Have to spin an event loop or two for the surfaceFormatChanged() signal
|
|
qApp->processEvents();
|
|
}
|
|
}
|
|
|
|
class tst_QDeclarativeVideoOutput : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
tst_QDeclarativeVideoOutput();
|
|
|
|
~tst_QDeclarativeVideoOutput()
|
|
{
|
|
delete m_mappingOutput;
|
|
delete m_mappingSurface;
|
|
delete m_mappingComponent;
|
|
}
|
|
|
|
public slots:
|
|
void initTestCase();
|
|
|
|
private slots:
|
|
void fillMode();
|
|
void orientation();
|
|
void surfaceSource();
|
|
void sourceRect();
|
|
|
|
void contentRect();
|
|
void contentRect_data();
|
|
|
|
void mappingPoint();
|
|
void mappingPoint_data();
|
|
void mappingRect();
|
|
void mappingRect_data();
|
|
|
|
// XXX May be worth adding tests that the surface activeChanged signals are sent appropriately
|
|
// to holder?
|
|
|
|
private:
|
|
QQmlEngine m_engine;
|
|
QByteArray m_plainQML;
|
|
|
|
// Variables used for the mapping test
|
|
QQmlComponent *m_mappingComponent;
|
|
QObject *m_mappingOutput;
|
|
SurfaceHolder *m_mappingSurface;
|
|
|
|
void updateOutputGeometry(QObject *output);
|
|
|
|
QRectF invokeR2R(QObject *object, const char *signature, const QRectF &rect);
|
|
QPointF invokeP2P(QObject *object, const char *signature, const QPointF &point);
|
|
};
|
|
|
|
void tst_QDeclarativeVideoOutput::initTestCase()
|
|
{
|
|
m_plainQML = \
|
|
"import QtQuick 2.0\n" \
|
|
"import QtMultimedia 5.0\n" \
|
|
"VideoOutput {" \
|
|
" width: 150;" \
|
|
" height: 100;" \
|
|
"}";
|
|
|
|
// We initialize the mapping vars here
|
|
m_mappingComponent = new QQmlComponent(&m_engine);
|
|
m_mappingComponent->setData(m_plainQML, QUrl());
|
|
m_mappingSurface = new SurfaceHolder(this);
|
|
|
|
m_mappingOutput = m_mappingComponent->create();
|
|
QVERIFY(m_mappingOutput != 0);
|
|
|
|
m_mappingOutput->setProperty("source", QVariant::fromValue(static_cast<QObject*>(m_mappingSurface)));
|
|
|
|
m_mappingSurface->presentDummyFrame(QSize(200,100)); // this should start m_surface
|
|
updateOutputGeometry(m_mappingOutput);
|
|
}
|
|
|
|
Q_DECLARE_METATYPE(QDeclarativeVideoOutput::FillMode)
|
|
|
|
tst_QDeclarativeVideoOutput::tst_QDeclarativeVideoOutput()
|
|
: m_mappingComponent(0)
|
|
, m_mappingOutput(0)
|
|
, m_mappingSurface(0)
|
|
{
|
|
qRegisterMetaType<QDeclarativeVideoOutput::FillMode>();
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::fillMode()
|
|
{
|
|
QQmlComponent component(&m_engine);
|
|
component.setData(m_plainQML, QUrl());
|
|
|
|
QObject *videoOutput = component.create();
|
|
QVERIFY(videoOutput != 0);
|
|
|
|
QSignalSpy propSpy(videoOutput, SIGNAL(fillModeChanged(QDeclarativeVideoOutput::FillMode)));
|
|
|
|
// Default is preserveaspectfit
|
|
QCOMPARE(videoOutput->property("fillMode").value<QDeclarativeVideoOutput::FillMode>(), QDeclarativeVideoOutput::PreserveAspectFit);
|
|
QCOMPARE(propSpy.count(), 0);
|
|
|
|
videoOutput->setProperty("fillMode", QVariant(int(QDeclarativeVideoOutput::PreserveAspectCrop)));
|
|
QCOMPARE(videoOutput->property("fillMode").value<QDeclarativeVideoOutput::FillMode>(), QDeclarativeVideoOutput::PreserveAspectCrop);
|
|
QCOMPARE(propSpy.count(), 1);
|
|
|
|
videoOutput->setProperty("fillMode", QVariant(int(QDeclarativeVideoOutput::Stretch)));
|
|
QCOMPARE(videoOutput->property("fillMode").value<QDeclarativeVideoOutput::FillMode>(), QDeclarativeVideoOutput::Stretch);
|
|
QCOMPARE(propSpy.count(), 2);
|
|
|
|
videoOutput->setProperty("fillMode", QVariant(int(QDeclarativeVideoOutput::Stretch)));
|
|
QCOMPARE(videoOutput->property("fillMode").value<QDeclarativeVideoOutput::FillMode>(), QDeclarativeVideoOutput::Stretch);
|
|
QCOMPARE(propSpy.count(), 2);
|
|
|
|
delete videoOutput;
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::orientation()
|
|
{
|
|
QQmlComponent component(&m_engine);
|
|
component.setData(m_plainQML, QUrl());
|
|
|
|
QObject *videoOutput = component.create();
|
|
QVERIFY(videoOutput != 0);
|
|
|
|
QSignalSpy propSpy(videoOutput, SIGNAL(orientationChanged()));
|
|
|
|
// Default orientation is 0
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), 0);
|
|
QCOMPARE(propSpy.count(), 0);
|
|
|
|
videoOutput->setProperty("orientation", QVariant(90));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), 90);
|
|
QCOMPARE(propSpy.count(), 1);
|
|
|
|
videoOutput->setProperty("orientation", QVariant(180));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), 180);
|
|
QCOMPARE(propSpy.count(), 2);
|
|
|
|
videoOutput->setProperty("orientation", QVariant(270));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), 270);
|
|
QCOMPARE(propSpy.count(), 3);
|
|
|
|
videoOutput->setProperty("orientation", QVariant(360));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), 360);
|
|
QCOMPARE(propSpy.count(), 4);
|
|
|
|
// More than 360 should be fine
|
|
videoOutput->setProperty("orientation", QVariant(540));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), 540);
|
|
QCOMPARE(propSpy.count(), 5);
|
|
|
|
// Negative should be fine
|
|
videoOutput->setProperty("orientation", QVariant(-180));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), -180);
|
|
QCOMPARE(propSpy.count(), 6);
|
|
|
|
// Same value should not reemit
|
|
videoOutput->setProperty("orientation", QVariant(-180));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), -180);
|
|
QCOMPARE(propSpy.count(), 6);
|
|
|
|
// Non multiples of 90 should not work
|
|
videoOutput->setProperty("orientation", QVariant(-1));
|
|
QCOMPARE(videoOutput->property("orientation").toInt(), -180);
|
|
QCOMPARE(propSpy.count(), 6);
|
|
|
|
delete videoOutput;
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::surfaceSource()
|
|
{
|
|
QQmlComponent component(&m_engine);
|
|
component.setData(m_plainQML, QUrl());
|
|
|
|
QObject *videoOutput = component.create();
|
|
QVERIFY(videoOutput != 0);
|
|
|
|
SurfaceHolder holder(this);
|
|
|
|
QCOMPARE(holder.videoSurface(), static_cast<QAbstractVideoSurface*>(0));
|
|
|
|
videoOutput->setProperty("source", QVariant::fromValue(static_cast<QObject*>(&holder)));
|
|
|
|
QVERIFY(holder.videoSurface() != 0);
|
|
|
|
// Now we could do things with the surface..
|
|
QList<QVideoFrame::PixelFormat> formats = holder.videoSurface()->supportedPixelFormats();
|
|
QVERIFY(formats.count() > 0);
|
|
|
|
// See if we can start and stop each pixel format (..)
|
|
foreach (QVideoFrame::PixelFormat format, formats) {
|
|
QVideoSurfaceFormat surfaceFormat(QSize(200,100), format);
|
|
QVERIFY(holder.videoSurface()->isFormatSupported(surfaceFormat)); // This does kind of depend on node factories
|
|
|
|
QVERIFY(holder.videoSurface()->start(surfaceFormat));
|
|
QVERIFY(holder.videoSurface()->surfaceFormat() == surfaceFormat);
|
|
QVERIFY(holder.videoSurface()->isActive());
|
|
|
|
holder.videoSurface()->stop();
|
|
|
|
QVERIFY(!holder.videoSurface()->isActive());
|
|
}
|
|
|
|
delete videoOutput;
|
|
|
|
// This should clear the surface
|
|
QCOMPARE(holder.videoSurface(), static_cast<QAbstractVideoSurface*>(0));
|
|
|
|
// Also, creating two sources, setting them in order, and destroying the first
|
|
// should not zero holder.videoSurface()
|
|
videoOutput = component.create();
|
|
videoOutput->setProperty("source", QVariant::fromValue(static_cast<QObject*>(&holder)));
|
|
|
|
QAbstractVideoSurface *surface = holder.videoSurface();
|
|
QVERIFY(holder.videoSurface());
|
|
|
|
QObject *videoOutput2 = component.create();
|
|
QVERIFY(videoOutput2);
|
|
videoOutput2->setProperty("source", QVariant::fromValue(static_cast<QObject*>(&holder)));
|
|
QVERIFY(holder.videoSurface());
|
|
QVERIFY(holder.videoSurface() != surface); // Surface should have changed
|
|
surface = holder.videoSurface();
|
|
|
|
// Now delete first one
|
|
delete videoOutput;
|
|
QVERIFY(holder.videoSurface());
|
|
QVERIFY(holder.videoSurface() == surface); // Should not have changed surface
|
|
|
|
// Now create a second surface and assign it as the source
|
|
// The old surface holder should be zeroed
|
|
SurfaceHolder holder2(this);
|
|
videoOutput2->setProperty("source", QVariant::fromValue(static_cast<QObject*>(&holder2)));
|
|
|
|
QCOMPARE(holder.videoSurface(), static_cast<QAbstractVideoSurface*>(0));
|
|
QVERIFY(holder2.videoSurface() != 0);
|
|
|
|
// Finally a combination - set the same source to two things, then assign a new source
|
|
// to the first output - should not reset the first source
|
|
videoOutput = component.create();
|
|
videoOutput->setProperty("source", QVariant::fromValue(static_cast<QObject*>(&holder2)));
|
|
|
|
// Both vo and vo2 were pointed to holder2 - setting vo2 should not clear holder2
|
|
QVERIFY(holder2.videoSurface() != 0);
|
|
QVERIFY(holder.videoSurface() == 0);
|
|
videoOutput2->setProperty("source", QVariant::fromValue(static_cast<QObject*>(&holder)));
|
|
QVERIFY(holder2.videoSurface() != 0);
|
|
QVERIFY(holder.videoSurface() != 0);
|
|
|
|
// They should also be independent
|
|
QVERIFY(holder.videoSurface() != holder2.videoSurface());
|
|
|
|
delete videoOutput;
|
|
delete videoOutput2;
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::sourceRect()
|
|
{
|
|
QQmlComponent component(&m_engine);
|
|
component.setData(m_plainQML, QUrl());
|
|
|
|
QObject *videoOutput = component.create();
|
|
QVERIFY(videoOutput != 0);
|
|
|
|
SurfaceHolder holder(this);
|
|
|
|
QSignalSpy propSpy(videoOutput, SIGNAL(sourceRectChanged()));
|
|
|
|
videoOutput->setProperty("source", QVariant::fromValue(static_cast<QObject*>(&holder)));
|
|
|
|
QRectF invalid(0,0,-1,-1);
|
|
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), invalid);
|
|
|
|
holder.presentDummyFrame(QSize(200,100));
|
|
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
QCOMPARE(propSpy.count(), 1);
|
|
|
|
// Another frame shouldn't cause a source rect change
|
|
holder.presentDummyFrame(QSize(200,100));
|
|
QCOMPARE(propSpy.count(), 1);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
// Changing orientation and stretch modes should not affect this
|
|
videoOutput->setProperty("orientation", QVariant(90));
|
|
updateOutputGeometry(videoOutput);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
videoOutput->setProperty("orientation", QVariant(180));
|
|
updateOutputGeometry(videoOutput);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
videoOutput->setProperty("orientation", QVariant(270));
|
|
updateOutputGeometry(videoOutput);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
videoOutput->setProperty("orientation", QVariant(-90));
|
|
updateOutputGeometry(videoOutput);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
videoOutput->setProperty("fillMode", QVariant(int(QDeclarativeVideoOutput::PreserveAspectCrop)));
|
|
updateOutputGeometry(videoOutput);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
videoOutput->setProperty("fillMode", QVariant(int(QDeclarativeVideoOutput::Stretch)));
|
|
updateOutputGeometry(videoOutput);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
videoOutput->setProperty("fillMode", QVariant(int(QDeclarativeVideoOutput::Stretch)));
|
|
updateOutputGeometry(videoOutput);
|
|
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
|
|
|
|
delete videoOutput;
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::mappingPoint()
|
|
{
|
|
QFETCH(QPointF, point);
|
|
QFETCH(int, orientation);
|
|
QFETCH(QDeclarativeVideoOutput::FillMode, fillMode);
|
|
QFETCH(QPointF, expected);
|
|
|
|
QVERIFY(m_mappingOutput);
|
|
m_mappingOutput->setProperty("orientation", QVariant(orientation));
|
|
m_mappingOutput->setProperty("fillMode", QVariant::fromValue(fillMode));
|
|
|
|
updateOutputGeometry(m_mappingOutput);
|
|
|
|
QPointF output = invokeP2P(m_mappingOutput, "mapPointToItem", point);
|
|
QPointF reverse = invokeP2P(m_mappingOutput, "mapPointToSource", output);
|
|
|
|
QCOMPARE(output, expected);
|
|
QCOMPARE(reverse, point);
|
|
|
|
// Now the normalized versions
|
|
// Source rectangle is 200x100
|
|
QPointF normal(point.x() / 200, point.y() / 100);
|
|
|
|
output = invokeP2P(m_mappingOutput, "mapNormalizedPointToItem", normal);
|
|
reverse = invokeP2P(m_mappingOutput, "mapPointToSourceNormalized", output);
|
|
|
|
QCOMPARE(output, expected);
|
|
QCOMPARE(reverse, normal);
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::mappingPoint_data()
|
|
{
|
|
QTest::addColumn<QPointF>("point");
|
|
QTest::addColumn<int>("orientation");
|
|
QTest::addColumn<QDeclarativeVideoOutput::FillMode>("fillMode");
|
|
QTest::addColumn<QPointF>("expected");
|
|
|
|
QDeclarativeVideoOutput::FillMode stretch = QDeclarativeVideoOutput::Stretch;
|
|
QDeclarativeVideoOutput::FillMode fit = QDeclarativeVideoOutput::PreserveAspectFit;
|
|
QDeclarativeVideoOutput::FillMode crop = QDeclarativeVideoOutput::PreserveAspectCrop;
|
|
|
|
// First make sure the component has processed the frame
|
|
QCOMPARE(m_mappingOutput->property("sourceRect").toRectF(), QRectF(0,0,200,100));
|
|
|
|
// 200x100 -> 150,100 stretch, 150x75 fit @ 12.5f, 200x100 @-25,0 crop
|
|
|
|
// Corners, then the center, then a point in the middle somewhere
|
|
QTest::newRow("s0-0") << QPointF(0,0) << 0 << stretch << QPointF(0,0);
|
|
QTest::newRow("s1-0") << QPointF(200,0) << 0 << stretch << QPointF(150,0);
|
|
QTest::newRow("s2-0") << QPointF(0,100) << 0 << stretch << QPointF(0,100);
|
|
QTest::newRow("s3-0") << QPointF(200,100) << 0 << stretch << QPointF(150,100);
|
|
QTest::newRow("s4-0") << QPointF(100,50) << 0 << stretch << QPointF(75,50);
|
|
QTest::newRow("s5-0") << QPointF(40,80) << 0 << stretch << QPointF(30,80);
|
|
|
|
QTest::newRow("f0-0") << QPointF(0,0) << 0 << fit << QPointF(0,12.5f);
|
|
QTest::newRow("f1-0") << QPointF(200,0) << 0 << fit << QPointF(150,12.5f);
|
|
QTest::newRow("f2-0") << QPointF(0,100) << 0 << fit << QPointF(0,87.5f);
|
|
QTest::newRow("f3-0") << QPointF(200,100) << 0 << fit << QPointF(150,87.5f);
|
|
QTest::newRow("f4-0") << QPointF(100,50) << 0 << stretch << QPointF(75,50);
|
|
QTest::newRow("f5-0") << QPointF(40,80) << 0 << stretch << QPointF(30,80);
|
|
|
|
QTest::newRow("c0-0") << QPointF(0,0) << 0 << crop << QPointF(-25,0);
|
|
QTest::newRow("c1-0") << QPointF(200,0) << 0 << crop << QPointF(175,0);
|
|
QTest::newRow("c2-0") << QPointF(0,100) << 0 << crop << QPointF(-25,100);
|
|
QTest::newRow("c3-0") << QPointF(200,100) << 0 << crop << QPointF(175,100);
|
|
QTest::newRow("c4-0") << QPointF(100,50) << 0 << stretch << QPointF(75,50);
|
|
QTest::newRow("c5-0") << QPointF(40,80) << 0 << stretch << QPointF(30,80);
|
|
|
|
// 90 degrees (anti clockwise)
|
|
QTest::newRow("s0-90") << QPointF(0,0) << 90 << stretch << QPointF(0,100);
|
|
QTest::newRow("s1-90") << QPointF(200,0) << 90 << stretch << QPointF(0,0);
|
|
QTest::newRow("s2-90") << QPointF(0,100) << 90 << stretch << QPointF(150,100);
|
|
QTest::newRow("s3-90") << QPointF(200,100) << 90 << stretch << QPointF(150,0);
|
|
QTest::newRow("s4-90") << QPointF(100,50) << 90 << stretch << QPointF(75,50);
|
|
QTest::newRow("s5-90") << QPointF(40,80) << 90 << stretch << QPointF(120,80);
|
|
|
|
QTest::newRow("f0-90") << QPointF(0,0) << 90 << fit << QPointF(50,100);
|
|
QTest::newRow("f1-90") << QPointF(200,0) << 90 << fit << QPointF(50,0);
|
|
QTest::newRow("f2-90") << QPointF(0,100) << 90 << fit << QPointF(100,100);
|
|
QTest::newRow("f3-90") << QPointF(200,100) << 90 << fit << QPointF(100,0);
|
|
QTest::newRow("f4-90") << QPointF(100,50) << 90 << fit << QPointF(75,50);
|
|
QTest::newRow("f5-90") << QPointF(40,80) << 90 << fit << QPointF(90,80);
|
|
|
|
QTest::newRow("c0-90") << QPointF(0,0) << 90 << crop << QPointF(0,200);
|
|
QTest::newRow("c1-90") << QPointF(200,0) << 90 << crop << QPointF(0,-100);
|
|
QTest::newRow("c2-90") << QPointF(0,100) << 90 << crop << QPointF(150,200);
|
|
QTest::newRow("c3-90") << QPointF(200,100) << 90 << crop << QPointF(150,-100);
|
|
QTest::newRow("c4-90") << QPointF(100,50) << 90 << crop << QPointF(75,50);
|
|
QTest::newRow("c5-90") << QPointF(40,80) << 90 << crop << QPointF(120,140);
|
|
|
|
// 180
|
|
QTest::newRow("s0-180") << QPointF(0,0) << 180 << stretch << QPointF(150,100);
|
|
QTest::newRow("s1-180") << QPointF(200,0) << 180 << stretch << QPointF(0,100);
|
|
QTest::newRow("s2-180") << QPointF(0,100) << 180 << stretch << QPointF(150,0);
|
|
QTest::newRow("s3-180") << QPointF(200,100) << 180 << stretch << QPointF(0,0);
|
|
QTest::newRow("s4-180") << QPointF(100,50) << 180 << stretch << QPointF(75,50);
|
|
QTest::newRow("s5-180") << QPointF(40,80) << 180 << stretch << QPointF(120,20);
|
|
|
|
QTest::newRow("f0-180") << QPointF(0,0) << 180 << fit << QPointF(150,87.5f);
|
|
QTest::newRow("f1-180") << QPointF(200,0) << 180 << fit << QPointF(0,87.5f);
|
|
QTest::newRow("f2-180") << QPointF(0,100) << 180 << fit << QPointF(150,12.5f);
|
|
QTest::newRow("f3-180") << QPointF(200,100) << 180 << fit << QPointF(0,12.5f);
|
|
QTest::newRow("f4-180") << QPointF(100,50) << 180 << fit << QPointF(75,50);
|
|
QTest::newRow("f5-180") << QPointF(40,80) << 180 << fit << QPointF(120,27.5f);
|
|
|
|
QTest::newRow("c0-180") << QPointF(0,0) << 180 << crop << QPointF(175,100);
|
|
QTest::newRow("c1-180") << QPointF(200,0) << 180 << crop << QPointF(-25,100);
|
|
QTest::newRow("c2-180") << QPointF(0,100) << 180 << crop << QPointF(175,0);
|
|
QTest::newRow("c3-180") << QPointF(200,100) << 180 << crop << QPointF(-25,0);
|
|
QTest::newRow("c4-180") << QPointF(100,50) << 180 << crop << QPointF(75,50);
|
|
QTest::newRow("c5-180") << QPointF(40,80) << 180 << crop << QPointF(135,20);
|
|
|
|
// 270
|
|
QTest::newRow("s0-270") << QPointF(0,0) << 270 << stretch << QPointF(150,0);
|
|
QTest::newRow("s1-270") << QPointF(200,0) << 270 << stretch << QPointF(150,100);
|
|
QTest::newRow("s2-270") << QPointF(0,100) << 270 << stretch << QPointF(0,0);
|
|
QTest::newRow("s3-270") << QPointF(200,100) << 270 << stretch << QPointF(0,100);
|
|
QTest::newRow("s4-270") << QPointF(100,50) << 270 << stretch << QPointF(75,50);
|
|
QTest::newRow("s5-270") << QPointF(40,80) << 270 << stretch << QPointF(30,20);
|
|
|
|
QTest::newRow("f0-270") << QPointF(0,0) << 270 << fit << QPointF(100,0);
|
|
QTest::newRow("f1-270") << QPointF(200,0) << 270 << fit << QPointF(100,100);
|
|
QTest::newRow("f2-270") << QPointF(0,100) << 270 << fit << QPointF(50,0);
|
|
QTest::newRow("f3-270") << QPointF(200,100) << 270 << fit << QPointF(50,100);
|
|
QTest::newRow("f4-270") << QPointF(100,50) << 270 << fit << QPointF(75,50);
|
|
QTest::newRow("f5-270") << QPointF(40,80) << 270 << fit << QPointF(60,20);
|
|
|
|
QTest::newRow("c0-270") << QPointF(0,0) << 270 << crop << QPointF(150,-100);
|
|
QTest::newRow("c1-270") << QPointF(200,0) << 270 << crop << QPointF(150,200);
|
|
QTest::newRow("c2-270") << QPointF(0,100) << 270 << crop << QPointF(0,-100);
|
|
QTest::newRow("c3-270") << QPointF(200,100) << 270 << crop << QPointF(0,200);
|
|
QTest::newRow("c4-270") << QPointF(100,50) << 270 << crop << QPointF(75,50);
|
|
QTest::newRow("c5-270") << QPointF(40,80) << 270 << crop << QPointF(30,-40);
|
|
}
|
|
|
|
/* Test all rectangle mapping */
|
|
void tst_QDeclarativeVideoOutput::mappingRect()
|
|
{
|
|
QFETCH(QRectF, rect);
|
|
QFETCH(int, orientation);
|
|
QFETCH(QDeclarativeVideoOutput::FillMode, fillMode);
|
|
QFETCH(QRectF, expected);
|
|
|
|
QVERIFY(m_mappingOutput);
|
|
m_mappingOutput->setProperty("orientation", QVariant(orientation));
|
|
m_mappingOutput->setProperty("fillMode", QVariant::fromValue(fillMode));
|
|
|
|
updateOutputGeometry(m_mappingOutput);
|
|
|
|
QRectF output = invokeR2R(m_mappingOutput, "mapRectToItem", rect);
|
|
QRectF reverse = invokeR2R(m_mappingOutput, "mapRectToSource", output);
|
|
|
|
QCOMPARE(output, expected);
|
|
QCOMPARE(reverse, rect);
|
|
|
|
// Now the normalized versions
|
|
// Source rectangle is 200x100
|
|
QRectF normal(rect.x() / 200, rect.y() / 100, rect.width() / 200, rect.height() / 100);
|
|
|
|
output = invokeR2R(m_mappingOutput, "mapNormalizedRectToItem", normal);
|
|
reverse = invokeR2R(m_mappingOutput, "mapRectToSourceNormalized", output);
|
|
|
|
QCOMPARE(output, expected);
|
|
QCOMPARE(reverse, normal);
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::mappingRect_data()
|
|
{
|
|
QTest::addColumn<QRectF>("rect");
|
|
QTest::addColumn<int>("orientation");
|
|
QTest::addColumn<QDeclarativeVideoOutput::FillMode>("fillMode");
|
|
QTest::addColumn<QRectF>("expected");
|
|
|
|
// First make sure the component has processed the frame
|
|
QCOMPARE(m_mappingOutput->property("sourceRect").toRectF(), QRectF(0,0,200,100));
|
|
|
|
QDeclarativeVideoOutput::FillMode stretch = QDeclarativeVideoOutput::Stretch;
|
|
QDeclarativeVideoOutput::FillMode fit = QDeclarativeVideoOutput::PreserveAspectFit;
|
|
QDeclarativeVideoOutput::FillMode crop = QDeclarativeVideoOutput::PreserveAspectCrop;
|
|
|
|
// Full rectangle mapping
|
|
// Stretch
|
|
QTest::newRow("s0") << QRectF(0,0, 200, 100) << 0 << stretch << QRectF(0,0,150,100);
|
|
QTest::newRow("s90") << QRectF(0,0, 200, 100) << 90 << stretch << QRectF(0,0,150,100);
|
|
QTest::newRow("s180") << QRectF(0,0, 200, 100) << 180 << stretch << QRectF(0,0,150,100);
|
|
QTest::newRow("s270") << QRectF(0,0, 200, 100) << 270 << stretch << QRectF(0,0,150,100);
|
|
|
|
// Fit
|
|
QTest::newRow("f0") << QRectF(0,0, 200, 100) << 0 << fit << QRectF(0,12.5f,150,75);
|
|
QTest::newRow("f90") << QRectF(0,0, 200, 100) << 90 << fit << QRectF(50,0,50,100);
|
|
QTest::newRow("f180") << QRectF(0,0, 200, 100) << 180 << fit << QRectF(0,12.5f,150,75);
|
|
QTest::newRow("f270") << QRectF(0,0, 200, 100) << 270 << fit << QRectF(50,0,50,100);
|
|
|
|
// Crop
|
|
QTest::newRow("c0") << QRectF(0,0, 200, 100) << 0 << crop << QRectF(-25,0,200,100);
|
|
QTest::newRow("c90") << QRectF(0,0, 200, 100) << 90 << crop << QRectF(0,-100,150,300);
|
|
QTest::newRow("c180") << QRectF(0,0, 200, 100) << 180 << crop << QRectF(-25,0,200,100);
|
|
QTest::newRow("c270") << QRectF(0,0, 200, 100) << 270 << crop << QRectF(0,-100,150,300);
|
|
|
|
// Partial rectangle mapping
|
|
// Stretch
|
|
// 50-130 in x (0.25 - 0.65), 25-50 (0.25 - 0.5) in y (out of 200, 100) -> 150x100
|
|
QTest::newRow("p-s0") << QRectF(50, 25, 80, 25) << 0 << stretch << QRectF(37.5f,25,60,25);
|
|
QTest::newRow("p-s90") << QRectF(50, 25, 80, 25) << 90 << stretch << QRectF(37.5f,35,37.5f,40);
|
|
QTest::newRow("p-s180") << QRectF(50, 25, 80, 25) << 180 << stretch << QRectF(52.5f,50,60,25);
|
|
QTest::newRow("p-s270") << QRectF(50, 25, 80, 25) << 270 << stretch << QRectF(75,25,37.5f,40);
|
|
|
|
// Fit
|
|
QTest::newRow("p-f0") << QRectF(50, 25, 80, 25) << 0 << fit << QRectF(37.5f,31.25f,60,18.75f);
|
|
QTest::newRow("p-f90") << QRectF(50, 25, 80, 25) << 90 << fit << QRectF(62.5f,35,12.5f,40);
|
|
QTest::newRow("p-f180") << QRectF(50, 25, 80, 25) << 180 << fit << QRectF(52.5f,50,60,18.75f);
|
|
QTest::newRow("p-f270") << QRectF(50, 25, 80, 25) << 270 << fit << QRectF(75,25,12.5f,40);
|
|
|
|
// Crop
|
|
QTest::newRow("p-c0") << QRectF(50, 25, 80, 25) << 0 << crop << QRectF(25,25,80,25);
|
|
QTest::newRow("p-c90") << QRectF(50, 25, 80, 25) << 90 << crop << QRectF(37.5f,5,37.5f,120);
|
|
QTest::newRow("p-c180") << QRectF(50, 25, 80, 25) << 180 << crop << QRectF(45,50,80,25);
|
|
QTest::newRow("p-c270") << QRectF(50, 25, 80, 25) << 270 << crop << QRectF(75,-25,37.5f,120);
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::updateOutputGeometry(QObject *output)
|
|
{
|
|
// Since the object isn't visible, update() doesn't do anything
|
|
// so we manually force this
|
|
QMetaObject::invokeMethod(output, "_q_updateGeometry");
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::contentRect()
|
|
{
|
|
QFETCH(int, orientation);
|
|
QFETCH(QDeclarativeVideoOutput::FillMode, fillMode);
|
|
QFETCH(QRectF, expected);
|
|
|
|
QVERIFY(m_mappingOutput);
|
|
m_mappingOutput->setProperty("orientation", QVariant(orientation));
|
|
m_mappingOutput->setProperty("fillMode", QVariant::fromValue(fillMode));
|
|
|
|
updateOutputGeometry(m_mappingOutput);
|
|
|
|
QRectF output = m_mappingOutput->property("contentRect").toRectF();
|
|
QCOMPARE(output, expected);
|
|
}
|
|
|
|
void tst_QDeclarativeVideoOutput::contentRect_data()
|
|
{
|
|
QTest::addColumn<int>("orientation");
|
|
QTest::addColumn<QDeclarativeVideoOutput::FillMode>("fillMode");
|
|
QTest::addColumn<QRectF>("expected");
|
|
|
|
// First make sure the component has processed the frame
|
|
QCOMPARE(m_mappingOutput->property("sourceRect").toRectF(), QRectF(0,0,200,100));
|
|
|
|
QDeclarativeVideoOutput::FillMode stretch = QDeclarativeVideoOutput::Stretch;
|
|
QDeclarativeVideoOutput::FillMode fit = QDeclarativeVideoOutput::PreserveAspectFit;
|
|
QDeclarativeVideoOutput::FillMode crop = QDeclarativeVideoOutput::PreserveAspectCrop;
|
|
|
|
// Stretch just keeps the full render rect regardless of orientation
|
|
QTest::newRow("s0") << 0 << stretch << QRectF(0,0,150,100);
|
|
QTest::newRow("s90") << 90 << stretch << QRectF(0,0,150,100);
|
|
QTest::newRow("s180") << 180 << stretch << QRectF(0,0,150,100);
|
|
QTest::newRow("s270") << 270 << stretch << QRectF(0,0,150,100);
|
|
|
|
// Fit depends on orientation
|
|
// Source is 200x100, fitting in 150x100 -> 150x75
|
|
// or 100x200 -> 50x100
|
|
QTest::newRow("f0") << 0 << fit << QRectF(0,12.5f,150,75);
|
|
QTest::newRow("f90") << 90 << fit << QRectF(50,0,50,100);
|
|
QTest::newRow("f180") << 180 << fit << QRectF(0,12.5,150,75);
|
|
QTest::newRow("f270") << 270 << fit << QRectF(50,0,50,100);
|
|
|
|
// Crop also depends on orientation, may go outside render rect
|
|
// 200x100 -> -25,0 200x100
|
|
// 100x200 -> 0,-100 150x300
|
|
QTest::newRow("c0") << 0 << crop << QRectF(-25,0,200,100);
|
|
QTest::newRow("c90") << 90 << crop << QRectF(0,-100,150,300);
|
|
QTest::newRow("c180") << 180 << crop << QRectF(-25,0,200,100);
|
|
QTest::newRow("c270") << 270 << crop << QRectF(0,-100,150,300);
|
|
}
|
|
|
|
|
|
QRectF tst_QDeclarativeVideoOutput::invokeR2R(QObject *object, const char *signature, const QRectF &rect)
|
|
{
|
|
QRectF r;
|
|
const QMetaObject *meta = object->metaObject();
|
|
meta->invokeMethod(object, signature, Q_RETURN_ARG(QRectF, r), Q_ARG(QRectF, rect));
|
|
return r;
|
|
}
|
|
|
|
QPointF tst_QDeclarativeVideoOutput::invokeP2P(QObject *object, const char *signature, const QPointF &point)
|
|
{
|
|
QPointF p;
|
|
const QMetaObject *meta = object->metaObject();
|
|
meta->invokeMethod(object, signature, Q_RETURN_ARG(QPointF, p), Q_ARG(QPointF, point));
|
|
return p;
|
|
}
|
|
|
|
|
|
QTEST_MAIN(tst_QDeclarativeVideoOutput)
|
|
|
|
#include "tst_qdeclarativevideooutput.moc"
|