qt: Add navigation events support

Currently handles only mouse events.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/567>
This commit is contained in:
Philippe Normand 2020-04-15 10:38:04 +01:00
parent a9bb6d4572
commit 5f1b290fe8
3 changed files with 239 additions and 6 deletions

View file

@ -81,6 +81,7 @@
#define GST_CAT_DEFAULT gst_debug_qt_gl_sink
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
static void gst_qt_sink_navigation_interface_init (GstNavigationInterface * iface);
static void gst_qt_sink_finalize (GObject * object);
static void gst_qt_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * param_spec);
@ -134,7 +135,9 @@ enum
#define gst_qt_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstQtSink, gst_qt_sink,
GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
"qtsink", 0, "Qt Video Sink"));
"qtsink", 0, "Qt Video Sink");
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
gst_qt_sink_navigation_interface_init));
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qmlglsink, "qmlglsink",
GST_RANK_NONE, GST_TYPE_QT_SINK, qt5_element_init (plugin));
@ -194,6 +197,8 @@ static void
gst_qt_sink_init (GstQtSink * qt_sink)
{
qt_sink->widget = QSharedPointer<QtGLVideoItemInterface>();
if (qt_sink->widget)
qt_sink->widget->setSink (GST_ELEMENT_CAST (qt_sink));
}
static void
@ -205,10 +210,14 @@ gst_qt_sink_set_property (GObject * object, guint prop_id,
switch (prop_id) {
case PROP_WIDGET: {
QtGLVideoItem *qt_item = static_cast<QtGLVideoItem *> (g_value_get_pointer (value));
if (qt_item)
if (qt_item) {
qt_sink->widget = qt_item->getInterface();
else
if (qt_sink->widget) {
qt_sink->widget->setSink (GST_ELEMENT_CAST (qt_sink));
}
} else {
qt_sink->widget.clear();
}
break;
}
case PROP_FORCE_ASPECT_RATIO:
@ -546,3 +555,33 @@ config_failed:
return FALSE;
}
}
static void
gst_qt_sink_navigation_send_event (GstNavigation * navigation,
GstStructure * structure)
{
GstQtSink *qt_sink = GST_QT_SINK (navigation);
GstEvent *event;
GstPad *pad;
event = gst_event_new_navigation (structure);
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (qt_sink));
GST_TRACE_OBJECT (qt_sink, "navigation event %" GST_PTR_FORMAT, structure);
if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
if (!gst_pad_send_event (pad, gst_event_ref (event))) {
/* If upstream didn't handle the event we'll post a message with it
* for the application in case it wants to do something with it */
gst_element_post_message (GST_ELEMENT_CAST (qt_sink),
gst_navigation_message_new_event (GST_OBJECT_CAST (qt_sink), event));
}
gst_event_unref (event);
gst_object_unref (pad);
}
}
static void gst_qt_sink_navigation_interface_init (GstNavigationInterface * iface)
{
iface->send_event = gst_qt_sink_navigation_send_event;
}

View file

