qt: Only include qtgui-config.h on qt >= 5.9.0
[platform/upstream/gst-plugins-good.git] / ext / qt / qtitem.cc
1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
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 "qtitem.h"
29 #include "gstqsgtexture.h"
30 #include "gstqtglutility.h"
31
32 #include <QtCore/QRunnable>
33 #include <QtCore/QMutexLocker>
34 #include <QtGui/QGuiApplication>
35 #include <QtQuick/QQuickWindow>
36 #include <QtQuick/QSGSimpleTextureNode>
37
38 /**
39  * SECTION:gtkgstglwidget
40  * @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers
41  * @see_also: #GtkGLArea, #GstBuffer
42  *
43  * #QtGLVideoItem is an #QQuickItem that renders GStreamer video buffers.
44  */
45
46 #define GST_CAT_DEFAULT qt_item_debug
47 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
48
49 #define DEFAULT_FORCE_ASPECT_RATIO  TRUE
50 #define DEFAULT_PAR_N               0
51 #define DEFAULT_PAR_D               1
52
53 enum
54 {
55   PROP_0,
56   PROP_FORCE_ASPECT_RATIO,
57   PROP_PIXEL_ASPECT_RATIO,
58 };
59
60 struct _QtGLVideoItemPrivate
61 {
62   GMutex lock;
63
64   /* properties */
65   gboolean force_aspect_ratio;
66   gint par_n, par_d;
67
68   gint display_width;
69   gint display_height;
70
71   gboolean negotiated;
72   GstBuffer *buffer;
73   GstCaps *caps;
74   GstVideoInfo v_info;
75
76   gboolean initted;
77   GstGLDisplay *display;
78   QOpenGLContext *qt_context;
79   GstGLContext *other_context;
80   GstGLContext *context;
81 };
82
83 class InitializeSceneGraph : public QRunnable
84 {
85 public:
86   InitializeSceneGraph(QtGLVideoItem *item);
87   void run();
88
89 private:
90   QtGLVideoItem *item_;
91 };
92
93 InitializeSceneGraph::InitializeSceneGraph(QtGLVideoItem *item) :
94   item_(item)
95 {
96 }
97
98 void InitializeSceneGraph::run()
99 {
100   item_->onSceneGraphInitialized();
101 }
102
103 QtGLVideoItem::QtGLVideoItem()
104 {
105   static volatile gsize _debug;
106
107   if (g_once_init_enter (&_debug)) {
108     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtglwidget", 0, "Qt GL Widget");
109     g_once_init_leave (&_debug, 1);
110   }
111   this->m_openGlContextInitialized = false;
112   this->setFlag (QQuickItem::ItemHasContents, true);
113
114   this->priv = g_new0 (QtGLVideoItemPrivate, 1);
115
116   this->priv->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
117   this->priv->par_n = DEFAULT_PAR_N;
118   this->priv->par_d = DEFAULT_PAR_D;
119
120   g_mutex_init (&this->priv->lock);
121
122   this->priv->display = gst_qt_get_gl_display();
123
124   connect(this, SIGNAL(windowChanged(QQuickWindow*)), this,
125           SLOT(handleWindowChanged(QQuickWindow*)));
126
127   this->proxy = QSharedPointer<QtGLVideoItemInterface>(new QtGLVideoItemInterface(this));
128
129   GST_DEBUG ("%p init Qt Video Item", this);
130 }
131
132 QtGLVideoItem::~QtGLVideoItem()
133 {
134   /* Before destroying the priv info, make sure
135    * no qmlglsink's will call in again, and that
136    * any ongoing calls are done by invalidating the proxy
137    * pointer */
138   GST_INFO ("Destroying QtGLVideoItem and invalidating the proxy");
139   proxy->invalidateRef();
140   proxy.clear();
141
142   g_mutex_clear (&this->priv->lock);
143   if (this->priv->context)
144     gst_object_unref(this->priv->context);
145   if (this->priv->other_context)
146     gst_object_unref(this->priv->other_context);
147   if (this->priv->display)
148     gst_object_unref(this->priv->display);
149   g_free (this->priv);
150   this->priv = NULL;
151 }
152
153 void
154 QtGLVideoItem::setDAR(gint num, gint den)
155 {
156   this->priv->par_n = num;
157   this->priv->par_d = den;
158 }
159
160 void
161 QtGLVideoItem::getDAR(gint * num, gint * den)
162 {
163   if (num)
164     *num = this->priv->par_n;
165   if (den)
166     *den = this->priv->par_d;
167 }
168
169 void
170 QtGLVideoItem::setForceAspectRatio(bool force_aspect_ratio)
171 {
172   this->priv->force_aspect_ratio = !!force_aspect_ratio;
173 }
174
175 bool
176 QtGLVideoItem::getForceAspectRatio()
177 {
178   return this->priv->force_aspect_ratio;
179 }
180
181 QSGNode *
182 QtGLVideoItem::updatePaintNode(QSGNode * oldNode,
183     UpdatePaintNodeData * updatePaintNodeData)
184 {
185   if (!m_openGlContextInitialized) {
186     return oldNode;
187   }
188
189   QSGSimpleTextureNode *texNode = static_cast<QSGSimpleTextureNode *> (oldNode);
190   GstVideoRectangle src, dst, result;
191   GstQSGTexture *tex;
192
193   g_mutex_lock (&this->priv->lock);
194   gst_gl_context_activate (this->priv->other_context, TRUE);
195
196   GST_TRACE ("%p updatePaintNode", this);
197
198   if (!this->priv->caps) {
199     g_mutex_unlock (&this->priv->lock);
200     return NULL;
201   }
202
203   if (!texNode) {
204     texNode = new QSGSimpleTextureNode ();
205     texNode->setOwnsTexture (true);
206     texNode->setTexture (new GstQSGTexture ());
207   }
208
209   tex = static_cast<GstQSGTexture *> (texNode->texture());
210   tex->setCaps (this->priv->caps);
211   tex->setBuffer (this->priv->buffer);
212   texNode->markDirty(QSGNode::DirtyMaterial);
213
214   if (this->priv->force_aspect_ratio) {
215     src.w = this->priv->display_width;
216     src.h = this->priv->display_height;
217
218     dst.x = boundingRect().x();
219     dst.y = boundingRect().y();
220     dst.w = boundingRect().width();
221     dst.h = boundingRect().height();
222
223     gst_video_sink_center_rect (src, dst, &result, TRUE);
224   } else {
225     result.x = boundingRect().x();
226     result.y = boundingRect().y();
227     result.w = boundingRect().width();
228     result.h = boundingRect().height();
229   }
230
231   texNode->setRect (QRectF (result.x, result.y, result.w, result.h));
232
233   gst_gl_context_activate (this->priv->other_context, FALSE);
234   g_mutex_unlock (&this->priv->lock);
235
236   return texNode;
237 }
238
239 static void
240 _reset (QtGLVideoItem * qt_item)
241 {
242   gst_buffer_replace (&qt_item->priv->buffer, NULL);
243
244   gst_caps_replace (&qt_item->priv->caps, NULL);
245
246   qt_item->priv->negotiated = FALSE;
247   qt_item->priv->initted = FALSE;
248 }
249
250 void
251 QtGLVideoItemInterface::setBuffer (GstBuffer * buffer)
252 {
253   QMutexLocker locker(&lock);
254
255   if (qt_item == NULL)
256     return;
257
258   if (!qt_item->priv->negotiated) {
259     GST_WARNING ("Got buffer on unnegotiated QtGLVideoItem. Dropping");
260     return;
261   }
262
263   g_mutex_lock (&qt_item->priv->lock);
264
265   gst_buffer_replace (&qt_item->priv->buffer, buffer);
266
267   QMetaObject::invokeMethod(qt_item, "update", Qt::QueuedConnection);
268
269   g_mutex_unlock (&qt_item->priv->lock);
270 }
271
272 void
273 QtGLVideoItem::onSceneGraphInitialized ()
274 {
275   GST_DEBUG ("scene graph initialization with Qt GL context %p",
276       this->window()->openglContext ());
277
278   if (this->priv->qt_context == this->window()->openglContext ())
279     return;
280
281   this->priv->qt_context = this->window()->openglContext ();
282   if (this->priv->qt_context == NULL) {
283     g_assert_not_reached ();
284     return;
285   }
286
287   m_openGlContextInitialized = gst_qt_get_gl_wrapcontext (this->priv->display,
288       &this->priv->other_context, &this->priv->context);
289
290   GST_DEBUG ("%p created wrapped GL context %" GST_PTR_FORMAT, this,
291       this->priv->other_context);
292
293   emit itemInitialized();
294 }
295
296 void
297 QtGLVideoItem::onSceneGraphInvalidated ()
298 {
299   GST_FIXME ("%p scene graph invalidated", this);
300 }
301
302 gboolean
303 QtGLVideoItemInterface::initWinSys ()
304 {
305   QMutexLocker locker(&lock);
306
307   GError *error = NULL;
308
309   if (qt_item == NULL)
310     return FALSE;
311
312   g_mutex_lock (&qt_item->priv->lock);
313
314   if (qt_item->priv->display && qt_item->priv->qt_context
315       && qt_item->priv->other_context && qt_item->priv->context) {
316     /* already have the necessary state */
317     g_mutex_unlock (&qt_item->priv->lock);
318     return TRUE;
319   }
320
321   if (!GST_IS_GL_DISPLAY (qt_item->priv->display)) {
322     GST_ERROR ("%p failed to retrieve display connection %" GST_PTR_FORMAT,
323         qt_item, qt_item->priv->display);
324     g_mutex_unlock (&qt_item->priv->lock);
325     return FALSE;
326   }
327
328   if (!GST_IS_GL_CONTEXT (qt_item->priv->other_context)) {
329     GST_ERROR ("%p failed to retrieve wrapped context %" GST_PTR_FORMAT, qt_item,
330         qt_item->priv->other_context);
331     g_mutex_unlock (&qt_item->priv->lock);
332     return FALSE;
333   }
334
335   qt_item->priv->context = gst_gl_context_new (qt_item->priv->display);
336
337   if (!qt_item->priv->context) {
338     g_mutex_unlock (&qt_item->priv->lock);
339     return FALSE;
340   }
341
342   if (!gst_gl_context_create (qt_item->priv->context, qt_item->priv->other_context,
343         &error)) {
344     GST_ERROR ("%s", error->message);
345     g_mutex_unlock (&qt_item->priv->lock);
346     return FALSE;
347   }
348
349   g_mutex_unlock (&qt_item->priv->lock);
350   return TRUE;
351 }
352
353 void
354 QtGLVideoItem::handleWindowChanged(QQuickWindow *win)
355 {
356   if (win) {
357     if (win->isSceneGraphInitialized())
358       win->scheduleRenderJob(new InitializeSceneGraph(this), QQuickWindow::BeforeSynchronizingStage);
359     else
360       connect(win, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()), Qt::DirectConnection);
361
362     connect(win, SIGNAL(sceneGraphInvalidated()), this, SLOT(onSceneGraphInvalidated()), Qt::DirectConnection);
363   } else {
364     this->priv->qt_context = NULL;
365   }
366 }
367
368 static gboolean
369 _calculate_par (QtGLVideoItem * widget, GstVideoInfo * info)
370 {
371   gboolean ok;
372   gint width, height;
373   gint par_n, par_d;
374   gint display_par_n, display_par_d;
375   guint display_ratio_num, display_ratio_den;
376
377   width = GST_VIDEO_INFO_WIDTH (info);
378   height = GST_VIDEO_INFO_HEIGHT (info);
379
380   par_n = GST_VIDEO_INFO_PAR_N (info);
381   par_d = GST_VIDEO_INFO_PAR_D (info);
382
383   if (!par_n)
384     par_n = 1;
385
386   /* get display's PAR */
387   if (widget->priv->par_n != 0 && widget->priv->par_d != 0) {
388     display_par_n = widget->priv->par_n;
389     display_par_d = widget->priv->par_d;
390   } else {
391     display_par_n = 1;
392     display_par_d = 1;
393   }
394
395   ok = gst_video_calculate_display_ratio (&display_ratio_num,
396       &display_ratio_den, width, height, par_n, par_d, display_par_n,
397       display_par_d);
398
399   if (!ok)
400     return FALSE;
401
402   GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, display_par_d);
403
404   if (height % display_ratio_den == 0) {
405     GST_DEBUG ("keeping video height");
406     widget->priv->display_width = (guint)
407         gst_util_uint64_scale_int (height, display_ratio_num,
408         display_ratio_den);
409     widget->priv->display_height = height;
410   } else if (width % display_ratio_num == 0) {
411     GST_DEBUG ("keeping video width");
412     widget->priv->display_width = width;
413     widget->priv->display_height = (guint)
414         gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
415   } else {
416     GST_DEBUG ("approximating while keeping video height");
417     widget->priv->display_width = (guint)
418         gst_util_uint64_scale_int (height, display_ratio_num,
419         display_ratio_den);
420     widget->priv->display_height = height;
421   }
422   GST_DEBUG ("scaling to %dx%d", widget->priv->display_width,
423       widget->priv->display_height);
424
425   return TRUE;
426 }
427
428 gboolean
429 QtGLVideoItemInterface::setCaps (GstCaps * caps)
430 {
431   QMutexLocker locker(&lock);
432   GstVideoInfo v_info;
433
434   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
435   g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
436
437   if (qt_item == NULL)
438     return FALSE;
439
440   if (qt_item->priv->caps && gst_caps_is_equal_fixed (qt_item->priv->caps, caps))
441     return TRUE;
442
443   if (!gst_video_info_from_caps (&v_info, caps))
444     return FALSE;
445
446   g_mutex_lock (&qt_item->priv->lock);
447
448   _reset (qt_item);
449
450   gst_caps_replace (&qt_item->priv->caps, caps);
451
452   if (!_calculate_par (qt_item, &v_info)) {
453     g_mutex_unlock (&qt_item->priv->lock);
454     return FALSE;
455   }
456
457   qt_item->priv->v_info = v_info;
458   qt_item->priv->negotiated = TRUE;
459
460   g_mutex_unlock (&qt_item->priv->lock);
461
462   return TRUE;
463 }
464
465 GstGLContext *
466 QtGLVideoItemInterface::getQtContext ()
467 {
468   QMutexLocker locker(&lock);
469
470   if (!qt_item || !qt_item->priv->other_context)
471     return NULL;
472
473   return (GstGLContext *) gst_object_ref (qt_item->priv->other_context);
474 }
475
476 GstGLContext *
477 QtGLVideoItemInterface::getContext ()
478 {
479   QMutexLocker locker(&lock);
480
481   if (!qt_item || !qt_item->priv->context)
482     return NULL;
483
484   return (GstGLContext *) gst_object_ref (qt_item->priv->context);
485 }
486
487 GstGLDisplay *
488 QtGLVideoItemInterface::getDisplay() 
489 {
490   QMutexLocker locker(&lock);
491
492   if (!qt_item || !qt_item->priv->display)
493     return NULL;
494
495   return (GstGLDisplay *) gst_object_ref (qt_item->priv->display);
496 }
497
498 void
499 QtGLVideoItemInterface::setDAR(gint num, gint den)
500 {
501   QMutexLocker locker(&lock);
502   if (!qt_item)
503     return;
504   qt_item->setDAR(num, den);
505 }
506
507 void
508 QtGLVideoItemInterface::getDAR(gint * num, gint * den)
509 {
510   QMutexLocker locker(&lock);
511   if (!qt_item)
512     return;
513   qt_item->getDAR (num, den);
514 }
515
516 void
517 QtGLVideoItemInterface::setForceAspectRatio(bool force_aspect_ratio)
518 {
519   QMutexLocker locker(&lock);
520   if (!qt_item)
521     return;
522   qt_item->setForceAspectRatio(force_aspect_ratio);
523 }
524
525 bool
526 QtGLVideoItemInterface::getForceAspectRatio()
527 {
528   QMutexLocker locker(&lock);
529   if (!qt_item)
530     return FALSE;
531   return qt_item->getForceAspectRatio();
532 }
533
534 void
535 QtGLVideoItemInterface::invalidateRef()
536 {
537   QMutexLocker locker(&lock);
538   qt_item = NULL;
539 }
540