gtkglsink: Add rotate-method property
authorOlivier CrĂȘte <olivier.crete@collabora.com>
Thu, 3 Sep 2020 22:27:19 +0000 (18:27 -0400)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 12 Oct 2021 20:27:34 +0000 (20:27 +0000)
This mostly just takes code out of glimagesink and applies it here.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1088>

subprojects/gst-plugins-good/docs/gst_plugins_cache.json
subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.c
subprojects/gst-plugins-good/ext/gtk/gstgtkbasesink.h
subprojects/gst-plugins-good/ext/gtk/gstgtkglsink.c
subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.c
subprojects/gst-plugins-good/ext/gtk/gtkgstglwidget.h

index 2ac563d..2e40461 100644 (file)
                         "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": {
index b690c44..2c7fec8 100644 (file)
@@ -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:
index 6501750..314a0f2 100644 (file)
@@ -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__ */
index 18923f5..84e0ffe 100644 (file)
@@ -28,6 +28,7 @@
 #endif
 
 #include <gst/gl/gstglfuncs.h>
+#include <gst/video/gstvideoaffinetransformationmeta.h>
 
 #include "gstgtkglsink.h"
 #include "gtkgstglwidget.h"
 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 <matthew@centricular.com>");
@@ -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;
+}
index 6c423ad..c2d564d 100644 (file)
@@ -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;
+}
index 7f055c4..c5ff0ac 100644 (file)
@@ -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__ */