Updated I420 video node to be consistent with RGB node implementation.
authorDmytro Poplavskiy <dmytro.poplavskiy@nokia.com>
Thu, 24 Nov 2011 04:23:45 +0000 (14:23 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 24 Nov 2011 05:41:47 +0000 (06:41 +0100)
Don't keep the frame after it was uploaded to texture;
Upload frame data during rendering (in material shader updateState)
instead of updatePaintNode().

Change-Id: Ia6a4063bfa02d4d20f75735649fe4f777cf50494
Reviewed-by: Michael Goddard <michael.goddard@nokia.com>
Reviewed-by: Jonas Rabbe <jonas.rabbe@nokia.com>
src/imports/multimedia/qsgvideonode_i420.cpp
src/imports/multimedia/qsgvideonode_i420.h

index e71ea6e..34a27fd 100644 (file)
@@ -39,6 +39,7 @@
 **
 ****************************************************************************/
 #include "qsgvideonode_i420.h"
+#include <QtCore/qmutex.h>
 #include <QtDeclarative/qsgtexturematerial.h>
 #include <QtDeclarative/qsgmaterial.h>
 #include <QtGui/QOpenGLContext>
@@ -136,34 +137,8 @@ protected:
 class QSGVideoMaterial_YUV420 : public QSGMaterial
 {
 public:
-    QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format)
-    {
-        switch (format.yCbCrColorSpace()) {
-        case QVideoSurfaceFormat::YCbCr_JPEG:
-            colorMatrix = QMatrix4x4(
-                        1.0,  0.000,  1.402, -0.701,
-                        1.0, -0.344, -0.714,  0.529,
-                        1.0,  1.772,  0.000, -0.886,
-                        0.0,  0.000,  0.000,  1.0000);
-            break;
-        case QVideoSurfaceFormat::YCbCr_BT709:
-        case QVideoSurfaceFormat::YCbCr_xvYCC709:
-            colorMatrix = QMatrix4x4(
-                        1.164,  0.000,  1.793, -0.5727,
-                        1.164, -0.534, -0.213,  0.3007,
-                        1.164,  2.115,  0.000, -1.1302,
-                        0.0,    0.000,  0.000,  1.0000);
-            break;
-        default: //BT 601:
-            colorMatrix = QMatrix4x4(
-                        1.164,  0.000,  1.596, -0.8708,
-                        1.164, -0.392, -0.813,  0.5296,
-                        1.164,  2.017,  0.000, -1.081,
-                        0.0,    0.000,  0.000,  1.0000);
-        }
-
-        setFlag(Blending, false);
-    }
+    QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format);
+    ~QSGVideoMaterial_YUV420();
 
     virtual QSGMaterialType *type() const {
         static QSGMaterialType theType;
@@ -176,87 +151,132 @@ public:
 
     virtual int compare(const QSGMaterial *other) const {
         const QSGVideoMaterial_YUV420 *m = static_cast<const QSGVideoMaterial_YUV420 *>(other);
-        int d = idY - m->idY;
+        int d = m_textureIds[0] - m->m_textureIds[0];
         if (d)
             return d;
-        else if ((d = idU - m->idU) != 0)
+        else if ((d = m_textureIds[1] - m->m_textureIds[1]) != 0)
             return d;
         else
-            return idV - m->idV;
+            return m_textureIds[2] - m->m_textureIds[2];
     }
 
     void updateBlending() {
-        setFlag(Blending, qFuzzyCompare(opacity, 1.0) ? false : true);
+        setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true);
     }
 
-    GLuint idY;
-    GLuint idU;
-    GLuint idV;
-    qreal opacity;
-    QMatrix4x4 colorMatrix;
-};
+    void setCurrentFrame(const QVideoFrame &frame) {
+        QMutexLocker lock(&m_frameMutex);
+        m_frame = frame;
+    }
 
+    void bind();
+    void bindTexture(int id, int w, int h, const uchar *bits);
 
