gtk: implement video aspect-ratio handling
authorMatthew Waters <matthew@centricular.com>
Fri, 12 Jun 2015 05:17:30 +0000 (15:17 +1000)
committerMatthew Waters <matthew@centricular.com>
Fri, 12 Jun 2015 05:17:30 +0000 (15:17 +1000)
For both the software and the GL sink's.

Doesn't deal with the pixel-aspect-ratio field at all yet.

ext/gtk/gstgtkglsink.c
ext/gtk/gstgtkglsink.h
ext/gtk/gstgtksink.c
ext/gtk/gstgtksink.h
ext/gtk/gtkgstglwidget.c
ext/gtk/gtkgstwidget.c

index c89c940..372846f 100644 (file)
@@ -32,6 +32,8 @@
 GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
 #define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
 
+#define DEFAULT_FORCE_ASPECT_RATIO  TRUE
+
 static void gst_gtk_gl_sink_finalize (GObject * object);
 static void gst_gtk_gl_sink_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * param_spec);
@@ -62,8 +64,9 @@ GST_STATIC_PAD_TEMPLATE ("sink",
 
 enum
 {
-  ARG_0,
-  PROP_WIDGET
+  PROP_0,
+  PROP_WIDGET,
+  PROP_FORCE_ASPECT_RATIO,
 };
 
 enum
@@ -102,6 +105,13 @@ gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
           "The GtkWidget to place in the widget heirachy",
           GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
+      g_param_spec_boolean ("force-aspect-ratio",
+          "Force aspect ratio",
+          "When enabled, scaling will respect original aspect ratio",
+          DEFAULT_FORCE_ASPECT_RATIO,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&gst_gtk_gl_sink_template));
 
@@ -120,17 +130,7 @@ gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
 static void
 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) {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
+  gtk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
 }
 
 static void
@@ -157,6 +157,9 @@ gst_gtk_gl_sink_get_widget (GstGtkGLSink * gtk_sink)
   }
 
   gtk_sink->widget = (GtkGstGLWidget *) gtk_gst_gl_widget_new ();
+  gtk_sink->bind_aspect_ratio =
+      g_object_bind_property (gtk_sink, "force-aspect-ratio", gtk_sink->widget,
+      "force-aspect-ratio", G_BINDING_BIDIRECTIONAL);
 
   /* Take the floating ref, otherwise the destruction of the container will
    * make this widget disapear possibly before we are done. */
@@ -177,6 +180,25 @@ gst_gtk_gl_sink_get_property (GObject * object, guint prop_id,
     case PROP_WIDGET:
       g_value_set_object (value, gst_gtk_gl_sink_get_widget (gtk_sink));
       break;
+    case PROP_FORCE_ASPECT_RATIO:
+      g_value_set_boolean (value, gtk_sink->force_aspect_ratio);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_gtk_gl_sink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      gtk_sink->force_aspect_ratio = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
index 1457260..4c13059 100644 (file)
@@ -65,6 +65,10 @@ struct _GstGtkGLSink
   GstGLUpload          *upload;
   GstBuffer            *uploaded_buffer;
 
+  /* properties */
+  gboolean              force_aspect_ratio;
+  GBinding             *bind_aspect_ratio;
+
   GstGtkGLSinkPrivate  *priv;
 };
 
index 94b7047..1b34956 100644 (file)
@@ -58,10 +58,13 @@ GST_STATIC_PAD_TEMPLATE ("sink",
     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("BGRA"))
     );
 
+#define DEFAULT_FORCE_ASPECT_RATIO  TRUE
+
 enum
 {
-  ARG_0,
-  PROP_WIDGET
+  PROP_0,
+  PROP_WIDGET,
+  PROP_FORCE_ASPECT_RATIO,
 };
 
 enum
@@ -100,6 +103,13 @@ gst_gtk_sink_class_init (GstGtkSinkClass * klass)
           "The GtkWidget to place in the widget heirachy",
           GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
+      g_param_spec_boolean ("force-aspect-ratio",
+          "Force aspect ratio",
+          "When enabled, scaling will respect original aspect ratio",
+          DEFAULT_FORCE_ASPECT_RATIO,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&gst_gtk_sink_template));
 
@@ -120,17 +130,6 @@ gst_gtk_sink_init (GstGtkSink * gtk_sink)
 }
 
 static void
