qt: Fix failing build on RPI
[platform/upstream/gst-plugins-good.git] / ext / qt / qtwindow.cc
1 /*
2  * GStreamer
3  * Copyright (C) 2016 Freescale Semiconductor, Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26
27 #include <gst/video/video.h>
28 #include "qtwindow.h"
29 #include "gstqsgtexture.h"
30 #include "gstqtglutility.h"
31
32 #include <QtCore/QRunnable>
33 #include <QtGui/QGuiApplication>
34 #include <QtQuick/QQuickWindow>
35 #include <QOpenGLFramebufferObject>
36
37 /* compatability definitions... */
38 #ifndef GL_READ_FRAMEBUFFER
39 #define GL_READ_FRAMEBUFFER 0x8CA8
40 #endif
41
42 /**
43  * SECTION:
44  *
45  * #QtGLWindow is an #QQuickWindow that grab QtQuick view to GStreamer OpenGL video buffers.
46  */
47
48 GST_DEBUG_CATEGORY_STATIC (qt_window_debug);
49 #define GST_CAT_DEFAULT qt_window_debug
50
51 struct _QtGLWindowPrivate
52 {
53   GMutex lock;
54   GCond update_cond;
55
56   GstBuffer *buffer;
57   GstCaps *caps;
58   GstVideoInfo v_info;
59
60   gboolean initted;
61   gboolean updated;
62   gboolean quit;
63   gboolean result;
64   gboolean useDefaultFbo;
65
66   GstGLDisplay *display;
67   GstGLContext *other_context;
68
69   /* frames that qmlview rendered in its gl thread */
70   guint64 frames_rendered;
71 };
72
73 class InitQtGLContext : public QRunnable
74 {
75 public:
76   InitQtGLContext(QtGLWindow *window);
77   void run();
78
79 private:
80   QtGLWindow *window_;
81 };
82
83 InitQtGLContext::InitQtGLContext(QtGLWindow *window) :
84   window_(window)
85 {
86 }
87
88 void InitQtGLContext::run()
89 {
90   window_->onSceneGraphInitialized();
91 }
92
93 QtGLWindow::QtGLWindow ( QWindow * parent, QQuickWindow *src ) :
94   QQuickWindow( parent ), source (src)
95 {
96   QGuiApplication *app = static_cast<QGuiApplication *> (QCoreApplication::instance ());
97   static volatile gsize _debug;
98
99   g_assert (app != NULL);
100
101   if (g_once_init_enter (&_debug)) {
102     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglwindow", 0, "Qt GL QuickWindow");
103     g_once_init_leave (&_debug, 1);
104   }
105
106   this->priv = g_new0 (QtGLWindowPrivate, 1);
107
108   g_mutex_init (&this->priv->lock);
109   g_cond_init (&this->priv->update_cond);
110
111   this->priv->display = gst_qt_get_gl_display();
112
113   connect (source, SIGNAL(beforeRendering()), this, SLOT(beforeRendering()), Qt::DirectConnection);
114   connect (source, SIGNAL(afterRendering()), this, SLOT(afterRendering()), Qt::DirectConnection);
115   connect (app, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()), Qt::DirectConnection);
116   if (source->isSceneGraphInitialized())
117     source->scheduleRenderJob(new InitQtGLContext(this), QQuickWindow::BeforeSynchronizingStage);
118   else
119     connect (source, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection);
120
121   GST_DEBUG ("%p init Qt Window", this->priv->display);
122 }
123
124 QtGLWindow::~QtGLWindow()
125 {
126   GST_DEBUG ("deinit Qt Window");
127   g_mutex_clear (&this->priv->lock);
128   g_cond_clear (&this->priv->update_cond);
129   if (this->priv->other_context)
130     gst_object_unref(this->priv->other_context);
131   if (this->priv->display)
132     gst_object_unref(this->priv->display);
133   g_free (this->priv);
134   this->priv = NULL;
135 }
136
137 void
138 QtGLWindow::beforeRendering()
139 {
140   unsigned int width, height;
141
142   g_mutex_lock (&this->priv->lock);
143
144   if (!fbo && !this->priv->useDefaultFbo) {
145
146     width = source->width();
147     height = source->height();
148
149     GST_DEBUG ("create new framebuffer object %dX%d", width, height);
150
151     fbo.reset(new QOpenGLFramebufferObject (width, height,
152           QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGBA));
153
154     source->setRenderTarget(fbo.data());
155   }
156
157   g_mutex_unlock (&this->priv->lock);
158 }
159
160
161 void
162 QtGLWindow::afterRendering()
163 {
164   GstVideoFrame gl_frame;
165   GstVideoInfo *info;
166   GstGLContext *context;
167   gboolean ret;
168   guint width, height;
169   const GstGLFuncs *gl;
170   GLuint dst_tex;
171
172   g_mutex_lock (&this->priv->lock);
173
174   this->priv->frames_rendered++;
175
176   if(!this->priv->buffer || this->priv->updated == TRUE) {
177     GST_DEBUG ("skip this frame");
178     g_mutex_unlock (&this->priv->lock);
179     return;
180   }
181
182   GST_DEBUG ("copy buffer %p",this->priv->buffer);
183
184   width = GST_VIDEO_INFO_WIDTH (&this->priv->v_info);
185   height = GST_VIDEO_INFO_HEIGHT (&this->priv->v_info);
186   info = &this->priv->v_info;
187   context = this->priv->other_context;
188
189   gst_gl_context_activate (context, TRUE);
190   gl = context->gl_vtable;
191
192   ret = gst_video_frame_map (&gl_frame, info, this->priv->buffer,
193       (GstMapFlags) (GST_MAP_WRITE | GST_MAP_GL));
194
195   if (!ret) {
196     this->priv->buffer = NULL;
197     GST_ERROR ("Failed to map video frame");
198     goto errors;
199   }
200
201   gl->BindFramebuffer (GL_READ_FRAMEBUFFER, this->source->renderTargetId());
202
203   ret = gst_gl_context_check_framebuffer_status (context);
204   if (!ret) {
205     GST_ERROR ("FBO errors");
206     goto errors;
207   }
208
209   dst_tex = *(guint *) gl_frame.data[0];
210   GST_DEBUG ("qml render target id %d, render to tex %d %dX%d", 
211       this->source->renderTargetId(), dst_tex, width,height);
212
213   gl->BindTexture (GL_TEXTURE_2D, dst_tex);
214   gl->CopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0);
215   
216   GST_DEBUG ("rendering finished");
217
218 errors:
219   gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
220   gst_video_frame_unmap (&gl_frame);
221
222   gst_gl_context_activate (context, FALSE);
223
224   this->priv->result = ret;
225   this->priv->updated = TRUE;
226   g_cond_signal (&this->priv->update_cond);
227   g_mutex_unlock (&this->priv->lock);
228 }
229
230 void
231 QtGLWindow::aboutToQuit()
232 {
233   g_mutex_lock (&this->priv->lock);
234
235   this->priv->updated = TRUE;
236   this->priv->quit = TRUE;
237   g_cond_signal (&this->priv->update_cond);
238
239   g_mutex_unlock (&this->priv->lock);
240
241   GST_DEBUG("about to quit");
242 }
243
244 void
245 QtGLWindow::onSceneGraphInitialized()
246 {
247   GST_DEBUG ("scene graph initialization with Qt GL context %p",
248       this->source->openglContext ());
249
250   this->priv->initted = gst_qt_get_gl_wrapcontext (this->priv->display,
251       &this->priv->other_context, NULL);
252
253   GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this,
254       this->priv->other_context);
255 }
256
257 bool
258 QtGLWindow::getGeometry(int * width, int * height)
259 {
260   if (width == NULL || height == NULL)
261     return FALSE;
262
263   *width = this->source->width();
264   *height = this->source->height();
265
266   return TRUE;
267 }
268
269 GstGLContext *
270 qt_window_get_qt_context (QtGLWindow * qt_window)
271 {
272   g_return_val_if_fail (qt_window != NULL, NULL);
273
274   if (!qt_window->priv->other_context)
275     return NULL;
276
277   return (GstGLContext *) gst_object_ref (qt_window->priv->other_context);
278 }
279
280 GstGLDisplay *
281 qt_window_get_display (QtGLWindow * qt_window)
282 {
283   g_return_val_if_fail (qt_window != NULL, NULL);
284
285   if (!qt_window->priv->display)
286     return NULL;
287
288   return (GstGLDisplay *) gst_object_ref (qt_window->priv->display);
289 }
290
291 gboolean
292 qt_window_is_scenegraph_initialized (QtGLWindow * qt_window)
293 {
294   g_return_val_if_fail (qt_window != NULL, FALSE);
295
296   return qt_window->priv->initted;
297 }
298
299 gboolean
300 qt_window_set_caps (QtGLWindow * qt_window, GstCaps * caps)
301 {
302   GstVideoInfo v_info;
303
304   g_return_val_if_fail (qt_window != NULL, FALSE);
305   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
306   g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
307
308   if (qt_window->priv->caps && gst_caps_is_equal_fixed (qt_window->priv->caps, caps))
309     return TRUE;
310
311   if (!gst_video_info_from_caps (&v_info, caps))
312     return FALSE;
313
314   g_mutex_lock (&qt_window->priv->lock);
315
316   gst_caps_replace (&qt_window->priv->caps, caps);
317
318   qt_window->priv->v_info = v_info;
319
320   g_mutex_unlock (&qt_window->priv->lock);
321
322   return TRUE;
323 }
324
325 gboolean
326 qt_window_set_buffer (QtGLWindow * qt_window, GstBuffer * buffer)
327 {
328   g_return_val_if_fail (qt_window != NULL, FALSE);
329   g_return_val_if_fail (qt_window->priv->initted, FALSE);
330   gboolean ret;
331
332   g_mutex_lock (&qt_window->priv->lock);
333
334   if (qt_window->priv->quit){
335     GST_DEBUG("about to quit, drop this buffer");
336     g_mutex_unlock (&qt_window->priv->lock);
337     return TRUE;
338   }
339
340   qt_window->priv->updated = FALSE;
341   qt_window->priv->buffer = buffer;
342
343   while (!qt_window->priv->updated) 
344     g_cond_wait (&qt_window->priv->update_cond, &qt_window->priv->lock);
345   
346   ret = qt_window->priv->result;
347
348   g_mutex_unlock (&qt_window->priv->lock);
349
350   return ret;
351 }
352
353 void
354 qt_window_use_default_fbo (QtGLWindow * qt_window, gboolean useDefaultFbo)
355 {
356   g_return_if_fail (qt_window != NULL);
357
358   g_mutex_lock (&qt_window->priv->lock);
359
360   GST_DEBUG ("set to use default fbo %d", useDefaultFbo);
361   qt_window->priv->useDefaultFbo = useDefaultFbo;
362
363   g_mutex_unlock (&qt_window->priv->lock);
364 }
365
366 void
367 qt_window_get_total_frames (QtGLWindow * qt_window, guint64 *frames)
368 {
369   g_return_if_fail (qt_window != NULL);
370
371   g_mutex_lock (&qt_window->priv->lock);
372
373   *frames = qt_window->priv->frames_rendered;
374
375   g_mutex_unlock (&qt_window->priv->lock);
376 }