Add new property to QVideoSurfaceFormat.

The 'mirrored' property indicates the QVideoFrames need to be mirrored
along their vertical axis. This is typically needed for video frames
coming from a front camera on a mobile device.

This is implemented as a string-based property. In Qt 5.6, this should
be replaced by a new public function.

Change-Id: Ideb7de81e83f66826f4efb5f2951c4beec13546b
Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
This commit is contained in:
Yoann Lopes
2015-06-03 14:19:10 +02:00
parent bc9cfcd08a
commit 3c54acb6f7
4 changed files with 49 additions and 15 deletions

View File

@@ -61,6 +61,7 @@ public:
, pixelAspectRatio(1, 1) , pixelAspectRatio(1, 1)
, ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined) , ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined)
, frameRate(0.0) , frameRate(0.0)
, mirrored(false)
{ {
} }
@@ -76,6 +77,7 @@ public:
, ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined) , ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined)
, viewport(QPoint(0, 0), size) , viewport(QPoint(0, 0), size)
, frameRate(0.0) , frameRate(0.0)
, mirrored(false)
{ {
} }
@@ -89,6 +91,7 @@ public:
, ycbcrColorSpace(other.ycbcrColorSpace) , ycbcrColorSpace(other.ycbcrColorSpace)
, viewport(other.viewport) , viewport(other.viewport)
, frameRate(other.frameRate) , frameRate(other.frameRate)
, mirrored(other.mirrored)
, propertyNames(other.propertyNames) , propertyNames(other.propertyNames)
, propertyValues(other.propertyValues) , propertyValues(other.propertyValues)
{ {
@@ -104,6 +107,7 @@ public:
&& viewport == other.viewport && viewport == other.viewport
&& frameRatesEqual(frameRate, other.frameRate) && frameRatesEqual(frameRate, other.frameRate)
&& ycbcrColorSpace == other.ycbcrColorSpace && ycbcrColorSpace == other.ycbcrColorSpace
&& mirrored == other.mirrored
&& propertyNames.count() == other.propertyNames.count()) { && propertyNames.count() == other.propertyNames.count()) {
for (int i = 0; i < propertyNames.count(); ++i) { for (int i = 0; i < propertyNames.count(); ++i) {
int j = other.propertyNames.indexOf(propertyNames.at(i)); int j = other.propertyNames.indexOf(propertyNames.at(i));
@@ -130,6 +134,7 @@ public:
QVideoSurfaceFormat::YCbCrColorSpace ycbcrColorSpace; QVideoSurfaceFormat::YCbCrColorSpace ycbcrColorSpace;
QRect viewport; QRect viewport;
qreal frameRate; qreal frameRate;
bool mirrored;
QList<QByteArray> propertyNames; QList<QByteArray> propertyNames;
QList<QVariant> propertyValues; QList<QVariant> propertyValues;
}; };
@@ -468,7 +473,8 @@ QList<QByteArray> QVideoSurfaceFormat::propertyNames() const
<< "frameRate" << "frameRate"
<< "pixelAspectRatio" << "pixelAspectRatio"
<< "sizeHint" << "sizeHint"
<< "yCbCrColorSpace") << "yCbCrColorSpace"
<< "mirrored")
+ d->propertyNames; + d->propertyNames;
} }
@@ -499,6 +505,8 @@ QVariant QVideoSurfaceFormat::property(const char *name) const
return sizeHint(); return sizeHint();
} else if (qstrcmp(name, "yCbCrColorSpace") == 0) { } else if (qstrcmp(name, "yCbCrColorSpace") == 0) {
return qVariantFromValue(d->ycbcrColorSpace); return qVariantFromValue(d->ycbcrColorSpace);
} else if (qstrcmp(name, "mirrored") == 0) {
return d->mirrored;
} else { } else {
int id = 0; int id = 0;
for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {} for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {}
@@ -547,6 +555,9 @@ void QVideoSurfaceFormat::setProperty(const char *name, const QVariant &value)
} else if (qstrcmp(name, "yCbCrColorSpace") == 0) { } else if (qstrcmp(name, "yCbCrColorSpace") == 0) {
if (value.canConvert<YCbCrColorSpace>()) if (value.canConvert<YCbCrColorSpace>())
d->ycbcrColorSpace = qvariant_cast<YCbCrColorSpace>(value); d->ycbcrColorSpace = qvariant_cast<YCbCrColorSpace>(value);
} else if (qstrcmp(name, "mirrored") == 0) {
if (value.canConvert<bool>())
d->mirrored = qvariant_cast<bool>(value);
} else { } else {
int id = 0; int id = 0;
for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {} for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {}

View File

@@ -86,11 +86,13 @@ private:
QSize m_imageSize; QSize m_imageSize;
QImage::Format m_imageFormat; QImage::Format m_imageFormat;
QVideoSurfaceFormat::Direction m_scanLineDirection; QVideoSurfaceFormat::Direction m_scanLineDirection;
bool m_mirrored;
}; };
QVideoSurfaceGenericPainter::QVideoSurfaceGenericPainter() QVideoSurfaceGenericPainter::QVideoSurfaceGenericPainter()
: m_imageFormat(QImage::Format_Invalid) : m_imageFormat(QImage::Format_Invalid)
, m_scanLineDirection(QVideoSurfaceFormat::TopToBottom) , m_scanLineDirection(QVideoSurfaceFormat::TopToBottom)
, m_mirrored(false)
{ {
m_imagePixelFormats << QVideoFrame::Format_RGB32; m_imagePixelFormats << QVideoFrame::Format_RGB32;
@@ -137,6 +139,7 @@ QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::start(const QVideoSurf
m_imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat()); m_imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
m_imageSize = format.frameSize(); m_imageSize = format.frameSize();
m_scanLineDirection = format.scanLineDirection(); m_scanLineDirection = format.scanLineDirection();
m_mirrored = format.property("mirrored").toBool();
const QAbstractVideoBuffer::HandleType t = format.handleType(); const QAbstractVideoBuffer::HandleType t = format.handleType();
if (t == QAbstractVideoBuffer::NoHandle) { if (t == QAbstractVideoBuffer::NoHandle) {
@@ -183,17 +186,22 @@ QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::paint(
m_frame.bytesPerLine(), m_frame.bytesPerLine(),
m_imageFormat); m_imageFormat);
if (m_scanLineDirection == QVideoSurfaceFormat::BottomToTop) {
const QTransform oldTransform = painter->transform(); const QTransform oldTransform = painter->transform();
QTransform transform = oldTransform;
painter->scale(1, -1); QRectF targetRect = target;
painter->translate(0, -target.bottom()); if (m_scanLineDirection == QVideoSurfaceFormat::BottomToTop) {
painter->drawImage( transform.scale(1, -1);
QRectF(target.x(), 0, target.width(), target.height()), image, source); transform.translate(0, -target.bottom());
painter->setTransform(oldTransform); targetRect.setY(0);
} else {
painter->drawImage(target, image, source);
} }
if (m_mirrored) {
transform.scale(-1, 1);
transform.translate(-target.right(), 0);
targetRect.setX(0);
}
painter->setTransform(transform);
painter->drawImage(targetRect, image, source);
painter->setTransform(oldTransform);
m_frame.unmap(); m_frame.unmap();
} else if (m_frame.isValid()) { } else if (m_frame.isValid()) {
@@ -281,6 +289,7 @@ protected:
QGLContext *m_context; QGLContext *m_context;
QAbstractVideoBuffer::HandleType m_handleType; QAbstractVideoBuffer::HandleType m_handleType;
QVideoSurfaceFormat::Direction m_scanLineDirection; QVideoSurfaceFormat::Direction m_scanLineDirection;
bool m_mirrored;
QVideoSurfaceFormat::YCbCrColorSpace m_colorSpace; QVideoSurfaceFormat::YCbCrColorSpace m_colorSpace;
GLenum m_textureFormat; GLenum m_textureFormat;
GLuint m_textureInternalFormat; GLuint m_textureInternalFormat;
@@ -299,6 +308,7 @@ QVideoSurfaceGLPainter::QVideoSurfaceGLPainter(QGLContext *context)
: m_context(context) : m_context(context)
, m_handleType(QAbstractVideoBuffer::NoHandle) , m_handleType(QAbstractVideoBuffer::NoHandle)
, m_scanLineDirection(QVideoSurfaceFormat::TopToBottom) , m_scanLineDirection(QVideoSurfaceFormat::TopToBottom)
, m_mirrored(false)
, m_colorSpace(QVideoSurfaceFormat::YCbCr_BT601) , m_colorSpace(QVideoSurfaceFormat::YCbCr_BT601)
, m_textureFormat(0) , m_textureFormat(0)
, m_textureInternalFormat(0) , m_textureInternalFormat(0)
@@ -829,6 +839,7 @@ QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::start(const QVideoSurfac
} else { } else {
m_handleType = format.handleType(); m_handleType = format.handleType();
m_scanLineDirection = format.scanLineDirection(); m_scanLineDirection = format.scanLineDirection();
m_mirrored = format.property("mirrored").toBool();
m_frameSize = format.frameSize(); m_frameSize = format.frameSize();
m_colorSpace = format.yCbCrColorSpace(); m_colorSpace = format.yCbCrColorSpace();
@@ -878,8 +889,10 @@ QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::paint(
if (scissorTestEnabled) if (scissorTestEnabled)
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
const float txLeft = source.left() / m_frameSize.width(); const float txLeft = m_mirrored ? source.right() / m_frameSize.width()
const float txRight = source.right() / m_frameSize.width(); : source.left() / m_frameSize.width();
const float txRight = m_mirrored ? source.left() / m_frameSize.width()
: source.right() / m_frameSize.width();
const float txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom const float txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
? source.top() / m_frameSize.height() ? source.top() / m_frameSize.height()
: source.bottom() / m_frameSize.height(); : source.bottom() / m_frameSize.height();
@@ -1188,6 +1201,7 @@ QAbstractVideoSurface::Error QVideoSurfaceGlslPainter::start(const QVideoSurface
} else { } else {
m_handleType = format.handleType(); m_handleType = format.handleType();
m_scanLineDirection = format.scanLineDirection(); m_scanLineDirection = format.scanLineDirection();
m_mirrored = format.property("mirrored").toBool();
m_frameSize = format.frameSize(); m_frameSize = format.frameSize();
m_colorSpace = format.yCbCrColorSpace(); m_colorSpace = format.yCbCrColorSpace();
@@ -1276,8 +1290,10 @@ QAbstractVideoSurface::Error QVideoSurfaceGlslPainter::paint(
GLfloat(target.right() + 1), GLfloat(target.top()) GLfloat(target.right() + 1), GLfloat(target.top())
}; };
const GLfloat txLeft = source.left() / m_frameSize.width(); const GLfloat txLeft = m_mirrored ? source.right() / m_frameSize.width()
const GLfloat txRight = source.right() / m_frameSize.width(); : source.left() / m_frameSize.width();
const GLfloat txRight = m_mirrored ? source.left() / m_frameSize.width()
: source.right() / m_frameSize.width();
const GLfloat txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom const GLfloat txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
? source.top() / m_frameSize.height() ? source.top() / m_frameSize.height()
: source.bottom() / m_frameSize.height(); : source.bottom() / m_frameSize.height();

View File

@@ -255,6 +255,12 @@ void QDeclarativeVideoRendererBackend::updateGeometry()
m_sourceTextureRect.setTop(m_sourceTextureRect.bottom()); m_sourceTextureRect.setTop(m_sourceTextureRect.bottom());
m_sourceTextureRect.setBottom(top); m_sourceTextureRect.setBottom(top);
} }
if (videoSurface()->surfaceFormat().property("mirrored").toBool()) {
qreal left = m_sourceTextureRect.left();
m_sourceTextureRect.setLeft(m_sourceTextureRect.right());
m_sourceTextureRect.setRight(left);
}
} }
QSGNode *QDeclarativeVideoRendererBackend::updatePaintNode(QSGNode *oldNode, QSGNode *QDeclarativeVideoRendererBackend::updatePaintNode(QSGNode *oldNode,

View File

@@ -521,7 +521,8 @@ void tst_QVideoSurfaceFormat::staticPropertyNames()
QVERIFY(propertyNames.contains("pixelAspectRatio")); QVERIFY(propertyNames.contains("pixelAspectRatio"));
QVERIFY(propertyNames.contains("yCbCrColorSpace")); QVERIFY(propertyNames.contains("yCbCrColorSpace"));
QVERIFY(propertyNames.contains("sizeHint")); QVERIFY(propertyNames.contains("sizeHint"));
QCOMPARE(propertyNames.count(), 10); QVERIFY(propertyNames.contains("mirrored"));
QCOMPARE(propertyNames.count(), 11);
} }
void tst_QVideoSurfaceFormat::dynamicProperty() void tst_QVideoSurfaceFormat::dynamicProperty()