-gst_gtk_sink_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec)
-{
-  switch (prop_id) {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
 gst_gtk_sink_finalize (GObject * object)
 {
   GstGtkSink *gtk_sink = GST_GTK_SINK (object);;
@@ -154,6 +153,9 @@ gst_gtk_sink_get_widget (GstGtkSink * gtk_sink)
   }
 
   gtk_sink->widget = (GtkGstWidget *) gtk_gst_widget_new ();
+  gtk_sink->bind_aspect_ratio =
+      g_object_bind_property (gtk_sink, "force-aspect-ratio", gtk_sink->widget,
+      "force-aspect-ratio", G_BINDING_BIDIRECTIONAL);
 
   /* Take the floating ref, other wise the destruction of the container will
    * make this widget disapear possibly before we are done. */
@@ -166,14 +168,31 @@ static void
 gst_gtk_sink_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec)
 {
-  GstGtkSink *gtk_sink;
-
-  gtk_sink = GST_GTK_SINK (object);
+  GstGtkSink *gtk_sink = GST_GTK_SINK (object);
 
   switch (prop_id) {
     case PROP_WIDGET:
       g_value_set_object (value, gst_gtk_sink_get_widget (gtk_sink));
       break;
+    case PROP_FORCE_ASPECT_RATIO:
+      g_value_set_boolean (value, gtk_sink->force_aspect_ratio);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_gtk_sink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstGtkSink *gtk_sink = GST_GTK_SINK (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      gtk_sink->force_aspect_ratio = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
index e8cdf48..444c4b1 100644 (file)
@@ -56,6 +56,10 @@ struct _GstGtkSink
 
   GtkGstWidget        *widget;
 
+  /* properties */
+  gboolean             force_aspect_ratio;
+  GBinding             *bind_aspect_ratio;
+
   GstGtkSinkPrivate   *priv;
 };
 
index 11c5b6f..100fa1d 100644 (file)
@@ -51,10 +51,21 @@ G_DEFINE_TYPE_WITH_CODE (GtkGstGLWidget, gtk_gst_gl_widget, GTK_TYPE_GL_AREA,
 #define GTK_GST_GL_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
     GTK_TYPE_GST_GL_WIDGET, GtkGstGLWidgetPrivate))
 
+#define DEFAULT_FORCE_ASPECT_RATIO  TRUE
+
+enum
+{
+  PROP_0,
+  PROP_FORCE_ASPECT_RATIO,
+};
+
 struct _GtkGstGLWidgetPrivate
 {
   GMutex lock;
 
+  /* properties */
+  gboolean force_aspect_ratio;
+
   gboolean negotiated;
   GstBuffer *buffer;
   GstCaps *gl_caps;
@@ -182,6 +193,30 @@ _redraw_texture (GtkGstGLWidget * gst_widget, guint tex)
   const GstGLFuncs *gl = gst_widget->priv->context->gl_vtable;
   GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
 
+  if (gst_widget->priv->force_aspect_ratio) {
+    GstVideoRectangle src, dst, result;
+    gint gtk_viewport[4];
+
+    gl->ClearColor (0.0, 0.0, 0.0, 0.0);
+    gl->Clear (GL_COLOR_BUFFER_BIT);
+
+    gl->GetIntegerv (GL_VIEWPORT, gtk_viewport);
+
+    src.x = 0;
+    src.y = 0;
+    src.w = GST_VIDEO_INFO_WIDTH (&gst_widget->priv->v_info);
+    src.h = GST_VIDEO_INFO_HEIGHT (&gst_widget->priv->v_info);
+
+    dst.x = gtk_viewport[0];
+    dst.y = gtk_viewport[1];
+    dst.w = gtk_viewport[2];
+    dst.h = gtk_viewport[3];
+
+    gst_video_sink_center_rect (src, dst, &result, TRUE);
+
+    gl->Viewport (result.x, result.y, result.w, result.h);
+  }
+
   gst_gl_shader_use (gst_widget->priv->shader);
 
   if (gl->BindVertexArray)
@@ -391,18 +426,61 @@ gtk_gst_gl_widget_finalize (GObject * object)
 }
 
 static void
+gtk_gst_gl_widget_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GtkGstGLWidget *gtk_widget = GTK_GST_GL_WIDGET (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      gtk_widget->priv->force_aspect_ratio = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gtk_gst_gl_widget_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GtkGstGLWidget *gtk_widget = GTK_GST_GL_WIDGET (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      g_value_set_boolean (value, gtk_widget->priv->force_aspect_ratio);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
 gtk_gst_gl_widget_class_init (GtkGstGLWidgetClass * klass)
 {
+  GObjectClass *gobject_klass = (GObjectClass *) klass;
   GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
   GtkGLAreaClass *gl_widget_klass = (GtkGLAreaClass *) klass;
 
   g_type_class_add_private (klass, sizeof (GtkGstGLWidgetPrivate));
 
+  gobject_klass->set_property = gtk_gst_gl_widget_set_property;
+  gobject_klass->get_property = gtk_gst_gl_widget_get_property;
+  gobject_klass->finalize = gtk_gst_gl_widget_finalize;
+
+  g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
+      g_param_spec_boolean ("force-aspect-ratio",
+          "Force aspect ratio",
+          "When enabled, scaling will respect original aspect ratio",
+          DEFAULT_FORCE_ASPECT_RATIO,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gl_widget_klass->render = gtk_gst_gl_widget_render;
+
   widget_klass->get_preferred_width = gtk_gst_gl_widget_get_preferred_width;
   widget_klass->get_preferred_height = gtk_gst_gl_widget_get_preferred_height;
-
-  G_OBJECT_CLASS (klass)->finalize = gtk_gst_gl_widget_finalize;
 }
 
 static void
@@ -412,6 +490,8 @@ gtk_gst_gl_widget_init (GtkGstGLWidget * widget)
 
   widget->priv = GTK_GST_GL_WIDGET_GET_PRIVATE (widget);
 
+  widget->priv->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
+
   g_mutex_init (&widget->priv->lock);
 
   display = gdk_display_get_default ();
index c103df7..aa40f39 100644 (file)
@@ -40,10 +40,21 @@ G_DEFINE_TYPE (GtkGstWidget, gtk_gst_widget, GTK_TYPE_DRAWING_AREA);
 #define GTK_GST_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
     GTK_TYPE_GST_WIDGET, GtkGstWidgetPrivate))
 
+#define DEFAULT_FORCE_ASPECT_RATIO  TRUE
+
+enum
+{
+  PROP_0,
+  PROP_FORCE_ASPECT_RATIO,
+};
+
 struct _GtkGstWidgetPrivate
 {
   GMutex lock;
 
+  /* properties */
+  gboolean force_aspect_ratio;
+
   gboolean negotiated;
   GstBuffer *buffer;
   GstCaps *caps;
@@ -103,6 +114,7 @@ gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr)
         (gdouble) widget_width / GST_VIDEO_INFO_WIDTH (&frame.info);
     gdouble scale_y =
         (gdouble) widget_height / GST_VIDEO_INFO_HEIGHT (&frame.info);
+    GstVideoRectangle result;
 
     gst_widget->priv->v_info = frame.info;
 
@@ -110,8 +122,32 @@ gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr)
         CAIRO_FORMAT_ARGB32, frame.info.width, frame.info.height,
         frame.info.stride[0]);
 
+    if (gst_widget->priv->force_aspect_ratio) {
+      GstVideoRectangle src, dst;
+
+      src.x = 0;
+      src.y = 0;
+      src.w = GST_VIDEO_INFO_WIDTH (&frame.info);
+      src.h = GST_VIDEO_INFO_HEIGHT (&frame.info);
+
+      dst.x = 0;
+      dst.y = 0;
+      dst.w = widget_width;
+      dst.h = widget_height;
+
+      gst_video_sink_center_rect (src, dst, &result, TRUE);
+
+      scale_x = scale_y = MIN (scale_x, scale_y);
+    } else {
+      result.x = 0;
+      result.y = 0;
+      result.w = widget_width;
+      result.h = widget_height;
+    }
+
+    cairo_translate (cr, result.x, result.y);
     cairo_scale (cr, scale_x, scale_y);
-    cairo_rectangle (cr, 0, 0, widget_width, widget_height);
+    cairo_rectangle (cr, 0, 0, result.w, result.h);
     cairo_set_source_surface (cr, surface, 0, 0);
     cairo_paint (cr);
 
@@ -144,17 +180,59 @@ gtk_gst_widget_finalize (GObject * object)
 }
 
 static void
