Fixed QML video playback on Mac
authorDmytro Poplavskiy <dmytro.poplavskiy@nokia.com>
Wed, 20 Jun 2012 07:57:45 +0000 (17:57 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 29 Jun 2012 06:59:53 +0000 (08:59 +0200)
Use the same CIImage based video frames as with
QGraphicsVideoItem, but since CIImages can't be
rendered directly in Scene Graph,
the frame is rendered to FBO first.

Task-number: QT-5423
Change-Id: I16f7e6351578bae21f8642a8028538c441e1f544
Reviewed-by: Lev Zelenskiy <lev.zelenskiy@nokia.com>
Reviewed-by: Michael Goddard <michael.goddard@nokia.com>
src/imports/multimedia/multimedia.pro
src/imports/multimedia/qdeclarativevideooutput_render.cpp
src/imports/multimedia/qdeclarativevideooutput_render_p.h
src/imports/multimedia/qsgvideonode_texture.cpp [new file with mode: 0644]
src/imports/multimedia/qsgvideonode_texture.h [new file with mode: 0644]
src/plugins/qt7/mediaplayer/qt7playerservice.mm
src/plugins/qt7/qt7movieviewrenderer.h
src/plugins/qt7/qt7movieviewrenderer.mm

index c60416e..45a0f7b 100644 (file)
@@ -19,6 +19,7 @@ HEADERS += \
         qdeclarativevideooutput_window_p.h \
         qsgvideonode_i420.h \
         qsgvideonode_rgb.h \
+        qsgvideonode_texture.h \
         qdeclarativeradio_p.h \
         qdeclarativeradiodata_p.h \
         qdeclarativecamera_p.h \
@@ -39,6 +40,7 @@ SOURCES += \
         qdeclarativevideooutput_window.cpp \
         qsgvideonode_i420.cpp \
         qsgvideonode_rgb.cpp \
+        qsgvideonode_texture.cpp \
         qdeclarativeradio.cpp \
         qdeclarativeradiodata.cpp \
         qdeclarativecamera.cpp \
index 1f8bd99..f59d20a 100644 (file)
@@ -47,6 +47,8 @@
 #include <private/qmediapluginloader_p.h>
 #include <private/qsgvideonode_p.h>
 
+#include <QtGui/QOpenGLContext>
+
 QT_BEGIN_NAMESPACE
 
 Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, videoNodeFactoryLoader,
@@ -54,6 +56,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, videoNodeFactoryLoader,
 
 QDeclarativeVideoRendererBackend::QDeclarativeVideoRendererBackend(QDeclarativeVideoOutput *parent)
     : QDeclarativeVideoBackend(parent),
+      m_glContext(0),
       m_frameChanged(false)
 {
     m_surface = new QSGVideoItemSurface(this);
@@ -69,6 +72,7 @@ QDeclarativeVideoRendererBackend::QDeclarativeVideoRendererBackend(QDeclarativeV
     // Append existing node factories as fallback if we have no plugins
     m_videoNodeFactories.append(&m_i420Factory);
     m_videoNodeFactories.append(&m_rgbFactory);
+    m_videoNodeFactories.append(&m_textureFactory);
 }
 
 QDeclarativeVideoRendererBackend::~QDeclarativeVideoRendererBackend()
@@ -165,6 +169,11 @@ QSGNode *QDeclarativeVideoRendererBackend::updatePaintNode(QSGNode *oldNode,
 
     QMutexLocker lock(&m_frameMutex);
 
+    if (!m_glContext) {
+        m_glContext = QOpenGLContext::currentContext();
+        m_surface->setProperty("GLContext", QVariant::fromValue<QObject*>(m_glContext));
+    }
+
     if (m_frameChanged) {
         if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) {
 #ifdef DEBUG_VIDEOITEM
index e734215..b5421bf 100644 (file)
@@ -46,6 +46,8 @@
 #include "qdeclarativevideooutput_backend_p.h"
 #include "qsgvideonode_i420.h"
 #include "qsgvideonode_rgb.h"
+#include "qsgvideonode_texture.h"
+
 #include <QtCore/qmutex.h>
 #include <QtMultimedia/qabstractvideosurface.h>
 
@@ -53,6 +55,7 @@ QT_BEGIN_NAMESPACE
 
 class QSGVideoItemSurface;
 class QVideoRendererControl;
+class QOpenGLContext;
 
 class QDeclarativeVideoRendererBackend : public QDeclarativeVideoBackend
 {
@@ -77,10 +80,12 @@ private:
     QPointer<QVideoRendererControl> m_rendererControl;
     QList<QSGVideoNodeFactoryInterface*> m_videoNodeFactories;
     QSGVideoItemSurface *m_surface;
+    QOpenGLContext *m_glContext;
     QVideoFrame m_frame;
     bool m_frameChanged;
     QSGVideoNodeFactory_I420 m_i420Factory;
     QSGVideoNodeFactory_RGB m_rgbFactory;
+    QSGVideoNodeFactory_Texture m_textureFactory;
     QMutex m_frameMutex;
     QRectF m_renderedRect;         // Destination pixel coordinates, clipped
     QRectF m_sourceTextureRect;    // Source texture coordinates
diff --git a/src/imports/multimedia/qsgvideonode_texture.cpp b/src/imports/multimedia/qsgvideonode_texture.cpp
new file mode 100644 (file)
index 0000000..657e492
--- /dev/null
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qsgvideonode_texture.h"
+#include <QtQuick/qsgtexturematerial.h>
+#include <QtQuick/qsgmaterial.h>
+#include <QtCore/qmutex.h>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QOpenGLShaderProgram>
+
+QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_Texture::supportedPixelFormats(
+                                        QAbstractVideoBuffer::HandleType handleType) const
+{
+    QList<QVideoFrame::PixelFormat> pixelFormats;
+
+    if (handleType == QAbstractVideoBuffer::GLTextureHandle) {
+        pixelFormats.append(QVideoFrame::Format_RGB565);
+        pixelFormats.append(QVideoFrame::Format_RGB32);
+        pixelFormats.append(QVideoFrame::Format_ARGB32);
+        pixelFormats.append(QVideoFrame::Format_BGR32);
+        pixelFormats.append(QVideoFrame::Format_BGRA32);
+    }
+
+    return pixelFormats;
+}
+
+QSGVideoNode *QSGVideoNodeFactory_Texture::createNode(const QVideoSurfaceFormat &format)
+{
+    if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat()))
+        return new QSGVideoNode_Texture(format);
+
+    return 0;
+}
+
+
+class QSGVideoMaterialShader_Texture : public QSGMaterialShader
+{
+public:
+    QSGVideoMaterialShader_Texture(QVideoFrame::PixelFormat pixelFormat)
+        : QSGMaterialShader(),
+          m_pixelFormat(pixelFormat)
+    {
+    }
+
+    void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+    virtual char const *const *attributeNames() const {
+        static const char *names[] = {
+            "qt_VertexPosition",
+            "qt_VertexTexCoord",
+            0
+        };
+        return names;
+    }
+
+protected:
+
+    virtual const char *vertexShader() const {
+        const char *shader =
+        "uniform highp mat4 qt_Matrix;                      \n"
+        "attribute highp vec4 qt_VertexPosition;            \n"
+        "attribute highp vec2 qt_VertexTexCoord;            \n"
+        "varying highp vec2 qt_TexCoord;                    \n"
+        "void main() {                                      \n"
+        "    qt_TexCoord = qt_VertexTexCoord;               \n"
+        "    gl_Position = qt_Matrix * qt_VertexPosition;   \n"
+        "}";
+        return shader;
+    }
+
+    virtual const char *fragmentShader() const {
+        static const char *shader =
+        "uniform sampler2D rgbTexture;"
+        "uniform lowp float opacity;"
+        ""
+        "varying highp vec2 qt_TexCoord;"
+        ""
+        "void main()"
+        "{"
+        "    gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;"
+        "}";
+
+        static const char *colorsSwapShader =
+        "uniform sampler2D rgbTexture;"
+        "uniform lowp float opacity;"
+        ""
+        "varying highp vec2 qt_TexCoord;"
+        ""
+        "void main()"
+        "{"
+        "    gl_FragColor =  vec4(texture2D(rgbTexture, qt_TexCoord).bgr, 1.0) * opacity;"
+        "}";
+
+
+        switch (m_pixelFormat) {
+            case QVideoFrame::Format_RGB32:
+            case QVideoFrame::Format_ARGB32:
+                return colorsSwapShader;
+            default:
+                return shader;
+        }
+    }
+
+    virtual void initialize() {
+        m_id_matrix = program()->uniformLocation("qt_Matrix");
+        m_id_Texture = program()->uniformLocation("rgbTexture");
+        m_id_opacity = program()->uniformLocation("opacity");
+    }
+
+    int m_id_matrix;
+    int m_id_Texture;
+    int m_id_opacity;
+    QVideoFrame::PixelFormat m_pixelFormat;
+};
+
+
+class QSGVideoMaterial_Texture : public QSGMaterial
+{
+public:
+    QSGVideoMaterial_Texture(const QVideoSurfaceFormat &format) :
+        m_format(format),
+        m_textureId(0),
+        m_opacity(1.0)
+    {
+        setFlag(Blending, false);
+    }
+
+    ~QSGVideoMaterial_Texture()
+    {
+        m_frame = QVideoFrame();
+    }
+
+    virtual QSGMaterialType *type() const {
+        static QSGMaterialType theType;
+        return &theType;
+    }
+
+    virtual QSGMaterialShader *createShader() const {
+        return new QSGVideoMaterialShader_Texture(m_format.pixelFormat());
+    }
+
+    virtual int compare(const QSGMaterial *other) const {
+        const QSGVideoMaterial_Texture *m = static_cast<const QSGVideoMaterial_Texture *>(other);
+        int diff = m_textureId - m->m_textureId;
+        if (diff)
+            return diff;
+
+        diff = m_format.pixelFormat() - m->m_format.pixelFormat();
+        if (diff)
+            return diff;
+
+        return (m_opacity > m->m_opacity) ? 1 : -1;
+    }
+
+    void updateBlending() {
+        setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true);
+    }
+
+    void setVideoFrame(const QVideoFrame &frame) {
+        QMutexLocker lock(&m_frameMutex);
+        m_frame = frame;
+    }
+
+    void bind()
+    {
+        QMutexLocker lock(&m_frameMutex);
+        if (m_frame.isValid()) {
+            m_textureId = m_frame.handle().toUInt();
+            glBindTexture(GL_TEXTURE_2D, m_textureId);
+
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        } else {
+            m_textureId = 0;
+        }
+    }
+
+    QVideoFrame m_frame;
+    QMutex m_frameMutex;
+    QSize m_textureSize;
+    QVideoSurfaceFormat m_format;
+    GLuint m_textureId;
+    qreal m_opacity;
+};
+
+
+QSGVideoNode_Texture::QSGVideoNode_Texture(const QVideoSurfaceFormat &format) :
+    m_format(format)
+{
+    m_material = new QSGVideoMaterial_Texture(format);
+    setMaterial(m_material);
+}
+
+QSGVideoNode_Texture::~QSGVideoNode_Texture()
+{
+}
+
+void QSGVideoNode_Texture::setCurrentFrame(const QVideoFrame &frame)
+{
+    m_material->setVideoFrame(frame);
+    markDirty(DirtyMaterial);
+}
+
+void QSGVideoMaterialShader_Texture::updateState(const RenderState &state,
+                                                QSGMaterial *newMaterial,
+                                                QSGMaterial *oldMaterial)
+{
+    Q_UNUSED(oldMaterial);
+    QSGVideoMaterial_Texture *mat = static_cast<QSGVideoMaterial_Texture *>(newMaterial);
+    program()->setUniformValue(m_id_Texture, 0);
+
+    mat->bind();
+
+    if (state.isOpacityDirty()) {
+        mat->m_opacity = state.opacity();
+        mat->updateBlending();
+        program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity));
+    }
+
+    if (state.isMatrixDirty())
+        program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+}
diff --git a/src/imports/multimedia/qsgvideonode_texture.h b/src/imports/multimedia/qsgvideonode_texture.h
new file mode 100644 (file)
index 0000000..b2a1fc7
--- /dev/null
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGVIDEONODE_TEXTURE_H
+#define QSGVIDEONODE_TEXTURE_H
+
+#include <private/qsgvideonode_p.h>
+#include <QtMultimedia/qvideosurfaceformat.h>
+
+class QSGVideoMaterial_Texture;
+
+class QSGVideoNode_Texture : public QSGVideoNode
+{
+public:
+    QSGVideoNode_Texture(const QVideoSurfaceFormat &format);
+    ~QSGVideoNode_Texture();
+
+    virtual QVideoFrame::PixelFormat pixelFormat() const {
+        return m_format.pixelFormat();
+    }
+    void setCurrentFrame(const QVideoFrame &frame);
+
+private:
+    QVideoSurfaceFormat m_format;
+    QSGVideoMaterial_Texture *m_material;
+    QVideoFrame m_frame;
+};
+
+class QSGVideoNodeFactory_Texture : public QSGVideoNodeFactoryInterface {
+public:
+    QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
+    QSGVideoNode *createNode(const QVideoSurfaceFormat &format);
+};
+
+
+#endif
index cf2a3eb..67a7a81 100644 (file)
@@ -92,12 +92,8 @@ QMediaControl *QT7PlayerService::requestControl(const char *name)
         }
 
         if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
