diff options
Diffstat (limited to 'src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp')
-rw-r--r-- | src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp new file mode 100644 index 000000000..7b810fa41 --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2013 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 "qandroidvideorendercontrol.h" + +#include <QtPlatformSupport/private/qjnihelpers_p.h> +#include "jsurfacetextureholder.h" +#include <QAbstractVideoSurface> +#include <QOpenGLContext> +#include <QOffscreenSurface> +#include <QOpenGLFramebufferObject> +#include <QVideoSurfaceFormat> +#include <QOpenGLFunctions> +#include <QOpenGLShaderProgram> + +QT_BEGIN_NAMESPACE + +static const GLfloat g_vertex_data[] = { + -1.f, 1.f, + 1.f, 1.f, + 1.f, -1.f, + -1.f, -1.f +}; + +static const GLfloat g_texture_data[] = { + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f, + 0.f, 1.f +}; + +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; +}; + +class ImageVideoBuffer : public QAbstractVideoBuffer +{ +public: + ImageVideoBuffer(const QImage &image) + : QAbstractVideoBuffer(NoHandle) + , m_image(image) + , m_mode(NotMapped) + { + + } + + MapMode mapMode() const { return m_mode; } + uchar *map(MapMode mode, int *, int *) + { + if (mode != NotMapped && m_mode == NotMapped) { + m_mode = mode; + return m_image.bits(); + } + + return 0; + } + + void unmap() + { + m_mode = NotMapped; + } + +private: + QImage m_image; + MapMode m_mode; +}; + +QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) + : QVideoRendererControl(parent) + , m_surface(0) + , m_offscreenSurface(0) + , m_glContext(0) + , m_fbo(0) + , m_program(0) + , m_useImage(false) + , m_androidSurface(0) + , m_surfaceTexture(0) + , m_surfaceHolder(0) + , m_externalTex(0) +{ +} + +QAndroidVideoRendererControl::~QAndroidVideoRendererControl() +{ + if (m_glContext) + m_glContext->makeCurrent(m_offscreenSurface); + + if (m_surfaceTexture) { + QJNILocalRef<jobject> surfaceTex = m_surfaceTexture->surfaceTexture(); + QJNIObject obj(surfaceTex.object()); + obj.callMethod<void>("release"); + delete m_surfaceTexture; + m_surfaceTexture = 0; + } + if (m_androidSurface) { + m_androidSurface->callMethod<void>("release"); + delete m_androidSurface; + m_androidSurface = 0; + } + if (m_surfaceHolder) { + delete m_surfaceHolder; + m_surfaceHolder = 0; + } + if (m_externalTex) + glDeleteTextures(1, &m_externalTex); + + delete m_fbo; + delete m_program; + delete m_glContext; + delete m_offscreenSurface; +} + +QAbstractVideoSurface *QAndroidVideoRendererControl::surface() const +{ + return m_surface; +} + +void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (surface == m_surface) + return; + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + m_surface = surface; + + m_useImage = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32); +} + +jobject QAndroidVideoRendererControl::surfaceHolder() +{ + if (m_surfaceHolder) + return m_surfaceHolder->object(); + + QOpenGLContext *currContext = QOpenGLContext::currentContext(); + + // If we don't have a GL context in the current thread, create one and share it + // with the render thread GL context + if (!currContext && !m_glContext) { + m_offscreenSurface = new QOffscreenSurface; + QSurfaceFormat format; + format.setSwapBehavior(QSurfaceFormat::SingleBuffer); + m_offscreenSurface->setFormat(format); + m_offscreenSurface->create(); + + QOpenGLContext *shareContext = 0; + if (m_surface) + shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>()); + m_glContext = new QOpenGLContext; + m_glContext->setFormat(m_offscreenSurface->requestedFormat()); + + if (shareContext) + m_glContext->setShareContext(shareContext); + + if (!m_glContext->create()) + return 0; + + // if sharing contexts is not supported, fallback to image rendering and send the bits + // to the video surface + if (!m_glContext->shareContext()) + m_useImage = true; + } + + if (m_glContext) + m_glContext->makeCurrent(m_offscreenSurface); + + glGenTextures(1, &m_externalTex); + m_surfaceTexture = new JSurfaceTexture(m_externalTex); + + if (m_surfaceTexture->isValid()) { + connect(m_surfaceTexture, SIGNAL(frameAvailable()), this, SLOT(onFrameAvailable())); + + QJNILocalRef<jobject> surfaceTex = m_surfaceTexture->surfaceTexture(); + + m_androidSurface = new QJNIObject("android/view/Surface", + "(Landroid/graphics/SurfaceTexture;)V", + surfaceTex.object()); + + m_surfaceHolder = new JSurfaceTextureHolder(m_androidSurface->object()); + } else { + delete m_surfaceTexture; + m_surfaceTexture = 0; + glDeleteTextures(1, &m_externalTex); + } + + if (m_surfaceHolder) + return m_surfaceHolder->object(); + + return 0; +} + +void QAndroidVideoRendererControl::setVideoSize(const QSize &size) +{ + if (m_nativeSize == size) + return; + + m_nativeSize = size; + + delete m_fbo; + m_fbo = 0; +} + +void QAndroidVideoRendererControl::stop() +{ + if (m_surface && m_surface->isActive()) + m_surface->stop(); + m_nativeSize = QSize(); +} + +void QAndroidVideoRendererControl::onFrameAvailable() +{ + if (m_glContext) + m_glContext->makeCurrent(m_offscreenSurface); + + m_surfaceTexture->updateTexImage(); + + if (!m_nativeSize.isValid()) + return; + + renderFrameToFbo(); + + QAbstractVideoBuffer *buffer = 0; + QVideoFrame frame; + + if (m_useImage) { + buffer = new ImageVideoBuffer(m_fbo->toImage().mirrored()); + frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_RGB32); + } else { + buffer = new TextureVideoBuffer(m_fbo->texture()); + frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_BGR32); + } + + if (m_surface && frame.isValid()) { + if (m_surface->isActive() && (m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat() + || m_surface->nativeResolution() != frame.size())) { + m_surface->stop(); + } + + if (!m_surface->isActive()) { + QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), + m_useImage ? QAbstractVideoBuffer::NoHandle + : QAbstractVideoBuffer::GLTextureHandle); + + m_surface->start(format); + } + + if (m_surface->isActive()) + m_surface->present(frame); + } +} + +void QAndroidVideoRendererControl::renderFrameToFbo() +{ + createGLResources(); + + m_fbo->bind(); + + glViewport(0, 0, m_nativeSize.width(), m_nativeSize.height()); + + m_program->bind(); + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + m_program->setUniformValue("frameTexture", GLuint(0)); + m_program->setUniformValue("texMatrix", m_surfaceTexture->getTransformMatrix()); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, g_vertex_data); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, g_texture_data); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + m_program->disableAttributeArray(0); + m_program->disableAttributeArray(1); + m_program->release(); + + glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + m_fbo->release(); + + glFinish(); +} + +void QAndroidVideoRendererControl::createGLResources() +{ + if (!m_fbo) + m_fbo = new QOpenGLFramebufferObject(m_nativeSize); + + if (!m_program) { + m_program = new QOpenGLShaderProgram; + + QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_program); + vertexShader->compileSourceCode("attribute highp vec4 vertexCoordsArray; \n" \ + "attribute highp vec2 textureCoordArray; \n" \ + "uniform highp mat4 texMatrix; \n" \ + "varying highp vec2 textureCoords; \n" \ + "void main(void) \n" \ + "{ \n" \ + " gl_Position = vertexCoordsArray; \n" \ + " textureCoords = (texMatrix * vec4(textureCoordArray, 0.0, 1.0)).xy; \n" \ + "}\n"); + m_program->addShader(vertexShader); + + QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_program); + fragmentShader->compileSourceCode("#extension GL_OES_EGL_image_external : require \n" \ + "varying highp vec2 textureCoords; \n" \ + "uniform samplerExternalOES frameTexture; \n" \ + "void main() \n" \ + "{ \n" \ + " gl_FragColor = texture2D(frameTexture, textureCoords); \n" \ + "}\n"); + m_program->addShader(fragmentShader); + + m_program->bindAttributeLocation("vertexCoordsArray", 0); + m_program->bindAttributeLocation("textureCoordArray", 1); + m_program->link(); + } +} + +QT_END_NAMESPACE |