From 39c269441421e1e61fbfc698b65f769db82cad56 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Thu, 17 Apr 2014 22:50:13 +1000 Subject: Add an EGLImageKHR video node. [ChangeLog] Added a VideoNode plugin which allows direct rendering of EGLImageKHR backed video frames. Change-Id: I36fb6fd27680dbe9c71a446bbd54df95488725f8 Reviewed-by: Yoann Lopes --- src/multimedia/video/qabstractvideobuffer.cpp | 1 + src/multimedia/video/qabstractvideobuffer.h | 1 + src/plugins/videonode/egl/egl.json | 3 + src/plugins/videonode/egl/egl.pro | 17 ++ src/plugins/videonode/egl/qsgvideonode_egl.cpp | 243 +++++++++++++++++++++ src/plugins/videonode/egl/qsgvideonode_egl.h | 102 +++++++++ src/plugins/videonode/videonode.pro | 2 + .../qdeclarativevideooutput_render.cpp | 2 +- 8 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 src/plugins/videonode/egl/egl.json create mode 100644 src/plugins/videonode/egl/egl.pro create mode 100644 src/plugins/videonode/egl/qsgvideonode_egl.cpp create mode 100644 src/plugins/videonode/egl/qsgvideonode_egl.h (limited to 'src') diff --git a/src/multimedia/video/qabstractvideobuffer.cpp b/src/multimedia/video/qabstractvideobuffer.cpp index 5f29a01a1..3e0643479 100644 --- a/src/multimedia/video/qabstractvideobuffer.cpp +++ b/src/multimedia/video/qabstractvideobuffer.cpp @@ -102,6 +102,7 @@ int QAbstractVideoBufferPrivate::map( \value XvShmImageHandle The handle contains pointer to shared memory XVideo image. \value CoreImageHandle The handle contains pointer to Mac OS X CIImage. \value QPixmapHandle The handle of the buffer is a QPixmap. + \value EGLImageHandle The handle of the buffer is an EGLImageKHR. \value UserHandle Start value for user defined handle types. \sa handleType() diff --git a/src/multimedia/video/qabstractvideobuffer.h b/src/multimedia/video/qabstractvideobuffer.h index 7f3edf569..25d70e4ba 100644 --- a/src/multimedia/video/qabstractvideobuffer.h +++ b/src/multimedia/video/qabstractvideobuffer.h @@ -65,6 +65,7 @@ public: XvShmImageHandle, CoreImageHandle, QPixmapHandle, + EGLImageHandle, UserHandle = 1000 }; diff --git a/src/plugins/videonode/egl/egl.json b/src/plugins/videonode/egl/egl.json new file mode 100644 index 000000000..08bb12c3f --- /dev/null +++ b/src/plugins/videonode/egl/egl.json @@ -0,0 +1,3 @@ +{ + "Keys": ["sgvideonodes"] +} diff --git a/src/plugins/videonode/egl/egl.pro b/src/plugins/videonode/egl/egl.pro new file mode 100644 index 000000000..a6256cea1 --- /dev/null +++ b/src/plugins/videonode/egl/egl.pro @@ -0,0 +1,17 @@ +TARGET = eglvideonode +QT += multimedia-private qtmultimediaquicktools-private +CONFIG += egl + +PLUGIN_TYPE=video/videonode +PLUGIN_EXTENDS = quick +PLUGIN_CLASS_NAME = QSGVideoNodeFactory_EGL +load(qt_plugin) + +HEADERS += \ + qsgvideonode_egl.h + +SOURCES += \ + qsgvideonode_egl.cpp + +OTHER_FILES += \ + egl.json diff --git a/src/plugins/videonode/egl/qsgvideonode_egl.cpp b/src/plugins/videonode/egl/qsgvideonode_egl.cpp new file mode 100644 index 000000000..33e92a963 --- /dev/null +++ b/src/plugins/videonode/egl/qsgvideonode_egl.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd. +** 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$ +** +****************************************************************************/ + +#include "qsgvideonode_egl.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QSGVideoMaterial_EGLShader : public QSGMaterialShader +{ +public: + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + char const *const *attributeNames() const; + + static QSGMaterialType type; + +protected: + void initialize(); + + const char *vertexShader() const; + const char *fragmentShader() const; + +private: + int id_matrix; + int id_opacity; + int id_texture; +}; + +void QSGVideoMaterial_EGLShader::updateState( + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + QSGVideoMaterial_EGL *material = static_cast(newEffect); + if (!oldEffect) { + program()->setUniformValue(id_texture, 0); + } + + if (state.isMatrixDirty()) { + program()->setUniformValue(id_matrix, state.combinedMatrix()); + } + + if (state.isOpacityDirty()) { + program()->setUniformValue(id_opacity, state.opacity()); + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, material->m_textureId); +} + +char const *const *QSGVideoMaterial_EGLShader::attributeNames() const +{ + static char const *const attr[] = { "position", "texcoord", 0 }; + return attr; +} + +QSGMaterialType QSGVideoMaterial_EGLShader::type; + +void QSGVideoMaterial_EGLShader::initialize() +{ + id_matrix = program()->uniformLocation("matrix"); + id_opacity = program()->uniformLocation("opacity"); + id_texture = program()->uniformLocation("texture"); +} + +const char *QSGVideoMaterial_EGLShader::vertexShader() const +{ + return "\n uniform highp mat4 matrix;" + "\n attribute highp vec4 position;" + "\n attribute highp vec2 texcoord;" + "\n varying highp vec2 frag_tx;" + "\n void main(void)" + "\n {" + "\n gl_Position = matrix * position;" + "\n frag_tx = texcoord;" + "\n }"; +} + +const char *QSGVideoMaterial_EGLShader::fragmentShader() const +{ + return "\n #extension GL_OES_EGL_image_external : require" + "\n uniform samplerExternalOES texture;" + "\n varying highp vec2 frag_tx;" + "\n void main(void)" + "\n {" + "\n gl_FragColor = texture2D(texture, frag_tx.st);" + "\n }"; +} + +QSGVideoMaterial_EGL::QSGVideoMaterial_EGL() + : m_image(0) + , m_textureId(0) +{ +} + +QSGVideoMaterial_EGL::~QSGVideoMaterial_EGL() +{ + if (m_textureId) { + glDeleteTextures(1, &m_textureId); + m_textureId = 0; + } +} + +QSGMaterialShader *QSGVideoMaterial_EGL::createShader() const +{ + return new QSGVideoMaterial_EGLShader; +} + +QSGMaterialType *QSGVideoMaterial_EGL::type() const +{ + return &QSGVideoMaterial_EGLShader::type; +} + +int QSGVideoMaterial_EGL::compare(const QSGMaterial *other) const +{ + return m_textureId - static_cast(other)->m_textureId; +} + +void QSGVideoMaterial_EGL::setImage(EGLImageKHR image) +{ + static const PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES + = reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + + if (m_image == image || !glEGLImageTargetTexture2DOES) + return; + + m_image = image; + + if (!m_image) { + if (m_textureId) { + glDeleteTextures(1, &m_textureId); + m_textureId = 0; + } + } else { + if (!m_textureId) { + glGenTextures(1, &m_textureId); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_textureId); + glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_textureId); + } + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, m_image); + } +} + +QSGVideoNode_EGL::QSGVideoNode_EGL(const QVideoSurfaceFormat &format) + : m_pixelFormat(format.pixelFormat()) +{ + setMaterial(&m_material); +} + +QSGVideoNode_EGL::~QSGVideoNode_EGL() +{ +} + +void QSGVideoNode_EGL::setCurrentFrame(const QVideoFrame &frame) +{ + EGLImageKHR image = frame.handle().value(); + m_material.setImage(image); + markDirty(DirtyMaterial); +} + +QVideoFrame::PixelFormat QSGVideoNode_EGL::pixelFormat() const +{ + return m_pixelFormat; +} + +static bool isExtensionSupported() +{ + static const bool supported = eglGetProcAddress("glEGLImageTargetTexture2DOES"); + return supported; +} + +QList QSGVideoNodeFactory_EGL::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + if (handleType != QAbstractVideoBuffer::EGLImageHandle || !isExtensionSupported()) + return QList(); + + return QList() + << QVideoFrame::Format_Invalid + << QVideoFrame::Format_YV12 + << QVideoFrame::Format_UYVY + << QVideoFrame::Format_NV21 + << QVideoFrame::Format_YUYV + << QVideoFrame::Format_RGB32 + << QVideoFrame::Format_BGR32 + << QVideoFrame::Format_RGB24 + << QVideoFrame::Format_BGR24 + << QVideoFrame::Format_RGB565 + << QVideoFrame::Format_BGR565; +} + +QSGVideoNode *QSGVideoNodeFactory_EGL::createNode(const QVideoSurfaceFormat &format) +{ + return format.handleType() == QAbstractVideoBuffer::EGLImageHandle && isExtensionSupported() + ? new QSGVideoNode_EGL(format) + : 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/videonode/egl/qsgvideonode_egl.h b/src/plugins/videonode/egl/qsgvideonode_egl.h new file mode 100644 index 000000000..23294a123 --- /dev/null +++ b/src/plugins/videonode/egl/qsgvideonode_egl.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd. +** 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$ +** +****************************************************************************/ + +#ifndef EGLVIDEONODE_H +#define EGLVIDEONODE_H + +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGVideoMaterial_EGL : public QSGMaterial +{ +public: + QSGVideoMaterial_EGL(); + ~QSGVideoMaterial_EGL(); + + QSGMaterialShader *createShader() const; + QSGMaterialType *type() const; + int compare(const QSGMaterial *other) const; + + void setImage(EGLImageKHR image); + +private: + friend class QSGVideoMaterial_EGLShader; + + QRectF m_subrect; + EGLImageKHR m_image; + GLuint m_textureId; +}; + +class QSGVideoNode_EGL : public QSGVideoNode +{ +public: + QSGVideoNode_EGL(const QVideoSurfaceFormat &format); + ~QSGVideoNode_EGL(); + + void setCurrentFrame(const QVideoFrame &frame); + QVideoFrame::PixelFormat pixelFormat() const; + +private: + QSGVideoMaterial_EGL m_material; + QVideoFrame::PixelFormat m_pixelFormat; +}; + +class QSGVideoNodeFactory_EGL : public QSGVideoNodeFactoryPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.sgvideonodefactory/5.2" FILE "egl.json") +public: + QList supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const; + QSGVideoNode *createNode(const QVideoSurfaceFormat &format); +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/plugins/videonode/videonode.pro b/src/plugins/videonode/videonode.pro index c311200d3..e38b0a6a0 100644 --- a/src/plugins/videonode/videonode.pro +++ b/src/plugins/videonode/videonode.pro @@ -3,3 +3,5 @@ TEMPLATE = subdirs config_gpu_vivante { SUBDIRS += imx6 } + +contains(QT_CONFIG, egl):contains(QT_CONFIG, opengles2):!android: SUBDIRS += egl diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp index 647732485..46dfc9468 100644 --- a/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp @@ -321,7 +321,7 @@ QList QSGVideoItemSurface::supportedPixelFormats( bool QSGVideoItemSurface::start(const QVideoSurfaceFormat &format) { #ifdef DEBUG_VIDEOITEM - qDebug() << Q_FUNC_INFO << format; + qDebug() << Q_FUNC_INFO << format << supportedPixelFormats(format.handleType()); #endif if (!supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) -- cgit v1.2.3