From 7fc93fbebc9320a48c439d34721075270181f824 Mon Sep 17 00:00:00 2001 From: "Eugenio Paolantonio (g7)" Date: Sun, 26 Mar 2023 16:14:22 +0200 Subject: [PATCH 1/3] plugin: Add helper to get the actual width and height for a specific rotation Signed-off-by: Eugenio Paolantonio (g7) --- lib/gst/plugin/gstgtkutils.c | 20 ++++++++++++++++++++ lib/gst/plugin/gstgtkutils.h | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/lib/gst/plugin/gstgtkutils.c b/lib/gst/plugin/gstgtkutils.c index 19cf233b..4570726f 100644 --- a/lib/gst/plugin/gstgtkutils.c +++ b/lib/gst/plugin/gstgtkutils.c @@ -140,3 +140,23 @@ gst_video_frame_into_gdk_texture (GstVideoFrame *frame) return texture; } + +void +gst_gtk_get_width_height_for_rotation (gint width, gint height, + gint *out_width, gint *out_height, + GstVideoOrientationMethod rotation) +{ + switch (rotation) { + case GST_VIDEO_ORIENTATION_90R: + case GST_VIDEO_ORIENTATION_90L: + case GST_VIDEO_ORIENTATION_UL_LR: + case GST_VIDEO_ORIENTATION_UR_LL: + *out_width = height; + *out_height = width; + break; + default: + *out_width = width; + *out_height = height; + break; + } +} diff --git a/lib/gst/plugin/gstgtkutils.h b/lib/gst/plugin/gstgtkutils.h index 3e8fa5dc..972eccba 100644 --- a/lib/gst/plugin/gstgtkutils.h +++ b/lib/gst/plugin/gstgtkutils.h @@ -32,4 +32,8 @@ gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointe GdkTexture * gst_video_frame_into_gdk_texture (GstVideoFrame *frame); +void gst_gtk_get_width_height_for_rotation (gint width, gint height, + gint *out_width, gint *out_height, + GstVideoOrientationMethod rotation); + G_END_DECLS From 4e250c8f2aa4a6b81a572761bfc0d62170da6e18 Mon Sep 17 00:00:00 2001 From: "Eugenio Paolantonio (g7)" Date: Sun, 26 Mar 2023 16:20:56 +0200 Subject: [PATCH 2/3] plugin: paintable: Support setting media rotation. Contributes to #310 Rotation can be set by passing a GstVideoOrientationMethod to the gst_clapper_paintable_set_rotation() function. The actual rotation is done through GskTransforms directly on the Snapshot. Signed-off-by: Eugenio Paolantonio (g7) --- lib/gst/plugin/gstclapperpaintable.c | 105 +++++++++++++++++++++++++-- lib/gst/plugin/gstclapperpaintable.h | 15 ++-- 2 files changed, 109 insertions(+), 11 deletions(-) diff --git a/lib/gst/plugin/gstclapperpaintable.c b/lib/gst/plugin/gstclapperpaintable.c index 5710355d..c28257e4 100644 --- a/lib/gst/plugin/gstclapperpaintable.c +++ b/lib/gst/plugin/gstclapperpaintable.c @@ -23,6 +23,8 @@ #include "gstclapperpaintable.h" +#include "gstgtkutils.h" + #define DEFAULT_PAR_N 1 #define DEFAULT_PAR_D 1 @@ -57,6 +59,8 @@ gst_clapper_paintable_init (GstClapperPaintable *self) self->display_height = 1; self->display_aspect_ratio = 1.0; + self->rotation = GST_VIDEO_ORIENTATION_IDENTITY; + self->par_n = DEFAULT_PAR_N; self->par_d = DEFAULT_PAR_D; @@ -111,8 +115,8 @@ calculate_display_par (GstClapperPaintable *self, const GstVideoInfo *info) gint width, height, par_n, par_d, req_par_n, req_par_d; gboolean success; - width = GST_VIDEO_INFO_WIDTH (info); - height = GST_VIDEO_INFO_HEIGHT (info); + gst_gtk_get_width_height_for_rotation (GST_VIDEO_INFO_WIDTH (info), + GST_VIDEO_INFO_HEIGHT (info), &width, &height, self->rotation); /* Cannot apply aspect ratio if there is no video */ if (width == 0 || height == 0) @@ -152,8 +156,9 @@ invalidate_paintable_size_internal (GstClapperPaintable *self) GST_CLAPPER_PAINTABLE_LOCK (self); - video_width = GST_VIDEO_INFO_WIDTH (&self->v_info); - video_height = GST_VIDEO_INFO_HEIGHT (&self->v_info); + gst_gtk_get_width_height_for_rotation (GST_VIDEO_INFO_WIDTH (&self->v_info), + GST_VIDEO_INFO_HEIGHT (&self->v_info), &video_height, &video_width, + self->rotation); display_ratio_num = self->display_ratio_num; display_ratio_den = self->display_ratio_den; @@ -322,6 +327,38 @@ gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *self, GST_CLAPPER_PAINTABLE_UNLOCK (self); } +void +gst_clapper_paintable_set_rotation (GstClapperPaintable *self, + GstVideoOrientationMethod rotation) +{ + GST_CLAPPER_PAINTABLE_LOCK (self); + + self->rotation = rotation; + + if (G_UNLIKELY (!calculate_display_par (self, &self->v_info))) { + GST_CLAPPER_PAINTABLE_UNLOCK (self); + return; + } + + self->pending_resize = TRUE; + + GST_CLAPPER_PAINTABLE_UNLOCK (self); +} + +GstVideoOrientationMethod +gst_clapper_paintable_get_rotation (GstClapperPaintable *self) +{ + GstVideoOrientationMethod rotation; + + GST_CLAPPER_PAINTABLE_LOCK (self); + + rotation = self->rotation; + + GST_CLAPPER_PAINTABLE_UNLOCK (self); + + return rotation; +} + /* * GdkPaintableInterface */ @@ -331,6 +368,8 @@ gst_clapper_paintable_snapshot_internal (GstClapperPaintable *self, gint widget_width, gint widget_height) { gfloat scale_x, scale_y; + gdouble snapshot_width, snapshot_height; + GskTransform *transform = NULL; GST_LOG_OBJECT (self, "Snapshot"); @@ -358,7 +397,63 @@ gst_clapper_paintable_snapshot_internal (GstClapperPaintable *self, GST_CLAPPER_PAINTABLE_IMPORTER_LOCK (self); if (self->importer) { - gst_clapper_importer_snapshot (self->importer, snapshot, width, height); + switch (self->rotation) { + case GST_VIDEO_ORIENTATION_IDENTITY: + default: + snapshot_width = width; + snapshot_height = height; + break; + case GST_VIDEO_ORIENTATION_90R: + transform = gsk_transform_rotate (transform, 90); + transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (0, -width)); + snapshot_width = height; + snapshot_height = width; + break; + case GST_VIDEO_ORIENTATION_180: + transform = gsk_transform_rotate (transform, 180); + transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width, -height)); + snapshot_width = width; + snapshot_height = height; + break; + case GST_VIDEO_ORIENTATION_90L: + transform = gsk_transform_rotate (transform, 270); + transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height, 0)); + snapshot_width = height; + snapshot_height = width; + break; + case GST_VIDEO_ORIENTATION_HORIZ: + transform = gsk_transform_rotate_3d (transform, 180, graphene_vec3_y_axis ()); + transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width, 0)); + snapshot_width = width; + snapshot_height = height; + break; + case GST_VIDEO_ORIENTATION_VERT: + transform = gsk_transform_rotate_3d (transform, 180, graphene_vec3_x_axis ()); + transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (0, -height)); + snapshot_width = width; + snapshot_height = height; + break; + case GST_VIDEO_ORIENTATION_UL_LR: + transform = gsk_transform_rotate (transform, 90); + transform = gsk_transform_rotate_3d (transform, 180, graphene_vec3_x_axis ()); + snapshot_width = height; + snapshot_height = width; + break; + case GST_VIDEO_ORIENTATION_UR_LL: + transform = gsk_transform_rotate (transform, 90); + transform = gsk_transform_rotate_3d (transform, 180, graphene_vec3_y_axis ()); + transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height, -width)); + snapshot_width = height; + snapshot_height = width; + break; + } + + if (transform) { + gtk_snapshot_transform (snapshot, transform); + gsk_transform_unref (transform); + } + + gst_clapper_importer_snapshot (self->importer, snapshot, snapshot_width, snapshot_height); } else { GST_LOG_OBJECT (self, "No texture importer, drawing black"); gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height)); diff --git a/lib/gst/plugin/gstclapperpaintable.h b/lib/gst/plugin/gstclapperpaintable.h index 9a0d0750..6606037c 100644 --- a/lib/gst/plugin/gstclapperpaintable.h +++ b/lib/gst/plugin/gstclapperpaintable.h @@ -56,6 +56,7 @@ struct _GstClapperPaintable /* Sink properties */ gint par_n, par_d; + GstVideoOrientationMethod rotation; /* Resize */ gboolean pending_resize; @@ -71,11 +72,13 @@ struct _GstClapperPaintable guint draw_id; }; -GstClapperPaintable * gst_clapper_paintable_new (void); -void gst_clapper_paintable_queue_draw (GstClapperPaintable *paintable); -void gst_clapper_paintable_set_widget (GstClapperPaintable *paintable, GtkWidget *widget); -void gst_clapper_paintable_set_importer (GstClapperPaintable *paintable, GstClapperImporter *importer); -gboolean gst_clapper_paintable_set_video_info (GstClapperPaintable *paintable, const GstVideoInfo *v_info); -void gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *paintable, gint par_n, gint par_d); +GstClapperPaintable * gst_clapper_paintable_new (void); +void gst_clapper_paintable_queue_draw (GstClapperPaintable *paintable); +void gst_clapper_paintable_set_widget (GstClapperPaintable *paintable, GtkWidget *widget); +void gst_clapper_paintable_set_importer (GstClapperPaintable *paintable, GstClapperImporter *importer); +gboolean gst_clapper_paintable_set_video_info (GstClapperPaintable *paintable, const GstVideoInfo *v_info); +void gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *paintable, gint par_n, gint par_d); +void gst_clapper_paintable_set_rotation (GstClapperPaintable *paintable, GstVideoOrientationMethod rotation); +GstVideoOrientationMethod gst_clapper_paintable_get_rotation (GstClapperPaintable *paintable); G_END_DECLS From e275c3017ae32eea713ae326e7d8a29a7481fbb0 Mon Sep 17 00:00:00 2001 From: "Eugenio Paolantonio (g7)" Date: Sat, 1 Apr 2023 15:08:43 +0200 Subject: [PATCH 3/3] plugin: sink: allow detecting and/or setting media rotation. Fixes #310 GstClapperSink now allows to specify the media rotation via the `rotation` property, which should be of type GstVideoOrientationMethod. The default behaviour is to detect the media rotation automatically. Signed-off-by: Eugenio Paolantonio (g7) --- lib/gst/plugin/gstclappersink.c | 59 +++++++++++++++++++++++++++++++-- lib/gst/plugin/gstclappersink.h | 2 ++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/lib/gst/plugin/gstclappersink.c b/lib/gst/plugin/gstclappersink.c index ae4c3e4c..0672f31c 100644 --- a/lib/gst/plugin/gstclappersink.c +++ b/lib/gst/plugin/gstclappersink.c @@ -28,6 +28,7 @@ #define DEFAULT_PAR_N 1 #define DEFAULT_PAR_D 1 #define DEFAULT_KEEP_LAST_FRAME FALSE +#define DEFAULT_ROTATION GST_VIDEO_ORIENTATION_AUTO #define WINDOW_CSS_CLASS_NAME "clappersinkwindow" @@ -38,6 +39,7 @@ enum PROP_FORCE_ASPECT_RATIO, PROP_PIXEL_ASPECT_RATIO, PROP_KEEP_LAST_FRAME, + PROP_ROTATE_METHOD, PROP_LAST }; @@ -116,8 +118,9 @@ calculate_stream_coords (GstClapperSink *self, GtkWidget *widget, GST_CLAPPER_SINK_LOCK (self); - video_width = GST_VIDEO_INFO_WIDTH (&self->v_info); - video_height = GST_VIDEO_INFO_HEIGHT (&self->v_info); + gst_gtk_get_width_height_for_rotation (GST_VIDEO_INFO_WIDTH (&self->v_info), + GST_VIDEO_INFO_HEIGHT (&self->v_info), &video_height, &video_width, + gst_clapper_paintable_get_rotation (self->paintable)); force_aspect_ratio = self->force_aspect_ratio; GST_CLAPPER_SINK_UNLOCK (self); @@ -343,6 +346,9 @@ gst_clapper_sink_get_property (GObject *object, guint prop_id, case PROP_KEEP_LAST_FRAME: g_value_set_boolean (value, self->keep_last_frame); break; + case PROP_ROTATE_METHOD: + g_value_set_enum (value, self->rotation_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -385,6 +391,13 @@ gst_clapper_sink_set_property (GObject *object, guint prop_id, case PROP_KEEP_LAST_FRAME: self->keep_last_frame = g_value_get_boolean (value); break; + case PROP_ROTATE_METHOD: + self->rotation_mode = g_value_get_enum (value); + + gst_clapper_paintable_set_rotation (self->paintable, + (self->rotation_mode == GST_VIDEO_ORIENTATION_AUTO) ? + self->stream_orientation : self->rotation_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -633,6 +646,32 @@ gst_clapper_sink_stop (GstBaseSink *bsink) return TRUE; } +static gboolean +gst_clapper_sink_event (GstBaseSink *bsink, GstEvent *event) +{ + GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink); + GstTagList *taglist; + GstVideoOrientationMethod orientation; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG: + gst_event_parse_tag (event, &taglist); + + if (gst_video_orientation_from_tag (taglist, &orientation)) { + GST_CLAPPER_SINK_LOCK (self); + self->stream_orientation = orientation; + if (self->rotation_mode == GST_VIDEO_ORIENTATION_AUTO) + gst_clapper_paintable_set_rotation (self->paintable, orientation); + GST_CLAPPER_SINK_UNLOCK (self); + } + break; + default: + break; + } + + return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event); +} + static GstStateChangeReturn gst_clapper_sink_change_state (GstElement *element, GstStateChange transition) { @@ -643,6 +682,14 @@ gst_clapper_sink_change_state (GstElement *element, GstStateChange transition) gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + /* Reset stream_orientation */ + GST_CLAPPER_SINK_LOCK (self); + self->stream_orientation = GST_VIDEO_ORIENTATION_IDENTITY; + if (self->rotation_mode == GST_VIDEO_ORIENTATION_AUTO) + gst_clapper_paintable_set_rotation (self->paintable, self->stream_orientation); + GST_CLAPPER_SINK_UNLOCK (self); + break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_CLAPPER_SINK_LOCK (self); if (!self->keep_last_frame && self->importer) { @@ -794,6 +841,7 @@ gst_clapper_sink_init (GstClapperSink *self) self->par_n = DEFAULT_PAR_N; self->par_d = DEFAULT_PAR_D; self->keep_last_frame = DEFAULT_KEEP_LAST_FRAME; + self->rotation_mode = DEFAULT_ROTATION; g_mutex_init (&self->lock); gst_video_info_init (&self->v_info); @@ -869,6 +917,12 @@ gst_clapper_sink_class_init (GstClapperSinkClass *klass) DEFAULT_KEEP_LAST_FRAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ROTATE_METHOD, + g_param_spec_enum ("rotate-method", "Rotate Method", + "Rotate method to use", + GST_TYPE_VIDEO_ORIENTATION_METHOD, DEFAULT_ROTATION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = gst_clapper_sink_change_state; gstbasesink_class->get_caps = gst_clapper_sink_get_caps; @@ -878,6 +932,7 @@ gst_clapper_sink_class_init (GstClapperSinkClass *klass) gstbasesink_class->query = gst_clapper_sink_query; gstbasesink_class->start = gst_clapper_sink_start; gstbasesink_class->stop = gst_clapper_sink_stop; + gstbasesink_class->event = gst_clapper_sink_event; gstvideosink_class->set_info = gst_clapper_sink_set_info; gstvideosink_class->show_frame = gst_clapper_sink_show_frame; diff --git a/lib/gst/plugin/gstclappersink.h b/lib/gst/plugin/gstclappersink.h index 489c64bb..2f220ec0 100644 --- a/lib/gst/plugin/gstclappersink.h +++ b/lib/gst/plugin/gstclappersink.h @@ -50,6 +50,7 @@ struct _GstClapperSink GstClapperImporterLoader *loader; GstClapperImporter *importer; GstVideoInfo v_info; + GstVideoOrientationMethod stream_orientation; GtkWidget *widget; GtkWindow *window; @@ -58,6 +59,7 @@ struct _GstClapperSink gboolean force_aspect_ratio; gint par_n, par_d; gboolean keep_last_frame; + GstVideoOrientationMethod rotation_mode; /* Position coords */ gdouble last_pos_x;