-#ifdef QUICKTIME_C_API_AVAILABLE
-            m_videoOutput = new QT7MovieRenderer(this);
-#elif !defined(QT_NO_WIDGETS)
+#ifndef QT_NO_WIDGETS
             m_videoOutput = new QT7MovieViewRenderer(this);
-#else
-            return 0;
 #endif
         }
 
index 786adb4..8c83296 100644 (file)
 #include "qt7videooutput.h"
 #include <qvideoframe.h>
 
+#include <QuartzCore/CIContext.h>
+
 QT_BEGIN_NAMESPACE
 
 class QVideoFrame;
 
 class QT7PlayerSession;
 class QT7PlayerService;
+class QGLWidget;
+class QGLFramebufferObject;
 
 class QT7MovieViewRenderer : public QT7VideoRendererControl
 {
@@ -77,12 +81,17 @@ protected:
 
 private:
     void setupVideoOutput();
+    QVideoFrame convertCIImageToGLTexture(const QVideoFrame &frame);
 
     void *m_movie;
     void *m_movieView;
     QSize m_nativeSize;
     QAbstractVideoSurface *m_surface;
     QVideoFrame m_currentFrame;
+    QGLWidget *m_glWidget;
+    QGLFramebufferObject *m_fbo;
+    CIContext *m_ciContext;
+
     bool m_pendingRenderEvent;
     QMutex m_mutex;
 };
