/* Gnonlin * Copyright (C) <2001> Wim Taymans * <2004-2008> Edward Hervey * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "nle.h" #include "../shared/nlegesplugin.h" /** * SECTION:nleobject * @short_description: Base class for GNonLin elements * * NleObject encapsulates default behaviour and implements standard * properties provided by all the GNonLin elements. */ static void nle_query_parent_nle_object_free (NleQueryParentNleObject * query) { gst_clear_object (&query->nle_object); } void nle_query_parent_nle_object_release (NleQueryParentNleObject * query) { g_atomic_rc_box_release_full (query, (GDestroyNotify) nle_query_parent_nle_object_free); } G_DEFINE_BOXED_TYPE (NleQueryParentNleObject, nle_query_parent_nle_object, g_atomic_rc_box_acquire, nle_query_parent_nle_object_release); GST_DEBUG_CATEGORY_STATIC (nleobject_debug); #define GST_CAT_DEFAULT nleobject_debug static GObjectClass *parent_class = NULL; /**************************************************** * Helper macros * ****************************************************/ #define CHECK_AND_SET(PROPERTY, property, prop_str, print_format) \ { \ if (object->pending_##property != object->property) { \ object->property = object->pending_##property; \ GST_DEBUG_OBJECT(object, "Setting " prop_str " to %" \ print_format, object->property); \ } else \ GST_DEBUG_OBJECT(object, "Nothing to do for " prop_str); \ } #define SET_PENDING_VALUE(property, property_str, type, print_format) \ nleobject->pending_##property = g_value_get_##type (value); \ if (nleobject->property != nleobject->pending_##property) { \ GST_DEBUG_OBJECT(object, "Setting pending " property_str " to %" \ print_format, nleobject->pending_##property); \ nle_object_set_commit_needed (nleobject); \ } else \ GST_DEBUG_OBJECT(object, "Pending " property_str " did not change"); enum { PROP_0, PROP_START, PROP_DURATION, PROP_STOP, PROP_INPOINT, PROP_PRIORITY, PROP_ACTIVE, PROP_CAPS, PROP_EXPANDABLE, PROP_MEDIA_DURATION_FACTOR, PROP_LAST }; enum { COMMIT_SIGNAL, LAST_SIGNAL }; static guint _signals[LAST_SIGNAL] = { 0 }; static GParamSpec *properties[PROP_LAST]; static void nle_object_dispose (GObject * object); static void nle_object_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void nle_object_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void nle_object_constructed (GObject * object); static GstStateChangeReturn nle_object_change_state (GstElement * element, GstStateChange transition); static gboolean nle_object_prepare_func (NleObject * object); static gboolean nle_object_cleanup_func (NleObject * object); static gboolean nle_object_commit_func (NleObject * object, gboolean recurse); static GstStateChangeReturn nle_object_prepare (NleObject * object); static void nle_bin_handle_message (GstBin * bin, GstMessage * message) { if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) { const GstStructure *s = gst_message_get_structure (message); if (gst_structure_has_name (s, NLE_QUERY_PARENT_NLE_OBJECT)) { NleQueryParentNleObject *query; gst_structure_get (s, "query", NLE_TYPE_QUERY_PARENT_NLE_OBJECT, &query, NULL); g_assert (query); g_mutex_lock (&query->lock); query->nle_object = gst_object_ref (GST_ELEMENT (bin)); g_mutex_unlock (&query->lock); nle_query_parent_nle_object_release (query); return; } } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STREAM_COLLECTION) { GST_INFO_OBJECT (bin, "Dropping stream collection message, " " those are internal to and should be kept as such"); return; } return GST_BIN_CLASS (parent_class)->handle_message (bin, message); } static void nle_object_class_init (NleObjectClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBinClass *gstbin_class; NleObjectClass *nleobject_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstbin_class = (GstBinClass *) klass; nleobject_class = (NleObjectClass *) klass; GST_DEBUG_CATEGORY_INIT (nleobject_debug, "nleobject", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object"); parent_class = g_type_class_ref (GST_TYPE_BIN); /* Ensure the NleQueryParentObject GType is registered */ GType t = NLE_TYPE_QUERY_PARENT_NLE_OBJECT; g_assert (t); gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_object_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_object_get_property); gobject_class->constructed = GST_DEBUG_FUNCPTR (nle_object_constructed); gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_object_dispose); gstelement_class->change_state = GST_DEBUG_FUNCPTR (nle_object_change_state); gstbin_class->handle_message = GST_DEBUG_FUNCPTR (nle_bin_handle_message); nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_object_prepare_func); nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_object_cleanup_func); nleobject_class->commit_signal_handler = GST_DEBUG_FUNCPTR (nle_object_commit); nleobject_class->commit = GST_DEBUG_FUNCPTR (nle_object_commit_func); /** * NleObject:start * * The start position relative to the parent in nanoseconds. */ properties[PROP_START] = g_param_spec_uint64 ("start", "Start", "The start position relative to the parent (in nanoseconds)", 0, G_MAXUINT64, 0, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_START, properties[PROP_START]); /** * NleObject:duration * * The outgoing duration in nanoseconds. */ properties[PROP_DURATION] = g_param_spec_int64 ("duration", "Duration", "Outgoing duration (in nanoseconds)", 0, G_MAXINT64, 0, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_DURATION, properties[PROP_DURATION]); /** * NleObject:stop * * The stop position relative to the parent in nanoseconds. * * This value is computed based on the values of start and duration. */ properties[PROP_STOP] = g_param_spec_uint64 ("stop", "Stop", "The stop position relative to the parent (in nanoseconds)", 0, G_MAXUINT64, 0, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_STOP, properties[PROP_STOP]); /** * NleObject:inpoint * * The media start position in nanoseconds. * * Also called 'in-point' in video-editing, this corresponds to * what position in the 'contained' object we should start outputting from. */ properties[PROP_INPOINT] = g_param_spec_uint64 ("inpoint", "Media start", "The media start position (in nanoseconds)", 0, G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_INPOINT, properties[PROP_INPOINT]); /** * NleObject:priority * * The priority of the object in the container. * * The highest priority is 0, meaning this object will be selected over * any other between start and stop. * * The lowest priority is G_MAXUINT32. * * Objects whose priority is (-1) will be considered as 'default' objects * in NleComposition and their start/stop values will be modified as to * fit the whole duration of the composition. */ properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority", "The priority of the object (0 = highest priority)", 0, G_MAXUINT, 0, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_PRIORITY, properties[PROP_PRIORITY]); /** * NleObject:active * * Indicates whether this object should be used by its container. * * Set to #TRUE to temporarily disable this object in a #NleComposition. */ properties[PROP_ACTIVE] = g_param_spec_boolean ("active", "Active", "Use this object in the NleComposition", TRUE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_ACTIVE, properties[PROP_ACTIVE]); /** * NleObject:caps * * Caps used to filter/choose the output stream. * * If the controlled object produces several stream, you can set this * property to choose a specific stream. * * If nothing is specified then a source pad will be chosen at random. */ properties[PROP_CAPS] = g_param_spec_boxed ("caps", "Caps", "Caps used to filter/choose the output stream", GST_TYPE_CAPS, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CAPS, properties[PROP_CAPS]); /** * NleObject:expandable * * Indicates whether this object should expand to the full duration of its * container #NleComposition. */ properties[PROP_EXPANDABLE] = g_param_spec_boolean ("expandable", "Expandable", "Expand to the full duration of the container composition", FALSE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_EXPANDABLE, properties[PROP_EXPANDABLE]); /** * NleObject:media-duration-factor * * Indicates the relative rate caused by this object, in other words, the * relation between the rate of media entering and leaving this object. I.e. * if object pulls data at twice the speed it sends it (e.g. `pitch * tempo=2.0`), this value is set to 2.0. * * Deprecated: 1.18: This property is ignored since the wrapped * #GstElement-s themselves should internally perform any additional time * translations. */ properties[PROP_MEDIA_DURATION_FACTOR] = g_param_spec_double ("media-duration-factor", "Media duration factor", "The relative rate caused by this object", 0.01, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE | G_PARAM_DEPRECATED); g_object_class_install_property (gobject_class, PROP_MEDIA_DURATION_FACTOR, properties[PROP_MEDIA_DURATION_FACTOR]); /** * NleObject::commit * @object: a #NleObject * @recurse: Whether to commit recursiverly into (NleComposition) children of * @object. This is used in case we have composition inside * a nlesource composition, telling it to commit the included * composition state. * * Action signal to commit all the pending changes of the composition and * its children timing properties * * Returns: %TRUE if changes have been commited, %FALSE if nothing had to * be commited */ _signals[COMMIT_SIGNAL] = g_signal_new ("commit", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (NleObjectClass, commit_signal_handler), NULL, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); gst_type_mark_as_plugin_api (NLE_TYPE_OBJECT, 0); } static void nle_object_init (NleObject * object, NleObjectClass * klass) { object->start = object->pending_start = 0; object->duration = object->pending_duration = 0; object->stop = 0; object->inpoint = object->pending_inpoint = GST_CLOCK_TIME_NONE; object->priority = object->pending_priority = 0; object->active = object->pending_active = TRUE; object->caps = gst_caps_new_any (); object->segment_rate = 1.0; object->segment_start = -1; object->segment_stop = -1; object->srcpad = nle_object_ghost_pad_no_target (object, "src", GST_PAD_SRC, gst_element_class_get_pad_template ((GstElementClass *) klass, "src")); gst_element_add_pad (GST_ELEMENT (object), object->srcpad); } static void nle_object_dispose (GObject * object) { NleObject *nle = (NleObject *) object; if (nle->caps) { gst_caps_unref (nle->caps); nle->caps = NULL; } if (nle->srcpad) { nle_object_remove_ghost_pad (nle, nle->srcpad); nle->srcpad = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); } /** * nle_object_to_media_time: * @object: a #NleObject * @objecttime: The #GstClockTime we want to convert * @mediatime: A pointer on a #GstClockTime to fill * * Converts a #GstClockTime timestamp received from another nleobject pad * in the same nlecomposition, or from the nlecomposition itself, to an * internal source time. * * If the object is furthest downstream in the nlecomposition (highest * priority in the current stack), this will convert the timestamp from * the composition coordinate time to the internal source coordinate time * of the object. * * If the object is upstream from another nleobject, then this can convert * the timestamp received from the downstream sink pad to the internal * source coordinates of the object, to be passed to its internal * elements. * * If the object is downstream from another nleobject, then this can * convert the timestamp received from the upstream source pad to the * internal sink coordinates of the object, to be passed to its internal * elements. * * In these latter two cases, the timestamp should have been converted * by the peer pad using nle_media_to_object_time(). * * Note, if an object introduces a time effect, it must have a 0 in-point * and the same #nleobject:start and #nleobject:duration as all the other * objects that are further upstream. * * Returns: TRUE if @objecttime was below the @object start time, * FALSE otherwise. */ gboolean nle_object_to_media_time (NleObject * object, GstClockTime otime, GstClockTime * mtime) { gboolean ret = TRUE; g_return_val_if_fail (mtime, FALSE); GST_DEBUG_OBJECT (object, "ObjectTime : %" GST_TIME_FORMAT, GST_TIME_ARGS (otime)); GST_DEBUG_OBJECT (object, "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] " "Media start: %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint)); if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (otime))) { GST_DEBUG_OBJECT (object, "converting none object time to none"); *mtime = GST_CLOCK_TIME_NONE; return TRUE; } /* We do not allow @otime to be below the start of the object. * If it was below, then the object would have a negative external * source/sink time. * * Note that ges only supports time effects that map the time 0 to * 0. Such time effects would also not produce an external timestamp * below start, nor can they receive such a timestamp. */ if (G_UNLIKELY ((otime < object->start))) { GST_DEBUG_OBJECT (object, "ObjectTime is before start"); otime = object->start; ret = FALSE; } /* NOTE: if an nlecomposition contains time effect operations, then * @otime can reasonably exceed the stop time of the object. So we * do not limit it here. */ /* first we convert the timestamp to the object's external source/sink * coordinates: * + For an object that is furthest downstream, we translate from the * composition coordinates to the external source coordinates by * subtracting the object start. * + For an object that is upstream from d_object, we need to * translate from its external sink coordinates to our external * source coordinates. This is done by adding * (d_object->start - object->start) * However, the sink pad of d_object should have already added the * d_object->start to the timestamp (see nle_media_to_object_time) * so we also only need to subtract the object start. * + For an object that is downstream from u_object, we need to * translate from its external source coordinates to our external * sink coordinates. This is similarly done by adding * (u_object->start - object->start) * However, the source pad of u_object should have already added the * u_object->start to the timestamp (see nle_media_to_object_time) * so we also only need to subtract the object start. */ *mtime = otime - object->start; /* we then convert the timestamp from the object's external source/sink * coordinates to its internal source/sink coordinates, to be used by * internal elements that the object wraps. This is done by adding * the object in-point. */ if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint))) *mtime += object->inpoint; GST_DEBUG_OBJECT (object, "Returning MediaTime : %" GST_TIME_FORMAT, GST_TIME_ARGS (*mtime)); return ret; } /** * nle_media_to_object_time: * @object: The #NleObject * @mediatime: The #GstClockTime we want to convert * @objecttime: A pointer on a #GstClockTime to fill * * Converts a #GstClockTime timestamp from an internal time to an * nleobject pad time. * * If the object is furthest downstream in an nlecomposition (highest * priority in the current stack), this will convert the timestamp from * the internal source coordinate time of the object to the composition * coordinate time. * * If the object is upstream from another nleobject, then this can convert * the timestamp from the internal source coordinates of the object to be * sent to the downstream sink pad. * * If the object is downstream from another nleobject, then this can * convert the timestamp from the internal sink coordinates of the object * to be sent to the upstream source pad. * * Note, if an object introduces a time effect, it must have a 0 in-point * and the same #nleobject:start and #nleobject:duration as all the other * objects that are further upstream. * * Returns: TRUE if @objecttime was below the @object in-point time, * FALSE otherwise. */ gboolean nle_media_to_object_time (NleObject * object, GstClockTime mtime, GstClockTime * otime) { g_return_val_if_fail (otime, FALSE); GST_DEBUG_OBJECT (object, "MediaTime : %" GST_TIME_FORMAT, GST_TIME_ARGS (mtime)); GST_DEBUG_OBJECT (object, "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] " "inpoint %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint)); if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (mtime))) { GST_DEBUG_OBJECT (object, "converting none media time to none"); *otime = GST_CLOCK_TIME_NONE; return TRUE; } /* the internal time should never go below the in-point! */ if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint) && (mtime < object->inpoint))) { GST_DEBUG_OBJECT (object, "media time is before inpoint, forcing to start"); *otime = object->start; return FALSE; } /* first we convert the timestamp to the object's external source/sink * coordinates by removing the in-point */ if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint))) *otime = mtime - object->inpoint; else *otime = mtime; /* then we convert the timestamp by adding start. * If the object is furthest downstream, this will translate it from * the external source coordinates to the composition coordinates. * Otherwise, this will perform part of the conversion from the object's * source/sink coordinates to the downstream/upstream sink/source * coordinates (the conversion is completed in * nle_object_to_media_time). */ *otime += object->start; GST_DEBUG_OBJECT (object, "Returning ObjectTime : %" GST_TIME_FORMAT, GST_TIME_ARGS (*otime)); return TRUE; } static gboolean nle_object_prepare_func (NleObject * object) { GST_DEBUG_OBJECT (object, "default prepare function, returning TRUE"); return TRUE; } static GstStateChangeReturn nle_object_prepare (NleObject * object) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GST_DEBUG_OBJECT (object, "preparing"); if (!(NLE_OBJECT_GET_CLASS (object)->prepare (object))) ret = GST_STATE_CHANGE_FAILURE; GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); return ret; } static gboolean nle_object_cleanup_func (NleObject * object) { GST_DEBUG_OBJECT (object, "default cleanup function, returning TRUE"); return TRUE; } GstStateChangeReturn nle_object_cleanup (NleObject * object) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GST_DEBUG_OBJECT (object, "cleaning-up"); if (!(NLE_OBJECT_GET_CLASS (object)->cleanup (object))) ret = GST_STATE_CHANGE_FAILURE; GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); return ret; } void nle_object_set_caps (NleObject * object, const GstCaps * caps) { if (object->caps) gst_caps_unref (object->caps); object->caps = gst_caps_copy (caps); } static inline void _update_stop (NleObject * nleobject) { /* check if start/duration has changed */ if ((nleobject->pending_start + nleobject->pending_duration) != nleobject->stop) { nleobject->stop = nleobject->pending_start + nleobject->pending_duration; GST_LOG_OBJECT (nleobject, "Updating stop value : %" GST_TIME_FORMAT " [start:%" GST_TIME_FORMAT ", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (nleobject->stop), GST_TIME_ARGS (nleobject->pending_start), GST_TIME_ARGS (nleobject->pending_duration)); g_object_notify_by_pspec (G_OBJECT (nleobject), properties[PROP_STOP]); } } static void update_values (NleObject * object) { CHECK_AND_SET (START, start, "start", G_GUINT64_FORMAT); CHECK_AND_SET (INPOINT, inpoint, "inpoint", G_GUINT64_FORMAT); CHECK_AND_SET (DURATION, duration, "duration", G_GINT64_FORMAT); CHECK_AND_SET (PRIORITY, priority, "priority", G_GUINT32_FORMAT); CHECK_AND_SET (ACTIVE, active, "active", G_GUINT32_FORMAT); _update_stop (object); } static gboolean nle_object_commit_func (NleObject * object, gboolean recurse) { GST_DEBUG_OBJECT (object, "Commiting object changed"); if (object->commit_needed == FALSE) { GST_INFO_OBJECT (object, "No changes to commit"); return FALSE; } update_values (object); GST_DEBUG_OBJECT (object, "Done commiting"); return TRUE; } static void nle_object_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { NleObject *nleobject = (NleObject *) object; g_return_if_fail (NLE_IS_OBJECT (object)); GST_OBJECT_LOCK (object); switch (prop_id) { case PROP_START: SET_PENDING_VALUE (start, "start", uint64, G_GUINT64_FORMAT); break; case PROP_DURATION: SET_PENDING_VALUE (duration, "duration", int64, G_GINT64_FORMAT); break; case PROP_INPOINT: SET_PENDING_VALUE (inpoint, "inpoint", uint64, G_GUINT64_FORMAT); break; case PROP_PRIORITY: SET_PENDING_VALUE (priority, "priority", uint, G_GUINT32_FORMAT); break; case PROP_ACTIVE: SET_PENDING_VALUE (active, "active", boolean, G_GUINT32_FORMAT); break; case PROP_CAPS: nle_object_set_caps (nleobject, gst_value_get_caps (value)); break; case PROP_EXPANDABLE: if (g_value_get_boolean (value)) GST_OBJECT_FLAG_SET (nleobject, NLE_OBJECT_EXPANDABLE); else GST_OBJECT_FLAG_UNSET (nleobject, NLE_OBJECT_EXPANDABLE); break; case PROP_MEDIA_DURATION_FACTOR: { gdouble val = g_value_get_double (value); if (val != 1.0) g_warning ("Ignoring media-duration-factor value of %g since the " "property is deprecated", val); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } GST_OBJECT_UNLOCK (object); } static void nle_object_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { NleObject *nleobject = (NleObject *) object; switch (prop_id) { case PROP_START: g_value_set_uint64 (value, nleobject->pending_start); break; case PROP_DURATION: g_value_set_int64 (value, nleobject->pending_duration); break; case PROP_STOP: g_value_set_uint64 (value, nleobject->stop); break; case PROP_INPOINT: g_value_set_uint64 (value, nleobject->pending_inpoint); break; case PROP_PRIORITY: g_value_set_uint (value, nleobject->pending_priority); break; case PROP_ACTIVE: g_value_set_boolean (value, nleobject->pending_active); break; case PROP_CAPS: gst_value_set_caps (value, nleobject->caps); break; case PROP_EXPANDABLE: g_value_set_boolean (value, NLE_OBJECT_IS_EXPANDABLE (object)); break; case PROP_MEDIA_DURATION_FACTOR: g_value_set_double (value, 1.0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void nle_object_constructed (GObject * object) { NleObject *nleobject = (NleObject *) object; _update_stop (nleobject); } static GstStateChangeReturn nle_object_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: { GstObject *parent = gst_object_get_parent (GST_OBJECT (element)); /* Going to READY and if we are not in a composition, we need to make * sure that the object positioning state is properly commited */ if (parent) { if (g_strcmp0 (GST_ELEMENT_NAME (GST_ELEMENT (parent)), "current-bin") && !NLE_OBJECT_IS_COMPOSITION (NLE_OBJECT (element))) { GST_INFO ("Adding nleobject to something that is not a composition," " commiting ourself"); nle_object_commit (NLE_OBJECT (element), FALSE); } gst_object_unref (parent); } } break; case GST_STATE_CHANGE_READY_TO_PAUSED: if (nle_object_prepare (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) { ret = GST_STATE_CHANGE_FAILURE; goto beach; } break; default: break; } GST_DEBUG_OBJECT (element, "Calling parent change_state"); ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); GST_DEBUG_OBJECT (element, "Return from parent change_state was %d", ret); if (ret == GST_STATE_CHANGE_FAILURE) goto beach; switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: /* cleanup nleobject */ if (nle_object_cleanup (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) ret = GST_STATE_CHANGE_FAILURE; break; default: break; } beach: return ret; } void nle_object_set_commit_needed (NleObject * object) { if (G_UNLIKELY (object->commiting)) { GST_WARNING_OBJECT (object, "Trying to set 'commit-needed' while commiting"); return; } GST_DEBUG_OBJECT (object, "Setting 'commit_needed'"); object->commit_needed = TRUE; } gboolean nle_object_commit (NleObject * object, gboolean recurse) { gboolean ret; GST_DEBUG_OBJECT (object, "Commiting object state"); object->commiting = TRUE; ret = NLE_OBJECT_GET_CLASS (object)->commit (object, recurse); object->commiting = FALSE; return ret; } static void _send_seek_event (const GValue * item, gpointer seek_event) { GstElement *child = g_value_get_object (item); gst_element_send_event (child, gst_event_ref (seek_event)); } void nle_object_seek_all_children (NleObject * object, GstEvent * seek_event) { GstIterator *it = gst_bin_iterate_recurse (GST_BIN (object)); while (gst_iterator_foreach (it, _send_seek_event, seek_event) == GST_ITERATOR_RESYNC) gst_iterator_resync (it); gst_iterator_free (it); gst_event_unref (seek_event); } void nle_object_reset (NleObject * object) { GST_INFO_OBJECT (object, "Resetting child timing values to default"); object->start = 0; object->duration = 0; object->stop = 0; object->inpoint = GST_CLOCK_TIME_NONE; object->priority = 0; object->active = TRUE; object->in_composition = FALSE; } GType nle_object_get_type (void) { static gsize type = 0; if (g_once_init_enter (&type)) { GType _type; static const GTypeInfo info = { sizeof (NleObjectClass), NULL, NULL, (GClassInitFunc) nle_object_class_init, NULL, NULL, sizeof (NleObject), 0, (GInstanceInitFunc) nle_object_init, }; _type = g_type_register_static (GST_TYPE_BIN, "NleObject", &info, G_TYPE_FLAG_ABSTRACT); g_once_init_leave (&type, _type); } return type; }