@ -37,9 +37,8 @@
#include <QtQuick/QSGSimpleTextureNode>
/**
* SECTION:gtkgstglwidget
* @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers
* @see_also: #GtkGLArea, #GstBuffer
* SECTION:QtGLVideoItem
* @short_description: a Qt5 QtQuick item that renders GStreamer video #GstBuffers
*
* #QtGLVideoItem is an #QQuickItem that renders GStreamer video buffers.
*/
@ -66,6 +65,8 @@ struct _QtGLVideoItemPrivate
gboolean force_aspect_ratio;
gint par_n, par_d;
GWeakRef sink;
gint display_width;
gint display_height;
@ -129,6 +130,8 @@ QtGLVideoItem::QtGLVideoItem()
g_mutex_init (&this->priv->lock);
g_weak_ref_init (&priv->sink, NULL);
this->priv->display = gst_qt_get_gl_display(TRUE);
connect(this, SIGNAL(windowChanged(QQuickWindow*)), this,
@ -136,6 +139,10 @@ QtGLVideoItem::QtGLVideoItem()
this->proxy = QSharedPointer<QtGLVideoItemInterface>(new QtGLVideoItemInterface(this));
setFlag(ItemHasContents, true);
setAcceptedMouseButtons(Qt::AllButtons);
setAcceptHoverEvents(true);
GST_DEBUG ("%p init Qt Video Item", this);
}
@ -171,6 +178,9 @@ QtGLVideoItem::~QtGLVideoItem()
gst_buffer_replace (&this->priv->buffer, NULL);
gst_caps_replace (&this->priv->caps, NULL);
g_weak_ref_clear (&this->priv->sink);
g_free (this->priv);
this->priv = NULL;
}
@ -305,6 +315,165 @@ QtGLVideoItem::updatePaintNode(QSGNode * oldNode,
return texNode;
}
/* This method has to be invoked with the the priv->lock taken */
void
QtGLVideoItem::fitStreamToAllocatedSize(GstVideoRectangle * result)
{
if (this->priv->force_aspect_ratio) {
GstVideoRectangle src, dst;
src.x = 0;
src.y = 0;
src.w = this->priv->display_width;
src.h = this->priv->display_height;
dst.x = 0;
dst.y = 0;
dst.w = size().width();
dst.h = size().height();
gst_video_sink_center_rect (src, dst, result, TRUE);
} else {
result->x = 0;
result->y = 0;
result->w = size().width();
result->h = size().height();
}
}
/* This method has to be invoked with the the priv->lock taken */
QPointF
QtGLVideoItem::mapPointToStreamSize(QPointF pos)
{
gdouble stream_width, stream_height;
GstVideoRectangle result;
double stream_x, stream_y;
double x, y;
fitStreamToAllocatedSize(&result);
stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&this->priv->v_info);
stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&this->priv->v_info);
x = pos.x();
y = pos.y();
/* from display coordinates to stream coordinates */
if (result.w > 0)
stream_x = (x - result.x) / result.w * stream_width;
else
stream_x = 0.;
/* clip to stream size */
stream_x = CLAMP(stream_x, 0., stream_width);
/* same for y-axis */
if (result.h > 0)
stream_y = (y - result.y) / result.h * stream_height;
else
stream_y = 0.;
stream_y = CLAMP(stream_y, 0., stream_height);
GST_TRACE ("transform %fx%f into %fx%f", x, y, stream_x, stream_y);
return QPointF(stream_x, stream_y);
}
void
QtGLVideoItem::wheelEvent(QWheelEvent * event)
{
g_mutex_lock (&this->priv->lock);
QPoint delta = event->angleDelta();
GstElement *element = GST_ELEMENT_CAST (g_weak_ref_get (&this->priv->sink));
if (element != NULL) {
#if (QT_VERSION >= QT_VERSION_CHECK (5, 14, 0))
auto position = event->position();
#else
auto position = *event;
#endif
gst_navigation_send_mouse_scroll_event (GST_NAVIGATION (element),
position.x(), position.y(), delta.x(), delta.y());
g_object_unref (element);
}
g_mutex_unlock (&this->priv->lock);
}
void
QtGLVideoItem::hoverEnterEvent(QHoverEvent *)
{
m_hovering = true;
}
void
QtGLVideoItem::hoverLeaveEvent(QHoverEvent *)
{
m_hovering = false;
}
void
QtGLVideoItem::hoverMoveEvent(QHoverEvent * event)
{
if (!m_hovering)
return;
int button = !!m_mousePressedButton;
g_mutex_lock (&this->priv->lock);
if (event->pos() != event->oldPos()) {
QPointF pos = mapPointToStreamSize(event->pos());
GstElement *element = GST_ELEMENT_CAST (g_weak_ref_get (&this->priv->sink));
if (element != NULL) {
gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move",
button, pos.x(), pos.y());
g_object_unref (element);
}
}
g_mutex_unlock (&this->priv->lock);
}
void
QtGLVideoItem::sendMouseEvent(QMouseEvent * event, const gchar * type)
{
int button = 0;
switch (event->button()) {
case Qt::LeftButton:
button = 1;
break;
case Qt::RightButton:
button = 2;
break;
default:
break;
}
m_mousePressedButton = button;
g_mutex_lock (&this->priv->lock);
QPointF pos = mapPointToStreamSize(event->pos());
gchar* event_type = g_strconcat ("mouse-button-", type, NULL);
GstElement *element = GST_ELEMENT_CAST (g_weak_ref_get (&this->priv->sink));
if (element != NULL) {
gst_navigation_send_mouse_event (GST_NAVIGATION (element), event_type,
button, pos.x(), pos.y());
g_object_unref (element);
}
g_free (event_type);
g_mutex_unlock (&this->priv->lock);
}
void
QtGLVideoItem::mousePressEvent(QMouseEvent * event)
{
forceActiveFocus();
sendMouseEvent(event, "press");
}
void
QtGLVideoItem::mouseReleaseEvent(QMouseEvent * event)
{
sendMouseEvent(event, "release");
}
static void
_reset (QtGLVideoItem * qt_item)
{
@ -327,6 +496,18 @@ _reset (QtGLVideoItem * qt_item)
}
}
void
QtGLVideoItemInterface::setSink (GstElement * sink)
{
QMutexLocker locker(&lock);
if (qt_item == NULL)
return;
g_mutex_lock (&qt_item->priv->lock);
g_weak_ref_set (&qt_item->priv->sink, sink);
g_mutex_unlock (&qt_item->priv->lock);
}
void
QtGLVideoItemInterface::setBuffer (GstBuffer * buffer)
{

View file

@ -42,6 +42,7 @@ public:
void invalidateRef();
void setSink (GstElement * sink);
void setBuffer (GstBuffer * buffer);
gboolean setCaps (GstCaps *caps);
gboolean initWinSys ();
@ -98,6 +99,12 @@ private Q_SLOTS:
protected:
QSGNode * updatePaintNode (QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData);
void wheelEvent(QWheelEvent *) override;
void hoverEnterEvent(QHoverEvent *) override;
void hoverLeaveEvent (QHoverEvent *) override;
void hoverMoveEvent (QHoverEvent *) override;
void mousePressEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
private:
@ -105,8 +112,14 @@ private:
void setViewportSize(const QSize &size);
void shareContext();
void fitStreamToAllocatedSize(GstVideoRectangle * result);
QPointF mapPointToStreamSize(QPointF);
void sendMouseEvent(QMouseEvent * event, const gchar * type);
QSize m_viewportSize;
bool m_openGlContextInitialized;
bool m_hovering;
uint32_t m_mousePressedButton;
QSharedPointer<QtGLVideoItemInterface> proxy;
};