From 2cbc1f14dfd4e5038c2d0ef1fd395dac5c99de9f Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 21 Feb 2020 17:17:10 -0300 Subject: [PATCH] ges: Add a timeoverlay to video test sources This is often very useful to have a timeoverlay inside test sources. We do not want to use it as an effect as segments are not the sames in GES when it comes to nleoperations. --- ges/ges-source.c | 6 +- ges/ges-video-test-source.c | 224 +++++++++++++++++++++++++++++++++++- 2 files changed, 224 insertions(+), 6 deletions(-) diff --git a/ges/ges-source.c b/ges/ges-source.c index 8b080827db..7129e785cb 100644 --- a/ges/ges-source.c +++ b/ges/ges-source.c @@ -99,8 +99,10 @@ ges_source_create_topbin (const gchar * bin_name, GstElement * sub_element, if (prev) { if (!gst_element_link_pads_full (prev, "src", element, "sink", GST_PAD_LINK_CHECK_NOTHING)) { - g_error ("Could not link %s and %s", - GST_OBJECT_NAME (prev), GST_OBJECT_NAME (element)); + if (!gst_element_link (prev, element)) { + g_error ("Could not link %s and %s", + GST_OBJECT_NAME (prev), GST_OBJECT_NAME (element)); + } } } prev = element; diff --git a/ges/ges-video-test-source.c b/ges/ges-video-test-source.c index ae9ae96a69..f548308be4 100644 --- a/ges/ges-video-test-source.c +++ b/ges/ges-video-test-source.c @@ -21,7 +21,8 @@ /** * SECTION:gesvideotestsource * @title: GESVideoTestSource - * @short_description: produce solid colors and patterns + * @short_description: produce solid colors and patterns, possibly with a time + * overlay. */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -36,6 +37,15 @@ struct _GESVideoTestSourcePrivate { GESVideoTestPattern pattern; + + gboolean use_overlay; + GstElement *overlay; + + GstPad *is_passthrough_pad; + GstPad *os_passthrough_pad; + + GstPad *is_overlay_pad; + GstPad *os_overlay_pad; }; G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTestSource, ges_video_test_source, @@ -52,13 +62,146 @@ get_natural_size (GESVideoSource * source, gint * width, gint * height) return TRUE; } +enum +{ + PROP_0, + PROP_USE_TIME_OVERLAY, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +static void +get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (object); + + switch (property_id) { + case PROP_USE_TIME_OVERLAY: + g_value_set_boolean (value, self->priv->use_overlay); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (object); + + switch (property_id) { + case PROP_USE_TIME_OVERLAY: + { + GstElement *os, *is; + gboolean used_overlay = self->priv->use_overlay; + + if (!self->priv->overlay) { + GST_ERROR_OBJECT (object, "Overlaying is disabled, you are probably" + "missing some GStreamer plugin"); + return; + } + + self->priv->use_overlay = g_value_get_boolean (value); + if (used_overlay == self->priv->use_overlay) + return; + + is = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (self-> + priv->is_overlay_pad))); + + if (!is) + return; + + os = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (self-> + priv->os_overlay_pad))); + if (!os) { + gst_object_unref (is); + return; + } + + g_object_set (is, "active-pad", + self->priv->use_overlay ? self->priv->is_overlay_pad : self-> + priv->is_passthrough_pad, NULL); + g_object_set (os, "active-pad", + self->priv->use_overlay ? self->priv->os_overlay_pad : self-> + priv->os_passthrough_pad, NULL); + + gst_object_unref (is); + gst_object_unref (os); + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +_set_child_property (GESTimelineElement * self, GObject * child, + GParamSpec * pspec, GValue * value) +{ + GstElementFactory *f = + GST_IS_ELEMENT (child) ? gst_element_get_factory (GST_ELEMENT (child)) : + NULL; + + if (!f || g_strcmp0 (GST_OBJECT_NAME (f), "timeoverlay")) + goto done; + + GST_INFO_OBJECT (self, "Activating time overlay as time mode is being set"); + g_object_set (self, "use-time-overlay", TRUE, NULL); + +done: + GES_TIMELINE_ELEMENT_CLASS (ges_video_test_source_parent_class) + ->set_child_property (self, child, pspec, value); +} + +static void +dispose (GObject * object) +{ + GESVideoTestSourcePrivate *priv = GES_VIDEO_TEST_SOURCE (object)->priv; + + gst_clear_object (&priv->is_overlay_pad); + gst_clear_object (&priv->is_passthrough_pad); + gst_clear_object (&priv->os_overlay_pad); + gst_clear_object (&priv->os_passthrough_pad); + + G_OBJECT_CLASS (ges_video_test_source_parent_class)->dispose (object); +} + static void ges_video_test_source_class_init (GESVideoTestSourceClass * klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_CLASS (klass); source_class->create_source = ges_video_test_source_create_source; source_class->ABI.abi.get_natural_size = get_natural_size; + + /** + * GESVideoTestSource:use-overlay: + * + * Whether to overlay the test video source with a clock time. + * This property is also registered as a child property for the video + * test source, so you can set it like any other child property. + * + * The properties of the corresponding #timeoverlay are also registered + * as children properties. If you set any child property from the underlying + * `timeoverlay`, this property will be set to %TRUE by default. + */ + properties[PROP_USE_TIME_OVERLAY] = + g_param_spec_boolean ("use-time-overlay", "Use-time-overlay", + "Use time overlay, setting a child property corresponding to GstTimeOverlay will switch this on" + " by default.", FALSE, G_PARAM_READWRITE); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + + GES_TIMELINE_ELEMENT_CLASS (klass)->set_child_property = _set_child_property; + + g_object_class_install_properties (object_class, PROP_LAST, properties); } static void @@ -69,18 +212,77 @@ ges_video_test_source_init (GESVideoTestSource * self) self->priv->pattern = DEFAULT_VPATTERN; } +#define CREATE_ELEMENT(var, ename) \ + if (!(var = gst_element_factory_make(ename, NULL))) { \ + GST_ERROR_OBJECT(self, "Could not create %s", ename); \ + goto done; \ + } \ + gst_object_ref(var); + static GstElement * -ges_video_test_source_create_source (GESTrackElement * self) +ges_video_test_source_create_overlay (GESVideoTestSource * self) +{ + GstElement *bin = NULL, *os = NULL, *is = NULL, *toverlay = NULL, *tcstamper = + NULL; + GstPad *tmppad; + + CREATE_ELEMENT (os, "output-selector"); + CREATE_ELEMENT (is, "input-selector"); + CREATE_ELEMENT (tcstamper, "timecodestamper"); + CREATE_ELEMENT (toverlay, "timeoverlay"); + + bin = gst_bin_new (NULL); + + gst_bin_add_many (GST_BIN (bin), os, is, tcstamper, toverlay, NULL); + self->priv->os_passthrough_pad = gst_element_get_request_pad (os, "src_%u"); + self->priv->is_passthrough_pad = gst_element_get_request_pad (is, "sink_%u"); + + gst_pad_link_full (self->priv->os_passthrough_pad, + self->priv->is_passthrough_pad, GST_PAD_LINK_CHECK_NOTHING); + gst_element_link (tcstamper, toverlay); + + self->priv->os_overlay_pad = gst_element_get_request_pad (os, "src_%u"); + tmppad = gst_element_get_static_pad (tcstamper, "sink"); + gst_pad_link_full (self->priv->os_overlay_pad, tmppad, + GST_PAD_LINK_CHECK_NOTHING); + gst_object_unref (tmppad); + + tmppad = gst_element_get_static_pad (toverlay, "src"); + self->priv->is_overlay_pad = gst_element_get_request_pad (is, "sink_%u"); + gst_pad_link_full (tmppad, self->priv->is_overlay_pad, + GST_PAD_LINK_CHECK_NOTHING); + gst_object_unref (tmppad); + + tmppad = gst_element_get_static_pad (os, "sink"); + gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("sink", tmppad)); + gst_object_unref (tmppad); + + tmppad = gst_element_get_static_pad (is, "src"); + gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("src", tmppad)); + gst_object_unref (tmppad); + +done: + gst_clear_object (&os); + gst_clear_object (&is); + gst_clear_object (&tcstamper); + gst_clear_object (&toverlay); + + return bin; +} + +static GstElement * +ges_video_test_source_create_source (GESTrackElement * element) { GstCaps *caps; gint pattern; GstElement *testsrc, *capsfilter, *res; const gchar *props[] = { "pattern", NULL }; GPtrArray *elements; + GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (element); testsrc = gst_element_factory_make ("videotestsrc", NULL); capsfilter = gst_element_factory_make ("capsfilter", NULL); - pattern = ((GESVideoTestSource *) self)->priv->pattern; + pattern = self->priv->pattern; g_object_set (testsrc, "pattern", pattern, NULL); @@ -94,7 +296,21 @@ ges_video_test_source_create_source (GESTrackElement * self) g_object_set (capsfilter, "caps", caps, NULL); gst_caps_unref (caps); - ges_track_element_add_children_props (self, testsrc, NULL, NULL, props); + self->priv->overlay = ges_video_test_source_create_overlay (self); + if (self->priv->overlay) { + const gchar *overlay_props[] = + { "time-mode", "text-y", "text-x", "text-width", "test-height", + "halignment", "valignment", "font-desc", NULL + }; + + ges_track_element_add_children_props (element, self->priv->overlay, NULL, + NULL, overlay_props); + ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (self), + properties[PROP_USE_TIME_OVERLAY], G_OBJECT (self)); + g_ptr_array_add (elements, self->priv->overlay); + } + + ges_track_element_add_children_props (element, testsrc, NULL, NULL, props); res = ges_source_create_topbin ("videotestsrc", testsrc, elements); g_ptr_array_free (elements, TRUE);