summaryrefslogtreecommitdiffstats
path: root/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp')
-rw-r--r--src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp375
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