+gtk_gst_widget_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GtkGstWidget *gtk_widget = GTK_GST_WIDGET (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      gtk_widget->priv->force_aspect_ratio = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gtk_gst_widget_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GtkGstWidget *gtk_widget = GTK_GST_WIDGET (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      g_value_set_boolean (value, gtk_widget->priv->force_aspect_ratio);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
 gtk_gst_widget_class_init (GtkGstWidgetClass * klass)
 {
+  GObjectClass *gobject_klass = (GObjectClass *) klass;
   GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
 
   g_type_class_add_private (klass, sizeof (GtkGstWidgetPrivate));
 
+  gobject_klass->set_property = gtk_gst_widget_set_property;
+  gobject_klass->get_property = gtk_gst_widget_get_property;
+  gobject_klass->finalize = gtk_gst_widget_finalize;
+
+  g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
+      g_param_spec_boolean ("force-aspect-ratio",
+          "Force aspect ratio",
+          "When enabled, scaling will respect original aspect ratio",
+          DEFAULT_FORCE_ASPECT_RATIO,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   widget_klass->draw = gtk_gst_widget_draw;
   widget_klass->get_preferred_width = gtk_gst_widget_get_preferred_width;
   widget_klass->get_preferred_height = gtk_gst_widget_get_preferred_height;
-
-  G_OBJECT_CLASS (klass)->finalize = gtk_gst_widget_finalize;
 }
 
 static void
@@ -162,6 +240,8 @@ gtk_gst_widget_init (GtkGstWidget * widget)
 {
   widget->priv = GTK_GST_WIDGET_GET_PRIVATE (widget);
 
+  widget->priv->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
+
   g_mutex_init (&widget->priv->lock);
 }