From 48db03200dcf569821448aac61125831ddcbc1a9 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 15 Jun 2015 18:28:37 +1000 Subject: [PATCH] gtk: implement pixel and display aspect ratio handling --- ext/gtk/gstgtkglsink.c | 22 ++++++++- ext/gtk/gstgtkglsink.h | 6 ++- ext/gtk/gstgtksink.c | 21 ++++++++ ext/gtk/gstgtksink.h | 4 ++ ext/gtk/gtkgstglwidget.c | 96 ++++++++++++++++++++++++++++++++++-- ext/gtk/gtkgstwidget.c | 102 +++++++++++++++++++++++++++++++++++++-- 6 files changed, 239 insertions(+), 12 deletions(-) diff --git a/ext/gtk/gstgtkglsink.c b/ext/gtk/gstgtkglsink.c index 372846f0e7..660bf03592 100644 --- a/ext/gtk/gstgtkglsink.c +++ b/ext/gtk/gstgtkglsink.c @@ -33,6 +33,8 @@ GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink); #define GST_CAT_DEFAULT gst_debug_gtk_gl_sink #define DEFAULT_FORCE_ASPECT_RATIO TRUE +#define DEFAULT_PAR_N 0 +#define DEFAULT_PAR_D 1 static void gst_gtk_gl_sink_finalize (GObject * object); static void gst_gtk_gl_sink_set_property (GObject * object, guint prop_id, @@ -67,6 +69,7 @@ enum PROP_0, PROP_WIDGET, PROP_FORCE_ASPECT_RATIO, + PROP_PIXEL_ASPECT_RATIO, }; enum @@ -112,6 +115,11 @@ gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass) DEFAULT_FORCE_ASPECT_RATIO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, + G_MAXINT, 1, 1, 1, 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)); @@ -131,6 +139,8 @@ static void gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink) { gtk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; + gtk_sink->par_n = DEFAULT_PAR_N; + gtk_sink->par_d = DEFAULT_PAR_D; } static void @@ -157,9 +167,12 @@ gst_gtk_gl_sink_get_widget (GstGtkGLSink * gtk_sink) } gtk_sink->widget = (GtkGstGLWidget *) gtk_gst_gl_widget_new (); - gtk_sink->bind_aspect_ratio = + gtk_sink->bind_force_aspect_ratio = g_object_bind_property (gtk_sink, "force-aspect-ratio", gtk_sink->widget, "force-aspect-ratio", G_BINDING_BIDIRECTIONAL); + gtk_sink->bind_pixel_aspect_ratio = + g_object_bind_property (gtk_sink, "pixel-aspect-ratio", gtk_sink->widget, + "pixel-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. */ @@ -183,6 +196,9 @@ gst_gtk_gl_sink_get_property (GObject * object, guint prop_id, case PROP_FORCE_ASPECT_RATIO: g_value_set_boolean (value, gtk_sink->force_aspect_ratio); break; + case PROP_PIXEL_ASPECT_RATIO: + gst_value_set_fraction (value, gtk_sink->par_n, gtk_sink->par_d); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -199,6 +215,10 @@ gst_gtk_gl_sink_set_property (GObject * object, guint prop_id, case PROP_FORCE_ASPECT_RATIO: gtk_sink->force_aspect_ratio = g_value_get_boolean (value); break; + case PROP_PIXEL_ASPECT_RATIO: + gtk_sink->par_n = gst_value_get_fraction_numerator (value); + gtk_sink->par_d = gst_value_get_fraction_denominator (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/ext/gtk/gstgtkglsink.h b/ext/gtk/gstgtkglsink.h index 4c13059f5f..eaf2f0c433 100644 --- a/ext/gtk/gstgtkglsink.h +++ b/ext/gtk/gstgtkglsink.h @@ -67,7 +67,11 @@ struct _GstGtkGLSink /* properties */ gboolean force_aspect_ratio; - GBinding *bind_aspect_ratio; + GBinding *bind_force_aspect_ratio; + + gint par_n; + gint par_d; + GBinding *bind_pixel_aspect_ratio; GstGtkGLSinkPrivate *priv; }; diff --git a/ext/gtk/gstgtksink.c b/ext/gtk/gstgtksink.c index 1b349561c4..67fe7de381 100644 --- a/ext/gtk/gstgtksink.c +++ b/ext/gtk/gstgtksink.c @@ -59,12 +59,15 @@ GST_STATIC_PAD_TEMPLATE ("sink", ); #define DEFAULT_FORCE_ASPECT_RATIO TRUE +#define DEFAULT_PAR_N 0 +#define DEFAULT_PAR_D 1 enum { PROP_0, PROP_WIDGET, PROP_FORCE_ASPECT_RATIO, + PROP_PIXEL_ASPECT_RATIO, }; enum @@ -110,6 +113,11 @@ gst_gtk_sink_class_init (GstGtkSinkClass * klass) DEFAULT_FORCE_ASPECT_RATIO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, + G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&gst_gtk_sink_template)); @@ -127,6 +135,9 @@ gst_gtk_sink_class_init (GstGtkSinkClass * klass) static void gst_gtk_sink_init (GstGtkSink * gtk_sink) { + gtk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; + gtk_sink->par_n = DEFAULT_PAR_N; + gtk_sink->par_d = DEFAULT_PAR_D; } static void @@ -156,6 +167,9 @@ gst_gtk_sink_get_widget (GstGtkSink * gtk_sink) gtk_sink->bind_aspect_ratio = g_object_bind_property (gtk_sink, "force-aspect-ratio", gtk_sink->widget, "force-aspect-ratio", G_BINDING_BIDIRECTIONAL); + gtk_sink->bind_pixel_aspect_ratio = + g_object_bind_property (gtk_sink, "pixel-aspect-ratio", gtk_sink->widget, + "pixel-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. */ @@ -177,6 +191,9 @@ gst_gtk_sink_get_property (GObject * object, guint prop_id, case PROP_FORCE_ASPECT_RATIO: g_value_set_boolean (value, gtk_sink->force_aspect_ratio); break; + case PROP_PIXEL_ASPECT_RATIO: + gst_value_set_fraction (value, gtk_sink->par_n, gtk_sink->par_d); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -193,6 +210,10 @@ gst_gtk_sink_set_property (GObject * object, guint prop_id, case PROP_FORCE_ASPECT_RATIO: gtk_sink->force_aspect_ratio = g_value_get_boolean (value); break; + case PROP_PIXEL_ASPECT_RATIO: + gtk_sink->par_n = gst_value_get_fraction_numerator (value); + gtk_sink->par_d = gst_value_get_fraction_denominator (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/ext/gtk/gstgtksink.h b/ext/gtk/gstgtksink.h index 444c4b1980..d3e0b7b1ef 100644 --- a/ext/gtk/gstgtksink.h +++ b/ext/gtk/gstgtksink.h @@ -60,6 +60,10 @@ struct _GstGtkSink gboolean force_aspect_ratio; GBinding *bind_aspect_ratio; + gint par_n; + gint par_d; + GBinding *bind_pixel_aspect_ratio; + GstGtkSinkPrivate *priv; }; diff --git a/ext/gtk/gtkgstglwidget.c b/ext/gtk/gtkgstglwidget.c index ba68ba34af..26fa0dcd4a 100644 --- a/ext/gtk/gtkgstglwidget.c +++ b/ext/gtk/gtkgstglwidget.c @@ -51,18 +51,20 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); G_DEFINE_TYPE_WITH_CODE (GtkGstGLWidget, gtk_gst_gl_widget, GTK_TYPE_GL_AREA, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkgstglwidget", 0, - "Gtk Gst GL Widget"); - ); + "Gtk Gst GL Widget");); #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 +#define DEFAULT_PAR_N 0 +#define DEFAULT_PAR_D 1 enum { PROP_0, PROP_FORCE_ASPECT_RATIO, + PROP_PIXEL_ASPECT_RATIO, }; struct _GtkGstGLWidgetPrivate @@ -71,6 +73,10 @@ struct _GtkGstGLWidgetPrivate /* properties */ gboolean force_aspect_ratio; + gint par_n, par_d; + + gint display_width; + gint display_height; gboolean negotiated; GstBuffer *buffer; @@ -210,8 +216,8 @@ _redraw_texture (GtkGstGLWidget * gst_widget, guint tex) 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); + src.w = gst_widget->priv->display_width; + src.h = gst_widget->priv->display_height; dst.x = gtk_viewport[0]; dst.y = gtk_viewport[1]; @@ -442,6 +448,10 @@ gtk_gst_gl_widget_set_property (GObject * object, guint prop_id, case PROP_FORCE_ASPECT_RATIO: gtk_widget->priv->force_aspect_ratio = g_value_get_boolean (value); break; + case PROP_PIXEL_ASPECT_RATIO: + gtk_widget->priv->par_n = gst_value_get_fraction_numerator (value); + gtk_widget->priv->par_d = gst_value_get_fraction_denominator (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -458,6 +468,10 @@ gtk_gst_gl_widget_get_property (GObject * object, guint prop_id, GValue * value, case PROP_FORCE_ASPECT_RATIO: g_value_set_boolean (value, gtk_widget->priv->force_aspect_ratio); break; + case PROP_PIXEL_ASPECT_RATIO: + gst_value_set_fraction (value, gtk_widget->priv->par_n, + gtk_widget->priv->par_d); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -484,6 +498,11 @@ gtk_gst_gl_widget_class_init (GtkGstGLWidgetClass * klass) DEFAULT_FORCE_ASPECT_RATIO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, + G_MAXINT, 1, 1, 1, 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; @@ -498,6 +517,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; + widget->priv->par_n = DEFAULT_PAR_N; + widget->priv->par_d = DEFAULT_PAR_D; g_mutex_init (&widget->priv->lock); @@ -523,7 +544,7 @@ gtk_gst_gl_widget_init (GtkGstGLWidget * widget) if (!widget->priv->display) widget->priv->display = gst_gl_display_new (); - gtk_gl_area_set_has_alpha ((GtkGLArea *) widget, TRUE); + gtk_gl_area_set_has_alpha ((GtkGLArea *) widget, FALSE); } GtkWidget * @@ -665,6 +686,66 @@ gtk_gst_gl_widget_init_winsys (GtkGstGLWidget * widget) return TRUE; } +static gboolean +_calculate_par (GtkGstGLWidget * widget, GstVideoInfo * info) +{ + gboolean ok; + gint width, height; + gint par_n, par_d; + gint display_par_n, display_par_d; + guint display_ratio_num, display_ratio_den; + + width = GST_VIDEO_INFO_WIDTH (info); + height = GST_VIDEO_INFO_HEIGHT (info); + + par_n = GST_VIDEO_INFO_PAR_N (info); + par_d = GST_VIDEO_INFO_PAR_D (info); + + if (!par_n) + par_n = 1; + + /* get display's PAR */ + if (widget->priv->par_n != 0 && widget->priv->par_d != 0) { + display_par_n = widget->priv->par_n; + display_par_d = widget->priv->par_d; + } else { + display_par_n = 1; + display_par_d = 1; + } + + ok = gst_video_calculate_display_ratio (&display_ratio_num, + &display_ratio_den, width, height, par_n, par_d, display_par_n, + display_par_d); + + if (!ok) + return FALSE; + + GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, display_par_d); + + if (height % display_ratio_den == 0) { + GST_DEBUG ("keeping video height"); + widget->priv->display_width = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + widget->priv->display_height = height; + } else if (width % display_ratio_num == 0) { + GST_DEBUG ("keeping video width"); + widget->priv->display_width = width; + widget->priv->display_height = (guint) + gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num); + } else { + GST_DEBUG ("approximating while keeping video height"); + widget->priv->display_width = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + widget->priv->display_height = height; + } + GST_DEBUG ("scaling to %dx%d", widget->priv->display_width, + widget->priv->display_height); + + return TRUE; +} + gboolean gtk_gst_gl_widget_set_caps (GtkGstGLWidget * widget, GstCaps * caps) { @@ -691,6 +772,11 @@ gtk_gst_gl_widget_set_caps (GtkGstGLWidget * widget, GstCaps * caps) gst_caps_set_features (widget->priv->gl_caps, 0, gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY)); + if (!_calculate_par (widget, &v_info)) { + g_mutex_unlock (&widget->priv->lock); + return FALSE; + } + widget->priv->v_info = v_info; widget->priv->negotiated = TRUE; diff --git a/ext/gtk/gtkgstwidget.c b/ext/gtk/gtkgstwidget.c index aa40f39391..7607bd53b7 100644 --- a/ext/gtk/gtkgstwidget.c +++ b/ext/gtk/gtkgstwidget.c @@ -41,11 +41,14 @@ G_DEFINE_TYPE (GtkGstWidget, gtk_gst_widget, GTK_TYPE_DRAWING_AREA); GTK_TYPE_GST_WIDGET, GtkGstWidgetPrivate)) #define DEFAULT_FORCE_ASPECT_RATIO TRUE +#define DEFAULT_PAR_N 0 +#define DEFAULT_PAR_D 1 enum { PROP_0, PROP_FORCE_ASPECT_RATIO, + PROP_PIXEL_ASPECT_RATIO, }; struct _GtkGstWidgetPrivate @@ -54,6 +57,10 @@ struct _GtkGstWidgetPrivate /* properties */ gboolean force_aspect_ratio; + gint par_n, par_d; + + gint display_width; + gint display_height; gboolean negotiated; GstBuffer *buffer; @@ -110,10 +117,9 @@ gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr) if (gst_widget->priv->negotiated && gst_widget->priv->buffer && gst_video_frame_map (&frame, &gst_widget->priv->v_info, gst_widget->priv->buffer, GST_MAP_READ)) { - gdouble scale_x = - (gdouble) widget_width / GST_VIDEO_INFO_WIDTH (&frame.info); + gdouble scale_x = (gdouble) widget_width / gst_widget->priv->display_width; gdouble scale_y = - (gdouble) widget_height / GST_VIDEO_INFO_HEIGHT (&frame.info); + (gdouble) widget_height / gst_widget->priv->display_height; GstVideoRectangle result; gst_widget->priv->v_info = frame.info; @@ -127,8 +133,8 @@ gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr) src.x = 0; src.y = 0; - src.w = GST_VIDEO_INFO_WIDTH (&frame.info); - src.h = GST_VIDEO_INFO_HEIGHT (&frame.info); + src.w = gst_widget->priv->display_width; + src.h = gst_widget->priv->display_height; dst.x = 0; dst.y = 0; @@ -145,6 +151,12 @@ gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr) result.h = widget_height; } + scale_x *= + (gdouble) gst_widget->priv->display_width / (gdouble) frame.info.width; + scale_y *= + (gdouble) gst_widget->priv->display_height / + (gdouble) frame.info.height; + cairo_translate (cr, result.x, result.y); cairo_scale (cr, scale_x, scale_y); cairo_rectangle (cr, 0, 0, result.w, result.h); @@ -189,6 +201,10 @@ gtk_gst_widget_set_property (GObject * object, guint prop_id, case PROP_FORCE_ASPECT_RATIO: gtk_widget->priv->force_aspect_ratio = g_value_get_boolean (value); break; + case PROP_PIXEL_ASPECT_RATIO: + gtk_widget->priv->par_n = gst_value_get_fraction_numerator (value); + gtk_widget->priv->par_d = gst_value_get_fraction_denominator (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -205,6 +221,10 @@ gtk_gst_widget_get_property (GObject * object, guint prop_id, GValue * value, case PROP_FORCE_ASPECT_RATIO: g_value_set_boolean (value, gtk_widget->priv->force_aspect_ratio); break; + case PROP_PIXEL_ASPECT_RATIO: + gst_value_set_fraction (value, gtk_widget->priv->par_n, + gtk_widget->priv->par_d); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -230,6 +250,11 @@ gtk_gst_widget_class_init (GtkGstWidgetClass * klass) DEFAULT_FORCE_ASPECT_RATIO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, + G_MAXINT, 1, 1, 1, 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; @@ -241,6 +266,8 @@ gtk_gst_widget_init (GtkGstWidget * widget) widget->priv = GTK_GST_WIDGET_GET_PRIVATE (widget); widget->priv->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; + widget->priv->par_n = DEFAULT_PAR_N; + widget->priv->par_d = DEFAULT_PAR_D; g_mutex_init (&widget->priv->lock); } @@ -284,6 +311,66 @@ _queue_resize (GtkGstWidget * widget) return G_SOURCE_REMOVE; } +static gboolean +_calculate_par (GtkGstWidget * widget, GstVideoInfo * info) +{ + gboolean ok; + gint width, height; + gint par_n, par_d; + gint display_par_n, display_par_d; + guint display_ratio_num, display_ratio_den; + + width = GST_VIDEO_INFO_WIDTH (info); + height = GST_VIDEO_INFO_HEIGHT (info); + + par_n = GST_VIDEO_INFO_PAR_N (info); + par_d = GST_VIDEO_INFO_PAR_D (info); + + if (!par_n) + par_n = 1; + + /* get display's PAR */ + if (widget->priv->par_n != 0 && widget->priv->par_d != 0) { + display_par_n = widget->priv->par_n; + display_par_d = widget->priv->par_d; + } else { + display_par_n = 1; + display_par_d = 1; + } + + ok = gst_video_calculate_display_ratio (&display_ratio_num, + &display_ratio_den, width, height, par_n, par_d, display_par_n, + display_par_d); + + if (!ok) + return FALSE; + + GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, display_par_d); + + if (height % display_ratio_den == 0) { + GST_DEBUG ("keeping video height"); + widget->priv->display_width = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + widget->priv->display_height = height; + } else if (width % display_ratio_num == 0) { + GST_DEBUG ("keeping video width"); + widget->priv->display_width = width; + widget->priv->display_height = (guint) + gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num); + } else { + GST_DEBUG ("approximating while keeping video height"); + widget->priv->display_width = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + widget->priv->display_height = height; + } + GST_DEBUG ("scaling to %dx%d", widget->priv->display_width, + widget->priv->display_height); + + return TRUE; +} + gboolean gtk_gst_widget_set_caps (GtkGstWidget * widget, GstCaps * caps) { @@ -306,6 +393,11 @@ gtk_gst_widget_set_caps (GtkGstWidget * widget, GstCaps * caps) g_mutex_lock (&widget->priv->lock); + if (!_calculate_par (widget, &v_info)) { + g_mutex_unlock (&widget->priv->lock); + return FALSE; + } + gst_caps_replace (&widget->priv->caps, caps); widget->priv->v_info = v_info; widget->priv->negotiated = TRUE;