-QSGVideoNode_I420::QSGVideoNode_I420(const QVideoSurfaceFormat &format) :
-    m_width(0),
-    m_height(0),
-    m_format(format)
+    QVideoSurfaceFormat m_format;
+    QSize m_textureSize;
+    GLuint m_textureIds[3];
+
+    qreal m_opacity;
+    QMatrix4x4 m_colorMatrix;
+
+    QVideoFrame m_frame;
+    QMutex m_frameMutex;
+};
+
+QSGVideoMaterial_YUV420::QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format) :
+    m_format(format),
+    m_opacity(1.0)
 {
-    m_material = new QSGVideoMaterial_YUV420(format);
-    setMaterial(m_material);
-    m_material->opacity = 1;
+    switch (format.yCbCrColorSpace()) {
+    case QVideoSurfaceFormat::YCbCr_JPEG:
+        m_colorMatrix = QMatrix4x4(
+                    1.0,  0.000,  1.402, -0.701,
+                    1.0, -0.344, -0.714,  0.529,
+                    1.0,  1.772,  0.000, -0.886,
+                    0.0,  0.000,  0.000,  1.0000);
+        break;
+    case QVideoSurfaceFormat::YCbCr_BT709:
+    case QVideoSurfaceFormat::YCbCr_xvYCC709:
+        m_colorMatrix = QMatrix4x4(
+                    1.164,  0.000,  1.793, -0.5727,
+                    1.164, -0.534, -0.213,  0.3007,
+                    1.164,  2.115,  0.000, -1.1302,
+                    0.0,    0.000,  0.000,  1.0000);
+        break;
+    default: //BT 601:
+        m_colorMatrix = QMatrix4x4(
+                    1.164,  0.000,  1.596, -0.8708,
+                    1.164, -0.392, -0.813,  0.5296,
+                    1.164,  2.017,  0.000, -1.081,
+                    0.0,    0.000,  0.000,  1.0000);
+    }
+
+    setFlag(Blending, false);
 }
 
-QSGVideoNode_I420::~QSGVideoNode_I420()
+QSGVideoMaterial_YUV420::~QSGVideoMaterial_YUV420()
 {
-    if (m_width != 0 && m_height != 0)
-        glDeleteTextures(3, m_id);
+    if (!m_textureSize.isEmpty())
+        glDeleteTextures(3, m_textureIds);
 }
 
-void QSGVideoNode_I420::setCurrentFrame(const QVideoFrame &frame)
+void QSGVideoMaterial_YUV420::bind()
 {
-    m_frame = frame;
+    QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
 
-    m_frame.map(QAbstractVideoBuffer::ReadOnly);
+    QMutexLocker lock(&m_frameMutex);
+    if (m_frame.isValid() && m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
+        bool wasMapped = m_frame.isMapped();
 
-    int fw = frame.width();
-    int fh = frame.height();
+        if (!wasMapped)
+            m_frame.map(QAbstractVideoBuffer::ReadOnly);
 
-    // Frame has changed size, recreate textures...
-    if (fw != m_width || fh != m_height) {
-        if (m_width != 0 && m_height != 0)
-            glDeleteTextures(3, m_id);
-        glGenTextures(3, m_id);
-        m_width = fw;
-        m_height = fh;
+        if (m_frame.isMapped()) {
+            int fw = m_frame.width();
+            int fh = m_frame.height();
 
-        m_material->idY = m_id[0];
-        m_material->idU = m_id[1];
-        m_material->idV = m_id[2];
-    }
+            // Frame has changed size, recreate textures...
+            if (m_textureSize != m_frame.size()) {
+                if (!m_textureSize.isEmpty())
+                    glDeleteTextures(3, m_textureIds);
+                glGenTextures(3, m_textureIds);
+                m_textureSize = m_frame.size();
+            }
 
-    const uchar *bits = frame.bits();
-    int bpl = frame.bytesPerLine();
-    int bpl2 = (bpl / 2 + 3) & ~3;
-    int offsetU = bpl * fh;
-    int offsetV = bpl * fh + bpl2 * fh / 2;
+            const uchar *bits = m_frame.bits();
+            int bpl = m_frame.bytesPerLine();
+            int bpl2 = (bpl / 2 + 3) & ~3;
+            int offsetU = bpl * fh;
+            int offsetV = bpl * fh + bpl2 * fh / 2;
 
-    if (m_frame.pixelFormat() == QVideoFrame::Format_YV12)
-        qSwap(offsetU, offsetV);
+            if (m_frame.pixelFormat() == QVideoFrame::Format_YV12)
+                qSwap(offsetU, offsetV);
 
-    bindTexture(m_id[0], GL_TEXTURE0, fw, fh, bits);
-    bindTexture(m_id[1], GL_TEXTURE1, fw/2, fh / 2, bits + offsetU);
-    bindTexture(m_id[2], GL_TEXTURE2, fw/2, fh / 2, bits + offsetV);
 
-    m_frame.unmap();
+            functions->glActiveTexture(GL_TEXTURE1);
+            bindTexture(m_textureIds[1], fw/2, fh / 2, bits + offsetU);
+            functions->glActiveTexture(GL_TEXTURE2);
+            bindTexture(m_textureIds[2], fw/2, fh / 2, bits + offsetV);
+            functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
+            bindTexture(m_textureIds[0], fw, fh, bits);
 
-    markDirty(DirtyMaterial);
+            if (!wasMapped)
+                m_frame.unmap();
+        }
+
+        m_frame = QVideoFrame();
+    } else {
+        functions->glActiveTexture(GL_TEXTURE1);
+        glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
+        functions->glActiveTexture(GL_TEXTURE2);
+        glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+        functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
+        glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+    }
 }
 
