2 * This file is part of CameraPlus.
4 * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "qtcamviewfinderrenderermeego.h"
23 #include <gst/video/video.h>
25 #include "qtcamconfig.h"
28 #include <QGLShaderProgram>
29 #include <gst/interfaces/meegovideotexture.h>
31 QT_CAM_VIEWFINDER_RENDERER(RENDERER_TYPE_MEEGO, QtCamViewfinderRendererMeeGo);
33 #define GL_TEXTURE_EXTERNAL_OES 0x8060
35 typedef void *EGLSyncKHR;
36 #define EGL_SYNC_FENCE_KHR 0x30F9
38 typedef EGLSyncKHR(EGLAPIENTRYP _PFNEGLCREATESYNCKHRPROC)(EGLDisplay dpy, EGLenum type,
39 const EGLint *attrib_list);
41 _PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = 0;
43 static const QString FRAGMENT_SHADER = ""
44 "#extension GL_OES_EGL_image_external: enable\n"
45 "uniform samplerExternalOES texture0;"
46 "varying lowp vec2 fragTexCoord;"
48 " gl_FragColor = texture2D(texture0, fragTexCoord);"
52 static const QString VERTEX_SHADER = ""
53 "attribute highp vec4 inputVertex;"
54 "attribute lowp vec2 textureCoord;"
55 "uniform highp mat4 matrix;"
56 "uniform highp mat4 matrixWorld;"
57 "varying lowp vec2 fragTexCoord;"
60 " gl_Position = matrix * matrixWorld * inputVertex;"
61 " fragTexCoord = textureCoord;"
65 QtCamViewfinderRendererMeeGo::QtCamViewfinderRendererMeeGo(QtCamConfig *config,
67 QtCamViewfinderRenderer(config, parent),
76 // Texture coordinates:
77 // Reversed because of Qt reversed coordinate system
78 m_texCoords[0] = 0; m_texCoords[1] = 1;
79 m_texCoords[2] = 1; m_texCoords[3] = 1;
80 m_texCoords[4] = 1; m_texCoords[5] = 0;
81 m_texCoords[6] = 0; m_texCoords[7] = 0;
83 bzero(&m_vertexCoords, 8);
86 QtCamViewfinderRendererMeeGo::~QtCamViewfinderRendererMeeGo() {
88 g_signal_handler_disconnect(m_sink, m_id);
89 g_signal_handler_disconnect(m_sink, m_notify);
90 g_object_remove_toggle_ref(G_OBJECT(m_sink), (GToggleNotify)sink_notify, this);
95 bool QtCamViewfinderRendererMeeGo::needsNativePainting() {
99 void QtCamViewfinderRendererMeeGo::paint(const QMatrix4x4& matrix, const QRectF& viewport) {
100 QMutexLocker locker(&m_frameMutex);
106 calculateProjectionMatrix(viewport);
108 if (!eglCreateSyncKHR && m_conf->viewfinderUseFence()) {
109 eglCreateSyncKHR = (_PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR");
111 if (!eglCreateSyncKHR) {
112 qWarning() << "eglCreateSyncKHR not found. Fences disabled";
120 // Program will be created if needed and will never be deleted even
121 // if attaching the shaders fail.
125 paintFrame(matrix, m_frame);
128 void QtCamViewfinderRendererMeeGo::resize(const QSizeF& size) {
129 if (size == m_size) {
135 m_renderArea = QRectF();
139 // This will destroy everything
140 // but we need a way to reset the viewport and the transformation matrix only.
143 emit renderAreaChanged();
146 void QtCamViewfinderRendererMeeGo::reset() {
147 QMutexLocker locker(&m_frameMutex);
151 GstElement *QtCamViewfinderRendererMeeGo::sinkElement() {
152 if (!QGLContext::currentContext()) {
153 qCritical() << "Cannot create the GStreamer element without an OpenGL context.";
158 m_sink = gst_element_factory_make(m_conf->viewfinderSink().toLatin1().data(),
159 "QtCamViewfinderRendererMeeGoSink");
161 qCritical() << "Failed to create" << m_conf->viewfinderSink();
165 g_object_add_toggle_ref(G_OBJECT(m_sink), (GToggleNotify)sink_notify, this);
168 // Displa can be replaced with a null pointer.
169 // We all know that the sink used for Harmattan ignores the x-display property ;-)
171 Display *d = QX11Info::display();
172 g_object_set(G_OBJECT(m_sink), "x-display", d, "use-framebuffer-memory", TRUE, NULL);
174 m_dpy = eglGetDisplay((EGLNativeDisplayType)d);
175 if (m_dpy == EGL_NO_DISPLAY) {
176 qCritical() << "Failed to obtain EGL Display";
179 EGLContext context = eglGetCurrentContext();
180 if (context == EGL_NO_CONTEXT) {
181 qCritical() << "Failed to obtain EGL context";
184 g_object_set(G_OBJECT(m_sink), "egl-display", m_dpy, "egl-context", context, NULL);
186 m_id = g_signal_connect(G_OBJECT(m_sink), "frame-ready", G_CALLBACK(frame_ready), this);
188 GstPad *pad = gst_element_get_static_pad(m_sink, "sink");
189 m_notify = g_signal_connect(G_OBJECT(pad), "notify::caps",
190 G_CALLBACK(sink_caps_changed), this);
191 gst_object_unref(pad);
196 void QtCamViewfinderRendererMeeGo::frame_ready(GstElement *sink, int frame,
197 QtCamViewfinderRendererMeeGo *r) {
201 r->m_frameMutex.lock();
203 r->m_frameMutex.unlock();
205 QMetaObject::invokeMethod(r, "updateRequested", Qt::QueuedConnection);
208 void QtCamViewfinderRendererMeeGo::sink_notify(QtCamViewfinderRendererMeeGo *q,
209 GObject *object, gboolean is_last_ref) {
214 g_signal_handler_disconnect(q->m_sink, q->m_id);
215 g_object_remove_toggle_ref(G_OBJECT(q->m_sink), (GToggleNotify)sink_notify, q);
220 void QtCamViewfinderRendererMeeGo::sink_caps_changed(GObject *obj, GParamSpec *pspec,
221 QtCamViewfinderRendererMeeGo *q) {
226 if (obj && gst_video_get_size(GST_PAD(obj), &width, &height)) {
227 QMetaObject::invokeMethod(q, "setVideoSize", Qt::QueuedConnection,
228 Q_ARG(QSizeF, QSizeF(width, height)));
232 void QtCamViewfinderRendererMeeGo::calculateProjectionMatrix(const QRectF& rect) {
233 m_projectionMatrix = QMatrix4x4();
234 m_projectionMatrix.ortho(rect);
237 void QtCamViewfinderRendererMeeGo::createProgram() {
242 m_program = new QGLShaderProgram(this);
244 if (!m_program->addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER)) {
245 qCritical() << "Failed to add vertex shader";
249 if (!m_program->addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER)) {
250 qCritical() << "Failed to add fragment shader";
254 m_program->bindAttributeLocation("inputVertex", 0);
255 m_program->bindAttributeLocation("textureCoord", 1);
257 if (!m_program->link()) {
258 qCritical() << "Failed to link program!";
262 if (!m_program->bind()) {
263 qCritical() << "Failed to bind program";
267 m_program->setUniformValue("texture0", 0); // texture UNIT 0
268 m_program->release();
271 void QtCamViewfinderRendererMeeGo::paintFrame(const QMatrix4x4& matrix, int frame) {
278 MeegoGstVideoTexture *sink = MEEGO_GST_VIDEO_TEXTURE(m_sink);
279 if (!meego_gst_video_texture_acquire_frame(sink, frame)) {
280 qDebug() << "Failed to acquire frame";
286 m_program->setUniformValue("matrix", m_projectionMatrix);
287 m_program->setUniformValue("matrixWorld", matrix);
289 if (!meego_gst_video_texture_bind_frame(sink, GL_TEXTURE_EXTERNAL_OES, frame)) {
290 qDebug() << "Failed to bind frame";
291 m_program->release();
295 glEnableVertexAttribArray(0);
296 glEnableVertexAttribArray(1);
298 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &m_vertexCoords);
299 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, &m_texCoords);
301 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
303 if (!meego_gst_video_texture_bind_frame(sink, GL_TEXTURE_EXTERNAL_OES, -1)) {
304 qDebug() << "Failed to unbind frame";
307 glDisableVertexAttribArray(1);
308 glDisableVertexAttribArray(0);
310 m_program->release();
312 if (eglCreateSyncKHR) {
313 sync = eglCreateSyncKHR(m_dpy, EGL_SYNC_FENCE_KHR, NULL);
316 meego_gst_video_texture_release_frame(sink, frame, sync);
319 void QtCamViewfinderRendererMeeGo::calculateCoords() {
320 if (!m_size.isValid() || !m_videoSize.isValid()) {
324 QRectF area = renderArea();
326 qreal leftMargin = area.x();
327 qreal topMargin = area.y();
328 QSizeF renderSize = area.size();
330 m_vertexCoords[0] = leftMargin;
331 m_vertexCoords[1] = topMargin + renderSize.height();
333 m_vertexCoords[2] = renderSize.width() + leftMargin;
334 m_vertexCoords[3] = topMargin + renderSize.height();
336 m_vertexCoords[4] = renderSize.width() + leftMargin;
337 m_vertexCoords[5] = topMargin;
339 m_vertexCoords[6] = leftMargin;
340 m_vertexCoords[7] = topMargin;
343 QRectF QtCamViewfinderRendererMeeGo::renderArea() {
344 if (!m_renderArea.isNull()) {
348 QSizeF renderSize = m_videoSize;
349 renderSize.scale(m_size, Qt::KeepAspectRatio);
351 qreal leftMargin = (m_size.width() - renderSize.width())/2.0;
352 qreal topMargin = (m_size.height() - renderSize.height())/2.0;
354 m_renderArea = QRectF(QPointF(leftMargin, topMargin), renderSize);
359 QSizeF QtCamViewfinderRendererMeeGo::videoResolution() {
363 void QtCamViewfinderRendererMeeGo::setVideoSize(const QSizeF& size) {
364 if (size == m_videoSize) {
370 m_renderArea = QRectF();
376 emit renderAreaChanged();
377 emit videoResolutionChanged();