From 5f3e8caabc98dcd78bdb98ce7e5f5f55e6025bdd Mon Sep 17 00:00:00 2001 From: Henry Wilkes Date: Tue, 10 Mar 2020 11:29:40 +0000 Subject: [PATCH] track-element: add has-internal-source property Unless this property is set to TRUE, the in-point must be 0 and the max-duration must be GST_CLOCK_TIME_NONE. Also added EXPLICIT_NOTIFY flags to the active and track-type properties such that their notifies are emitted only if the property changes, even when the g_object_set, etc, methods are used. Also added a missing notify signal to the set_active method. --- ges/ges-image-source.c | 3 +- ges/ges-source.c | 1 + ges/ges-title-clip.c | 3 - ges/ges-title-source.c | 3 +- ges/ges-track-element.c | 173 ++++++++++++++++++++++++++++++++++++++++++++---- ges/ges-track-element.h | 27 +++++++- 6 files changed, 192 insertions(+), 18 deletions(-) diff --git a/ges/ges-image-source.c b/ges/ges-image-source.c index 206e36a..e627fe2 100644 --- a/ges/ges-image-source.c +++ b/ges/ges-image-source.c @@ -169,10 +169,11 @@ ges_image_source_class_init (GESImageSourceClass * klass) g_param_spec_string ("uri", "URI", "uri of the resource", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - GES_TIMELINE_ELEMENT_CLASS (klass)->set_inpoint = NULL; source_class->create_source = ges_image_source_create_source; source_class->ABI.abi.get_natural_size = ges_video_uri_source_get_natural_size; + + GES_TRACK_ELEMENT_CLASS_DEFAULT_HAS_INTERNAL_SOURCE (klass) = FALSE; } static void diff --git a/ges/ges-source.c b/ges/ges-source.c index 3c5eee8..8b08082 100644 --- a/ges/ges-source.c +++ b/ges/ges-source.c @@ -152,6 +152,7 @@ ges_source_class_init (GESSourceClass * klass) track_class->nleobject_factorytype = "nlesource"; track_class->create_element = NULL; + GES_TRACK_ELEMENT_CLASS_DEFAULT_HAS_INTERNAL_SOURCE (klass) = TRUE; } static void diff --git a/ges/ges-title-clip.c b/ges/ges-title-clip.c index 6d72288..b5fafd9 100644 --- a/ges/ges-title-clip.c +++ b/ges/ges-title-clip.c @@ -133,8 +133,6 @@ static void ges_title_clip_class_init (GESTitleClipClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - GESTimelineElementClass *timeline_element_class = - GES_TIMELINE_ELEMENT_CLASS (klass); GESClipClass *clip_class = GES_CLIP_CLASS (klass); GESContainerClass *container_class = GES_CONTAINER_CLASS (klass); @@ -205,7 +203,6 @@ ges_title_clip_class_init (GESTitleClipClass * klass) GES_PARAM_NO_SERIALIZATION)); clip_class->create_track_element = ges_title_clip_create_track_element; - timeline_element_class->set_inpoint = NULL; container_class->child_added = _child_added; container_class->child_removed = _child_removed; diff --git a/ges/ges-title-source.c b/ges/ges-title-source.c index 239780e..fd74f82 100644 --- a/ges/ges-title-source.c +++ b/ges/ges-title-source.c @@ -122,10 +122,11 @@ ges_title_source_class_init (GESTitleSourceClass * klass) object_class->set_property = ges_title_source_set_property; object_class->dispose = ges_title_source_dispose; - timeline_element_class->set_inpoint = NULL; timeline_element_class->lookup_child = _lookup_child; source_class->ABI.abi.disable_scale_in_compositor = TRUE; source_class->create_source = ges_title_source_create_source; + + GES_TRACK_ELEMENT_CLASS_DEFAULT_HAS_INTERNAL_SOURCE (klass) = FALSE; } static void diff --git a/ges/ges-track-element.c b/ges/ges-track-element.c index 3d1d2ae..0c87997 100644 --- a/ges/ges-track-element.c +++ b/ges/ges-track-element.c @@ -59,6 +59,7 @@ struct _GESTrackElementPrivate GstElement *element; /* The element contained in the nleobject (can be NULL) */ GESTrack *track; + gboolean has_internal_source; gboolean locked; /* If TRUE, then moves in sync with its controlling * GESClip */ @@ -73,6 +74,7 @@ enum PROP_ACTIVE, PROP_TRACK_TYPE, PROP_TRACK, + PROP_HAS_INTERNAL_SOURCE, PROP_LAST }; @@ -98,6 +100,8 @@ static gboolean _set_inpoint (GESTimelineElement * element, GstClockTime inpoint); static gboolean _set_duration (GESTimelineElement * element, GstClockTime duration); +static gboolean _set_max_duration (GESTimelineElement * element, + GstClockTime max_duration); static gboolean _set_priority (GESTimelineElement * element, guint32 priority); GESTrackType _get_track_types (GESTimelineElement * object); @@ -155,6 +159,10 @@ ges_track_element_get_property (GObject * object, guint property_id, case PROP_TRACK: g_value_set_object (value, track_element->priv->track); break; + case PROP_HAS_INTERNAL_SOURCE: + g_value_set_boolean (value, + ges_track_element_has_internal_source (track_element)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -171,7 +179,12 @@ ges_track_element_set_property (GObject * object, guint property_id, ges_track_element_set_active (track_element, g_value_get_boolean (value)); break; case PROP_TRACK_TYPE: - track_element->priv->track_type = g_value_get_flags (value); + ges_track_element_set_track_type (track_element, + g_value_get_flags (value)); + break; + case PROP_HAS_INTERNAL_SOURCE: + ges_track_element_set_has_internal_source (track_element, + g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -257,6 +270,11 @@ ges_track_element_constructed (GObject * gobject) g_object_set (object->priv->nleobject, "media-duration-factor", media_duration_factor, NULL); + /* set the default has-internal-source */ + ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (gobject), + GES_TRACK_ELEMENT_CLASS_DEFAULT_HAS_INTERNAL_SOURCE + (GES_TRACK_ELEMENT_GET_CLASS (gobject))); + G_OBJECT_CLASS (ges_track_element_parent_class)->constructed (gobject); } @@ -281,7 +299,7 @@ ges_track_element_class_init (GESTrackElementClass * klass) */ properties[PROP_ACTIVE] = g_param_spec_boolean ("active", "Active", "Use object in output", TRUE, - G_PARAM_READWRITE); + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_property (object_class, PROP_ACTIVE, properties[PROP_ACTIVE]); @@ -295,7 +313,8 @@ ges_track_element_class_init (GESTrackElementClass * klass) */ properties[PROP_TRACK_TYPE] = g_param_spec_flags ("track-type", "Track Type", "The track type of the object", GES_TYPE_TRACK_TYPE, - GES_TRACK_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + GES_TRACK_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_property (object_class, PROP_TRACK_TYPE, properties[PROP_TRACK_TYPE]); @@ -311,6 +330,51 @@ ges_track_element_class_init (GESTrackElementClass * klass) properties[PROP_TRACK]); /** + * GESTrackElement:has-internal-source: + * + * This property is used to determine whether the 'internal time' + * properties of the element have any meaning. In particular, unless + * this is set to %TRUE, the #GESTimelineElement:in-point and + * #GESTimelineElement:max-duration can not be set to any value other + * than the default 0 and #GST_CLOCK_TIME_NONE, respectively. + * + * If an element has some *internal* *timed* source #GstElement that it + * reads stream data from as part of its function in a #GESTrack, then + * you'll likely want to set this to %TRUE to allow the + * #GESTimelineElement:in-point and #GESTimelineElement:max-duration to + * be set. + * + * The default value is determined by the #GESTrackElementClass + * @default_has_internal_source class property. For most + * #GESSourceClass-es, this will be %TRUE, with the exception of those + * that have a potentially *static* source, such as #GESImageSourceClass + * and #GESTitleSourceClass. Otherwise, this will usually be %FALSE. + * + * For most #GESOperation-s you will likely want to leave this set to + * %FALSE. The exception may be for an operation that reads some stream + * data from some private internal source as part of manipulating the + * input data from the usual linked upstream #GESTrackElement. + * + * For example, you may want to set this to %TRUE for a + * #GES_TRACK_TYPE_VIDEO operation that wraps a #textoverlay that reads + * from a subtitle file and places its text on top of the received video + * data. The #GESTimelineElement:in-point of the element would be used + * to shift the initial seek time on the #textoverlay away from 0, and + * the #GESTimelineElement:max-duration could be set to reflect the + * time at which the subtitle file runs out of data. + * + * Note that GES can not support track elements that have both internal + * content and manipulate the timing of their data streams (time + * effects). + */ + properties[PROP_HAS_INTERNAL_SOURCE] = + g_param_spec_boolean ("has-internal-source", "Has Internal Source", + "Whether the element has some internal source of stream data", FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_property (object_class, PROP_HAS_INTERNAL_SOURCE, + properties[PROP_HAS_INTERNAL_SOURCE]); + + /** * GESTrackElement::control-binding-added: * @track_element: A #GESTrackElement * @control_binding: The control binding that has been added @@ -339,6 +403,7 @@ ges_track_element_class_init (GESTrackElementClass * klass) element_class->set_start = _set_start; element_class->set_duration = _set_duration; element_class->set_inpoint = _set_inpoint; + element_class->set_max_duration = _set_max_duration; element_class->set_priority = _set_priority; element_class->get_track_types = _get_track_types; element_class->deep_copy = ges_track_element_copy_properties; @@ -363,6 +428,20 @@ ges_track_element_init (GESTrackElement * self) priv->bindings_hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + /* NOTE: make sure we set this flag to TRUE so that + * g_object_new (, "has-internal-source", TRUE, "in-point", 10, NULL); + * can succeed. The problem is that "in-point" will always be set before + * has-internal-source is set, so we first assume that it is TRUE. + * Note that if we call + * g_object_new (, "has-internal-source", FALSE, "in-point", 10, NULL); + * then "in-point" will be allowed to be set, but then when + * "has-internal-source" is later set to TRUE, this will set the + * "in-point" back to 0. + * This is particularly needed for the ges_timeline_element_copy method + * because it calls g_object_new_with_properties. + */ + self->priv->has_internal_source = TRUE; } static gfloat @@ -515,9 +594,11 @@ _set_inpoint (GESTimelineElement * element, GstClockTime inpoint) GESTrackElement *object = GES_TRACK_ELEMENT (element); g_return_val_if_fail (object->priv->nleobject, FALSE); - - if (G_UNLIKELY (inpoint == _INPOINT (object))) - return -1; + if (inpoint && !object->priv->has_internal_source) { + GST_WARNING_OBJECT (element, "Can not set an in-point for a track " + "element that is not registered with internal content"); + return FALSE; + } g_object_set (object->priv->nleobject, "inpoint", inpoint, NULL); _update_control_bindings (element, inpoint, GST_CLOCK_TIME_NONE); @@ -549,6 +630,22 @@ _set_duration (GESTimelineElement * element, GstClockTime duration) } static gboolean +_set_max_duration (GESTimelineElement * element, GstClockTime max_duration) +{ + GESTrackElement *object = GES_TRACK_ELEMENT (element); + + if (GST_CLOCK_TIME_IS_VALID (max_duration) + && !object->priv->has_internal_source) { + GST_WARNING_OBJECT (element, "Can not set a max-duration for a track " + "element that is not registered with internal content"); + return FALSE; + } + + return TRUE; +} + + +static gboolean _set_priority (GESTimelineElement * element, guint32 priority) { GESTrackElement *object = GES_TRACK_ELEMENT (element); @@ -599,17 +696,53 @@ ges_track_element_set_active (GESTrackElement * object, gboolean active) g_object_set (object->priv->nleobject, "active", active, NULL); - /* FIXME: no need to check again at this point */ - if (active != object->active) { - object->active = active; - if (GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed) - GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed (object, active); - } + object->active = active; + if (GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed) + GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed (object, active); + + g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_ACTIVE]); return TRUE; } /** + * ges_track_element_set_has_internal_source: + * @object: A #GESTrackElement + * @has_internal_source: Whether the @object should be allowed to have its + * 'internal time' properties set. + * + * Sets #GESTrackElement:has-internal-source for the element. If this is + * set to %FALSE, this method will also set the + * #GESTimelineElement:in-point of the element to 0 and its + * #GESTimelineElement:max-duration to #GST_CLOCK_TIME_NONE. + */ +void +ges_track_element_set_has_internal_source (GESTrackElement * object, + gboolean has_internal_source) +{ + GESTimelineElement *element; + + g_return_if_fail (GES_IS_TRACK_ELEMENT (object)); + + GST_DEBUG_OBJECT (object, "object:%p, has-internal-source: %s", object, + has_internal_source ? "TRUE" : "FALSE"); + + if (G_UNLIKELY (has_internal_source == object->priv->has_internal_source)) + return; + + object->priv->has_internal_source = has_internal_source; + + if (!has_internal_source) { + element = GES_TIMELINE_ELEMENT (object); + ges_timeline_element_set_inpoint (element, 0); + ges_timeline_element_set_max_duration (element, GST_CLOCK_TIME_NONE); + } + + g_object_notify_by_pspec (G_OBJECT (object), + properties[PROP_HAS_INTERNAL_SOURCE]); +} + +/** * ges_track_element_set_track_type: * @object: A #GESTrackElement * @type: The new track-type for @object @@ -983,6 +1116,22 @@ ges_track_element_is_active (GESTrackElement * object) } /** + * ges_track_element_has_internal_source: + * @object: A #GESTrackElement + * + * Gets #GESTrackElement:has-internal-source for the element. + * + * Returns: %TRUE if @object can have its 'internal time' properties set. + */ +gboolean +ges_track_element_has_internal_source (GESTrackElement * object) +{ + g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE); + + return object->priv->has_internal_source; +} + +/** * ges_track_element_lookup_child: * @object: Object to lookup the property in * @prop_name: Name of the property to look up. You can specify the name of the diff --git a/ges/ges-track-element.h b/ges/ges-track-element.h index f3e9110..2f7e276 100644 --- a/ges/ges-track-element.h +++ b/ges/ges-track-element.h @@ -51,6 +51,16 @@ G_BEGIN_DECLS typedef struct _GESTrackElementPrivate GESTrackElementPrivate; /** + * GES_TRACK_ELEMENT_CLASS_DEFAULT_HAS_INTERNAL_SOURCE: + * @klass: A #GESTrackElementClass + * + * What the default #GESTrackElement:has-internal-source value should be + * for new elements from this class. + */ +#define GES_TRACK_ELEMENT_CLASS_DEFAULT_HAS_INTERNAL_SOURCE(klass) \ + ((GES_TRACK_ELEMENT_CLASS (klass))->ABI.abi.default_has_internal_source) + +/** * GESTrackElement: * * The #GESTrackElement base class. @@ -85,6 +95,9 @@ struct _GESTrackElement { * handled by ges_timeline_element_list_children_properties() instead. * @lookup_child: Deprecated: Use #GESTimelineElement.lookup_child() * instead. + * @default_has_internal_source: What the default + * #GESTrackElement:has-internal-source value should be for new elements + * from this class. */ struct _GESTrackElementClass { /*< private >*/ @@ -112,7 +125,12 @@ struct _GESTrackElementClass { GParamSpec **pspec); /*< private >*/ /* Padding for API extension */ - gpointer _ges_reserved[GES_PADDING_LARGE]; + union { + gpointer _ges_reserved[GES_PADDING_LARGE]; + struct { + gboolean default_has_internal_source; + } abi; + } ABI; }; GES_API @@ -140,6 +158,13 @@ gboolean ges_track_element_set_active (GESTrackElement * object, GES_API gboolean ges_track_element_is_active (GESTrackElement * object); +GES_API +void ges_track_element_set_has_internal_source (GESTrackElement * object, + gboolean has_internal_source); + +GES_API +gboolean ges_track_element_has_internal_source (GESTrackElement * object); + GES_API void ges_track_element_get_child_property_by_pspec (GESTrackElement * object, GParamSpec * pspec, -- 2.7.4