summaryrefslogtreecommitdiffstats
path: root/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm')
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm258
1 files changed, 258 insertions, 0 deletions
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm
new file mode 100644
index 000000000..ea787dc16
--- /dev/null
+++ b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** 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 "avfvideoframerenderer.h"
+
+#include <QtMultimedia/qabstractvideosurface.h>
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QWindow>
+
+#ifndef QT_NO_WIDGETS
+#include <QtOpenGL/QGLWidget>
+#endif
+
+#ifdef QT_DEBUG_AVF
+#include <QtCore/qdebug.h>
+#endif
+
+#import <CoreVideo/CVBase.h>
+#import <AVFoundation/AVFoundation.h>
+
+QT_USE_NAMESPACE
+
+AVFVideoFrameRenderer::AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent)
+ : QObject(parent)
+ , m_videoLayerRenderer(0)
+ , m_surface(surface)
+ , m_glContext(0)
+ , m_currentBuffer(1)
+ , m_isContextShared(true)
+{
+ m_fbo[0] = 0;
+ m_fbo[1] = 0;
+
+ //Create Hidden QWindow surface to create context in this thread
+ m_offscreenSurface = new QWindow();
+ m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface);
+ //Needs geometry to be a valid surface, but size is not important
+ m_offscreenSurface->setGeometry(0, 0, 1, 1);
+ m_offscreenSurface->create();
+}
+#ifndef QT_NO_WIDGETS
+AVFVideoFrameRenderer::AVFVideoFrameRenderer(QGLWidget *glWidget, const QSize &size, QObject *parent)
+ : QObject(parent)
+ , m_videoLayerRenderer(0)
+ , m_glWidget(glWidget)
+ , m_surface(0)
+ , m_offscreenSurface(0)
+ , m_glContext(0)
+ , m_targetSize(size)
+ , m_currentBuffer(1)
+ , m_isContextShared(true)
+{
+ m_fbo[0] = 0;
+ m_fbo[1] = 0;
+
+ //Create Hidden QWindow surface to create context in this thread
+ m_offscreenSurface = new QWindow();
+ m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface);
+ //Needs geometry to be a valid surface, but size is not important
+ m_offscreenSurface->setGeometry(0, 0, 1, 1);
+ m_offscreenSurface->create();
+
+
+}
+#endif
+
+AVFVideoFrameRenderer::~AVFVideoFrameRenderer()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+
+ [m_videoLayerRenderer release];
+ delete m_fbo[0];
+ delete m_fbo[1];
+ delete m_offscreenSurface;
+ delete m_glContext;
+}
+
+GLuint AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer)
+{
+ //Is layer valid
+ if (!layer)
+ return 0;
+
+ //If the glContext isn't shared, it doesn't make sense to return a texture for us
+ if (m_offscreenSurface && !m_isContextShared)
+ return 0;
+
+ QOpenGLFramebufferObject *fbo = initRenderer(layer);
+
+ if (!fbo)
+ return 0;
+
+ renderLayerToFBO(layer, fbo);
+
+ return fbo->texture();
+}
+
+QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer)
+{
+ //Is layer valid
+ if (!layer) {
+ return QImage();
+ }
+
+ QOpenGLFramebufferObject *fbo = initRenderer(layer);
+
+ if (!fbo)
+ return QImage();
+
+ renderLayerToFBO(layer, fbo);
+
+ return fbo->toImage();
+}
+
+QOpenGLFramebufferObject *AVFVideoFrameRenderer::initRenderer(AVPlayerLayer *layer)
+{
+
+ //Get size from AVPlayerLayer
+ m_targetSize = QSize(layer.bounds.size.width, layer.bounds.size.height);
+
+ //Make sure we have an OpenGL context to make current
+ if (!m_glContext) {
+ //Create OpenGL context and set share context from surface
+ QOpenGLContext *shareContext = 0;
+ if (m_surface) {
+ //QOpenGLContext *renderThreadContext = 0;
+ shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
+#ifndef QT_NO_WIDGETS
+ } else {
+ shareContext = m_glWidget->context()->contextHandle();
+#endif
+ }
+ m_glContext = new QOpenGLContext();
+ m_glContext->setFormat(m_offscreenSurface->requestedFormat());
+
+ if (shareContext) {
+ m_glContext->setShareContext(shareContext);
+ m_isContextShared = true;
+ } else {
+#ifdef QT_DEBUG_AVF
+ qWarning("failed to get Render Thread context");
+ m_isContextShared = false;
+#endif
+ }
+ if (!m_glContext->create()) {
+ qWarning("failed to create QOpenGLContext");
+ return 0;
+ }
+ }
+
+ //Need current context
+ m_glContext->makeCurrent(m_offscreenSurface);
+
+ //Create the CARenderer if needed
+ if (!m_videoLayerRenderer) {
+ m_videoLayerRenderer = [CARenderer rendererWithCGLContext: CGLGetCurrentContext() options: nil];
+ [m_videoLayerRenderer retain];
+ }
+
+ //Set/Change render source if needed
+ if (m_videoLayerRenderer.layer != layer) {
+ m_videoLayerRenderer.layer = layer;
+ m_videoLayerRenderer.bounds = layer.bounds;
+ }
+
+ //Do we have FBO's already?
+ if ((!m_fbo[0] && !m_fbo[0]) || (m_fbo[0]->size() != m_targetSize)) {
+ delete m_fbo[0];
+ delete m_fbo[1];
+ m_fbo[0] = new QOpenGLFramebufferObject(m_targetSize);
+ m_fbo[1] = new QOpenGLFramebufferObject(m_targetSize);
+ }
+
+ //Switch buffer target
+ m_currentBuffer = !m_currentBuffer;
+ return m_fbo[m_currentBuffer];
+}
+
+void AVFVideoFrameRenderer::renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo)
+{
+ //Start Rendering
+ //NOTE: This rendering method will NOT work on iOS as there is no CARenderer in iOS
+ if (!fbo->bind()) {
+ qWarning("AVFVideoRender FBO failed to bind");
+ return;
+ }
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glViewport(0, 0, m_targetSize.width(), m_targetSize.height());
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glOrtho(0.0f, m_targetSize.width(), m_targetSize.height(), 0.0f, 0.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ [m_videoLayerRenderer beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
+ [m_videoLayerRenderer addUpdateRect:layer.bounds];
+ [m_videoLayerRenderer render];
+ [m_videoLayerRenderer endFrame];
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glFinish(); //Rendering needs to be done before passing texture to video frame
+
+ fbo->release();
+
+ m_glContext->doneCurrent();
+}