27#include <gst/video/video.h>
32#include <QtCore/QMutexLocker>
33#include <QtCore/QPointer>
34#include <QtGui/QGuiApplication>
35#include <QtQuick/QQuickWindow>
36#include <QtQuick/QSGSimpleTextureNode>
45#define GST_CAT_DEFAULT qt_item_debug
48#define DEFAULT_FORCE_ASPECT_RATIO TRUE
49#define DEFAULT_PAR_N 0
50#define DEFAULT_PAR_D 1
95Qt6GLVideoItem::Qt6GLVideoItem()
99 if (g_once_init_enter (&_debug)) {
100 GST_DEBUG_CATEGORY_INIT (
GST_CAT_DEFAULT,
"qtglwidget", 0,
"Qt GL Widget");
101 g_once_init_leave (&_debug, 1);
104 this->setFlag (QQuickItem::ItemHasContents,
true);
114 g_mutex_init (&this->priv->
lock);
116 g_weak_ref_init (&priv->
sink, NULL);
120 connect(
this, &QQuickItem::windowChanged,
this, &Qt6GLVideoItem::handleWindowChanged);
124 setFlag(ItemHasContents,
true);
125 setAcceptedMouseButtons(Qt::AllButtons);
126 setAcceptHoverEvents(
true);
128 setAcceptTouchEvents(
true);
130 GST_DEBUG (
"%p init Qt6 Video Item",
this);
133Qt6GLVideoItem::~Qt6GLVideoItem()
135 GstBuffer *tmp_buffer;
141 GST_INFO (
"%p Destroying QtGLVideoItem and invalidating the proxy %p",
this, proxy.data());
142 proxy->invalidateRef();
145 g_mutex_clear (&this->priv->
lock);
147 gst_object_unref(this->priv->
context);
151 gst_object_unref(this->priv->
display);
154 GST_TRACE (
"old buffer %p should be unbound now, unreffing", tmp_buffer);
155 gst_buffer_unref (tmp_buffer);
157 while ((tmp_buffer = (GstBuffer*) g_queue_pop_head (&this->priv->
bound_buffers))) {
158 GST_TRACE (
"old buffer %p should be unbound now, unreffing", tmp_buffer);
159 gst_buffer_unref (tmp_buffer);
162 gst_buffer_replace (&this->priv->
buffer, NULL);
164 gst_caps_replace (&this->priv->
caps, NULL);
165 gst_caps_replace (&this->priv->
new_caps, NULL);
167 g_weak_ref_clear (&this->priv->
sink);
174Qt6GLVideoItem::setDAR(gint num, gint den)
176 this->priv->
par_n = num;
177 this->priv->
par_d = den;
181Qt6GLVideoItem::getDAR(gint * num, gint * den)
184 *num = this->priv->
par_n;
186 *den = this->priv->
par_d;
190Qt6GLVideoItem::setForceAspectRatio(
bool force_aspect_ratio)
198Qt6GLVideoItem::getForceAspectRatio()
204Qt6GLVideoItem::itemInitialized()
215 gint display_par_n, display_par_d;
216 guint display_ratio_num, display_ratio_den;
218 width = GST_VIDEO_INFO_WIDTH (info);
219 height = GST_VIDEO_INFO_HEIGHT (info);
221 par_n = GST_VIDEO_INFO_PAR_N (info);
222 par_d = GST_VIDEO_INFO_PAR_D (info);
228 if (widget->priv->
par_n != 0 && widget->priv->
par_d != 0) {
229 display_par_n = widget->priv->
par_n;
230 display_par_d = widget->priv->
par_d;
236 ok = gst_video_calculate_display_ratio (&display_ratio_num,
237 &display_ratio_den, width, height, par_n, par_d, display_par_n,
243 widget->setImplicitWidth (width);
244 widget->setImplicitHeight (height);
246 GST_LOG (
"%p PAR: %u/%u DAR:%u/%u", widget, par_n, par_d, display_par_n,
249 if (height % display_ratio_den == 0) {
250 GST_DEBUG (
"%p keeping video height", widget);
252 gst_util_uint64_scale_int (height, display_ratio_num,
255 }
else if (width % display_ratio_num == 0) {
256 GST_DEBUG (
"%p keeping video width", widget);
259 gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
261 GST_DEBUG (
"%p approximating while keeping video height", widget);
263 gst_util_uint64_scale_int (height, display_ratio_num,
267 GST_DEBUG (
"%p scaling to %dx%d", widget, widget->priv->
display_width,
275 UpdatePaintNodeData * updatePaintNodeData)
277 GstBuffer *old_buffer;
283 GstVideoRectangle src, dst, result;
285 g_mutex_lock (&this->priv->
lock);
287 GST_TRACE (
"%p updatePaintNode",
this);
289 if (gst_gl_context_get_current() == NULL)
294 this->priv->
m_node = texNode;
297 if ((old_buffer = texNode->
getBuffer())) {
298 if (old_buffer == this->priv->
buffer) {
300 gst_buffer_unref (old_buffer);
302 GstBuffer *tmp_buffer;
304 GST_TRACE (
"old buffer %p was bound, queueing up for later", old_buffer);
309 GST_TRACE (
"old buffer %p should be unbound now, unreffing", tmp_buffer);
310 gst_buffer_unref (tmp_buffer);
316 while ((tmp_buffer = (GstBuffer*) g_queue_pop_head (&this->priv->
bound_buffers))) {
317 GST_TRACE (
"old buffer %p is potentially unbound now", tmp_buffer);
332 dst.x = boundingRect().x();
333 dst.y = boundingRect().y();
334 dst.w = boundingRect().width();
335 dst.h = boundingRect().height();
337 gst_video_sink_center_rect (src, dst, &result, TRUE);
339 result.x = boundingRect().x();
340 result.y = boundingRect().y();
341 result.w = boundingRect().width();
342 result.h = boundingRect().height();
345 texNode->setRect (QRectF (result.x, result.y, result.w, result.h));
347 g_mutex_unlock (&this->priv->
lock);
354Qt6GLVideoItem::fitStreamToAllocatedSize(GstVideoRectangle * result)
357 GstVideoRectangle src, dst;
369 gst_video_sink_center_rect (src, dst, result, TRUE);
374 result->h = height();
380Qt6GLVideoItem::mapPointToStreamSize(QPointF pos)
382 gdouble stream_width, stream_height;
383 GstVideoRectangle result;
384 double stream_x, stream_y;
387 fitStreamToAllocatedSize(&result);
389 stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&this->priv->
v_info);
390 stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&this->priv->
v_info);
396 stream_x = (x - result.x) / result.w * stream_width;
401 stream_x = CLAMP(stream_x, 0., stream_width);
405 stream_y = (y - result.y) / result.h * stream_height;
409 stream_y = CLAMP(stream_y, 0., stream_height);
410 GST_TRACE (
"transform %fx%f into %fx%f", x, y, stream_x, stream_y);
412 return QPointF(stream_x, stream_y);
424 mouseHovering =
true;
430 mouseHovering =
false;
446Qt6GLVideoItem::sendMouseEvent(QMouseEvent * event, gboolean is_press)
455 sendMouseEvent(event, TRUE);
461 sendMouseEvent(event, FALSE);
467 QMutexLocker locker(&lock);
471 g_mutex_lock (&qt_item->priv->
lock);
472 g_weak_ref_set (&qt_item->priv->
sink, sink);
473 g_mutex_unlock (&qt_item->priv->
lock);
479 QMutexLocker locker(&lock);
481 if (qt_item == NULL) {
482 GST_WARNING (
"%p actual item is NULL. setBuffer call ignored",
this);
487 GST_WARNING (
"%p Got buffer on unnegotiated QtGLVideoItem. Dropping",
this);
491 g_mutex_lock (&qt_item->priv->
lock);
494 GST_DEBUG (
"%p caps change from %" GST_PTR_FORMAT
" to %" GST_PTR_FORMAT,
496 gst_caps_take (&qt_item->priv->
caps, qt_item->priv->
new_caps);
501 g_mutex_unlock (&qt_item->priv->
lock);
506 gst_buffer_replace (&qt_item->priv->
buffer, buffer);
508 QMetaObject::invokeMethod(qt_item,
"update", Qt::QueuedConnection);
510 g_mutex_unlock (&qt_item->priv->
lock);
514Qt6GLVideoItem::onSceneGraphInitialized ()
516 QSGRendererInterface *renderer;
517 QOpenGLContext *gl_context;
519 if (this->window() == NULL)
522 renderer = this->window()->rendererInterface();
526 if (renderer->graphicsApi() != QSGRendererInterface::GraphicsApi::OpenGL) {
527 GST_WARNING (
"%p scene graph initialized with a non-OpenGL renderer interface",
this);
532 static_cast<QOpenGLContext *
> (
533 renderer->getResource(
535 QSGRendererInterface::Resource::OpenGLContextResource));
537 GST_DEBUG (
"%p scene graph initialization with Qt GL context %p",
this,
545 GST_ERROR (
"%p failed to retrieve Qt GL context",
this);
546 g_assert_not_reached ();
551 &this->priv->other_context, &this->priv->context);
553 GST_DEBUG (
"%p created wrapped GL context %" GST_PTR_FORMAT,
this,
560Qt6GLVideoItem::onSceneGraphInvalidated ()
562 this->priv->
m_node =
nullptr;
563 GST_FIXME (
"%p scene graph invalidated",
this);
573 QMutexLocker locker(&lock);
575 GError *
error = NULL;
580 g_mutex_lock (&qt_item->priv->
lock);
585 g_mutex_unlock (&qt_item->priv->
lock);
589 if (!GST_IS_GL_DISPLAY (qt_item->priv->
display)) {
590 GST_ERROR (
"%p failed to retrieve display connection %" GST_PTR_FORMAT,
591 qt_item, qt_item->priv->
display);
592 g_mutex_unlock (&qt_item->priv->
lock);
597 GST_ERROR (
"%p failed to retrieve wrapped context %" GST_PTR_FORMAT, qt_item,
599 g_mutex_unlock (&qt_item->priv->
lock);
603 qt_item->priv->
context = gst_gl_context_new (qt_item->priv->
display);
606 g_mutex_unlock (&qt_item->priv->
lock);
612 GST_ERROR (
"%s",
error->message);
613 g_mutex_unlock (&qt_item->priv->
lock);
617 g_mutex_unlock (&qt_item->priv->
lock);
622Qt6GLVideoItem::handleWindowChanged (QQuickWindow * win)
625 if (win->isSceneGraphInitialized ())
626 win->scheduleRenderJob (
new RenderJob (std::
628 QQuickWindow::BeforeSynchronizingStage);
630 connect (win, SIGNAL (sceneGraphInitialized ()),
this,
631 SLOT (onSceneGraphInitialized ()), Qt::DirectConnection);
633 connect (win, SIGNAL (sceneGraphInvalidated ()),
this,
634 SLOT (onSceneGraphInvalidated ()), Qt::DirectConnection);
639 this->priv->
m_node =
nullptr;
645 this->priv->
m_node =
nullptr;
651 QMutexLocker locker(&lock);
654 g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
655 g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
660 if (qt_item->priv->
caps && gst_caps_is_equal_fixed (qt_item->priv->
caps, caps))
663 if (!gst_video_info_from_caps (&v_info, caps))
666 g_mutex_lock (&qt_item->priv->
lock);
668 GST_DEBUG (
"%p set caps %" GST_PTR_FORMAT, qt_item, caps);
670 gst_caps_replace (&qt_item->priv->
new_caps, caps);
674 g_mutex_unlock (&qt_item->priv->
lock);
682 QMutexLocker locker(&lock);
687 return (GstGLContext *) gst_object_ref (qt_item->priv->
other_context);
693 QMutexLocker locker(&lock);
695 if (!qt_item || !qt_item->priv->
context)
698 return (GstGLContext *) gst_object_ref (qt_item->priv->
context);
704 QMutexLocker locker(&lock);
706 if (!qt_item || !qt_item->priv->
display)
709 return (GstGLDisplay *) gst_object_ref (qt_item->priv->
display);
715 QMutexLocker locker(&lock);
718 qt_item->setDAR(num, den);
724 QMutexLocker locker(&lock);
727 qt_item->getDAR (num, den);
733 QMutexLocker locker(&lock);
736 qt_item->setForceAspectRatio(force_aspect_ratio);
742 QMutexLocker locker(&lock);
745 return qt_item->getForceAspectRatio();
751 QMutexLocker locker(&lock);
struct _GstElement GstElement
void setBuffer(GstBuffer *buffer)
void setCaps(GstCaps *caps)
GstGLDisplay * getDisplay()
void getDAR(gint *, gint *)
bool getForceAspectRatio()
void setBuffer(GstBuffer *buffer)
gboolean setCaps(GstCaps *caps)
GstGLContext * getContext()
void setForceAspectRatio(bool)
void setSink(GstElement *sink)
GstGLContext * getQtContext()
void mouseReleaseEvent(QMouseEvent *) override
void hoverEnterEvent(QHoverEvent *) override
void hoverLeaveEvent(QHoverEvent *) override
void mousePressEvent(QMouseEvent *) override
QSGNode * updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override
void forceAspectRatioChanged(bool)
void itemInitializedChanged()
void wheelEvent(QWheelEvent *) override
void hoverMoveEvent(QHoverEvent *) override
void releaseResources() override
void touchEvent(QTouchEvent *) override
#define DEFAULT_FORCE_ASPECT_RATIO
gboolean gst_qml6_get_gl_wrapcontext(GstGLDisplay *display, GstGLContext **wrap_glcontext, GstGLContext **context)
GstGLDisplay * gst_qml6_get_gl_display(gboolean sink)
@ PROP_FORCE_ASPECT_RATIO
@ PROP_PIXEL_ASPECT_RATIO
GST_DEBUG_CATEGORY_STATIC(qt_item_debug)
static gboolean _calculate_par(Qt6GLVideoItem *widget, GstVideoInfo *info)
GstQSG6OpenGLNode * m_node
GQueue potentially_unbound_buffers
GstGLContext * other_context
QOpenGLContext * qt_context
gboolean force_aspect_ratio