index 0f4bf27..3a6ff8a 100644 (file)
 #include <QtCore/qdebug.h>
 #include <QtCore/qcoreevent.h>
 #include <QtCore/qcoreapplication.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtOpenGL/qgl.h>
+#include <QtOpenGL/qglframebufferobject.h>
 
 #include <QtCore/qreadwritelock.h>
 
@@ -104,6 +108,29 @@ private:
     MapMode m_mode;
 };
 
+class TextureVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+    TextureVideoBuffer(GLuint textureId)
+        : QAbstractVideoBuffer(GLTextureHandle)
+        , m_textureId(textureId)
+    {}
+
+    virtual ~TextureVideoBuffer() {}
+
+    MapMode mapMode() const { return NotMapped; }
+    uchar *map(MapMode, int*, int*) { return 0; }
+    void unmap() {}
+
+    QVariant handle() const
+    {
+        return QVariant::fromValue<unsigned int>(m_textureId);
+    }
+
+private:
+    GLuint m_textureId;
+};
+
 
 #define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];}
 
@@ -146,6 +173,7 @@ private:
 
 - (void) dealloc
 {
+    delete self->m_window;
     [super dealloc];
 }
 
@@ -171,6 +199,7 @@ private:
     // before the image will be drawn.
     Q_UNUSED(view);
     QReadLocker lock(&m_rendererLock);
