From 103ceb853a5f7dade07a1ac4aa517e9df6ed5ded Mon Sep 17 00:00:00 2001 From: =?utf8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 3 Sep 2020 18:27:19 -0400 Subject: [PATCH] gtkglsink: Add rotate-method property This mostly just takes code out of glimagesink and applies it here. Part-of: --- .../gst-plugins-good/docs/gst_plugins_cache.json | 15 +- .../gst-plugins-good/ext/gtk/gstgtkbasesink.c | 32 ++-- .../gst-plugins-good/ext/gtk/gstgtkbasesink.h | 3 + .../gst-plugins-good/ext/gtk/gstgtkglsink.c | 99 +++++++++++ .../gst-plugins-good/ext/gtk/gtkgstglwidget.c | 196 ++++++++++++++++++++- .../gst-plugins-good/ext/gtk/gtkgstglwidget.h | 6 + 6 files changed, 328 insertions(+), 23 deletions(-) diff --git a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json index 2ac563d..2e40461 100644 --- a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json @@ -7114,7 +7114,20 @@ "presence": "always" } }, - "properties": {}, + "properties": { + "rotate-method": { + "blurb": "rotate method", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "identity (0)", + "mutable": "null", + "readable": true, + "type": "GstVideoOrientationMethod", + "writable": true + } + }, "rank": "none" }, "gtksink": { diff --git a/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.c b/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.c index b690c44..2c7fec8 100644 --- a/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.c +++ b/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.c @@ -214,6 +214,24 @@ gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink) return gtk_sink->widget; } +GtkWidget * +gst_gtk_base_sink_acquire_widget (GstGtkBaseSink * gtk_sink) +{ + gpointer widget = NULL; + + GST_OBJECT_LOCK (gtk_sink); + if (gtk_sink->widget != NULL) + widget = gtk_sink->widget; + GST_OBJECT_UNLOCK (gtk_sink); + + if (!widget) + widget = + gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_base_sink_get_widget, + gtk_sink); + + return widget; +} + static void gst_gtk_base_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -223,19 +241,7 @@ gst_gtk_base_sink_get_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_WIDGET: { - GObject *widget = NULL; - - GST_OBJECT_LOCK (gtk_sink); - if (gtk_sink->widget != NULL) - widget = G_OBJECT (gtk_sink->widget); - GST_OBJECT_UNLOCK (gtk_sink); - - if (!widget) - widget = - gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_base_sink_get_widget, - gtk_sink); - - g_value_set_object (value, widget); + g_value_set_object (value, gst_gtk_base_sink_acquire_widget (gtk_sink)); break; } case PROP_FORCE_ASPECT_RATIO: diff --git a/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.h b/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.h index 6501750..314a0f2 100644 --- a/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.h +++ b/subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.h @@ -91,6 +91,9 @@ struct _GstGtkBaseSinkClass G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstGtkBaseSink, gst_object_unref) +GtkWidget * +gst_gtk_base_sink_acquire_widget (GstGtkBaseSink * gtk_sink); + G_END_DECLS #endif /* __GST_GTK_BASE_SINK_H__ */ diff --git a/subprojects/gst-plugins-good/ext/gtk/gstgtkglsink.c b/subprojects/gst-plugins-good/ext/gtk/gstgtkglsink.c index 18923f5..84e0ffe 100644 --- a/subprojects/gst-plugins-good/ext/gtk/gstgtkglsink.c +++ b/subprojects/gst-plugins-good/ext/gtk/gstgtkglsink.c @@ -28,6 +28,7 @@ #endif #include +#include #include "gstgtkglsink.h" #include "gtkgstglwidget.h" @@ -35,6 +36,11 @@ GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink); #define GST_CAT_DEFAULT gst_debug_gtk_gl_sink +static void gst_gtk_gl_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gtk_gl_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + static gboolean gst_gtk_gl_sink_start (GstBaseSink * bsink); static gboolean gst_gtk_gl_sink_stop (GstBaseSink * bsink); static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query); @@ -42,6 +48,7 @@ static gboolean gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query); static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter); +static gboolean gst_gtk_gl_sink_event (GstBaseSink * sink, GstEvent * event); static void gst_gtk_gl_sink_finalize (GObject * object); @@ -63,6 +70,12 @@ GST_ELEMENT_REGISTER_DEFINE (gtkglsink, "gtkglsink", GST_RANK_NONE, GST_TYPE_GTK_GL_SINK); +enum +{ + PROP_0, + PROP_ROTATE_METHOD, +}; + static void gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass) { @@ -76,6 +89,8 @@ gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass) gstbasesink_class = (GstBaseSinkClass *) klass; gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass; + gobject_class->set_property = gst_gtk_gl_sink_set_property; + gobject_class->get_property = gst_gtk_gl_sink_get_property; gobject_class->finalize = gst_gtk_gl_sink_finalize; gstbasesink_class->query = gst_gtk_gl_sink_query; @@ -83,10 +98,25 @@ gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass) gstbasesink_class->start = gst_gtk_gl_sink_start; gstbasesink_class->stop = gst_gtk_gl_sink_stop; gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps; + gstbasesink_class->event = gst_gtk_gl_sink_event; gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new; gstgtkbasesink_class->window_title = "Gtk+ GL renderer"; + /** + * gtkglsink:rotate-method: + * + * Rotation method #GstVideoOrientationMethod used to render the media + * + * Since: 1.20 + */ + g_object_class_install_property (gobject_class, PROP_ROTATE_METHOD, + g_param_spec_enum ("rotate-method", + "rotate method", + "rotate method", + GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_set_metadata (gstelement_class, "Gtk GL Video Sink", "Sink/Video", "A video sink that renders to a GtkWidget using OpenGL", "Matthew Waters "); @@ -100,6 +130,41 @@ gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink) { } +static void +gst_gtk_gl_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case PROP_ROTATE_METHOD: + gtk_gst_gl_widget_set_rotate_method (GTK_GST_GL_WIDGET + (gst_gtk_base_sink_acquire_widget (GST_GTK_BASE_SINK (object))), + g_value_get_enum (value), FALSE); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_gtk_gl_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case PROP_ROTATE_METHOD: + g_value_set_enum (value, + gtk_gst_gl_widget_get_rotate_method (GTK_GST_GL_WIDGET + (gst_gtk_base_sink_acquire_widget (GST_GTK_BASE_SINK (object))))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query) { @@ -296,6 +361,8 @@ gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) /* we also support various metadata */ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0); + gst_query_add_allocation_meta (query, + GST_VIDEO_AFFINE_TRANSFORMATION_META_API_TYPE, 0); if (gtk_sink->context->gl_vtable->FenceSync) gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0); @@ -365,3 +432,35 @@ gst_gtk_gl_sink_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } + + +static gboolean +gst_gtk_gl_sink_event (GstBaseSink * sink, GstEvent * event) +{ + GstTagList *taglist; + GstVideoOrientationMethod orientation; + gboolean ret; + GtkGstGLWidget *widget; + + GST_DEBUG_OBJECT (sink, "handling %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG: + gst_event_parse_tag (event, &taglist); + + if (gst_video_orientation_from_tag (taglist, &orientation)) { + + widget = GTK_GST_GL_WIDGET + (gst_gtk_base_sink_acquire_widget (GST_GTK_BASE_SINK (sink))); + + gtk_gst_gl_widget_set_rotate_method (widget, orientation, TRUE); + } + break; + default: + break; + } + + ret = GST_BASE_SINK_CLASS (parent_class)->event (sink, event); + + return ret; +} diff --git a/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.c b/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.c index 6c423ad..c2d564d 100644 --- a/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.c +++ b/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.c @@ -66,6 +66,9 @@ struct _GtkGstGLWidgetPrivate GLint attr_texture; GLuint current_tex; GstGLOverlayCompositor *overlay_compositor; + GstVideoOrientationMethod rotate_method; + GstVideoOrientationMethod current_rotate_method; + const gfloat *transform_matrix; }; static const GLfloat vertices[] = { @@ -119,9 +122,18 @@ gtk_gst_gl_widget_init_redisplay (GtkGstGLWidget * gst_widget) GtkGstGLWidgetPrivate *priv = gst_widget->priv; const GstGLFuncs *gl = priv->context->gl_vtable; GError *error = NULL; + GstGLSLStage *frag_stage, *vert_stage; + + vert_stage = gst_glsl_stage_new_with_string (priv->context, + GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE, + GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, + gst_gl_shader_string_vertex_mat4_vertex_transform); + frag_stage = gst_glsl_stage_new_default_fragment (priv->context); gst_gl_insert_debug_marker (priv->other_context, "initializing redisplay"); - if (!(priv->shader = gst_gl_shader_new_default (priv->context, &error))) { + if (!(priv->shader = + gst_gl_shader_new_link_with_stages (priv->context, &error, vert_stage, + frag_stage, NULL))) { GST_ERROR ("Failed to initialize shader: %s", error->message); return; } @@ -154,36 +166,107 @@ gtk_gst_gl_widget_init_redisplay (GtkGstGLWidget * gst_widget) priv->initted = TRUE; } +/* rotate 90 */ +static const gfloat clockwise_matrix[] = { + 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* rotate 180 */ +static const gfloat clockwise_180_matrix[] = { + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* rotate 270 */ +static const gfloat counterclockwise_matrix[] = { + 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* horizontal-flip */ +static const gfloat horizontal_flip_matrix[] = { + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* vertical-flip */ +static const gfloat vertical_flip_matrix[] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* upper-left-diagonal */ +static const gfloat upper_left_matrix[] = { + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* upper-right-diagonal */ +static const gfloat upper_right_matrix[] = { + 0.0f, -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + static void _redraw_texture (GtkGstGLWidget * gst_widget, guint tex) { GtkGstGLWidgetPrivate *priv = gst_widget->priv; const GstGLFuncs *gl = priv->context->gl_vtable; const GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; + GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (gst_widget); + GtkWidget *widget = GTK_WIDGET (gst_widget); + if (gst_widget->base.force_aspect_ratio) { GstVideoRectangle src, dst, result; - gint widget_width, widget_height, widget_scale; + gint video_width, video_height, widget_scale; gl->ClearColor (0.0, 0.0, 0.0, 0.0); gl->Clear (GL_COLOR_BUFFER_BIT); - widget_scale = gtk_widget_get_scale_factor ((GtkWidget *) gst_widget); - widget_width = gtk_widget_get_allocated_width ((GtkWidget *) gst_widget); - widget_height = gtk_widget_get_allocated_height ((GtkWidget *) gst_widget); + widget_scale = gtk_widget_get_scale_factor (widget); + + if (priv->current_rotate_method == GST_VIDEO_ORIENTATION_90R + || priv->current_rotate_method == GST_VIDEO_ORIENTATION_90L + || priv->current_rotate_method == GST_VIDEO_ORIENTATION_UL_LR + || priv->current_rotate_method == GST_VIDEO_ORIENTATION_UR_LL) { + video_width = base_widget->display_height; + video_height = base_widget->display_width; + } else { + video_width = base_widget->display_width; + video_height = base_widget->display_height; + } src.x = 0; src.y = 0; - src.w = gst_widget->base.display_width; - src.h = gst_widget->base.display_height; + src.w = video_width; + src.h = video_height; dst.x = 0; dst.y = 0; - dst.w = widget_width * widget_scale; - dst.h = widget_height * widget_scale; + dst.w = gtk_widget_get_allocated_width (widget) * widget_scale; + dst.h = gtk_widget_get_allocated_height (widget) * widget_scale; gst_video_sink_center_rect (src, dst, &result, TRUE); + GST_LOG ("Center src %dx%d into dst %dx%d result -> %dx%d", + src.w, src.h, dst.w, dst.h, result.w, result.h); + gl->Viewport (result.x, result.y, result.w, result.h); } @@ -197,6 +280,26 @@ _redraw_texture (GtkGstGLWidget * gst_widget, guint tex) gl->BindTexture (GL_TEXTURE_2D, tex); gst_gl_shader_set_uniform_1i (priv->shader, "tex", 0); + { + GstVideoAffineTransformationMeta *af_meta; + gfloat matrix[16]; + + af_meta = + gst_buffer_get_video_affine_transformation_meta (base_widget->buffer); + + if (priv->transform_matrix) { + gfloat tmp[16]; + + gst_gl_get_affine_transformation_meta_as_ndc (af_meta, tmp); + gst_gl_multiply_matrix4 (tmp, priv->transform_matrix, matrix); + } else { + gst_gl_get_affine_transformation_meta_as_ndc (af_meta, matrix); + } + + gst_gl_shader_set_uniform_matrix_4fv (priv->shader, + "u_transformation", 1, FALSE, matrix); + } + gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); if (gl->BindVertexArray) @@ -579,3 +682,78 @@ gtk_gst_gl_widget_get_display (GtkGstGLWidget * gst_widget) return gst_object_ref (gst_widget->priv->display); } + +void +gtk_gst_gl_widget_set_rotate_method (GtkGstGLWidget * gst_widget, + GstVideoOrientationMethod method, gboolean from_tag) +{ + GstVideoOrientationMethod tag_method = GST_VIDEO_ORIENTATION_AUTO; + GtkGstGLWidgetPrivate *priv = gst_widget->priv; + + if (method == GST_VIDEO_ORIENTATION_CUSTOM) { + GST_WARNING_OBJECT (gst_widget, "unsupported custom orientation"); + return; + } + + GTK_GST_BASE_WIDGET_LOCK (gst_widget); + if (from_tag) + tag_method = method; + else + priv->rotate_method = method; + + if (priv->rotate_method == GST_VIDEO_ORIENTATION_AUTO) + method = tag_method; + else + method = priv->rotate_method; + + if (method != priv->current_rotate_method) { + GST_DEBUG ("Changing method from %d to %d", + priv->current_rotate_method, method); + + switch (method) { + case GST_VIDEO_ORIENTATION_IDENTITY: + priv->transform_matrix = NULL; + break; + case GST_VIDEO_ORIENTATION_90R: + priv->transform_matrix = clockwise_matrix; + break; + case GST_VIDEO_ORIENTATION_180: + priv->transform_matrix = clockwise_180_matrix; + break; + case GST_VIDEO_ORIENTATION_90L: + priv->transform_matrix = counterclockwise_matrix; + break; + case GST_VIDEO_ORIENTATION_HORIZ: + priv->transform_matrix = horizontal_flip_matrix; + break; + case GST_VIDEO_ORIENTATION_VERT: + priv->transform_matrix = vertical_flip_matrix; + break; + case GST_VIDEO_ORIENTATION_UL_LR: + priv->transform_matrix = upper_left_matrix; + break; + case GST_VIDEO_ORIENTATION_UR_LL: + priv->transform_matrix = upper_right_matrix; + break; + default: + g_assert_not_reached (); + break; + } + + priv->current_rotate_method = method; + } + GTK_GST_BASE_WIDGET_UNLOCK (gst_widget); +} + +GstVideoOrientationMethod +gtk_gst_gl_widget_get_rotate_method (GtkGstGLWidget * gst_widget) +{ + GtkGstGLWidgetPrivate *priv = gst_widget->priv; + GstVideoOrientationMethod method; + + GTK_GST_BASE_WIDGET_LOCK (gst_widget); + method = priv->current_rotate_method; + GTK_GST_BASE_WIDGET_UNLOCK (gst_widget); + + return method; +} diff --git a/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.h b/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.h index 7f055c4..c5ff0ac 100644 --- a/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.h +++ b/subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.h @@ -72,6 +72,12 @@ GstGLDisplay * gtk_gst_gl_widget_get_display (GtkGstGLWidget * widget) GstGLContext * gtk_gst_gl_widget_get_context (GtkGstGLWidget * widget); GstGLContext * gtk_gst_gl_widget_get_gtk_context (GtkGstGLWidget * widget); +void gtk_gst_gl_widget_set_rotate_method (GtkGstGLWidget * gst_widget, + GstVideoOrientationMethod method, gboolean from_tag); +GstVideoOrientationMethod gtk_gst_gl_widget_get_rotate_method ( + GtkGstGLWidget * gst_widget); + + G_END_DECLS #endif /* __GTK_GST_GL_WIDGET_H__ */ -- 2.7.4