-void QSGVideoNode_I420::bindTexture(int id, int unit, int w, int h, const uchar *bits)
+void QSGVideoMaterial_YUV420::bindTexture(int id, int w, int h, const uchar *bits)
 {
-    QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
-    functions->glActiveTexture(unit);
     glBindTexture(GL_TEXTURE_2D, id);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bits);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -265,6 +285,23 @@ void QSGVideoNode_I420::bindTexture(int id, int unit, int w, int h, const uchar
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 }
 
+QSGVideoNode_I420::QSGVideoNode_I420(const QVideoSurfaceFormat &format) :
+    m_format(format)
+{
+    m_material = new QSGVideoMaterial_YUV420(format);
+    setMaterial(m_material);
+}
+
+QSGVideoNode_I420::~QSGVideoNode_I420()
+{
+}
+
+void QSGVideoNode_I420::setCurrentFrame(const QVideoFrame &frame)
+{
+    m_material->setCurrentFrame(frame);
+    markDirty(DirtyMaterial);
+}
+
 
 void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state,
                                                 QSGMaterial *newMaterial,
@@ -272,23 +309,17 @@ void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state,
 {
     Q_UNUSED(oldMaterial);
 
-    QOpenGLFunctions *functions = state.context()->functions();
     QSGVideoMaterial_YUV420 *mat = static_cast<QSGVideoMaterial_YUV420 *>(newMaterial);
     program()->setUniformValue(m_id_yTexture, 0);
     program()->setUniformValue(m_id_uTexture, 1);
     program()->setUniformValue(m_id_vTexture, 2);
 
-    functions->glActiveTexture(GL_TEXTURE1);
-    glBindTexture(GL_TEXTURE_2D, mat->idU);
-    functions->glActiveTexture(GL_TEXTURE2);
-    glBindTexture(GL_TEXTURE_2D, mat->idV);
-    functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
-    glBindTexture(GL_TEXTURE_2D, mat->idY);
+    mat->bind();
 
-    program()->setUniformValue(m_id_colorMatrix, mat->colorMatrix);
+    program()->setUniformValue(m_id_colorMatrix, mat->m_colorMatrix);
     if (state.isOpacityDirty()) {
-        mat->opacity = state.opacity();
-        program()->setUniformValue(m_id_opacity, GLfloat(mat->opacity));
+        mat->m_opacity = state.opacity();
+        program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity));
     }
 
     if (state.isMatrixDirty())
index b02864a..0d10cf9 100644 (file)
@@ -60,13 +60,8 @@ public:
 private:
     void bindTexture(int id, int unit, int w, int h, const uchar *bits);
 
-    int m_width;
-    int m_height;
-    GLuint m_id[3];
-
     QVideoSurfaceFormat m_format;
     QSGVideoMaterial_YUV420 *m_material;
-    QVideoFrame m_frame;
 };
 
 class QSGVideoNodeFactory_I420 : public QSGVideoNodeFactory {