+    AutoReleasePool pool;
 
     if (m_renderer) {
         CGRect bounds = [img extent];
@@ -183,7 +212,8 @@ private:
         if (!surface || !surface->isActive())
             return img;
 
-        if (surface->surfaceFormat().handleType() == QAbstractVideoBuffer::CoreImageHandle) {
+        if (surface->surfaceFormat().handleType() == QAbstractVideoBuffer::CoreImageHandle ||
+            surface->surfaceFormat().handleType() == QAbstractVideoBuffer::GLTextureHandle) {
             //surface supports rendering of opengl based CIImage
             frame = QVideoFrame(new QT7CIImageVideoBuffer(img), QSize(w,h), QVideoFrame::Format_RGB32 );
         } else {
@@ -243,6 +273,9 @@ QT7MovieViewRenderer::QT7MovieViewRenderer(QObject *parent)
     m_movie(0),
     m_movieView(0),
     m_surface(0),
+    m_glWidget(0),
+    m_fbo(0),
+    m_ciContext(0),
     m_pendingRenderEvent(false)
 {    
 }
@@ -254,6 +287,9 @@ QT7MovieViewRenderer::~QT7MovieViewRenderer()
     QMutexLocker locker(&m_mutex);
     m_currentFrame = QVideoFrame();
     [(HiddenQTMovieView*)m_movieView release];
+    [m_ciContext release];
+    delete m_fbo;
+    delete m_glWidget;
 }
 
 void QT7MovieViewRenderer::setupVideoOutput()
@@ -288,11 +324,20 @@ void QT7MovieViewRenderer::setupVideoOutput()
     }
 
     if (m_surface && !m_nativeSize.isEmpty()) {
-        bool coreImageFrameSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::CoreImageHandle).isEmpty() &&
-                                       !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty();
+        bool coreImageFrameSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::CoreImageHandle).isEmpty();
+        bool glTextureSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty();
+
+        QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle;
+        QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_RGB32;
 
