mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
4c26b89e3c
This property was firstly added to TrackFileSource, but in the end, it makes more sense for it to be directly in TrackOject as it can be usefull in other cases.
1481 lines
41 KiB
C
1481 lines
41 KiB
C
/* GStreamer Editing Services
|
|
* Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
|
|
* 2009 Nokia Corporation
|
|
*
|
|
* 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., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:ges-track-object
|
|
* @short_description: Base Class for objects contained in a #GESTrack
|
|
*
|
|
* #GESTrackObject is the Base Class for any object that can be contained in a
|
|
* #GESTrack.
|
|
*
|
|
* It contains the basic information as to the location of the object within
|
|
* its container, like the start position, the in-point, the duration and the
|
|
* priority.
|
|
*/
|
|
|
|
#include "ges-internal.h"
|
|
#include "gesmarshal.h"
|
|
#include "ges-track-object.h"
|
|
#include "ges-timeline-object.h"
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
G_DEFINE_ABSTRACT_TYPE (GESTrackObject, ges_track_object,
|
|
G_TYPE_INITIALLY_UNOWNED);
|
|
|
|
struct _GESTrackObjectPrivate
|
|
{
|
|
/* These fields are only used before the gnlobject is available */
|
|
guint64 pending_start;
|
|
guint64 pending_inpoint;
|
|
guint64 pending_duration;
|
|
guint32 pending_priority;
|
|
gboolean pending_active;
|
|
|
|
GstElement *gnlobject; /* The GnlObject */
|
|
GstElement *element; /* The element contained in the gnlobject (can be NULL) */
|
|
|
|
/* We keep a link between properties name and elements internally
|
|
* The hashtable should look like
|
|
* {GParamaSpec ---> element,}*/
|
|
GHashTable *properties_hashtable;
|
|
|
|
GESTimelineObject *timelineobj;
|
|
GESTrack *track;
|
|
|
|
gboolean valid;
|
|
|
|
guint64 maxduration;
|
|
|
|
gboolean locked; /* If TRUE, then moves in sync with its controlling
|
|
* GESTimelineObject */
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_START,
|
|
PROP_INPOINT,
|
|
PROP_DURATION,
|
|
PROP_PRIORITY,
|
|
PROP_ACTIVE,
|
|
PROP_LOCKED,
|
|
PROP_MAX_DURATION,
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *properties[PROP_LAST];
|
|
|
|
enum
|
|
{
|
|
DEEP_NOTIFY,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint ges_track_object_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
static GstElement *ges_track_object_create_gnl_object_func (GESTrackObject *
|
|
object);
|
|
|
|
static void gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrackObject * obj);
|
|
|
|
static void gnlobject_media_start_cb (GstElement * gnlobject, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrackObject * obj);
|
|
|
|
static void gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrackObject * obj);
|
|
|
|
static void gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrackObject * obj);
|
|
|
|
static void gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrackObject * obj);
|
|
|
|
static void connect_properties_signals (GESTrackObject * object);
|
|
static void connect_signal (gpointer key, gpointer value, gpointer user_data);
|
|
static void gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrackObject * obj);
|
|
|
|
static inline gboolean
|
|
ges_track_object_set_start_internal (GESTrackObject * object, guint64 start);
|
|
static inline gboolean
|
|
ges_track_object_set_inpoint_internal (GESTrackObject * object,
|
|
guint64 inpoint);
|
|
static inline gboolean ges_track_object_set_duration_internal (GESTrackObject *
|
|
object, guint64 duration);
|
|
static inline gboolean ges_track_object_set_priority_internal (GESTrackObject *
|
|
object, guint32 priority);
|
|
static inline void
|
|
ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked);
|
|
|
|
static GParamSpec **default_list_children_properties (GESTrackObject * object,
|
|
guint * n_properties);
|
|
|
|
static void
|
|
ges_track_object_get_property (GObject * object, guint property_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GESTrackObject *tobj = GES_TRACK_OBJECT (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_START:
|
|
g_value_set_uint64 (value, ges_track_object_get_start (tobj));
|
|
break;
|
|
case PROP_INPOINT:
|
|
g_value_set_uint64 (value, ges_track_object_get_inpoint (tobj));
|
|
break;
|
|
case PROP_DURATION:
|
|
g_value_set_uint64 (value, ges_track_object_get_duration (tobj));
|
|
break;
|
|
case PROP_PRIORITY:
|
|
g_value_set_uint (value, ges_track_object_get_priority (tobj));
|
|
break;
|
|
case PROP_ACTIVE:
|
|
g_value_set_boolean (value, ges_track_object_is_active (tobj));
|
|
break;
|
|
case PROP_LOCKED:
|
|
g_value_set_boolean (value, ges_track_object_is_locked (tobj));
|
|
break;
|
|
case PROP_MAX_DURATION:
|
|
g_value_set_uint64 (value, tobj->priv->maxduration);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ges_track_object_set_property (GObject * object, guint property_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GESTrackObject *tobj = GES_TRACK_OBJECT (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_START:
|
|
ges_track_object_set_start_internal (tobj, g_value_get_uint64 (value));
|
|
break;
|
|
case PROP_INPOINT:
|
|
ges_track_object_set_inpoint_internal (tobj, g_value_get_uint64 (value));
|
|
break;
|
|
case PROP_DURATION:
|
|
ges_track_object_set_duration_internal (tobj, g_value_get_uint64 (value));
|
|
break;
|
|
case PROP_PRIORITY:
|
|
ges_track_object_set_priority_internal (tobj, g_value_get_uint (value));
|
|
break;
|
|
case PROP_ACTIVE:
|
|
ges_track_object_set_active (tobj, g_value_get_boolean (value));
|
|
break;
|
|
case PROP_LOCKED:
|
|
ges_track_object_set_locked_internal (tobj, g_value_get_boolean (value));
|
|
break;
|
|
case PROP_MAX_DURATION:
|
|
ges_track_object_set_max_duration (tobj, g_value_get_uint64 (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ges_track_object_dispose (GObject * object)
|
|
{
|
|
GESTrackObjectPrivate *priv = GES_TRACK_OBJECT (object)->priv;
|
|
if (priv->properties_hashtable)
|
|
g_hash_table_destroy (priv->properties_hashtable);
|
|
|
|
G_OBJECT_CLASS (ges_track_object_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
ges_track_object_finalize (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS (ges_track_object_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
ges_track_object_class_init (GESTrackObjectClass * klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (GESTrackObjectPrivate));
|
|
|
|
object_class->get_property = ges_track_object_get_property;
|
|
object_class->set_property = ges_track_object_set_property;
|
|
object_class->dispose = ges_track_object_dispose;
|
|
object_class->finalize = ges_track_object_finalize;
|
|
|
|
/**
|
|
* GESTrackObject:start
|
|
*
|
|
* The position of the object in the container #GESTrack (in nanoseconds).
|
|
*/
|
|
properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
|
|
"The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_START,
|
|
properties[PROP_START]);
|
|
|
|
/**
|
|
* GESTrackObject:in-point
|
|
*
|
|
* The in-point at which this #GESTrackObject will start outputting data
|
|
* from its contents (in nanoseconds).
|
|
*
|
|
* Ex: an in-point of 5 seconds means that the first outputted buffer will
|
|
* be the one located 5 seconds in the controlled resource.
|
|
*/
|
|
properties[PROP_INPOINT] =
|
|
g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
|
|
G_MAXUINT64, 0, G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_INPOINT,
|
|
properties[PROP_INPOINT]);
|
|
|
|
/**
|
|
* GESTrackObject:duration
|
|
*
|
|
* The duration (in nanoseconds) which will be used in the container #GESTrack
|
|
* starting from 'in-point'.
|
|
*
|
|
*/
|
|
properties[PROP_DURATION] =
|
|
g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
|
|
G_MAXUINT64, GST_SECOND, G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_DURATION,
|
|
properties[PROP_DURATION]);
|
|
|
|
/**
|
|
* GESTrackObject:priority
|
|
*
|
|
* The priority of the object within the containing #GESTrack.
|
|
* If two objects intersect over the same region of time, the @priority
|
|
* property is used to decide which one takes precedence.
|
|
*
|
|
* The highest priority (that supercedes everything) is 0, and then lowering
|
|
* priorities go in increasing numerical value (with #G_MAXUINT64 being the
|
|
* lowest priority).
|
|
*/
|
|
properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
|
|
"The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_PRIORITY,
|
|
properties[PROP_PRIORITY]);
|
|
|
|
/**
|
|
* GESTrackObject:active
|
|
*
|
|
* Whether the object should be taken into account in the #GESTrack output.
|
|
* If #FALSE, then its contents will not be used in the resulting track.
|
|
*/
|
|
properties[PROP_ACTIVE] =
|
|
g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_ACTIVE,
|
|
properties[PROP_ACTIVE]);
|
|
|
|
/**
|
|
* GESTrackObject:locked
|
|
*
|
|
* If %TRUE, then moves in sync with its controlling #GESTimelineObject
|
|
*/
|
|
properties[PROP_LOCKED] =
|
|
g_param_spec_boolean ("locked", "Locked",
|
|
"Moves in sync with its controling TimelineObject", TRUE,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_LOCKED,
|
|
properties[PROP_LOCKED]);
|
|
|
|
/**
|
|
* GESTrackObject:max-duration:
|
|
*
|
|
* The maximum duration (in nanoseconds) of the #GESTrackObject.
|
|
*
|
|
* Since: 0.10.XX
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_MAX_DURATION,
|
|
g_param_spec_uint64 ("max-duration", "Maximum duration",
|
|
"The duration of the object", 0, G_MAXUINT64, G_MAXUINT64,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
|
|
/**
|
|
* GESTrackObject::deep-notify:
|
|
* @track_object: a #GESTrackObject
|
|
* @prop_object: the object that originated the signal
|
|
* @prop: the property that changed
|
|
*
|
|
* The deep notify signal is used to be notified of property changes of all
|
|
* the childs of @track_object
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
ges_track_object_signals[DEEP_NOTIFY] =
|
|
g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
|
|
G_SIGNAL_NO_HOOKS, 0, NULL, NULL, gst_marshal_VOID__OBJECT_PARAM,
|
|
G_TYPE_NONE, 2, GST_TYPE_ELEMENT, G_TYPE_PARAM);
|
|
|
|
klass->create_gnl_object = ges_track_object_create_gnl_object_func;
|
|
/* There is no 'get_props_hashtable' default implementation */
|
|
klass->get_props_hastable = NULL;
|
|
klass->list_children_properties = default_list_children_properties;
|
|
}
|
|
|
|
static void
|
|
ges_track_object_init (GESTrackObject * self)
|
|
{
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
|
GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
|
|
|
|
/* Sane default values */
|
|
self->priv->pending_start = 0;
|
|
self->priv->pending_inpoint = 0;
|
|
self->priv->pending_duration = GST_SECOND;
|
|
self->priv->pending_priority = 1;
|
|
self->priv->pending_active = TRUE;
|
|
self->priv->locked = TRUE;
|
|
self->priv->properties_hashtable = NULL;
|
|
}
|
|
|
|
static inline gboolean
|
|
ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
|
|
{
|
|
GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
|
|
object, GST_TIME_ARGS (start));
|
|
|
|
if (object->priv->gnlobject != NULL) {
|
|
if (G_UNLIKELY (start == object->start))
|
|
return FALSE;
|
|
|
|
g_object_set (object->priv->gnlobject, "start", start, NULL);
|
|
} else
|
|
object->priv->pending_start = start;
|
|
return TRUE;
|
|
};
|
|
|
|
/**
|
|
* ges_track_object_set_start:
|
|
* @object: a #GESTrackObject
|
|
* @start: the start position (in #GstClockTime)
|
|
*
|
|
* Sets the position of the object in the container #GESTrack.
|
|
*/
|
|
void
|
|
ges_track_object_set_start (GESTrackObject * object, guint64 start)
|
|
{
|
|
if (ges_track_object_set_start_internal (object, start))
|
|
#if GLIB_CHECK_VERSION(2,26,0)
|
|
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
|
|
#else
|
|
g_object_notify (G_OBJECT (object), "start");
|
|
#endif
|
|
}
|
|
|
|
static inline gboolean
|
|
ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
|
|
{
|
|
|
|
GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
|
|
object, GST_TIME_ARGS (inpoint));
|
|
|
|
if (object->priv->gnlobject != NULL) {
|
|
if (G_UNLIKELY (inpoint == object->inpoint))
|
|
return FALSE;
|
|
|
|
g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
|
|
} else
|
|
object->priv->pending_inpoint = inpoint;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_inpoint:
|
|
* @object: a #GESTrackObject
|
|
* @inpoint: the in-point (in #GstClockTime)
|
|
*
|
|
* Set the offset within the contents of this #GESTrackObject
|
|
*/
|
|
void
|
|
ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
|
|
{
|
|
if (ges_track_object_set_inpoint_internal (object, inpoint))
|
|
#if GLIB_CHECK_VERSION(2,26,0)
|
|
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
|
|
#else
|
|
g_object_notify (G_OBJECT (object), "in-point");
|
|
#endif
|
|
}
|
|
|
|
static inline gboolean
|
|
ges_track_object_set_duration_internal (GESTrackObject * object,
|
|
guint64 duration)
|
|
{
|
|
GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
|
|
object, GST_TIME_ARGS (duration));
|
|
|
|
if (object->priv->gnlobject != NULL) {
|
|
if (G_UNLIKELY (duration == object->duration))
|
|
return FALSE;
|
|
|
|
g_object_set (object->priv->gnlobject, "duration", duration,
|
|
"media-duration", duration, NULL);
|
|
} else
|
|
object->priv->pending_duration = duration;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_duration:
|
|
* @object: a #GESTrackObject
|
|
* @duration: the duration (in #GstClockTime)
|
|
*
|
|
* Set the duration which will be used in the container #GESTrack
|
|
* starting from the 'in-point'
|
|
*/
|
|
void
|
|
ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
|
|
{
|
|
if (ges_track_object_set_duration_internal (object, duration))
|
|
#if GLIB_CHECK_VERSION(2,26,0)
|
|
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
|
|
#else
|
|
g_object_notify (G_OBJECT (object), "duration");
|
|
#endif
|
|
}
|
|
|
|
static inline gboolean
|
|
ges_track_object_set_priority_internal (GESTrackObject * object,
|
|
guint32 priority)
|
|
{
|
|
GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
|
|
|
|
if (object->priv->gnlobject != NULL) {
|
|
if (G_UNLIKELY (priority == object->priority))
|
|
return FALSE;
|
|
|
|
g_object_set (object->priv->gnlobject, "priority", priority, NULL);
|
|
} else
|
|
object->priv->pending_priority = priority;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_priority:
|
|
* @object: a #GESTrackObject
|
|
* @priority: the priority
|
|
*
|
|
* Sets the priority of the object withing the containing #GESTrack.
|
|
* If two objects intersect over the same region of time, the priority
|
|
* property is used to decide which one takes precedence.
|
|
*
|
|
* The highest priority (that supercedes everything) is 0, and then
|
|
* lowering priorities go in increasing numerical value (with G_MAXUINT32
|
|
* being the lowest priority).
|
|
*/
|
|
void
|
|
ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
|
|
{
|
|
if (ges_track_object_set_priority_internal (object, priority))
|
|
#if GLIB_CHECK_VERSION(2,26,0)
|
|
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
|
|
#else
|
|
g_object_notify (G_OBJECT (object), "priority");
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* ges_track_object_set_active:
|
|
* @object: a #GESTrackObject
|
|
* @active: visibility
|
|
*
|
|
* Sets the usage of the @object. If @active is %TRUE, the object will be used for
|
|
* playback and rendering, else it will be ignored.
|
|
*
|
|
* Returns: %TRUE if the property was toggled, else %FALSE
|
|
*/
|
|
gboolean
|
|
ges_track_object_set_active (GESTrackObject * object, gboolean active)
|
|
{
|
|
GST_DEBUG ("object:%p, active:%d", object, active);
|
|
|
|
if (object->priv->gnlobject != NULL) {
|
|
if (G_UNLIKELY (active == object->active))
|
|
return FALSE;
|
|
|
|
g_object_set (object->priv->gnlobject, "active", active, NULL);
|
|
} else
|
|
object->priv->pending_active = active;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Callbacks from the GNonLin object */
|
|
static void
|
|
gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
|
|
GESTrackObject * obj)
|
|
{
|
|
guint64 start;
|
|
GESTrackObjectClass *klass;
|
|
|
|
klass = GES_TRACK_OBJECT_GET_CLASS (obj);
|
|
|
|
g_object_get (gnlobject, "start", &start, NULL);
|
|
|
|
GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
|
|
|
|
if (start != obj->start) {
|
|
obj->start = start;
|
|
if (klass->start_changed)
|
|
klass->start_changed (obj, start);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrackObject * obj)
|
|
{
|
|
g_signal_emit (obj, ges_track_object_signals[DEEP_NOTIFY], 0,
|
|
GST_ELEMENT (element), arg);
|
|
}
|
|
|
|
static void
|
|
connect_signal (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
gchar *signame = g_strconcat ("notify::", G_PARAM_SPEC (key)->name, NULL);
|
|
|
|
g_signal_connect (G_OBJECT (value),
|
|
signame, G_CALLBACK (gst_element_prop_changed_cb),
|
|
GES_TRACK_OBJECT (user_data));
|
|
|
|
g_free (signame);
|
|
}
|
|
|
|
static void
|
|
connect_properties_signals (GESTrackObject * object)
|
|
{
|
|
if (G_UNLIKELY (!object->priv->properties_hashtable)) {
|
|
GST_WARNING ("The properties_hashtable hasn't been set");
|
|
return;
|
|
}
|
|
|
|
g_hash_table_foreach (object->priv->properties_hashtable,
|
|
(GHFunc) connect_signal, object);
|
|
|
|
}
|
|
|
|
/* Callbacks from the GNonLin object */
|
|
static void
|
|
gnlobject_media_start_cb (GstElement * gnlobject,
|
|
GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
|
|
{
|
|
guint64 start;
|
|
GESTrackObjectClass *klass;
|
|
|
|
klass = GES_TRACK_OBJECT_GET_CLASS (obj);
|
|
|
|
g_object_get (gnlobject, "media-start", &start, NULL);
|
|
|
|
GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
|
|
|
|
if (start != obj->inpoint) {
|
|
obj->inpoint = start;
|
|
if (klass->media_start_changed)
|
|
klass->media_start_changed (obj, start);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
|
|
GESTrackObject * obj)
|
|
{
|
|
guint32 priority;
|
|
GESTrackObjectClass *klass;
|
|
|
|
klass = GES_TRACK_OBJECT_GET_CLASS (obj);
|
|
|
|
g_object_get (gnlobject, "priority", &priority, NULL);
|
|
|
|
GST_DEBUG ("gnlobject priority : %d current : %d", priority, obj->priority);
|
|
|
|
if (priority != obj->priority) {
|
|
obj->priority = priority;
|
|
if (klass->gnl_priority_changed)
|
|
klass->gnl_priority_changed (obj, priority);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
|
|
GESTrackObject * obj)
|
|
{
|
|
guint64 duration;
|
|
GESTrackObjectClass *klass;
|
|
|
|
klass = GES_TRACK_OBJECT_GET_CLASS (obj);
|
|
|
|
g_object_get (gnlobject, "duration", &duration, NULL);
|
|
|
|
GST_DEBUG ("gnlobject duration : %" GST_TIME_FORMAT " current : %"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
|
|
|
|
if (duration != obj->duration) {
|
|
obj->duration = duration;
|
|
if (klass->duration_changed)
|
|
klass->duration_changed (obj, duration);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
|
|
GESTrackObject * obj)
|
|
{
|
|
gboolean active;
|
|
GESTrackObjectClass *klass;
|
|
|
|
klass = GES_TRACK_OBJECT_GET_CLASS (obj);
|
|
|
|
g_object_get (gnlobject, "active", &active, NULL);
|
|
|
|
GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
|
|
|
|
if (active != obj->active) {
|
|
obj->active = active;
|
|
if (klass->active_changed)
|
|
klass->active_changed (obj, active);
|
|
}
|
|
}
|
|
|
|
|
|
/* default 'create_gnl_object' virtual method implementation */
|
|
static GstElement *
|
|
ges_track_object_create_gnl_object_func (GESTrackObject * self)
|
|
{
|
|
GESTrackObjectClass *klass = NULL;
|
|
GstElement *child = NULL;
|
|
GstElement *gnlobject;
|
|
|
|
klass = GES_TRACK_OBJECT_GET_CLASS (self);
|
|
|
|
if (G_UNLIKELY (self->priv->gnlobject != NULL))
|
|
goto already_have_gnlobject;
|
|
|
|
if (G_UNLIKELY (klass->gnlobject_factorytype == NULL))
|
|
goto no_gnlfactory;
|
|
|
|
GST_DEBUG ("Creating a supporting gnlobject of type '%s'",
|
|
klass->gnlobject_factorytype);
|
|
|
|
gnlobject = gst_element_factory_make (klass->gnlobject_factorytype, NULL);
|
|
|
|
if (G_UNLIKELY (gnlobject == NULL))
|
|
goto no_gnlobject;
|
|
|
|
if (klass->create_element) {
|
|
GST_DEBUG ("Calling subclass 'create_element' vmethod");
|
|
child = klass->create_element (self);
|
|
|
|
if (G_UNLIKELY (!child))
|
|
goto child_failure;
|
|
|
|
if (!gst_bin_add (GST_BIN (gnlobject), child))
|
|
goto add_failure;
|
|
|
|
GST_DEBUG ("Succesfully got the element to put in the gnlobject");
|
|
self->priv->element = child;
|
|
}
|
|
|
|
GST_DEBUG ("done");
|
|
return gnlobject;
|
|
|
|
|
|
/* ERROR CASES */
|
|
|
|
already_have_gnlobject:
|
|
{
|
|
GST_ERROR ("Already controlling a GnlObject %s",
|
|
GST_ELEMENT_NAME (self->priv->gnlobject));
|
|
return NULL;
|
|
}
|
|
|
|
no_gnlfactory:
|
|
{
|
|
GST_ERROR ("No GESTrackObject::gnlobject_factorytype implementation!");
|
|
return NULL;
|
|
}
|
|
|
|
no_gnlobject:
|
|
{
|
|
GST_ERROR ("Error creating a gnlobject of type '%s'",
|
|
klass->gnlobject_factorytype);
|
|
return NULL;
|
|
}
|
|
|
|
child_failure:
|
|
{
|
|
GST_ERROR ("create_element returned NULL");
|
|
gst_object_unref (gnlobject);
|
|
return NULL;
|
|
}
|
|
|
|
add_failure:
|
|
{
|
|
GST_ERROR ("Error adding the contents to the gnlobject");
|
|
gst_object_unref (child);
|
|
gst_object_unref (gnlobject);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
ensure_gnl_object (GESTrackObject * object)
|
|
{
|
|
GESTrackObjectClass *class;
|
|
GstElement *gnlobject;
|
|
GHashTable *props_hash;
|
|
gboolean res = TRUE;
|
|
|
|
if (object->priv->gnlobject && object->priv->valid)
|
|
return FALSE;
|
|
|
|
/* 1. Create the GnlObject */
|
|
GST_DEBUG ("Creating GnlObject");
|
|
|
|
class = GES_TRACK_OBJECT_GET_CLASS (object);
|
|
|
|
if (G_UNLIKELY (class->create_gnl_object == NULL)) {
|
|
GST_ERROR ("No 'create_gnl_object' implementation !");
|
|
goto done;
|
|
}
|
|
|
|
GST_DEBUG ("Calling virtual method");
|
|
|
|
/* call the create_gnl_object virtual method */
|
|
gnlobject = class->create_gnl_object (object);
|
|
|
|
if (G_UNLIKELY (gnlobject == NULL)) {
|
|
GST_ERROR
|
|
("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
|
|
goto done;
|
|
}
|
|
|
|
object->priv->gnlobject = gnlobject;
|
|
|
|
/* 2. Fill in the GnlObject */
|
|
if (gnlobject) {
|
|
GST_DEBUG ("Got a valid GnlObject, now filling it in");
|
|
|
|
if (object->priv->timelineobj)
|
|
res = ges_timeline_object_fill_track_object (object->priv->timelineobj,
|
|
object, object->priv->gnlobject);
|
|
else
|
|
res = TRUE;
|
|
|
|
if (res) {
|
|
/* Connect to property notifications */
|
|
/* FIXME : remember the signalids so we can remove them later on !!! */
|
|
g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
|
|
G_CALLBACK (gnlobject_start_cb), object);
|
|
g_signal_connect (G_OBJECT (object->priv->gnlobject),
|
|
"notify::media-start", G_CALLBACK (gnlobject_media_start_cb), object);
|
|
g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
|
|
G_CALLBACK (gnlobject_duration_cb), object);
|
|
g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
|
|
G_CALLBACK (gnlobject_priority_cb), object);
|
|
g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
|
|
G_CALLBACK (gnlobject_active_cb), object);
|
|
|
|
/* Set some properties on the GnlObject */
|
|
g_object_set (object->priv->gnlobject,
|
|
"caps", ges_track_get_caps (object->priv->track),
|
|
"duration", object->priv->pending_duration,
|
|
"media-duration", object->priv->pending_duration,
|
|
"start", object->priv->pending_start,
|
|
"media-start", object->priv->pending_inpoint,
|
|
"priority", object->priv->pending_priority,
|
|
"active", object->priv->pending_active, NULL);
|
|
|
|
/* We feed up the props_hashtable if possible */
|
|
if (class->get_props_hastable) {
|
|
props_hash = class->get_props_hastable (object);
|
|
|
|
if (props_hash == NULL) {
|
|
GST_DEBUG ("'get_props_hastable' implementation returned TRUE but no"
|
|
"properties_hashtable is available");
|
|
} else {
|
|
object->priv->properties_hashtable = props_hash;
|
|
connect_properties_signals (object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
object->priv->valid = res;
|
|
|
|
GST_DEBUG ("Returning res:%d", res);
|
|
|
|
return res;
|
|
}
|
|
|
|
/* INTERNAL USAGE */
|
|
gboolean
|
|
ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
|
|
{
|
|
GST_DEBUG ("object:%p, track:%p", object, track);
|
|
|
|
object->priv->track = track;
|
|
|
|
if (object->priv->track)
|
|
return ensure_gnl_object (object);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_track:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the #GESTrack to which this object belongs.
|
|
*
|
|
* Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
|
|
* is not in any track
|
|
*/
|
|
GESTrack *
|
|
ges_track_object_get_track (GESTrackObject * object)
|
|
{
|
|
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
|
|
|
|
return object->priv->track;
|
|
}
|
|
|
|
void
|
|
ges_track_object_set_timeline_object (GESTrackObject * object,
|
|
GESTimelineObject * tlobj)
|
|
{
|
|
GST_DEBUG ("object:%p, timeline-object:%p", object, tlobj);
|
|
|
|
object->priv->timelineobj = tlobj;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_timeline_object:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the #GESTimelineObject which is controlling this track object
|
|
*
|
|
* Returns: (transfer none): the #GESTimelineObject which is controlling
|
|
* this track object
|
|
*/
|
|
GESTimelineObject *
|
|
ges_track_object_get_timeline_object (GESTrackObject * object)
|
|
{
|
|
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
|
|
|
|
return object->priv->timelineobj;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_gnlobject:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the GNonLin object this object is controlling.
|
|
*
|
|
* Returns: (transfer none): the GNonLin object this object is controlling.
|
|
*/
|
|
GstElement *
|
|
ges_track_object_get_gnlobject (GESTrackObject * object)
|
|
{
|
|
return object->priv->gnlobject;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_element:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the #GstElement this track object is controlling within GNonLin.
|
|
*
|
|
* Returns: (transfer none): the #GstElement this track object is controlling
|
|
* within GNonLin.
|
|
*/
|
|
GstElement *
|
|
ges_track_object_get_element (GESTrackObject * object)
|
|
{
|
|
return object->priv->element;
|
|
}
|
|
|
|
static inline void
|
|
ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked)
|
|
{
|
|
object->priv->locked = locked;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_locked:
|
|
* @object: a #GESTrackObject
|
|
* @locked: whether the object is lock to its parent
|
|
*
|
|
* Set the locking status of the @object in relationship to its controlling
|
|
* #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
|
|
* with its controlling #GESTimelineObject.
|
|
*/
|
|
void
|
|
ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
|
|
{
|
|
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
|
|
|
|
GST_DEBUG_OBJECT (object, "%s object", locked ? "Locking" : "Unlocking");
|
|
|
|
ges_track_object_set_locked_internal (object, locked);
|
|
#if GLIB_CHECK_VERSION(2,26,0)
|
|
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_LOCKED]);
|
|
#else
|
|
g_object_notify (G_OBJECT (object), "locked");
|
|
#endif
|
|
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_is_locked:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Let you know if object us locked or not (moving synchronously).
|
|
*
|
|
* Returns: %TRUE if the object is moving synchronously to its controlling
|
|
* #GESTimelineObject, else %FALSE.
|
|
*/
|
|
gboolean
|
|
ges_track_object_is_locked (GESTrackObject * object)
|
|
{
|
|
return object->priv->locked;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_start:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the position of the object in the container #GESTrack.
|
|
*
|
|
* Returns: the start position (in #GstClockTime)
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
guint64
|
|
ges_track_object_get_start (GESTrackObject * object)
|
|
{
|
|
if (G_UNLIKELY (object->priv->gnlobject == NULL))
|
|
return object->priv->pending_start;
|
|
else
|
|
return object->start;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_inpoint:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the offset within the contents of this #GESTrackObject
|
|
*
|
|
* Returns: the in-point (in #GstClockTime)
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
guint64
|
|
ges_track_object_get_inpoint (GESTrackObject * object)
|
|
{
|
|
if (G_UNLIKELY (object->priv->gnlobject == NULL))
|
|
return object->priv->pending_inpoint;
|
|
else
|
|
return object->inpoint;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_duration:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the duration which will be used in the container #GESTrack
|
|
* starting from the 'in-point'
|
|
*
|
|
* Returns: the duration (in #GstClockTime)
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
guint64
|
|
ges_track_object_get_duration (GESTrackObject * object)
|
|
{
|
|
if (G_UNLIKELY (object->priv->gnlobject == NULL))
|
|
return object->priv->pending_duration;
|
|
else
|
|
return object->duration;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_priority:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Get the priority of the object withing the containing #GESTrack.
|
|
*
|
|
* Returns: the priority of @object
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
guint32
|
|
ges_track_object_get_priority (GESTrackObject * object)
|
|
{
|
|
if (G_UNLIKELY (object->priv->gnlobject == NULL))
|
|
return object->priv->pending_priority;
|
|
else
|
|
return object->priority;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_is_active:
|
|
* @object: a #GESTrackObject
|
|
*
|
|
* Lets you know if @object will be used for playback and rendering,
|
|
* or not.
|
|
*
|
|
* Returns: %TRUE if @object is active, %FALSE otherwize
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
gboolean
|
|
ges_track_object_is_active (GESTrackObject * object)
|
|
{
|
|
if (G_UNLIKELY (object->priv->gnlobject == NULL))
|
|
return object->priv->pending_active;
|
|
else
|
|
return object->active;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_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
|
|
* class as such: "ClassName::property-name", to guarantee that you get the
|
|
* proper GParamSpec in case various GstElement-s contain the same property
|
|
* name. If you don't do so, you will get the first element found, having
|
|
* this property and the and the corresponding GParamSpec.
|
|
* @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
|
|
* takes the real object to set property on
|
|
* @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
|
|
* describing the property
|
|
*
|
|
* Looks up which @element and @pspec would be effected by the given @name. If various
|
|
* contained elements have this property name you will get the first one, unless you
|
|
* specify the class name in @name.
|
|
*
|
|
* Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
|
|
* case the values for @pspec and @element are not modified. Unref @element after
|
|
* usage.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
gboolean
|
|
ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
|
|
GstElement ** element, GParamSpec ** pspec)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
gchar **names, *name, *classename;
|
|
gboolean res;
|
|
GESTrackObjectPrivate *priv = object->priv;
|
|
|
|
classename = NULL;
|
|
res = FALSE;
|
|
|
|
names = g_strsplit (prop_name, "::", 2);
|
|
if (names[1] != NULL) {
|
|
classename = names[0];
|
|
name = names[1];
|
|
} else
|
|
name = names[0];
|
|
|
|
g_hash_table_iter_init (&iter, priv->properties_hashtable);
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
|
|
if (classename == NULL ||
|
|
g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
|
|
GST_DEBUG ("The %s property from %s has been found", name, classename);
|
|
if (element)
|
|
*element = g_object_ref (value);
|
|
|
|
*pspec = g_param_spec_ref (key);
|
|
res = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
g_strfreev (names);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_child_property_by_pspec:
|
|
* @object: a #GESTrackObject
|
|
* @pspec: The #GParamSpec that specifies the property you want to set
|
|
* @value: the value
|
|
*
|
|
* Sets a property of a child of @object.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
void
|
|
ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
|
|
GParamSpec * pspec, GValue * value)
|
|
{
|
|
GstElement *element;
|
|
|
|
GESTrackObjectPrivate *priv = object->priv;
|
|
|
|
if (!priv->properties_hashtable)
|
|
goto prop_hash_not_set;
|
|
|
|
element = g_hash_table_lookup (priv->properties_hashtable, pspec);
|
|
if (!element)
|
|
goto not_found;
|
|
|
|
g_object_set_property (G_OBJECT (element), pspec->name, value);
|
|
|
|
return;
|
|
|
|
not_found:
|
|
{
|
|
GST_ERROR ("The %s property doesn't exist", pspec->name);
|
|
return;
|
|
}
|
|
prop_hash_not_set:
|
|
{
|
|
GST_DEBUG ("The child properties haven't been set on %p", object);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_child_property_valist:
|
|
* @object: The #GESTrackObject parent object
|
|
* @first_property_name: The name of the first property to set
|
|
* @var_args: value for the first property, followed optionally by more
|
|
* name/return location pairs, followed by NULL
|
|
*
|
|
* Sets a property of a child of @object. If there are various child elements
|
|
* that have the same property name, you can distinguish them using the following
|
|
* syntax: 'ClasseName::property_name' as property name. If you don't, the
|
|
* corresponding property of the first element found will be set.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
void
|
|
ges_track_object_set_child_property_valist (GESTrackObject * object,
|
|
const gchar * first_property_name, va_list var_args)
|
|
{
|
|
const gchar *name;
|
|
GParamSpec *pspec;
|
|
GstElement *element;
|
|
|
|
gchar *error = NULL;
|
|
GValue value = { 0, };
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
name = first_property_name;
|
|
|
|
/* Note: This part is in big part copied from the gst_child_object_set_valist
|
|
* method. */
|
|
|
|
/* iterate over pairs */
|
|
while (name) {
|
|
if (!ges_track_object_lookup_child (object, name, &element, &pspec))
|
|
goto not_found;
|
|
|
|
#if GLIB_CHECK_VERSION(2,23,3)
|
|
G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
|
|
G_VALUE_NOCOPY_CONTENTS, &error);
|
|
#else
|
|
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
|
|
G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
|
|
#endif
|
|
|
|
if (error)
|
|
goto cant_copy;
|
|
|
|
g_object_set_property (G_OBJECT (element), pspec->name, &value);
|
|
|
|
g_object_unref (element);
|
|
g_value_unset (&value);
|
|
|
|
name = va_arg (var_args, gchar *);
|
|
}
|
|
return;
|
|
|
|
not_found:
|
|
{
|
|
GST_WARNING ("No property %s in OBJECT\n", name);
|
|
return;
|
|
}
|
|
cant_copy:
|
|
{
|
|
GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
|
|
error);
|
|
g_value_unset (&value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_child_property:
|
|
* @object: The #GESTrackObject parent object
|
|
* @first_property_name: The name of the first property to set
|
|
* @...: value for the first property, followed optionally by more
|
|
* name/return location pairs, followed by NULL
|
|
*
|
|
* Sets a property of a child of @object. If there are various child elements
|
|
* that have the same property name, you can distinguish them using the following
|
|
* syntax: 'ClasseName::property_name' as property name. If you don't, the
|
|
* corresponding property of the first element found will be set.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
void
|
|
ges_track_object_set_child_property (GESTrackObject * object,
|
|
const gchar * first_property_name, ...)
|
|
{
|
|
va_list var_args;
|
|
|
|
va_start (var_args, first_property_name);
|
|
ges_track_object_set_child_property_valist (object, first_property_name,
|
|
var_args);
|
|
va_end (var_args);
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_child_property_valist:
|
|
* @object: The #GESTrackObject parent object
|
|
* @first_property_name: The name of the first property to get
|
|
* @var_args: value for the first property, followed optionally by more
|
|
* name/return location pairs, followed by NULL
|
|
*
|
|
* Gets a property of a child of @object. If there are various child elements
|
|
* that have the same property name, you can distinguish them using the following
|
|
* syntax: 'ClasseName::property_name' as property name. If you don't, the
|
|
* corresponding property of the first element found will be set.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
void
|
|
ges_track_object_get_child_property_valist (GESTrackObject * object,
|
|
const gchar * first_property_name, va_list var_args)
|
|
{
|
|
const gchar *name;
|
|
gchar *error = NULL;
|
|
GValue value = { 0, };
|
|
GParamSpec *pspec;
|
|
GstElement *element;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
name = first_property_name;
|
|
|
|
/* This part is in big part copied from the gst_child_object_get_valist method */
|
|
while (name) {
|
|
if (!ges_track_object_lookup_child (object, name, &element, &pspec))
|
|
goto not_found;
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
g_object_get_property (G_OBJECT (element), pspec->name, &value);
|
|
g_object_unref (element);
|
|
|
|
G_VALUE_LCOPY (&value, var_args, 0, &error);
|
|
if (error)
|
|
goto cant_copy;
|
|
g_value_unset (&value);
|
|
name = va_arg (var_args, gchar *);
|
|
}
|
|
return;
|
|
|
|
not_found:
|
|
{
|
|
GST_WARNING ("no property %s in object", name);
|
|
return;
|
|
}
|
|
cant_copy:
|
|
{
|
|
GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
|
|
error);
|
|
g_value_unset (&value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_list_children_properties:
|
|
* @object: The #GESTrackObject to get the list of children properties from
|
|
* @n_properties: return location for the length of the returned array
|
|
*
|
|
* Gets an array of #GParamSpec* for all configurable properties of the
|
|
* children of @object.
|
|
*
|
|
* Returns: (transfer full) (array): an array of #GParamSpec* which should be freed after use or
|
|
* %NULL if something went wrong
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
GParamSpec **
|
|
ges_track_object_list_children_properties (GESTrackObject * object,
|
|
guint * n_properties)
|
|
{
|
|
GESTrackObjectClass *class;
|
|
|
|
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
|
|
|
|
class = GES_TRACK_OBJECT_GET_CLASS (object);
|
|
|
|
return class->list_children_properties (object, n_properties);
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_child_property:
|
|
* @object: The origin #GESTrackObject
|
|
* @first_property_name: The name of the first property to get
|
|
* @...: return location for the first property, followed optionally by more
|
|
* name/return location pairs, followed by NULL
|
|
*
|
|
* Gets properties of a child of @object.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
void
|
|
ges_track_object_get_child_property (GESTrackObject * object,
|
|
const gchar * first_property_name, ...)
|
|
{
|
|
va_list var_args;
|
|
|
|
va_start (var_args, first_property_name);
|
|
ges_track_object_get_child_property_valist (object, first_property_name,
|
|
var_args);
|
|
va_end (var_args);
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_child_property_by_pspec:
|
|
* @object: a #GESTrackObject
|
|
* @pspec: The #GParamSpec that specifies the property you want to get
|
|
* @value: return location for the value
|
|
*
|
|
* Gets a property of a child of @object.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
void
|
|
ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
|
|
GParamSpec * pspec, GValue * value)
|
|
{
|
|
GstElement *element;
|
|
|
|
GESTrackObjectPrivate *priv = object->priv;
|
|
|
|
if (!priv->properties_hashtable)
|
|
goto prop_hash_not_set;
|
|
|
|
element = g_hash_table_lookup (priv->properties_hashtable, pspec);
|
|
if (!element)
|
|
goto not_found;
|
|
|
|
g_object_get_property (G_OBJECT (element), pspec->name, value);
|
|
|
|
return;
|
|
|
|
not_found:
|
|
{
|
|
GST_ERROR ("The %s property doesn't exist", pspec->name);
|
|
return;
|
|
}
|
|
prop_hash_not_set:
|
|
{
|
|
GST_ERROR ("The child properties haven't been set on %p", object);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static GParamSpec **
|
|
default_list_children_properties (GESTrackObject * object, guint * n_properties)
|
|
{
|
|
GParamSpec **pspec, *spec;
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
guint i = 0;
|
|
|
|
if (!object->priv->properties_hashtable)
|
|
goto prop_hash_not_set;
|
|
|
|
*n_properties = g_hash_table_size (object->priv->properties_hashtable);
|
|
pspec = g_new (GParamSpec *, *n_properties);
|
|
|
|
g_hash_table_iter_init (&iter, object->priv->properties_hashtable);
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
spec = G_PARAM_SPEC (key);
|
|
pspec[i] = g_param_spec_ref (spec);
|
|
i++;
|
|
}
|
|
|
|
return pspec;
|
|
|
|
prop_hash_not_set:
|
|
{
|
|
*n_properties = 0;
|
|
GST_ERROR ("The child properties haven't been set on %p", object);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_get_max_duration:
|
|
* @object: The #GESTrackObject to retrieve max duration from
|
|
*
|
|
* Get the max duration of @object.
|
|
*
|
|
* Returns: The max duration of @object
|
|
*
|
|
* Since: 0.10.XX
|
|
*/
|
|
guint64
|
|
ges_track_object_get_max_duration (GESTrackObject * object)
|
|
{
|
|
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), 0);
|
|
|
|
return object->priv->maxduration;
|
|
}
|
|
|
|
/**
|
|
* ges_track_object_set_max_duration:
|
|
* @object: The #GESTrackObject to retrieve max duration from
|
|
* @maxduration: The maximum duration of @object
|
|
*
|
|
* Returns: Set the max duration of @object
|
|
*
|
|
* Since: 0.10.XX
|
|
*/
|
|
void
|
|
ges_track_object_set_max_duration (GESTrackObject * object, guint64 maxduration)
|
|
{
|
|
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
|
|
|
|
object->priv->maxduration = maxduration;
|
|
}
|