mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-03 21:12:26 +00:00
Merge remote-tracking branch 'origin/0.10'
Conflicts: bindings/python/ges.defs ges/ges-screenshot.c ges/ges-track-video-transition.c
This commit is contained in:
commit
f32fdf13b7
13 changed files with 804 additions and 344 deletions
|
@ -45,12 +45,17 @@ GES_VIDEO_TEST_PATTERN_TYPE
|
||||||
ges_video_test_pattern_get_type
|
ges_video_test_pattern_get_type
|
||||||
GES_TYPE_PIPELINE_FLAGS
|
GES_TYPE_PIPELINE_FLAGS
|
||||||
ges_pipeline_flags_get_type
|
ges_pipeline_flags_get_type
|
||||||
|
GES_TYPE_EDGE
|
||||||
|
ges_edge_get_type
|
||||||
|
GES_TYPE_EDIT_MODE
|
||||||
|
ges_edit_mode_get_type
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
|
||||||
<SECTION>
|
<SECTION>
|
||||||
<FILE>ges-track</FILE>
|
<FILE>ges-track</FILE>
|
||||||
<TITLE>GESTrack</TITLE>
|
<TITLE>GESTrack</TITLE>
|
||||||
GESTrack
|
GESTrack
|
||||||
|
GESCreateElementForGapFunc
|
||||||
ges_track_audio_raw_new
|
ges_track_audio_raw_new
|
||||||
ges_track_video_raw_new
|
ges_track_video_raw_new
|
||||||
ges_track_new
|
ges_track_new
|
||||||
|
@ -61,6 +66,7 @@ ges_track_get_caps
|
||||||
ges_track_enable_update
|
ges_track_enable_update
|
||||||
ges_track_get_objects
|
ges_track_get_objects
|
||||||
ges_track_is_updating
|
ges_track_is_updating
|
||||||
|
ges_track_set_create_element_for_gap_func
|
||||||
<SUBSECTION Standard>
|
<SUBSECTION Standard>
|
||||||
GESTrackClass
|
GESTrackClass
|
||||||
GESTrackPrivate
|
GESTrackPrivate
|
||||||
|
@ -266,6 +272,7 @@ ges_timeline_is_updating
|
||||||
ges_timeline_get_tracks
|
ges_timeline_get_tracks
|
||||||
ges_timeline_get_layers
|
ges_timeline_get_layers
|
||||||
ges_timeline_get_track_for_pad
|
ges_timeline_get_track_for_pad
|
||||||
|
ges_timeline_get_duration
|
||||||
<SUBSECTION Standard>
|
<SUBSECTION Standard>
|
||||||
GESTimelinePrivate
|
GESTimelinePrivate
|
||||||
GESTimelineClass
|
GESTimelineClass
|
||||||
|
@ -321,7 +328,6 @@ ges_timeline_object_set_duration
|
||||||
ges_timeline_object_get_layer
|
ges_timeline_object_get_layer
|
||||||
ges_timeline_object_find_track_object
|
ges_timeline_object_find_track_object
|
||||||
ges_timeline_object_add_track_object
|
ges_timeline_object_add_track_object
|
||||||
ges_timeline_object_release_track_object
|
|
||||||
ges_timeline_object_get_top_effects
|
ges_timeline_object_get_top_effects
|
||||||
ges_timeline_object_get_top_effect_position
|
ges_timeline_object_get_top_effect_position
|
||||||
ges_timeline_object_move_to_layer
|
ges_timeline_object_move_to_layer
|
||||||
|
@ -335,6 +341,9 @@ ges_timeline_object_ripple_end
|
||||||
ges_timeline_object_roll_start
|
ges_timeline_object_roll_start
|
||||||
ges_timeline_object_roll_end
|
ges_timeline_object_roll_end
|
||||||
ges_timeline_object_trim_start
|
ges_timeline_object_trim_start
|
||||||
|
ges_timeline_object_get_max_duration
|
||||||
|
ges_timeline_object_objects_set_locked
|
||||||
|
ges_timeline_object_set_max_duration
|
||||||
<SUBSECTION Standard>
|
<SUBSECTION Standard>
|
||||||
GES_TIMELINE_OBJECT_DURATION
|
GES_TIMELINE_OBJECT_DURATION
|
||||||
GES_TIMELINE_OBJECT_INPOINT
|
GES_TIMELINE_OBJECT_INPOINT
|
||||||
|
|
|
@ -400,6 +400,7 @@ gboolean
|
||||||
ges_formatter_load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
|
ges_formatter_load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
|
||||||
const gchar * uri)
|
const gchar * uri)
|
||||||
{
|
{
|
||||||
|
gboolean ret = FALSE;
|
||||||
GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
|
GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
|
||||||
|
|
||||||
g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
|
g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
|
||||||
|
@ -407,10 +408,13 @@ ges_formatter_load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
|
||||||
|
|
||||||
g_signal_connect (timeline, "discovery-error",
|
g_signal_connect (timeline, "discovery-error",
|
||||||
G_CALLBACK (discovery_error_cb), formatter);
|
G_CALLBACK (discovery_error_cb), formatter);
|
||||||
if (klass->load_from_uri)
|
if (klass->load_from_uri) {
|
||||||
return klass->load_from_uri (formatter, timeline, uri);
|
ges_timeline_enable_update (timeline, FALSE);
|
||||||
|
ret = klass->load_from_uri (formatter, timeline, uri);
|
||||||
|
ges_timeline_enable_update (timeline, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
return FALSE;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -599,6 +603,7 @@ discovery_error_cb (GESTimeline * timeline,
|
||||||
static gboolean
|
static gboolean
|
||||||
project_loaded (GESFormatter * formatter, GESTimeline * timeline)
|
project_loaded (GESFormatter * formatter, GESTimeline * timeline)
|
||||||
{
|
{
|
||||||
|
GST_INFO_OBJECT (formatter, "Emit project loaded");
|
||||||
g_signal_emit (formatter, ges_formatter_signals[LOADED_SIGNAL], 0, timeline);
|
g_signal_emit (formatter, ges_formatter_signals[LOADED_SIGNAL], 0, timeline);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION: ges-pitivi-formatter
|
||||||
|
* @short_description: A formatter for the PiTiVi project file format
|
||||||
|
*/
|
||||||
|
|
||||||
#include <libxml/xmlreader.h>
|
#include <libxml/xmlreader.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
|
|
|
@ -39,10 +39,11 @@
|
||||||
|
|
||||||
typedef struct _GESPitiviFormatterPrivate GESPitiviFormatterPrivate;
|
typedef struct _GESPitiviFormatterPrivate GESPitiviFormatterPrivate;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GESPitiviFormatter:
|
* GESPitiviFormatter:
|
||||||
*
|
*
|
||||||
* Serializes a #GESTimeline to a file using
|
* Serializes a #GESTimeline to a file using the Xml PiTiVi file format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct _GESPitiviFormatter {
|
struct _GESPitiviFormatter {
|
||||||
|
@ -55,7 +56,8 @@ struct _GESPitiviFormatter {
|
||||||
gpointer _ges_reserved[GES_PADDING];
|
gpointer _ges_reserved[GES_PADDING];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GESPitiviFormatterClass {
|
struct _GESPitiviFormatterClass
|
||||||
|
{
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
GESFormatterClass parent_class;
|
GESFormatterClass parent_class;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,16 @@
|
||||||
#include "ges-screenshot.h"
|
#include "ges-screenshot.h"
|
||||||
#include "ges-internal.h"
|
#include "ges-internal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ges_play_sink_convert_frame:
|
||||||
|
* @playsink: The olaysink to get last frame from
|
||||||
|
* @caps: The caps defining the format the return value will have
|
||||||
|
*
|
||||||
|
* Get the last buffer @playsink showed
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A #GstSample containing the last frame from
|
||||||
|
* @playsink in the format defined by the @caps
|
||||||
|
*/
|
||||||
GstSample *
|
GstSample *
|
||||||
ges_play_sink_convert_frame (GstElement * playsink, GstCaps * caps)
|
ges_play_sink_convert_frame (GstElement * playsink, GstCaps * caps)
|
||||||
{
|
{
|
||||||
|
|
|
@ -215,17 +215,18 @@ objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
|
||||||
static GList *
|
static GList *
|
||||||
track_get_by_layer (GESTimelineLayer * layer, GESTrack * track)
|
track_get_by_layer (GESTimelineLayer * layer, GESTrack * track)
|
||||||
{
|
{
|
||||||
|
GESTrackObject *tckobj;
|
||||||
|
guint32 layer_prio = layer->priv->priority;
|
||||||
|
|
||||||
GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL;
|
GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL;
|
||||||
GESTimelineObject *tl_obj;
|
|
||||||
|
|
||||||
tck_objects_list = ges_track_get_objects (track);
|
tck_objects_list = ges_track_get_objects (track);
|
||||||
for (tmp = tck_objects_list; tmp; tmp = tmp->next) {
|
for (tmp = tck_objects_list; tmp; tmp = tmp->next) {
|
||||||
tl_obj = ges_track_object_get_timeline_object (tmp->data);
|
tckobj = GES_TRACK_OBJECT (tmp->data);
|
||||||
|
|
||||||
if (ges_timeline_object_get_layer (tl_obj) == layer) {
|
if (tckobj->priority / LAYER_HEIGHT == layer_prio) {
|
||||||
/* We steal the reference from tck_objects_list */
|
/* We steal the reference from tck_objects_list */
|
||||||
return_list = g_list_append (return_list, tmp->data);
|
return_list = g_list_append (return_list, tmp->data);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
g_object_unref (tmp->data);
|
g_object_unref (tmp->data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1749,6 +1749,7 @@ ges_timeline_object_ripple_end (GESTimelineObject * object, guint64 end)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ges_timeline_object_roll_start:
|
* ges_timeline_object_roll_start:
|
||||||
|
* @object: The #GESTimelineObject to roll
|
||||||
* @start: The new start of @object in roll mode, it will also adapat
|
* @start: The new start of @object in roll mode, it will also adapat
|
||||||
* the in-point of @object according
|
* the in-point of @object according
|
||||||
*
|
*
|
||||||
|
|
|
@ -48,6 +48,10 @@ static inline void init_movecontext (MoveContext * mv_ctx);
|
||||||
|
|
||||||
G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
|
G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (ges_timeline_debug);
|
||||||
|
#undef GST_CAT_DEFAULT
|
||||||
|
#define GST_CAT_DEFAULT ges_timeline_debug
|
||||||
|
|
||||||
#define GES_TIMELINE_PENDINGOBJS_GET_LOCK(timeline) \
|
#define GES_TIMELINE_PENDINGOBJS_GET_LOCK(timeline) \
|
||||||
(&GES_TIMELINE(timeline)->priv->pendingobjects_lock)
|
(&GES_TIMELINE(timeline)->priv->pendingobjects_lock)
|
||||||
#define GES_TIMELINE_PENDINGOBJS_LOCK(timeline) \
|
#define GES_TIMELINE_PENDINGOBJS_LOCK(timeline) \
|
||||||
|
@ -55,8 +59,7 @@ G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
|
||||||
#define GES_TIMELINE_PENDINGOBJS_UNLOCK(timeline) \
|
#define GES_TIMELINE_PENDINGOBJS_UNLOCK(timeline) \
|
||||||
(g_mutex_unlock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
|
(g_mutex_unlock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
|
||||||
|
|
||||||
/**
|
/* The move context is used for the timeline editing modes functions in order to
|
||||||
* The move context is used for the timeline editing modes functions in order to
|
|
||||||
* + Ripple / Roll / Slide / Move / Trim
|
* + Ripple / Roll / Slide / Move / Trim
|
||||||
*
|
*
|
||||||
* The context aims at avoiding to recalculate values/objects on each call of the
|
* The context aims at avoiding to recalculate values/objects on each call of the
|
||||||
|
@ -90,7 +93,7 @@ struct _MoveContext
|
||||||
/* Last snapping properties */
|
/* Last snapping properties */
|
||||||
GESTrackObject *last_snaped1;
|
GESTrackObject *last_snaped1;
|
||||||
GESTrackObject *last_snaped2;
|
GESTrackObject *last_snaped2;
|
||||||
GstClockTime last_snap_ts;
|
GstClockTime *last_snap_ts;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GESTimelinePrivate
|
struct _GESTimelinePrivate
|
||||||
|
@ -143,6 +146,7 @@ enum
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_DURATION,
|
PROP_DURATION,
|
||||||
PROP_SNAPPING_DISTANCE,
|
PROP_SNAPPING_DISTANCE,
|
||||||
|
PROP_UPDATE,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,6 +177,26 @@ static void
|
||||||
discoverer_discovered_cb (GstDiscoverer * discoverer,
|
discoverer_discovered_cb (GstDiscoverer * discoverer,
|
||||||
GstDiscovererInfo * info, GError * err, GESTimeline * timeline);
|
GstDiscovererInfo * info, GError * err, GESTimeline * timeline);
|
||||||
|
|
||||||
|
/* Internal methods */
|
||||||
|
static gboolean
|
||||||
|
ges_timeline_enable_update_internal (GESTimeline * timeline, gboolean enabled)
|
||||||
|
{
|
||||||
|
GList *tmp;
|
||||||
|
gboolean res = TRUE;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (timeline, "%s updates", enabled ? "Enabling" : "Disabling");
|
||||||
|
|
||||||
|
for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
|
||||||
|
if (!ges_track_enable_update (((TrackPrivate *) tmp->data)->track, enabled))
|
||||||
|
res = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we reset the context */
|
||||||
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/* GObject Standard vmethods*/
|
/* GObject Standard vmethods*/
|
||||||
static void
|
static void
|
||||||
ges_timeline_get_property (GObject * object, guint property_id,
|
ges_timeline_get_property (GObject * object, guint property_id,
|
||||||
|
@ -189,6 +213,9 @@ ges_timeline_get_property (GObject * object, guint property_id,
|
||||||
case PROP_SNAPPING_DISTANCE:
|
case PROP_SNAPPING_DISTANCE:
|
||||||
g_value_set_uint64 (value, timeline->priv->snapping_distance);
|
g_value_set_uint64 (value, timeline->priv->snapping_distance);
|
||||||
break;
|
break;
|
||||||
|
case PROP_UPDATE:
|
||||||
|
g_value_set_boolean (value, ges_timeline_is_updating (timeline));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,6 +229,10 @@ ges_timeline_set_property (GObject * object, guint property_id,
|
||||||
case PROP_SNAPPING_DISTANCE:
|
case PROP_SNAPPING_DISTANCE:
|
||||||
timeline->priv->snapping_distance = g_value_get_uint64 (value);
|
timeline->priv->snapping_distance = g_value_get_uint64 (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_UPDATE:
|
||||||
|
ges_timeline_enable_update_internal (timeline,
|
||||||
|
g_value_get_boolean (value));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
}
|
}
|
||||||
|
@ -294,6 +325,27 @@ ges_timeline_class_init (GESTimelineClass * klass)
|
||||||
g_object_class_install_property (object_class, PROP_SNAPPING_DISTANCE,
|
g_object_class_install_property (object_class, PROP_SNAPPING_DISTANCE,
|
||||||
properties[PROP_SNAPPING_DISTANCE]);
|
properties[PROP_SNAPPING_DISTANCE]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GESTimeline:update:
|
||||||
|
*
|
||||||
|
* If %TRUE, then all modifications to objects within the timeline will
|
||||||
|
* cause a internal pipeline update (if required).
|
||||||
|
* If %FALSE, then only the timeline start/duration/stop properties
|
||||||
|
* will be updated, and the internal pipeline will only be updated when the
|
||||||
|
* property is set back to %TRUE.
|
||||||
|
*
|
||||||
|
* It is recommended to temporarily set this property to %FALSE before doing
|
||||||
|
* more than one modification in the timeline (like adding or moving
|
||||||
|
* several objects at once) in order to speed up the process, and then setting
|
||||||
|
* back the property to %TRUE when done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
properties[PROP_UPDATE] = g_param_spec_boolean ("update", "Update",
|
||||||
|
"Update the internal pipeline on every modification", TRUE,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
g_object_class_install_property (object_class, PROP_UPDATE,
|
||||||
|
properties[PROP_UPDATE]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GESTimeline::track-added
|
* GESTimeline::track-added
|
||||||
* @timeline: the #GESTimeline
|
* @timeline: the #GESTimeline
|
||||||
|
@ -397,6 +449,9 @@ ges_timeline_init (GESTimeline * self)
|
||||||
{
|
{
|
||||||
GESTimelinePrivate *priv;
|
GESTimelinePrivate *priv;
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (ges_timeline_debug, "gestimeline",
|
||||||
|
GST_DEBUG_FG_YELLOW, "ges timeline");
|
||||||
|
|
||||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||||
GES_TYPE_TIMELINE, GESTimelinePrivate);
|
GES_TYPE_TIMELINE, GESTimelinePrivate);
|
||||||
|
|
||||||
|
@ -449,6 +504,28 @@ sort_layers (gpointer a, gpointer b)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
timeline_update_duration (GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
GstClockTime *cduration;
|
||||||
|
GSequenceIter *it = g_sequence_get_end_iter (timeline->priv->starts_ends);
|
||||||
|
|
||||||
|
it = g_sequence_iter_prev (it);
|
||||||
|
if (g_sequence_iter_is_end (it))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cduration = g_sequence_get (it);
|
||||||
|
if (cduration && timeline->priv->duration != *cduration) {
|
||||||
|
GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (*cduration),
|
||||||
|
GST_TIME_ARGS (timeline->priv->duration));
|
||||||
|
|
||||||
|
timeline->priv->duration = *cduration;
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
objects_start_compare (GESTrackObject * a, GESTrackObject * b)
|
objects_start_compare (GESTrackObject * a, GESTrackObject * b)
|
||||||
{
|
{
|
||||||
|
@ -553,6 +630,7 @@ sort_starts_ends_end (GESTimeline * timeline, GESTrackObject * obj)
|
||||||
*end = obj->start + obj->duration;
|
*end = obj->start + obj->duration;
|
||||||
|
|
||||||
g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
|
g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
|
||||||
|
timeline_update_duration (timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -567,6 +645,7 @@ sort_starts_ends_start (GESTimeline * timeline, GESTrackObject * obj)
|
||||||
*start = obj->start;
|
*start = obj->start;
|
||||||
|
|
||||||
g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
|
g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
|
||||||
|
timeline_update_duration (timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -590,6 +669,7 @@ resort_all_starts_ends (GESTimeline * timeline)
|
||||||
}
|
}
|
||||||
|
|
||||||
g_sequence_sort (priv->starts_ends, (GCompareDataFunc) compare_uint64, NULL);
|
g_sequence_sort (priv->starts_ends, (GCompareDataFunc) compare_uint64, NULL);
|
||||||
|
timeline_update_duration (timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -610,7 +690,7 @@ init_movecontext (MoveContext * mv_ctx)
|
||||||
mv_ctx->max_layer_prio = 0;
|
mv_ctx->max_layer_prio = 0;
|
||||||
mv_ctx->last_snaped1 = NULL;
|
mv_ctx->last_snaped1 = NULL;
|
||||||
mv_ctx->last_snaped2 = NULL;
|
mv_ctx->last_snaped2 = NULL;
|
||||||
mv_ctx->last_snap_ts = GST_CLOCK_TIME_NONE;
|
mv_ctx->last_snap_ts = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -645,7 +725,7 @@ stop_tracking_for_snapping (GESTimeline * timeline, GESTrackObject * tckobj)
|
||||||
g_sequence_remove (iter_start);
|
g_sequence_remove (iter_start);
|
||||||
g_sequence_remove (iter_end);
|
g_sequence_remove (iter_end);
|
||||||
g_sequence_remove (tckobj_iter);
|
g_sequence_remove (tckobj_iter);
|
||||||
|
timeline_update_duration (timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -672,6 +752,8 @@ start_tracking_track_obj (GESTimeline * timeline, GESTrackObject * tckobj)
|
||||||
g_hash_table_insert (priv->by_object, pend, tckobj);
|
g_hash_table_insert (priv->by_object, pend, tckobj);
|
||||||
|
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
|
|
||||||
|
timeline_update_duration (timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -680,11 +762,18 @@ ges_timeline_emit_snappig (GESTimeline * timeline, GESTrackObject * obj1,
|
||||||
{
|
{
|
||||||
GESTrackObject *obj2;
|
GESTrackObject *obj2;
|
||||||
MoveContext *mv_ctx = &timeline->priv->movecontext;
|
MoveContext *mv_ctx = &timeline->priv->movecontext;
|
||||||
|
GstClockTime snap_time = timecode ? *timecode : 0;
|
||||||
|
GstClockTime last_snap_ts = mv_ctx->last_snap_ts ?
|
||||||
|
*mv_ctx->last_snap_ts : GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (timeline, "Distance: %" GST_TIME_FORMAT " snapping at %"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (timeline->priv->snapping_distance),
|
||||||
|
GST_TIME_ARGS (snap_time));
|
||||||
|
|
||||||
if (timecode == NULL) {
|
if (timecode == NULL) {
|
||||||
if (mv_ctx->last_snaped1 != NULL && mv_ctx->last_snaped2 != NULL) {
|
if (mv_ctx->last_snaped1 != NULL && mv_ctx->last_snaped2 != NULL) {
|
||||||
g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
|
g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
|
||||||
mv_ctx->last_snaped1, mv_ctx->last_snaped2, mv_ctx->last_snap_ts);
|
mv_ctx->last_snaped1, mv_ctx->last_snaped2, last_snap_ts);
|
||||||
|
|
||||||
/* We then need to recalculate the moving context */
|
/* We then need to recalculate the moving context */
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
|
@ -695,19 +784,19 @@ ges_timeline_emit_snappig (GESTimeline * timeline, GESTrackObject * obj1,
|
||||||
|
|
||||||
obj2 = g_hash_table_lookup (timeline->priv->by_object, timecode);
|
obj2 = g_hash_table_lookup (timeline->priv->by_object, timecode);
|
||||||
|
|
||||||
if (mv_ctx->last_snap_ts != *timecode) {
|
if (last_snap_ts != *timecode) {
|
||||||
g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
|
g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
|
||||||
mv_ctx->last_snaped1, mv_ctx->last_snaped2, mv_ctx->last_snap_ts);
|
mv_ctx->last_snaped1, mv_ctx->last_snaped2, (last_snap_ts));
|
||||||
|
|
||||||
/* We want the snap start signal to be emited anyway */
|
/* We want the snap start signal to be emited anyway */
|
||||||
mv_ctx->last_snap_ts = GST_CLOCK_TIME_NONE;
|
mv_ctx->last_snap_ts = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GST_CLOCK_TIME_IS_VALID (mv_ctx->last_snap_ts) == FALSE) {
|
if (mv_ctx->last_snap_ts == NULL) {
|
||||||
|
|
||||||
mv_ctx->last_snaped1 = obj1;
|
mv_ctx->last_snaped1 = obj1;
|
||||||
mv_ctx->last_snaped2 = obj2;
|
mv_ctx->last_snaped2 = obj2;
|
||||||
mv_ctx->last_snap_ts = *timecode;
|
mv_ctx->last_snap_ts = timecode;
|
||||||
|
|
||||||
g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0,
|
g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0,
|
||||||
obj1, obj2, *timecode);
|
obj1, obj2, *timecode);
|
||||||
|
@ -724,8 +813,8 @@ ges_timeline_snap_position (GESTimeline * timeline, GESTrackObject * trackobj,
|
||||||
GESTrackObject *tmp_tckobj;
|
GESTrackObject *tmp_tckobj;
|
||||||
GESTimelineObject *tmp_tlobj, *tlobj;
|
GESTimelineObject *tmp_tlobj, *tlobj;
|
||||||
|
|
||||||
|
GstClockTime *last_snap_ts = priv->movecontext.last_snap_ts;
|
||||||
guint64 snap_distance = timeline->priv->snapping_distance;
|
guint64 snap_distance = timeline->priv->snapping_distance;
|
||||||
|
|
||||||
guint64 *prev_tc, *next_tc, *ret = NULL, off = G_MAXUINT64, off1 =
|
guint64 *prev_tc, *next_tc, *ret = NULL, off = G_MAXUINT64, off1 =
|
||||||
G_MAXUINT64;
|
G_MAXUINT64;
|
||||||
|
|
||||||
|
@ -733,6 +822,16 @@ ges_timeline_snap_position (GESTimeline * timeline, GESTrackObject * trackobj,
|
||||||
if (snap_distance == 0)
|
if (snap_distance == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/* If we can just resnap as last snap... do it */
|
||||||
|
if (last_snap_ts) {
|
||||||
|
off = timecode > *last_snap_ts ?
|
||||||
|
timecode - *last_snap_ts : *last_snap_ts - timecode;
|
||||||
|
if (off <= snap_distance) {
|
||||||
|
ret = last_snap_ts;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tlobj = ges_track_object_get_timeline_object (trackobj);
|
tlobj = ges_track_object_get_timeline_object (trackobj);
|
||||||
|
|
||||||
iter = g_sequence_search (priv->starts_ends, &timecode,
|
iter = g_sequence_search (priv->starts_ends, &timecode,
|
||||||
|
@ -756,6 +855,9 @@ ges_timeline_snap_position (GESTimeline * timeline, GESTrackObject * trackobj,
|
||||||
nxt_iter = g_sequence_iter_next (nxt_iter);
|
nxt_iter = g_sequence_iter_next (nxt_iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret == NULL)
|
||||||
|
off = G_MAXUINT64;
|
||||||
|
|
||||||
prev_iter = g_sequence_iter_prev (iter);
|
prev_iter = g_sequence_iter_prev (iter);
|
||||||
while (!g_sequence_iter_is_begin (prev_iter)) {
|
while (!g_sequence_iter_is_begin (prev_iter)) {
|
||||||
prev_tc = g_sequence_get (prev_iter);
|
prev_tc = g_sequence_get (prev_iter);
|
||||||
|
@ -773,6 +875,7 @@ ges_timeline_snap_position (GESTimeline * timeline, GESTrackObject * trackobj,
|
||||||
prev_iter = g_sequence_iter_prev (prev_iter);
|
prev_iter = g_sequence_iter_prev (prev_iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
/* We emit the snapping signal only if we snapped with a different value
|
/* We emit the snapping signal only if we snapped with a different value
|
||||||
* than the current one */
|
* than the current one */
|
||||||
if (emit) {
|
if (emit) {
|
||||||
|
@ -1272,11 +1375,12 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
|
||||||
{
|
{
|
||||||
guint64 *snap_end, *snap_st, *cur, off1, off2, end;
|
guint64 *snap_end, *snap_st, *cur, off1, off2, end;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (timeline, "Moving to %" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (position));
|
|
||||||
|
|
||||||
end = position + object->duration;
|
end = position + object->duration;
|
||||||
cur = g_hash_table_lookup (timeline->priv->by_end, object);
|
cur = g_hash_table_lookup (timeline->priv->by_end, object);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (timeline, "Moving to %" GST_TIME_FORMAT " (end %"
|
||||||
|
GST_TIME_FORMAT ")", GST_TIME_ARGS (position), GST_TIME_ARGS (end));
|
||||||
|
|
||||||
snap_end = ges_timeline_snap_position (timeline, object, cur, end, FALSE);
|
snap_end = ges_timeline_snap_position (timeline, object, cur, end, FALSE);
|
||||||
if (snap_end)
|
if (snap_end)
|
||||||
off1 = end > *snap_end ? end - *snap_end : *snap_end - end;
|
off1 = end > *snap_end ? end - *snap_end : *snap_end - end;
|
||||||
|
@ -1294,13 +1398,9 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
|
||||||
if (snap_end && off1 <= off2) {
|
if (snap_end && off1 <= off2) {
|
||||||
position = position + *snap_end - end;
|
position = position + *snap_end - end;
|
||||||
ges_timeline_emit_snappig (timeline, object, snap_end);
|
ges_timeline_emit_snappig (timeline, object, snap_end);
|
||||||
GST_DEBUG_OBJECT (timeline, "Real snap at %" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (position));
|
|
||||||
} else if (snap_st) {
|
} else if (snap_st) {
|
||||||
position = position + *snap_st - position;
|
position = position + *snap_st - position;
|
||||||
ges_timeline_emit_snappig (timeline, object, snap_st);
|
ges_timeline_emit_snappig (timeline, object, snap_st);
|
||||||
GST_DEBUG_OBJECT (timeline, "Real snap at %" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (position));
|
|
||||||
} else
|
} else
|
||||||
ges_timeline_emit_snappig (timeline, object, NULL);
|
ges_timeline_emit_snappig (timeline, object, NULL);
|
||||||
|
|
||||||
|
@ -1437,6 +1537,7 @@ discoverer_discovered_cb (GstDiscoverer * discoverer,
|
||||||
{
|
{
|
||||||
GList *tmp;
|
GList *tmp;
|
||||||
GList *stream_list;
|
GList *stream_list;
|
||||||
|
GESTimelineObject *tlobj;
|
||||||
GESTrackType tfs_supportedformats;
|
GESTrackType tfs_supportedformats;
|
||||||
|
|
||||||
gboolean found = FALSE;
|
gboolean found = FALSE;
|
||||||
|
@ -1521,18 +1622,26 @@ discoverer_discovered_cb (GstDiscoverer * discoverer,
|
||||||
|
|
||||||
check_image:
|
check_image:
|
||||||
|
|
||||||
|
tlobj = GES_TIMELINE_OBJECT (tfs);
|
||||||
if (is_image) {
|
if (is_image) {
|
||||||
/* don't set max-duration on still images */
|
/* don't set max-duration on still images */
|
||||||
g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
|
g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
|
||||||
|
} else {
|
||||||
|
GstClockTime file_duration, tlobj_max_duration;
|
||||||
|
|
||||||
|
/* Properly set duration informations from the discovery */
|
||||||
|
file_duration = gst_discoverer_info_get_duration (info);
|
||||||
|
tlobj_max_duration = ges_timeline_object_get_max_duration (tlobj);
|
||||||
|
|
||||||
|
if (tlobj_max_duration == G_MAXUINT64)
|
||||||
|
ges_timeline_object_set_max_duration (tlobj, file_duration);
|
||||||
|
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (tlobj->duration) == FALSE)
|
||||||
|
ges_timeline_object_set_duration (tlobj, file_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Continue the processing on tfs */
|
/* Continue the processing on tfs */
|
||||||
add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs));
|
add_object_to_tracks (timeline, tlobj);
|
||||||
|
|
||||||
if (!is_image) {
|
|
||||||
g_object_set (tfs, "max-duration",
|
|
||||||
gst_discoverer_info_get_duration (info), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the ref as the timeline file source is no longer needed here */
|
/* Remove the ref as the timeline file source is no longer needed here */
|
||||||
g_object_unref (tfs);
|
g_object_unref (tfs);
|
||||||
|
@ -1545,8 +1654,7 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
|
||||||
if (ges_timeline_object_is_moving_from_layer (object)) {
|
if (ges_timeline_object_is_moving_from_layer (object)) {
|
||||||
GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
|
GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
|
||||||
" anything on it", object);
|
" anything on it", object);
|
||||||
if (!timeline->priv->movecontext.ignore_needs_ctx)
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1649,7 +1757,12 @@ trackobj_start_changed_cb (GESTrackObject * child,
|
||||||
sort_starts_ends_start (timeline, child);
|
sort_starts_ends_start (timeline, child);
|
||||||
sort_starts_ends_end (timeline, child);
|
sort_starts_ends_end (timeline, child);
|
||||||
|
|
||||||
if (!timeline->priv->movecontext.ignore_needs_ctx)
|
/* If the timeline is set to snap objects together, we
|
||||||
|
* are sure that all movement of TrackObject-s are done within
|
||||||
|
* the moving context, so we do not need to recalculate the
|
||||||
|
* move context as often */
|
||||||
|
if (timeline->priv->movecontext.ignore_needs_ctx &&
|
||||||
|
timeline->priv->snapping_distance == 0)
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1659,15 +1772,12 @@ trackobj_duration_changed_cb (GESTrackObject * child,
|
||||||
{
|
{
|
||||||
sort_starts_ends_end (timeline, child);
|
sort_starts_ends_end (timeline, child);
|
||||||
|
|
||||||
if (!timeline->priv->movecontext.ignore_needs_ctx)
|
/* If the timeline is set to snap objects together, we
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
* are sure that all movement of TrackObject-s are done within
|
||||||
}
|
* the moving context, so we do not need to recalculate the
|
||||||
|
* move context as often */
|
||||||
static void
|
if (timeline->priv->movecontext.ignore_needs_ctx &&
|
||||||
trackobj_inpoint_changed_cb (GESTrackObject * child,
|
timeline->priv->snapping_distance == 0)
|
||||||
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
|
|
||||||
{
|
|
||||||
if (!timeline->priv->movecontext.ignore_needs_ctx)
|
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1683,8 +1793,6 @@ track_object_added_cb (GESTrack * track, GESTrackObject * object,
|
||||||
G_CALLBACK (trackobj_start_changed_cb), timeline);
|
G_CALLBACK (trackobj_start_changed_cb), timeline);
|
||||||
g_signal_connect (GES_TRACK_OBJECT (object), "notify::duration",
|
g_signal_connect (GES_TRACK_OBJECT (object), "notify::duration",
|
||||||
G_CALLBACK (trackobj_duration_changed_cb), timeline);
|
G_CALLBACK (trackobj_duration_changed_cb), timeline);
|
||||||
g_signal_connect (GES_TRACK_OBJECT (object), "notify::in-point",
|
|
||||||
G_CALLBACK (trackobj_inpoint_changed_cb), timeline);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1698,8 +1806,6 @@ track_object_removed_cb (GESTrack * track, GESTrackObject * object,
|
||||||
NULL);
|
NULL);
|
||||||
g_signal_handlers_disconnect_by_func (object, trackobj_duration_changed_cb,
|
g_signal_handlers_disconnect_by_func (object, trackobj_duration_changed_cb,
|
||||||
NULL);
|
NULL);
|
||||||
g_signal_handlers_disconnect_by_func (object, trackobj_inpoint_changed_cb,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
/* Make sure to reinitialise the moving context next time */
|
/* Make sure to reinitialise the moving context next time */
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
|
@ -1708,32 +1814,72 @@ track_object_removed_cb (GESTrack * track, GESTrackObject * object,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
track_duration_cb (GstElement * track,
|
pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
|
||||||
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
|
|
||||||
{
|
{
|
||||||
guint64 duration, max_duration = 0;
|
gchar *padname;
|
||||||
|
gboolean no_more;
|
||||||
GList *tmp;
|
GList *tmp;
|
||||||
|
|
||||||
for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
|
GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
|
||||||
TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
|
|
||||||
g_object_get (tr_priv->track, "duration", &duration, NULL);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (track, "track duration : %" GST_TIME_FORMAT,
|
if (G_UNLIKELY (tr_priv->pad)) {
|
||||||
GST_TIME_ARGS (duration));
|
GST_WARNING ("We are already controlling a pad for this track");
|
||||||
max_duration = MAX (duration, max_duration);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeline->priv->duration != max_duration) {
|
/* Remember the pad */
|
||||||
GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
|
GST_OBJECT_LOCK (track);
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (max_duration),
|
tr_priv->pad = pad;
|
||||||
GST_TIME_ARGS (timeline->priv->duration));
|
|
||||||
|
|
||||||
timeline->priv->duration = max_duration;
|
no_more = TRUE;
|
||||||
|
for (tmp = tr_priv->timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
|
||||||
|
TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
|
||||||
|
|
||||||
g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
|
if (!tr_priv->pad) {
|
||||||
|
GST_LOG ("Found track without pad %p", tr_priv->track);
|
||||||
|
no_more = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (track);
|
||||||
|
|
||||||
|
/* ghost it ! */
|
||||||
|
GST_DEBUG ("Ghosting pad and adding it to ourself");
|
||||||
|
padname = g_strdup_printf ("track_%p_src", track);
|
||||||
|
tr_priv->ghostpad = gst_ghost_pad_new (padname, pad);
|
||||||
|
g_free (padname);
|
||||||
|
gst_pad_set_active (tr_priv->ghostpad, TRUE);
|
||||||
|
gst_element_add_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
|
||||||
|
|
||||||
|
if (no_more) {
|
||||||
|
GST_DEBUG ("Signaling no-more-pads");
|
||||||
|
gst_element_no_more_pads (GST_ELEMENT (tr_priv->timeline));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
|
||||||
|
{
|
||||||
|
GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
|
||||||
|
|
||||||
|
if (G_UNLIKELY (tr_priv->pad != pad)) {
|
||||||
|
GST_WARNING ("Not the pad we're controlling");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (tr_priv->ghostpad == NULL)) {
|
||||||
|
GST_WARNING ("We don't have a ghostpad for this pad !");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG ("Removing ghostpad");
|
||||||
|
gst_pad_set_active (tr_priv->ghostpad, FALSE);
|
||||||
|
gst_element_remove_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
|
||||||
|
tr_priv->ghostpad = NULL;
|
||||||
|
tr_priv->pad = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* GstElement Virtual methods */
|
||||||
static GstStateChangeReturn
|
static GstStateChangeReturn
|
||||||
ges_timeline_change_state (GstElement * element, GstStateChange transition)
|
ges_timeline_change_state (GstElement * element, GstStateChange transition)
|
||||||
{
|
{
|
||||||
|
@ -1777,6 +1923,7 @@ ges_timeline_change_state (GstElement * element, GstStateChange transition)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** API *****/
|
||||||
/**
|
/**
|
||||||
* ges_timeline_new:
|
* ges_timeline_new:
|
||||||
*
|
*
|
||||||
|
@ -2044,71 +2191,6 @@ ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
|
|
||||||
{
|
|
||||||
gchar *padname;
|
|
||||||
gboolean no_more;
|
|
||||||
GList *tmp;
|
|
||||||
|
|
||||||
GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
|
|
||||||
|
|
||||||
if (G_UNLIKELY (tr_priv->pad)) {
|
|
||||||
GST_WARNING ("We are already controlling a pad for this track");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remember the pad */
|
|
||||||
GST_OBJECT_LOCK (track);
|
|
||||||
tr_priv->pad = pad;
|
|
||||||
|
|
||||||
no_more = TRUE;
|
|
||||||
for (tmp = tr_priv->timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
|
|
||||||
TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
|
|
||||||
|
|
||||||
if (!tr_priv->pad) {
|
|
||||||
GST_LOG ("Found track without pad %p", tr_priv->track);
|
|
||||||
no_more = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GST_OBJECT_UNLOCK (track);
|
|
||||||
|
|
||||||
/* ghost it ! */
|
|
||||||
GST_DEBUG ("Ghosting pad and adding it to ourself");
|
|
||||||
padname = g_strdup_printf ("track_%p_src", track);
|
|
||||||
tr_priv->ghostpad = gst_ghost_pad_new (padname, pad);
|
|
||||||
g_free (padname);
|
|
||||||
gst_pad_set_active (tr_priv->ghostpad, TRUE);
|
|
||||||
gst_element_add_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
|
|
||||||
|
|
||||||
if (no_more) {
|
|
||||||
GST_DEBUG ("Signaling no-more-pads");
|
|
||||||
gst_element_no_more_pads (GST_ELEMENT (tr_priv->timeline));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
|
|
||||||
{
|
|
||||||
GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
|
|
||||||
|
|
||||||
if (G_UNLIKELY (tr_priv->pad != pad)) {
|
|
||||||
GST_WARNING ("Not the pad we're controlling");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (G_UNLIKELY (tr_priv->ghostpad == NULL)) {
|
|
||||||
GST_WARNING ("We don't have a ghostpad for this pad !");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG ("Removing ghostpad");
|
|
||||||
gst_pad_set_active (tr_priv->ghostpad, FALSE);
|
|
||||||
gst_element_remove_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
|
|
||||||
tr_priv->ghostpad = NULL;
|
|
||||||
tr_priv->pad = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ges_timeline_add_track:
|
* ges_timeline_add_track:
|
||||||
* @timeline: a #GESTimeline
|
* @timeline: a #GESTimeline
|
||||||
|
@ -2169,11 +2251,6 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
|
||||||
/* ensure that each existing timeline object has the opportunity to create a
|
/* ensure that each existing timeline object has the opportunity to create a
|
||||||
* track object for this track*/
|
* track object for this track*/
|
||||||
|
|
||||||
/* We connect to the duration change notify, so we can update
|
|
||||||
* our duration accordingly */
|
|
||||||
g_signal_connect (G_OBJECT (track), "notify::duration",
|
|
||||||
G_CALLBACK (track_duration_cb), timeline);
|
|
||||||
|
|
||||||
/* We connect to the object for the timeline editing mode management */
|
/* We connect to the object for the timeline editing mode management */
|
||||||
g_signal_connect (G_OBJECT (track), "track-object-added",
|
g_signal_connect (G_OBJECT (track), "track-object-added",
|
||||||
G_CALLBACK (track_object_added_cb), timeline);
|
G_CALLBACK (track_object_added_cb), timeline);
|
||||||
|
@ -2192,8 +2269,6 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
|
||||||
g_list_free (objects);
|
g_list_free (objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
track_duration_cb (GST_ELEMENT (track), NULL, timeline);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2246,8 +2321,6 @@ ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
|
||||||
/* Remove pad-added/-removed handlers */
|
/* Remove pad-added/-removed handlers */
|
||||||
g_signal_handlers_disconnect_by_func (track, pad_added_cb, tr_priv);
|
g_signal_handlers_disconnect_by_func (track, pad_added_cb, tr_priv);
|
||||||
g_signal_handlers_disconnect_by_func (track, pad_removed_cb, tr_priv);
|
g_signal_handlers_disconnect_by_func (track, pad_removed_cb, tr_priv);
|
||||||
g_signal_handlers_disconnect_by_func (track, track_duration_cb,
|
|
||||||
tr_priv->track);
|
|
||||||
g_signal_handlers_disconnect_by_func (track, track_object_added_cb, timeline);
|
g_signal_handlers_disconnect_by_func (track, track_object_added_cb, timeline);
|
||||||
g_signal_handlers_disconnect_by_func (track, track_object_removed_cb,
|
g_signal_handlers_disconnect_by_func (track, track_object_removed_cb,
|
||||||
timeline);
|
timeline);
|
||||||
|
@ -2382,18 +2455,27 @@ ges_timeline_is_updating (GESTimeline * timeline)
|
||||||
gboolean
|
gboolean
|
||||||
ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
|
ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
|
||||||
{
|
{
|
||||||
GList *tmp;
|
if (ges_timeline_enable_update_internal (timeline, enabled)) {
|
||||||
gboolean res = TRUE;
|
g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_UPDATE]);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (timeline, "%s updates", enabled ? "Enabling" : "Disabling");
|
return TRUE;
|
||||||
|
|
||||||
for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
|
|
||||||
if (!ges_track_enable_update (((TrackPrivate *) tmp->data)->track, enabled))
|
|
||||||
res = FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure we reset the context */
|
return FALSE;
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
}
|
||||||
|
|
||||||
return res;
|
/**
|
||||||
|
* ges_timeline_get_duration
|
||||||
|
* @timeline: a #GESTimeline
|
||||||
|
*
|
||||||
|
* Get the current duration of @timeline
|
||||||
|
*
|
||||||
|
* Returns: The current duration of @timeline
|
||||||
|
*/
|
||||||
|
GstClockTime
|
||||||
|
ges_timeline_get_duration (GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (GES_IS_TIMELINE (timeline), GST_CLOCK_TIME_NONE);
|
||||||
|
|
||||||
|
return timeline->priv->duration;
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,8 @@ GList *ges_timeline_get_tracks (GESTimeline *timeline);
|
||||||
gboolean ges_timeline_enable_update(GESTimeline * timeline, gboolean enabled);
|
gboolean ges_timeline_enable_update(GESTimeline * timeline, gboolean enabled);
|
||||||
gboolean ges_timeline_is_updating (GESTimeline * timeline);
|
gboolean ges_timeline_is_updating (GESTimeline * timeline);
|
||||||
|
|
||||||
|
GstClockTime ges_timeline_get_duration (GESTimeline *timeline);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* _GES_TIMELINE */
|
#endif /* _GES_TIMELINE */
|
||||||
|
|
|
@ -1554,7 +1554,7 @@ ges_track_object_set_max_duration (GESTrackObject * object, guint64 maxduration)
|
||||||
*
|
*
|
||||||
* Copies @object
|
* Copies @object
|
||||||
*
|
*
|
||||||
* Returns: The newly create #GESTrackObject, copied from @object
|
* Returns: (transfer full): The newly create #GESTrackObject, copied from @object
|
||||||
*
|
*
|
||||||
* Since: 0.10.XX
|
* Since: 0.10.XX
|
||||||
*/
|
*/
|
||||||
|
|
590
ges/ges-track.c
590
ges/ges-track.c
|
@ -35,20 +35,35 @@
|
||||||
|
|
||||||
G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
|
G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
|
||||||
|
|
||||||
|
/* Structure that represents gaps and keep knowledge
|
||||||
|
* of the gaps filled in the track */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GstElement *gnlobj;
|
||||||
|
|
||||||
|
GstClockTime start;
|
||||||
|
GstClockTime duration;
|
||||||
|
GESTrack *track;
|
||||||
|
} Gap;
|
||||||
|
|
||||||
struct _GESTrackPrivate
|
struct _GESTrackPrivate
|
||||||
{
|
{
|
||||||
/*< private > */
|
/*< private > */
|
||||||
GESTimeline *timeline;
|
GESTimeline *timeline;
|
||||||
GList *trackobjects;
|
GSequence *tckobjs_by_start;
|
||||||
|
GList *gaps;
|
||||||
|
|
||||||
guint64 duration;
|
guint64 duration;
|
||||||
|
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
|
|
||||||
GstElement *composition; /* The composition associated with this track */
|
GstElement *composition; /* The composition associated with this track */
|
||||||
GstElement *background; /* The backgrond, handle the gaps in the track */
|
|
||||||
GstPad *srcpad; /* The source GhostPad */
|
GstPad *srcpad; /* The source GhostPad */
|
||||||
|
|
||||||
gboolean updating;
|
gboolean updating;
|
||||||
|
|
||||||
|
/* Virtual method to create GstElement that fill gaps */
|
||||||
|
GESCreateElementForGapFunc create_element_for_gaps;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -72,13 +87,329 @@ static void
|
||||||
pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
|
pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
|
||||||
static void composition_duration_cb (GstElement * composition, GParamSpec * arg
|
static void composition_duration_cb (GstElement * composition, GParamSpec * arg
|
||||||
G_GNUC_UNUSED, GESTrack * obj);
|
G_GNUC_UNUSED, GESTrack * obj);
|
||||||
|
|
||||||
|
/* Private methods/functions/callbacks */
|
||||||
|
|
||||||
|
/* Utilities */
|
||||||
|
static gint
|
||||||
|
objects_start_compare (GESTrackObject * a, GESTrackObject * b,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_trackobj_to_list_foreach (GESTrackObject * trackobj, GList ** list)
|
||||||
|
{
|
||||||
|
g_object_ref (trackobj);
|
||||||
|
*list = g_list_prepend (*list, trackobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Gap *
|
||||||
|
gap_new (GESTrack * track, GstClockTime start, GstClockTime duration)
|
||||||
|
{
|
||||||
|
GstElement *gnlsrc, *elem;
|
||||||
|
|
||||||
|
Gap *new_gap;
|
||||||
|
|
||||||
|
gnlsrc = gst_element_factory_make ("gnlsource", NULL);
|
||||||
|
elem = track->priv->create_element_for_gaps (track);
|
||||||
|
if (G_UNLIKELY (gst_bin_add (GST_BIN (gnlsrc), elem) == FALSE)) {
|
||||||
|
GST_WARNING_OBJECT (track, "Could not create gap filler");
|
||||||
|
|
||||||
|
if (gnlsrc)
|
||||||
|
gst_object_unref (gnlsrc);
|
||||||
|
|
||||||
|
if (elem)
|
||||||
|
gst_object_unref (elem);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (gst_bin_add (GST_BIN (track->priv->composition),
|
||||||
|
gnlsrc) == FALSE)) {
|
||||||
|
GST_WARNING_OBJECT (track, "Could not add gap to the composition");
|
||||||
|
|
||||||
|
if (gnlsrc)
|
||||||
|
gst_object_unref (gnlsrc);
|
||||||
|
|
||||||
|
if (elem)
|
||||||
|
gst_object_unref (elem);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_gap = g_slice_new (Gap);
|
||||||
|
new_gap->start = start;
|
||||||
|
new_gap->duration = duration;
|
||||||
|
new_gap->track = track;
|
||||||
|
new_gap->gnlobj = gst_object_ref (gnlsrc);
|
||||||
|
|
||||||
|
|
||||||
|
g_object_set (gnlsrc, "start", new_gap->start, "duration", new_gap->duration,
|
||||||
|
"priority", 0, NULL);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (track,
|
||||||
|
"Created gap with start %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (new_gap->start), GST_TIME_ARGS (new_gap->duration));
|
||||||
|
|
||||||
|
|
||||||
|
return new_gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_gap (Gap * gap)
|
||||||
|
{
|
||||||
|
GESTrack *track = gap->track;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (track, "Removed gap with start %" GST_TIME_FORMAT
|
||||||
|
" duration %" GST_TIME_FORMAT, GST_TIME_ARGS (gap->start),
|
||||||
|
GST_TIME_ARGS (gap->duration));
|
||||||
|
gst_bin_remove (GST_BIN (track->priv->composition), gap->gnlobj);
|
||||||
|
gst_element_set_state (gap->gnlobj, GST_STATE_NULL);
|
||||||
|
gst_object_unref (gap->gnlobj);
|
||||||
|
|
||||||
|
g_slice_free (Gap, gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
update_gaps (GESTrack * track)
|
||||||
|
{
|
||||||
|
Gap *gap;
|
||||||
|
GSequenceIter *it;
|
||||||
|
|
||||||
|
GESTrackObject *tckobj;
|
||||||
|
GstClockTime start, end, duration = 0, timeline_duration;
|
||||||
|
|
||||||
|
GESTrackPrivate *priv = track->priv;
|
||||||
|
|
||||||
|
if (priv->create_element_for_gaps == NULL) {
|
||||||
|
GST_INFO ("Not filling the gaps as no create_element_for_gaps vmethod"
|
||||||
|
" provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1- Remove all gaps */
|
||||||
|
g_list_free_full (priv->gaps, (GDestroyNotify) free_gap);
|
||||||
|
priv->gaps = NULL;
|
||||||
|
|
||||||
|
/* 2- And recalculate gaps */
|
||||||
|
for (it = g_sequence_get_begin_iter (priv->tckobjs_by_start);
|
||||||
|
g_sequence_iter_is_end (it) == FALSE; it = g_sequence_iter_next (it)) {
|
||||||
|
tckobj = g_sequence_get (it);
|
||||||
|
|
||||||
|
start = GES_TRACK_OBJECT_START (tckobj);
|
||||||
|
end = start + GES_TRACK_OBJECT_DURATION (tckobj);
|
||||||
|
|
||||||
|
if (start > duration) {
|
||||||
|
/* 3- Fill gap */
|
||||||
|
gap = gap_new (track, duration, start - duration);
|
||||||
|
|
||||||
|
if (G_LIKELY (gap != NULL))
|
||||||
|
priv->gaps = g_list_prepend (priv->gaps, gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
duration = MAX (duration, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4- Add a gap at the end of the timeline if needed */
|
||||||
|
if (priv->timeline) {
|
||||||
|
g_object_get (priv->timeline, "duration", &timeline_duration, NULL);
|
||||||
|
|
||||||
|
if (duration < timeline_duration) {
|
||||||
|
gap = gap_new (track, duration, timeline_duration - duration);
|
||||||
|
|
||||||
|
if (G_LIKELY (gap != NULL)) {
|
||||||
|
priv->gaps = g_list_prepend (priv->gaps, gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->duration = timeline_duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
resort_and_fill_gaps (GESTrack * track)
|
||||||
|
{
|
||||||
|
g_sequence_sort (track->priv->tckobjs_by_start,
|
||||||
|
(GCompareDataFunc) objects_start_compare, NULL);
|
||||||
|
|
||||||
|
if (track->priv->updating == TRUE) {
|
||||||
|
update_gaps (track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* callbacks */
|
||||||
|
static void
|
||||||
|
timeline_duration_changed_cb (GESTimeline * timeline,
|
||||||
|
GParamSpec * arg, GESTrack * track)
|
||||||
|
{
|
||||||
|
GESTrackPrivate *priv = track->priv;
|
||||||
|
|
||||||
|
/* Remove the last gap on the timeline if not needed anymore */
|
||||||
|
if (priv->updating == TRUE && priv->gaps) {
|
||||||
|
Gap *gap = (Gap *) priv->gaps->data;
|
||||||
|
GstClockTime tl_duration = ges_timeline_get_duration (timeline);
|
||||||
|
|
||||||
|
if (gap->start + gap->duration > tl_duration) {
|
||||||
|
free_gap (gap);
|
||||||
|
priv->gaps = g_list_remove (priv->gaps, gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sort_track_objects_cb (GESTrackObject * child,
|
sort_track_objects_cb (GESTrackObject * child,
|
||||||
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
|
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
|
||||||
|
{
|
||||||
|
resort_and_fill_gaps (track);
|
||||||
|
}
|
||||||
|
|
||||||
static void timeline_duration_cb (GESTimeline * timeline,
|
static void
|
||||||
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
|
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 * track)
|
||||||
|
{
|
||||||
|
guint64 duration;
|
||||||
|
|
||||||
|
g_object_get (composition, "duration", &duration, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
if (track->priv->duration != duration) {
|
||||||
|
GST_DEBUG_OBJECT (track,
|
||||||
|
"composition duration : %" GST_TIME_FORMAT " current : %"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (duration),
|
||||||
|
GST_TIME_ARGS (track->priv->duration));
|
||||||
|
|
||||||
|
track->priv->duration = duration;
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (track), properties[ARG_DURATION]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GESCreateElementForGapFunc Gaps filler for raw tracks */
|
||||||
|
static GstElement *
|
||||||
|
create_element_for_raw_audio_gap (GESTrack * track)
|
||||||
|
{
|
||||||
|
GstElement *elem;
|
||||||
|
|
||||||
|
elem = gst_element_factory_make ("audiotestsrc", NULL);
|
||||||
|
g_object_set (elem, "wave", 4, NULL);
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstElement *
|
||||||
|
create_element_for_raw_video_gap (GESTrack * track)
|
||||||
|
{
|
||||||
|
GstElement *elem;
|
||||||
|
|
||||||
|
elem = gst_element_factory_make ("videotestsrc", NULL);
|
||||||
|
g_object_set (elem, "pattern", 2, NULL);
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove @object from @track, but keeps it in the sequence this is needed
|
||||||
|
* when finalizing as we can not change a GSequence at the same time we are
|
||||||
|
* accessing it
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
remove_object_internal (GESTrack * track, GESTrackObject * object)
|
||||||
|
{
|
||||||
|
GESTrackPrivate *priv;
|
||||||
|
GstElement *gnlobject;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (track, "object:%p", 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_element_set_state (gnlobject, GST_STATE_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_func (object, sort_track_objects_cb, NULL);
|
||||||
|
|
||||||
|
ges_track_object_set_track (object, NULL);
|
||||||
|
|
||||||
|
g_signal_emit (track, ges_track_signals[TRACK_OBJECT_REMOVED], 0,
|
||||||
|
GES_TRACK_OBJECT (object));
|
||||||
|
|
||||||
|
g_object_unref (object);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dispose_tckobjs_foreach (GESTrackObject * tckobj, GESTrack * track)
|
||||||
|
{
|
||||||
|
GESTimelineObject *tlobj;
|
||||||
|
|
||||||
|
tlobj = ges_track_object_get_timeline_object (tckobj);
|
||||||
|
|
||||||
|
remove_object_internal (track, tckobj);
|
||||||
|
ges_timeline_object_release_track_object (tlobj, tckobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GObject virtual methods */
|
||||||
static void
|
static void
|
||||||
ges_track_get_property (GObject * object, guint property_id,
|
ges_track_get_property (GObject * object, guint property_id,
|
||||||
GValue * value, GParamSpec * pspec)
|
GValue * value, GParamSpec * pspec)
|
||||||
|
@ -124,12 +455,11 @@ ges_track_dispose (GObject * object)
|
||||||
GESTrack *track = (GESTrack *) object;
|
GESTrack *track = (GESTrack *) object;
|
||||||
GESTrackPrivate *priv = track->priv;
|
GESTrackPrivate *priv = track->priv;
|
||||||
|
|
||||||
while (priv->trackobjects) {
|
/* Remove all TrackObjects and drop our reference */
|
||||||
GESTrackObject *trobj = GES_TRACK_OBJECT (priv->trackobjects->data);
|
g_sequence_foreach (track->priv->tckobjs_by_start,
|
||||||
ges_track_remove_object (track, trobj);
|
(GFunc) dispose_tckobjs_foreach, track);
|
||||||
ges_timeline_object_release_track_object ((GESTimelineObject *)
|
g_sequence_free (priv->tckobjs_by_start);
|
||||||
ges_track_object_get_timeline_object (trobj), trobj);
|
g_list_free_full (priv->gaps, (GDestroyNotify) free_gap);
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->composition) {
|
if (priv->composition) {
|
||||||
gst_bin_remove (GST_BIN (object), priv->composition);
|
gst_bin_remove (GST_BIN (object), priv->composition);
|
||||||
|
@ -144,48 +474,6 @@ ges_track_dispose (GObject * object)
|
||||||
G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
|
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 (priv->background, "priority", G_MAXUINT, 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
|
static void
|
||||||
ges_track_finalize (GObject * object)
|
ges_track_finalize (GObject * object)
|
||||||
{
|
{
|
||||||
|
@ -203,7 +491,6 @@ ges_track_class_init (GESTrackClass * klass)
|
||||||
object_class->set_property = ges_track_set_property;
|
object_class->set_property = ges_track_set_property;
|
||||||
object_class->dispose = ges_track_dispose;
|
object_class->dispose = ges_track_dispose;
|
||||||
object_class->finalize = ges_track_finalize;
|
object_class->finalize = ges_track_finalize;
|
||||||
object_class->constructed = ges_track_constructed;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GESTrack:caps
|
* GESTrack:caps
|
||||||
|
@ -286,6 +573,9 @@ ges_track_init (GESTrack * self)
|
||||||
|
|
||||||
self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
|
self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
|
||||||
self->priv->updating = TRUE;
|
self->priv->updating = TRUE;
|
||||||
|
self->priv->tckobjs_by_start = g_sequence_new (NULL);
|
||||||
|
self->priv->create_element_for_gaps = NULL;
|
||||||
|
self->priv->gaps = NULL;
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration",
|
g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration",
|
||||||
G_CALLBACK (composition_duration_cb), self);
|
G_CALLBACK (composition_duration_cb), self);
|
||||||
|
@ -336,6 +626,10 @@ ges_track_video_raw_new (void)
|
||||||
GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
|
GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
|
||||||
|
|
||||||
track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
|
track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
|
||||||
|
ges_track_set_create_element_for_gap_func (track,
|
||||||
|
create_element_for_raw_video_gap);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (track, "New raw video track");
|
||||||
|
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
@ -355,7 +649,11 @@ ges_track_audio_raw_new (void)
|
||||||
GstCaps *caps = gst_caps_new_empty_simple ("audio/x-raw");
|
GstCaps *caps = gst_caps_new_empty_simple ("audio/x-raw");
|
||||||
|
|
||||||
track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
|
track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
|
||||||
|
ges_track_set_create_element_for_gap_func (track,
|
||||||
|
create_element_for_raw_audio_gap);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (track, "New raw audio track %p",
|
||||||
|
track->priv->create_element_for_gaps);
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,12 +670,12 @@ ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
|
||||||
GST_DEBUG ("track:%p, timeline:%p", track, timeline);
|
GST_DEBUG ("track:%p, timeline:%p", track, timeline);
|
||||||
|
|
||||||
if (track->priv->timeline)
|
if (track->priv->timeline)
|
||||||
g_signal_handlers_disconnect_by_func (track,
|
g_signal_handlers_disconnect_by_func (track->priv->timeline,
|
||||||
timeline_duration_cb, track->priv->timeline);
|
timeline_duration_changed_cb, track);
|
||||||
|
|
||||||
if (timeline)
|
if (timeline)
|
||||||
g_signal_connect (G_OBJECT (timeline), "notify::duration",
|
g_signal_connect (timeline, "notify::duration",
|
||||||
G_CALLBACK (timeline_duration_cb), track);
|
G_CALLBACK (timeline_duration_changed_cb), track);
|
||||||
|
|
||||||
track->priv->timeline = timeline;
|
track->priv->timeline = timeline;
|
||||||
}
|
}
|
||||||
|
@ -409,26 +707,6 @@ ges_track_set_caps (GESTrack * track, const GstCaps * caps)
|
||||||
/* FIXME : update all trackobjects ? */
|
/* 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:
|
* ges_track_add_object:
|
||||||
* @track: a #GESTrack
|
* @track: a #GESTrack
|
||||||
|
@ -471,9 +749,8 @@ ges_track_add_object (GESTrack * track, GESTrackObject * object)
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_ref_sink (object);
|
g_object_ref_sink (object);
|
||||||
track->priv->trackobjects =
|
g_sequence_insert_sorted (track->priv->tckobjs_by_start, object,
|
||||||
g_list_insert_sorted (track->priv->trackobjects, object,
|
(GCompareDataFunc) objects_start_compare, NULL);
|
||||||
(GCompareFunc) objects_start_compare);
|
|
||||||
|
|
||||||
g_signal_emit (track, ges_track_signals[TRACK_OBJECT_ADDED], 0,
|
g_signal_emit (track, ges_track_signals[TRACK_OBJECT_ADDED], 0,
|
||||||
GES_TRACK_OBJECT (object));
|
GES_TRACK_OBJECT (object));
|
||||||
|
@ -481,9 +758,14 @@ ges_track_add_object (GESTrack * track, GESTrackObject * object)
|
||||||
g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
|
g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
|
||||||
G_CALLBACK (sort_track_objects_cb), track);
|
G_CALLBACK (sort_track_objects_cb), track);
|
||||||
|
|
||||||
|
g_signal_connect (GES_TRACK_OBJECT (object), "notify::duration",
|
||||||
|
G_CALLBACK (sort_track_objects_cb), track);
|
||||||
|
|
||||||
g_signal_connect (GES_TRACK_OBJECT (object), "notify::priority",
|
g_signal_connect (GES_TRACK_OBJECT (object), "notify::priority",
|
||||||
G_CALLBACK (sort_track_objects_cb), track);
|
G_CALLBACK (sort_track_objects_cb), track);
|
||||||
|
|
||||||
|
resort_and_fill_gaps (track);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,14 +782,11 @@ GList *
|
||||||
ges_track_get_objects (GESTrack * track)
|
ges_track_get_objects (GESTrack * track)
|
||||||
{
|
{
|
||||||
GList *ret = NULL;
|
GList *ret = NULL;
|
||||||
GList *tmp;
|
|
||||||
|
|
||||||
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
|
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
|
||||||
|
|
||||||
for (tmp = track->priv->trackobjects; tmp; tmp = tmp->next) {
|
g_sequence_foreach (track->priv->tckobjs_by_start,
|
||||||
ret = g_list_prepend (ret, tmp->data);
|
(GFunc) add_trackobj_to_list_foreach, &ret);
|
||||||
g_object_ref (tmp->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = g_list_reverse (ret);
|
ret = g_list_reverse (ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -529,121 +808,25 @@ ges_track_get_objects (GESTrack * track)
|
||||||
gboolean
|
gboolean
|
||||||
ges_track_remove_object (GESTrack * track, GESTrackObject * object)
|
ges_track_remove_object (GESTrack * track, GESTrackObject * object)
|
||||||
{
|
{
|
||||||
|
GSequenceIter *it;
|
||||||
GESTrackPrivate *priv;
|
GESTrackPrivate *priv;
|
||||||
GstElement *gnlobject;
|
|
||||||
|
|
||||||
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
||||||
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
|
g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
|
||||||
|
|
||||||
GST_DEBUG ("track:%p, object:%p", track, object);
|
|
||||||
|
|
||||||
priv = track->priv;
|
priv = track->priv;
|
||||||
|
|
||||||
if (G_UNLIKELY (ges_track_object_get_track (object) != track)) {
|
if (remove_object_internal (track, object) == TRUE) {
|
||||||
GST_WARNING ("Object belongs to another track");
|
it = g_sequence_lookup (priv->tckobjs_by_start, object,
|
||||||
return FALSE;
|
(GCompareDataFunc) objects_start_compare, NULL);
|
||||||
|
g_sequence_remove (it);
|
||||||
|
|
||||||
|
resort_and_fill_gaps (track);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((gnlobject = ges_track_object_get_gnlobject (object))) {
|
return FALSE;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_element_set_state (gnlobject, GST_STATE_NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_signal_handlers_disconnect_by_func (object, sort_track_objects_cb, NULL);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
g_object_notify_by_pspec (G_OBJECT (obj), properties[ARG_DURATION]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
timeline_duration_cb (GESTimeline * timeline,
|
|
||||||
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
|
|
||||||
{
|
|
||||||
guint64 duration;
|
|
||||||
|
|
||||||
g_object_get (timeline, "duration", &duration, NULL);
|
|
||||||
g_object_set (GES_TRACK (track)->priv->background, "duration", duration,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (track, "Updating background duration to %" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (duration));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -702,6 +885,9 @@ ges_track_enable_update (GESTrack * track, gboolean enabled)
|
||||||
|
|
||||||
track->priv->updating = update;
|
track->priv->updating = update;
|
||||||
|
|
||||||
|
if (update == TRUE)
|
||||||
|
resort_and_fill_gaps (track);
|
||||||
|
|
||||||
return update == enabled;
|
return update == enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,3 +906,23 @@ ges_track_is_updating (GESTrack * track)
|
||||||
|
|
||||||
return track->priv->updating;
|
return track->priv->updating;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ges_track_set_create_element_for_gap_func:
|
||||||
|
* @track: a #GESTrack
|
||||||
|
* @func: (scope notified): The #GESCreateElementForGapFunc that will be used
|
||||||
|
* to create #GstElement to fill gaps
|
||||||
|
*
|
||||||
|
* Sets the function that should be used to create the GstElement used to fill gaps.
|
||||||
|
* To avoid to provide such a function we advice you to use the
|
||||||
|
* #ges_track_audio_raw_new and #ges_track_video_raw_new constructor when possible.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ges_track_set_create_element_for_gap_func (GESTrack * track,
|
||||||
|
GESCreateElementForGapFunc func)
|
||||||
|
{
|
||||||
|
g_return_if_fail (GES_IS_TRACK (track));
|
||||||
|
|
||||||
|
track->priv->create_element_for_gaps = func;
|
||||||
|
}
|
||||||
|
|
|
@ -47,6 +47,18 @@ G_BEGIN_DECLS
|
||||||
|
|
||||||
typedef struct _GESTrackPrivate GESTrackPrivate;
|
typedef struct _GESTrackPrivate GESTrackPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GESCreateElementForGapFunc:
|
||||||
|
* @track: the #GESTrack
|
||||||
|
*
|
||||||
|
* A function that will be called to create the #GstElement that will be used
|
||||||
|
* as a source to fill the gaps in @track.
|
||||||
|
*
|
||||||
|
* Returns: A #GstElement (must be a source) that will be used to
|
||||||
|
* fill the gaps (periods of time in @track that containes no source).
|
||||||
|
*/
|
||||||
|
typedef GstElement* (*GESCreateElementForGapFunc) (GESTrack *track);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GESTrack:
|
* GESTrack:
|
||||||
* @type: a #GESTrackType indicting the basic type of the track.
|
* @type: a #GESTrackType indicting the basic type of the track.
|
||||||
|
@ -107,6 +119,10 @@ gboolean ges_track_is_updating (GESTrack * track);
|
||||||
|
|
||||||
GList* ges_track_get_objects (GESTrack *track);
|
GList* ges_track_get_objects (GESTrack *track);
|
||||||
|
|
||||||
|
void
|
||||||
|
ges_track_set_create_element_for_gap_func (GESTrack *track,
|
||||||
|
GESCreateElementForGapFunc func);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* _GES_TRACK */
|
#endif /* _GES_TRACK */
|
||||||
|
|
|
@ -206,6 +206,126 @@ GST_START_TEST (test_test_source_in_layer)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
static gint
|
||||||
|
find_composition_func (GstElement * element)
|
||||||
|
{
|
||||||
|
GstElementFactory *fac = gst_element_get_factory (element);
|
||||||
|
const gchar *name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (fac));
|
||||||
|
|
||||||
|
if (g_strcmp0 (name, "gnlcomposition") == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstElement *
|
||||||
|
find_composition (GESTrack * track)
|
||||||
|
{
|
||||||
|
GstIterator *it = gst_bin_iterate_recurse (GST_BIN (track));
|
||||||
|
GstElement *ret =
|
||||||
|
gst_iterator_find_custom (it, (GCompareFunc) find_composition_func, NULL);
|
||||||
|
|
||||||
|
gst_iterator_free (it);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define gap_object_check(gnlobj, start, duration, priority) \
|
||||||
|
{ \
|
||||||
|
guint64 pstart, pdur, pprio; \
|
||||||
|
g_object_get (gnlobj, "start", &pstart, "duration", &pdur, \
|
||||||
|
"priority", &pprio, NULL); \
|
||||||
|
assert_equals_uint64 (pstart, start); \
|
||||||
|
assert_equals_uint64 (pdur, duration); \
|
||||||
|
assert_equals_int (pprio, priority); \
|
||||||
|
}
|
||||||
|
GST_START_TEST (test_gap_filling_basic)
|
||||||
|
{
|
||||||
|
GESTrack *track;
|
||||||
|
GESTrackObject *trackobject, *trackobject1, *trackobject2;
|
||||||
|
/*GESTimelineLayer *layer; */
|
||||||
|
GESTimelineObject *object, *object1, *object2;
|
||||||
|
GstElement *gnlsrc, *gnlsrc1, *gap = NULL;
|
||||||
|
GstElement *composition;
|
||||||
|
GList *tmp;
|
||||||
|
|
||||||
|
ges_init ();
|
||||||
|
|
||||||
|
track = ges_track_audio_raw_new ();
|
||||||
|
fail_unless (track != NULL);
|
||||||
|
|
||||||
|
composition = find_composition (track);
|
||||||
|
fail_unless (composition != NULL);
|
||||||
|
|
||||||
|
object = GES_TIMELINE_OBJECT (ges_timeline_test_source_new ());
|
||||||
|
fail_unless (object != NULL);
|
||||||
|
|
||||||
|
/* Set some properties */
|
||||||
|
g_object_set (object, "start", (guint64) 0, "duration", (guint64) 5, NULL);
|
||||||
|
assert_equals_uint64 (GES_TIMELINE_OBJECT_START (object), 0);
|
||||||
|
assert_equals_uint64 (GES_TIMELINE_OBJECT_DURATION (object), 5);
|
||||||
|
|
||||||
|
trackobject = ges_timeline_object_create_track_object (object, track);
|
||||||
|
ges_timeline_object_add_track_object (object, trackobject);
|
||||||
|
fail_unless (ges_track_add_object (track, trackobject));
|
||||||
|
fail_unless (trackobject != NULL);
|
||||||
|
gnlsrc = ges_track_object_get_gnlobject (trackobject);
|
||||||
|
fail_unless (gnlsrc != NULL);
|
||||||
|
|
||||||
|
/* Check that trackobject has the same properties */
|
||||||
|
assert_equals_uint64 (GES_TRACK_OBJECT_START (trackobject), 0);
|
||||||
|
assert_equals_uint64 (GES_TRACK_OBJECT_DURATION (trackobject), 5);
|
||||||
|
|
||||||
|
/* Check no gap were wrongly added */
|
||||||
|
assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 1);
|
||||||
|
|
||||||
|
object1 = GES_TIMELINE_OBJECT (ges_timeline_test_source_new ());
|
||||||
|
fail_unless (object1 != NULL);
|
||||||
|
|
||||||
|
g_object_set (object1, "start", (guint64) 15, "duration", (guint64) 5, NULL);
|
||||||
|
assert_equals_uint64 (GES_TIMELINE_OBJECT_START (object1), 15);
|
||||||
|
assert_equals_uint64 (GES_TIMELINE_OBJECT_DURATION (object1), 5);
|
||||||
|
|
||||||
|
trackobject1 = ges_timeline_object_create_track_object (object1, track);
|
||||||
|
ges_timeline_object_add_track_object (object1, trackobject1);
|
||||||
|
fail_unless (ges_track_add_object (track, trackobject1));
|
||||||
|
fail_unless (trackobject1 != NULL);
|
||||||
|
gnlsrc1 = ges_track_object_get_gnlobject (trackobject1);
|
||||||
|
fail_unless (gnlsrc1 != NULL);
|
||||||
|
|
||||||
|
/* Check that trackobject1 has the same properties */
|
||||||
|
assert_equals_uint64 (GES_TRACK_OBJECT_START (trackobject1), 15);
|
||||||
|
assert_equals_uint64 (GES_TRACK_OBJECT_DURATION (trackobject1), 5);
|
||||||
|
|
||||||
|
/* Check the gap as properly been added */
|
||||||
|
assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 3);
|
||||||
|
|
||||||
|
for (tmp = GST_BIN_CHILDREN (composition); tmp; tmp = tmp->next) {
|
||||||
|
GstElement *tmp_gnlobj = GST_ELEMENT (tmp->data);
|
||||||
|
|
||||||
|
if (tmp_gnlobj != gnlsrc && tmp_gnlobj != gnlsrc1) {
|
||||||
|
gap = tmp_gnlobj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail_unless (gap != NULL);
|
||||||
|
gap_object_check (gap, 5, 10, 0)
|
||||||
|
|
||||||
|
object2 = GES_TIMELINE_OBJECT (ges_timeline_test_source_new ());
|
||||||
|
fail_unless (object2 != NULL);
|
||||||
|
g_object_set (object2, "start", (guint64) 35, "duration", (guint64) 5, NULL);
|
||||||
|
trackobject2 = ges_timeline_object_create_track_object (object2, track);
|
||||||
|
ges_timeline_object_add_track_object (object2, trackobject2);
|
||||||
|
fail_unless (ges_track_add_object (track, trackobject2));
|
||||||
|
fail_unless (trackobject2 != NULL);
|
||||||
|
assert_equals_uint64 (GES_TRACK_OBJECT_START (trackobject2), 35);
|
||||||
|
assert_equals_uint64 (GES_TRACK_OBJECT_DURATION (trackobject2), 5);
|
||||||
|
assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 5);
|
||||||
|
|
||||||
|
gst_object_unref (track);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
static Suite *
|
static Suite *
|
||||||
ges_suite (void)
|
ges_suite (void)
|
||||||
{
|
{
|
||||||
|
@ -217,6 +337,7 @@ ges_suite (void)
|
||||||
tcase_add_test (tc_chain, test_test_source_basic);
|
tcase_add_test (tc_chain, test_test_source_basic);
|
||||||
tcase_add_test (tc_chain, test_test_source_properties);
|
tcase_add_test (tc_chain, test_test_source_properties);
|
||||||
tcase_add_test (tc_chain, test_test_source_in_layer);
|
tcase_add_test (tc_chain, test_test_source_in_layer);
|
||||||
|
tcase_add_test (tc_chain, test_gap_filling_basic);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue