mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-30 12:10:37 +00:00
14480b43f4
This is in order to support gaps in the timeline. This is not the proper solution, we should make sure to fill gaps properly, but for the time being, it makes the trick
674 lines
18 KiB
C
674 lines
18 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
|
|
* @short_description: Composition of objects
|
|
*
|
|
* Corresponds to one output format (i.e. audio OR video).
|
|
*
|
|
* Contains the compatible TrackObject(s).
|
|
*
|
|
* Wraps GNonLin's 'gnlcomposition' element.
|
|
*/
|
|
|
|
#include "ges-internal.h"
|
|
#include "ges-track.h"
|
|
#include "ges-track-object.h"
|
|
#include "gesmarshal.h"
|
|
|
|
G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
|
|
|
|
struct _GESTrackPrivate
|
|
{
|
|
/*< private > */
|
|
GESTimeline *timeline;
|
|
GList *trackobjects;
|
|
guint64 duration;
|
|
|
|
GstCaps *caps;
|
|
|
|
GstElement *composition; /* The composition associated with this track */
|
|
GstElement *background; /* The backgrond, handle the gaps in the track */
|
|
GstPad *srcpad; /* The source GhostPad */
|
|
};
|
|
|
|
enum
|
|
{
|
|
ARG_0,
|
|
ARG_CAPS,
|
|
ARG_TYPE,
|
|
ARG_DURATION,
|
|
ARG_LAST,
|
|
TRACK_OBJECT_ADDED,
|
|
TRACK_OBJECT_REMOVED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint ges_track_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
static GParamSpec *properties[ARG_LAST];
|
|
|
|
static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track);
|
|
static void
|
|
pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
|
|
static void composition_duration_cb (GstElement * composition, GParamSpec * arg
|
|
G_GNUC_UNUSED, GESTrack * obj);
|
|
static void
|
|
sort_track_objects_cb (GESTrackObject * child,
|
|
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
|
|
|
|
static void
|
|
ges_track_get_property (GObject * object, guint property_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GESTrack *track = GES_TRACK (object);
|
|
|
|
switch (property_id) {
|
|
case ARG_CAPS:
|
|
gst_value_set_caps (value, track->priv->caps);
|
|
break;
|
|
case ARG_TYPE:
|
|
g_value_set_flags (value, track->type);
|
|
break;
|
|
case ARG_DURATION:
|
|
g_value_set_uint64 (value, track->priv->duration);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ges_track_set_property (GObject * object, guint property_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GESTrack *track = GES_TRACK (object);
|
|
|
|
switch (property_id) {
|
|
case ARG_CAPS:
|
|
ges_track_set_caps (track, gst_value_get_caps (value));
|
|
break;
|
|
case ARG_TYPE:
|
|
track->type = g_value_get_flags (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ges_track_dispose (GObject * object)
|
|
{
|
|
GESTrack *track = (GESTrack *) object;
|
|
GESTrackPrivate *priv = track->priv;
|
|
|
|
while (priv->trackobjects) {
|
|
GESTrackObject *trobj = GES_TRACK_OBJECT (priv->trackobjects->data);
|
|
ges_track_remove_object (track, trobj);
|
|
ges_timeline_object_release_track_object ((GESTimelineObject *)
|
|
ges_track_object_get_timeline_object (trobj), trobj);
|
|
}
|
|
|
|
if (priv->composition) {
|
|
gst_bin_remove (GST_BIN (object), priv->composition);
|
|
priv->composition = NULL;
|
|
}
|
|
|
|
if (priv->caps) {
|
|
gst_caps_unref (priv->caps);
|
|
priv->caps = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
ges_track_constructed (GObject * object)
|
|
{
|
|
GObjectClass *parent_class;
|
|
GstElement *background = NULL;
|
|
GESTrack *self = GES_TRACK (object);
|
|
GESTrackPrivate *priv = self->priv;
|
|
|
|
if ((priv->background = gst_element_factory_make ("gnlsource", "background"))) {
|
|
g_object_set (G_OBJECT (self->priv->background), "expandable", TRUE,
|
|
"priority", G_MAXINT, NULL);
|
|
|
|
switch (self->type) {
|
|
case GES_TRACK_TYPE_VIDEO:
|
|
background = gst_element_factory_make ("videotestsrc", "background");
|
|
g_object_set (background, "pattern", 2, NULL);
|
|
break;
|
|
case GES_TRACK_TYPE_AUDIO:
|
|
background = gst_element_factory_make ("audiotestsrc", "background");
|
|
g_object_set (background, "wave", 4, NULL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (background) {
|
|
if (!gst_bin_add (GST_BIN (priv->background), background))
|
|
GST_ERROR ("Couldn't add background");
|
|
else {
|
|
if (!gst_bin_add (GST_BIN (priv->composition), priv->background))
|
|
GST_ERROR ("Couldn't add background");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
parent_class = ges_track_parent_class;
|
|
if (parent_class->constructed)
|
|
parent_class->constructed (object);
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
}
|
|
|
|
static void
|
|
ges_track_finalize (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
ges_track_class_init (GESTrackClass * klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (GESTrackPrivate));
|
|
|
|
object_class->get_property = ges_track_get_property;
|
|
object_class->set_property = ges_track_set_property;
|
|
object_class->dispose = ges_track_dispose;
|
|
object_class->finalize = ges_track_finalize;
|
|
object_class->constructed = ges_track_constructed;
|
|
|
|
/**
|
|
* GESTrack:caps
|
|
*
|
|
* Caps used to filter/choose the output stream. This is generally set to
|
|
* a generic set of caps like 'video/x-raw-rgb;video/x-raw-yuv' for raw video.
|
|
*
|
|
* Default value: #GST_CAPS_ANY.
|
|
*/
|
|
properties[ARG_CAPS] = g_param_spec_boxed ("caps", "Caps",
|
|
"Caps used to filter/choose the output stream",
|
|
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
|
g_object_class_install_property (object_class, ARG_CAPS,
|
|
properties[ARG_CAPS]);
|
|
|
|
/**
|
|
* GESTrack:duration
|
|
*
|
|
* Current duration of the track
|
|
*
|
|
* Default value: O
|
|
*/
|
|
properties[ARG_DURATION] = g_param_spec_uint64 ("duration", "Duration",
|
|
"The current duration of the track", 0, G_MAXUINT64, GST_SECOND,
|
|
G_PARAM_READABLE);
|
|
g_object_class_install_property (object_class, ARG_DURATION,
|
|
properties[ARG_DURATION]);
|
|
|
|
/**
|
|
* GESTrack:track-type
|
|
*
|
|
* Type of stream the track outputs. This is used when creating the #GESTrack
|
|
* to specify in generic terms what type of content will be outputted.
|
|
*
|
|
* It also serves as a 'fast' way to check what type of data will be outputted
|
|
* from the #GESTrack without having to actually check the #GESTrack's caps
|
|
* property.
|
|
*/
|
|
properties[ARG_TYPE] = g_param_spec_flags ("track-type", "TrackType",
|
|
"Type of stream the track outputs",
|
|
GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_CUSTOM,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
g_object_class_install_property (object_class, ARG_TYPE,
|
|
properties[ARG_TYPE]);
|
|
|
|
/**
|
|
* GESTrack::track-object-added
|
|
* @object: the #GESTrack
|
|
* @effect: the #GESTrackObject that was added.
|
|
*
|
|
* Will be emitted after a track object was added to the track.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
ges_track_signals[TRACK_OBJECT_ADDED] =
|
|
g_signal_new ("track-object-added", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
|
|
|
|
/**
|
|
* GESTrack::track-object-removed
|
|
* @object: the #GESTrack
|
|
* @effect: the #GESTrackObject that was removed.
|
|
*
|
|
* Will be emitted after a track object was removed from the track.
|
|
*
|
|
* Since: 0.10.2
|
|
*/
|
|
ges_track_signals[TRACK_OBJECT_REMOVED] =
|
|
g_signal_new ("track-object-removed", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
|
|
}
|
|
|
|
static void
|
|
ges_track_init (GESTrack * self)
|
|
{
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
|
GES_TYPE_TRACK, GESTrackPrivate);
|
|
|
|
self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
|
|
|
|
g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration",
|
|
G_CALLBACK (composition_duration_cb), self);
|
|
g_signal_connect (self->priv->composition, "pad-added",
|
|
(GCallback) pad_added_cb, self);
|
|
g_signal_connect (self->priv->composition, "pad-removed",
|
|
(GCallback) pad_removed_cb, self);
|
|
|
|
if (!gst_bin_add (GST_BIN (self), self->priv->composition))
|
|
GST_ERROR ("Couldn't add composition to bin !");
|
|
}
|
|
|
|
/**
|
|
* ges_track_new:
|
|
* @type: The type of track
|
|
* @caps: The caps to restrict the output of the track to.
|
|
*
|
|
* Creates a new #GESTrack with the given @type and @caps.
|
|
*
|
|
* The newly created track will steal a reference to the caps. If you wish to
|
|
* use those caps elsewhere, you will have to take an extra reference.
|
|
*
|
|
* Returns: A new #GESTrack.
|
|
*/
|
|
GESTrack *
|
|
ges_track_new (GESTrackType type, GstCaps * caps)
|
|
{
|
|
GESTrack *track;
|
|
|
|
track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
|
|
gst_caps_unref (caps);
|
|
|
|
return track;
|
|
}
|
|
|
|
/**
|
|
* ges_track_video_raw_new:
|
|
*
|
|
* Creates a new #GESTrack of type #GES_TRACK_TYPE_VIDEO and with generic
|
|
* raw video caps ("video/x-raw-yuv;video/x-raw-rgb");
|
|
*
|
|
* Returns: A new #GESTrack.
|
|
*/
|
|
GESTrack *
|
|
ges_track_video_raw_new (void)
|
|
{
|
|
GESTrack *track;
|
|
GstCaps *caps = gst_caps_from_string ("video/x-raw-yuv;video/x-raw-rgb");
|
|
|
|
track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
|
|
|
|
return track;
|
|
}
|
|
|
|
/**
|
|
* ges_track_audio_raw_new:
|
|
*
|
|
* Creates a new #GESTrack of type #GES_TRACK_TYPE_AUDIO and with generic
|
|
* raw audio caps ("audio/x-raw-int;audio/x-raw-float");
|
|
*
|
|
* Returns: A new #GESTrack.
|
|
*/
|
|
GESTrack *
|
|
ges_track_audio_raw_new (void)
|
|
{
|
|
GESTrack *track;
|
|
GstCaps *caps = gst_caps_from_string ("audio/x-raw-int;audio/x-raw-float");
|
|
|
|
track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
|
|
|
|
return track;
|
|
}
|
|
|
|
/**
|
|
* ges_track_set_timeline:
|
|
* @track: a #GESTrack
|
|
* @timeline: a #GESTimeline
|
|
*
|
|
* Sets @timeline as the timeline controlling @track.
|
|
*/
|
|
void
|
|
ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
|
|
{
|
|
GST_DEBUG ("track:%p, timeline:%p", track, timeline);
|
|
|
|
track->priv->timeline = timeline;
|
|
}
|
|
|
|
/**
|
|
* ges_track_set_caps:
|
|
* @track: a #GESTrack
|
|
* @caps: the #GstCaps to set
|
|
*
|
|
* Sets the given @caps on the track.
|
|
*/
|
|
void
|
|
ges_track_set_caps (GESTrack * track, const GstCaps * caps)
|
|
{
|
|
GESTrackPrivate *priv;
|
|
|
|
g_return_if_fail (GES_IS_TRACK (track));
|
|
g_return_if_fail (GST_IS_CAPS (caps));
|
|
|
|
GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
|
|
|
|
priv = track->priv;
|
|
|
|
if (priv->caps)
|
|
gst_caps_unref (priv->caps);
|
|
priv->caps = gst_caps_copy (caps);
|
|
|
|
g_object_set (priv->composition, "caps", caps, NULL);
|
|
/* FIXME : update all trackobjects ? */
|
|
}
|
|
|
|
|
|
/* FIXME : put the compare function in the utils */
|
|
|
|
static gint
|
|
objects_start_compare (GESTrackObject * a, GESTrackObject * b)
|
|
{
|
|
if (a->start == b->start) {
|
|
if (a->priority < b->priority)
|
|
return -1;
|
|
if (a->priority > b->priority)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (a->start < b->start)
|
|
return -1;
|
|
if (a->start > b->start)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ges_track_add_object:
|
|
* @track: a #GESTrack
|
|
* @object: (transfer full): the #GESTrackObject to add
|
|
*
|
|
* Adds the given object to the track. Sets the object's controlling track,
|
|
* and thus takes ownership of the @object.
|
|
*
|
|
* An object can only be added to one track.
|
|
*
|
|
* Returns: #TRUE if the object was properly added. #FALSE if the track does not
|
|
* want to accept the object.
|
|
*/
|
|
gboolean
|
|
ges_track_add_object (GESTrack * track, GESTrackObject * object)
|
|
{
|
|
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
|
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
|
|
|
|
GST_DEBUG ("track:%p, object:%p", track, object);
|
|
|
|
if (G_UNLIKELY (ges_track_object_get_track (object) != NULL)) {
|
|
GST_WARNING ("Object already belongs to another track");
|
|
return FALSE;
|
|
}
|
|
|
|
/* At this point, the track object shouldn't have any gnlobject since
|
|
* it hasn't been added to a track yet.
|
|
* FIXME : This check seems a bit obsolete */
|
|
if (G_UNLIKELY (ges_track_object_get_gnlobject (object) != NULL)) {
|
|
GST_ERROR ("TrackObject already controls a gnlobject !");
|
|
return FALSE;
|
|
}
|
|
|
|
if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
|
|
GST_ERROR ("Couldn't properly add the object to the Track");
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG ("Adding object to ourself");
|
|
|
|
if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->priv->composition),
|
|
ges_track_object_get_gnlobject (object)))) {
|
|
GST_WARNING ("Couldn't add object to the GnlComposition");
|
|
return FALSE;
|
|
}
|
|
|
|
g_object_ref_sink (object);
|
|
track->priv->trackobjects =
|
|
g_list_insert_sorted (track->priv->trackobjects, object,
|
|
(GCompareFunc) objects_start_compare);
|
|
|
|
g_signal_emit (track, ges_track_signals[TRACK_OBJECT_ADDED], 0,
|
|
GES_TRACK_OBJECT (object));
|
|
|
|
g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
|
|
G_CALLBACK (sort_track_objects_cb), track);
|
|
|
|
g_signal_connect (GES_TRACK_OBJECT (object), "notify::priority",
|
|
G_CALLBACK (sort_track_objects_cb), track);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
ges_track_get_objects (GESTrack * track)
|
|
{
|
|
GList *ret = NULL;
|
|
GList *tmp;
|
|
|
|
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
|
|
|
|
for (tmp = track->priv->trackobjects; tmp; tmp = tmp->next) {
|
|
ret = g_list_prepend (ret, tmp->data);
|
|
g_object_ref (tmp->data);
|
|
}
|
|
|
|
ret = g_list_reverse (ret);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ges_track_remove_object:
|
|
* @track: a #GESTrack
|
|
* @object: the #GESTrackObject to remove
|
|
*
|
|
* Removes the object from the track and unparents it.
|
|
* Unparenting it means the reference owned by @track on the @object will be
|
|
* removed. If you wish to use the @object after this function, make sure you
|
|
* call g_object_ref() before removing it from the @track.
|
|
*
|
|
* Returns: #TRUE if the object was removed, else #FALSE if the track
|
|
* could not remove the object (like if it didn't belong to the track).
|
|
*/
|
|
gboolean
|
|
ges_track_remove_object (GESTrack * track, GESTrackObject * object)
|
|
{
|
|
GESTrackPrivate *priv;
|
|
GstElement *gnlobject;
|
|
|
|
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
|
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
|
|
|
|
GST_DEBUG ("track:%p, object:%p", track, object);
|
|
|
|
priv = track->priv;
|
|
|
|
if (G_UNLIKELY (ges_track_object_get_track (object) != track)) {
|
|
GST_WARNING ("Object belongs to another track");
|
|
return FALSE;
|
|
}
|
|
|
|
if ((gnlobject = ges_track_object_get_gnlobject (object))) {
|
|
GST_DEBUG ("Removing GnlObject '%s' from composition '%s'",
|
|
GST_ELEMENT_NAME (gnlobject), GST_ELEMENT_NAME (priv->composition));
|
|
if (!gst_bin_remove (GST_BIN (priv->composition), gnlobject)) {
|
|
GST_WARNING ("Failed to remove gnlobject from composition");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ges_track_object_set_track (object, NULL);
|
|
|
|
g_signal_emit (track, ges_track_signals[TRACK_OBJECT_REMOVED], 0,
|
|
GES_TRACK_OBJECT (object));
|
|
|
|
priv->trackobjects = g_list_remove (priv->trackobjects, object);
|
|
|
|
g_object_unref (object);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
|
|
{
|
|
GESTrackPrivate *priv = track->priv;
|
|
|
|
GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
|
|
|
|
/* ghost the pad */
|
|
priv->srcpad = gst_ghost_pad_new ("src", pad);
|
|
|
|
gst_pad_set_active (priv->srcpad, TRUE);
|
|
|
|
gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
|
|
|
|
GST_DEBUG ("done");
|
|
}
|
|
|
|
static void
|
|
pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
|
|
{
|
|
GESTrackPrivate *priv = track->priv;
|
|
|
|
GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
|
|
|
|
if (G_LIKELY (priv->srcpad)) {
|
|
gst_pad_set_active (priv->srcpad, FALSE);
|
|
gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
|
|
priv->srcpad = NULL;
|
|
}
|
|
|
|
GST_DEBUG ("done");
|
|
}
|
|
|
|
static void
|
|
composition_duration_cb (GstElement * composition,
|
|
GParamSpec * arg G_GNUC_UNUSED, GESTrack * obj)
|
|
{
|
|
guint64 duration;
|
|
|
|
g_object_get (composition, "duration", &duration, NULL);
|
|
|
|
|
|
if (obj->priv->duration != duration) {
|
|
GST_DEBUG ("composition duration : %" GST_TIME_FORMAT " current : %"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (duration),
|
|
GST_TIME_ARGS (obj->priv->duration));
|
|
|
|
obj->priv->duration = duration;
|
|
|
|
#if GLIB_CHECK_VERSION(2,26,0)
|
|
g_object_notify_by_pspec (G_OBJECT (obj), properties[ARG_DURATION]);
|
|
#else
|
|
g_object_notify (G_OBJECT (obj), "duration");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void
|
|
sort_track_objects_cb (GESTrackObject * child,
|
|
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
|
|
{
|
|
track->priv->trackobjects =
|
|
g_list_sort (track->priv->trackobjects,
|
|
(GCompareFunc) objects_start_compare);
|
|
}
|
|
|
|
/**
|
|
* ges_track_get_caps:
|
|
* @track: a #GESTrack
|
|
*
|
|
* Get the #GstCaps this track is configured to output.
|
|
*
|
|
* Returns: The #GstCaps this track is configured to output.
|
|
*/
|
|
const GstCaps *
|
|
ges_track_get_caps (GESTrack * track)
|
|
{
|
|
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
|
|
|
|
return track->priv->caps;
|
|
}
|
|
|
|
/**
|
|
* ges_track_get_timeline:
|
|
* @track: a #GESTrack
|
|
*
|
|
* Get the #GESTimeline this track belongs to. Can be %NULL.
|
|
*
|
|
* Returns: The #GESTimeline this track belongs to. Can be %NULL.
|
|
*/
|
|
const GESTimeline *
|
|
ges_track_get_timeline (GESTrack * track)
|
|
{
|
|
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
|
|
|
|
return track->priv->timeline;
|
|
}
|
|
|
|
/**
|
|
* ges_track_enable_update:
|
|
* @track : a #GESTrack
|
|
* @enabled : TRUE if the composition must be updated, FALSE otherwise.
|
|
*
|
|
* Sets the @track 's composition update property to @enabled .
|
|
*
|
|
* Returns : True if success, FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
ges_track_enable_update (GESTrack * track, gboolean enabled)
|
|
{
|
|
gboolean update;
|
|
|
|
g_object_set (track->priv->composition, "update", enabled, NULL);
|
|
|
|
g_object_get (track->priv->composition, "update", &update, NULL);
|
|
|
|
if (update == enabled) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|