-        QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32,
-                                   coreImageFrameSupported ? QAbstractVideoBuffer::CoreImageHandle : QAbstractVideoBuffer::NoHandle);
+        if (glTextureSupported) {
+            handleType = QAbstractVideoBuffer::GLTextureHandle;
+            pixelFormat = QVideoFrame::Format_BGR32;
+        } else if (coreImageFrameSupported) {
+            handleType = QAbstractVideoBuffer::CoreImageHandle;
+        }
+
+        QVideoSurfaceFormat format(m_nativeSize, pixelFormat, handleType);
 
         if (m_surface->isActive() && m_surface->surfaceFormat() != format) {
 #ifdef QT_DEBUG_QT7
@@ -311,6 +356,73 @@ void QT7MovieViewRenderer::setupVideoOutput()
     }
 }
 
+/*!
+    Render the CIImage based video frame to FBO and return the video frame with resulting texture
+*/
+QVideoFrame QT7MovieViewRenderer::convertCIImageToGLTexture(const QVideoFrame &frame)
+{
+    if (frame.handleType() != QAbstractVideoBuffer::CoreImageHandle)
+        return QVideoFrame();
+
+    if (!m_glWidget) {
+        QOpenGLContext *qGlContext = 0;
+
+        if (m_surface)
+            qGlContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
+
+        if (qGlContext) {
+            QGLContext *surfaceContext = QGLContext::fromOpenGLContext(qGlContext);
+            m_glWidget = new QGLWidget();
+
+            QGLContext *context = new QGLContext(surfaceContext->format());
+            m_glWidget->setContext(context, surfaceContext);
+        } else {
+            return QVideoFrame();
+        }
+    }
+
+    m_glWidget->makeCurrent();
+
+    if (!m_fbo || m_fbo->size() != frame.size()) {
+        delete m_fbo;
+        m_fbo = new QGLFramebufferObject(frame.size());
+    }
+
+    CIImage *ciImg = (CIImage*)(frame.handle().value<void*>());
+    if (ciImg) {
+        AutoReleasePool pool;
+
+        QPainter p(m_fbo);
+        p.beginNativePainting();
+        CGLContextObj cglContext = CGLGetCurrentContext();
+        if (cglContext) {
+            if (!m_ciContext) {
+                NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat];
+                CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]);
+
+                m_ciContext = [CIContext contextWithCGLContext:cglContext
+                                 pixelFormat:cglPixelFormat
+                                 colorSpace:nil
+                                 options:nil];
+
+                [m_ciContext retain];
+            }
+
+            QRect viewport = QRect(0, 0, frame.width(), frame.height());
+            CGRect sRect = CGRectMake(viewport.x(), viewport.y(), viewport.width(), viewport.height());
+            CGRect dRect = CGRectMake(viewport.x(), viewport.y(), viewport.width(), viewport.height());
+
+            [m_ciContext drawImage:ciImg inRect:dRect fromRect:sRect];
+        }
+        p.endNativePainting();
+
+        QAbstractVideoBuffer *buffer = new TextureVideoBuffer(m_fbo->texture());
+        return QVideoFrame(buffer, frame.size(), QVideoFrame::Format_BGR32);
+    }
+
+    return QVideoFrame();
+}
+
 void QT7MovieViewRenderer::setMovie(void *movie)
 {
     if (movie == m_movie)
@@ -365,8 +477,20 @@ bool QT7MovieViewRenderer::event(QEvent *event)
     if (event->type() == QEvent::User) {
         QMutexLocker locker(&m_mutex);
         m_pendingRenderEvent = false;
-        if (m_surface->isActive())
-            m_surface->present(m_currentFrame);
+
+        if (m_surface->isActive()) {
+            //For GL texture frames, render in the main thread CIImage based buffers
+            //to FBO shared with video surface shared context
+            if (m_surface->surfaceFormat().handleType() == QAbstractVideoBuffer::GLTextureHandle) {
+                m_currentFrame = convertCIImageToGLTexture(m_currentFrame);
+                if (m_currentFrame.isValid())
+                    m_surface->present(m_currentFrame);
+            } else {
+                m_surface->present(m_currentFrame);
+            }
+        }
+
+        m_currentFrame = QVideoFrame();
     }
 
     return QT7VideoRendererControl::event(event);