Add an EGLImageKHR video node.
authorAndrew den Exter <andrew.den.exter@jollamobile.com>
Thu, 17 Apr 2014 12:50:13 +0000 (22:50 +1000)
committerYoann Lopes <yoann.lopes@digia.com>
Fri, 18 Jul 2014 14:03:52 +0000 (16:03 +0200)
[ChangeLog] Added a VideoNode plugin which allows direct rendering of
EGLImageKHR backed video frames.

Change-Id: I36fb6fd27680dbe9c71a446bbd54df95488725f8
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
src/multimedia/video/qabstractvideobuffer.cpp
src/multimedia/video/qabstractvideobuffer.h
src/plugins/videonode/egl/egl.json [new file with mode: 0644]
src/plugins/videonode/egl/egl.pro [new file with mode: 0644]
src/plugins/videonode/egl/qsgvideonode_egl.cpp [new file with mode: 0644]
src/plugins/videonode/egl/qsgvideonode_egl.h [new file with mode: 0644]
src/plugins/videonode/videonode.pro
src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp

index 5f29a01..3e06434 100644 (file)
@@ -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()
index 7f3edf5..25d70e4 100644 (file)
@@ -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 (file)
index 0000000..08bb12c
--- /dev/null
@@ -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 (file)
index 0000000..a6256ce
--- /dev/null
@@ -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 (file)
index 0000000..33e92a9
--- /dev/null
@@ -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 <QtMultimedia/qvideosurfaceformat.h>
+
+#include <GLES2/gl2ext.h>
+
+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<QSGVideoMaterial_EGL *>(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<const QSGVideoMaterial_EGL *>(other)->m_textureId;
+}
+
+void QSGVideoMaterial_EGL::setImage(EGLImageKHR image)
+{
+    static const PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES
+            = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(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<void *>();
+    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<QVideoFrame::PixelFormat> QSGVideoNodeFactory_EGL::supportedPixelFormats(
+        QAbstractVideoBuffer::HandleType handleType) const
+{
+    if (handleType != QAbstractVideoBuffer::EGLImageHandle || !isExtensionSupported())
+        return QList<QVideoFrame::PixelFormat>();
+
+    return QList<QVideoFrame::PixelFormat>()
+            << 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 (file)
index 0000000..23294a1
--- /dev/null
@@ -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 <private/qsgvideonode_p.h>
+
+#include <QSGOpaqueTextureMaterial>
+#include <QSGTexture>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+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<QVideoFrame::PixelFormat> supportedPixelFormats(
+            QAbstractVideoBuffer::HandleType handleType) const;
+    QSGVideoNode *createNode(const QVideoSurfaceFormat &format);
+};
+
+QT_END_NAMESPACE
+
+#endif
+
index c311200..e38b0a6 100644 (file)
@@ -3,3 +3,5 @@ TEMPLATE = subdirs
 config_gpu_vivante {
     SUBDIRS += imx6
 }
+
+contains(QT_CONFIG, egl):contains(QT_CONFIG, opengles2):!android: SUBDIRS += egl
index 6477324..46dfc94 100644 (file)
@@ -321,7 +321,7 @@ QList<QVideoFrame::PixelFormat> 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()))