mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-25 00:28:21 +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_TYPE_PIPELINE_FLAGS
|
||||
ges_pipeline_flags_get_type
|
||||
GES_TYPE_EDGE
|
||||
ges_edge_get_type
|
||||
GES_TYPE_EDIT_MODE
|
||||
ges_edit_mode_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>ges-track</FILE>
|
||||
<TITLE>GESTrack</TITLE>
|
||||
GESTrack
|
||||
GESCreateElementForGapFunc
|
||||
ges_track_audio_raw_new
|
||||
ges_track_video_raw_new
|
||||
ges_track_new
|
||||
|
@ -61,6 +66,7 @@ ges_track_get_caps
|
|||
ges_track_enable_update
|
||||
ges_track_get_objects
|
||||
ges_track_is_updating
|
||||
ges_track_set_create_element_for_gap_func
|
||||
<SUBSECTION Standard>
|
||||
GESTrackClass
|
||||
GESTrackPrivate
|
||||
|
@ -266,6 +272,7 @@ ges_timeline_is_updating
|
|||
ges_timeline_get_tracks
|
||||
ges_timeline_get_layers
|
||||
ges_timeline_get_track_for_pad
|
||||
ges_timeline_get_duration
|
||||
<SUBSECTION Standard>
|
||||
GESTimelinePrivate
|
||||
GESTimelineClass
|
||||
|
@ -321,7 +328,6 @@ ges_timeline_object_set_duration
|
|||
ges_timeline_object_get_layer
|
||||
ges_timeline_object_find_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_effect_position
|
||||
ges_timeline_object_move_to_layer
|
||||
|
@ -335,6 +341,9 @@ ges_timeline_object_ripple_end
|
|||
ges_timeline_object_roll_start
|
||||
ges_timeline_object_roll_end
|
||||
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>
|
||||
GES_TIMELINE_OBJECT_DURATION
|
||||
GES_TIMELINE_OBJECT_INPOINT
|
||||
|
|
|
@ -400,6 +400,7 @@ gboolean
|
|||
ges_formatter_load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
|
||||
const gchar * uri)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
|
||||
|
||||
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_CALLBACK (discovery_error_cb), formatter);
|
||||
if (klass->load_from_uri)
|
||||
return klass->load_from_uri (formatter, timeline, uri);
|
||||
if (klass->load_from_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
|
||||
|
@ -599,6 +603,7 @@ discovery_error_cb (GESTimeline * timeline,
|
|||
static gboolean
|
||||
project_loaded (GESFormatter * formatter, GESTimeline * timeline)
|
||||
{
|
||||
GST_INFO_OBJECT (formatter, "Emit project loaded");
|
||||
g_signal_emit (formatter, ges_formatter_signals[LOADED_SIGNAL], 0, timeline);
|
||||
|
||||
return TRUE;
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
* 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/tree.h>
|
||||
#include <libxml/parser.h>
|
||||
|
|
|
@ -39,10 +39,11 @@
|
|||
|
||||
typedef struct _GESPitiviFormatterPrivate GESPitiviFormatterPrivate;
|
||||
|
||||
|
||||
/**
|
||||
* GESPitiviFormatter:
|
||||
*
|
||||
* Serializes a #GESTimeline to a file using
|
||||
* Serializes a #GESTimeline to a file using the Xml PiTiVi file format
|
||||
*/
|
||||
|
||||
struct _GESPitiviFormatter {
|
||||
|
@ -55,7 +56,8 @@ struct _GESPitiviFormatter {
|
|||
gpointer _ges_reserved[GES_PADDING];
|
||||
};
|
||||
|
||||
struct _GESPitiviFormatterClass {
|
||||
struct _GESPitiviFormatterClass
|
||||
{
|
||||
/*< private >*/
|
||||
GESFormatterClass parent_class;
|
||||
|
||||
|
|
|
@ -23,6 +23,16 @@
|
|||
#include "ges-screenshot.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 *
|
||||
ges_play_sink_convert_frame (GstElement * playsink, GstCaps * caps)
|
||||
{
|
||||
|
|
|
@ -215,17 +215,18 @@ objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
|
|||
static GList *
|
||||
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;
|
||||
GESTimelineObject *tl_obj;
|
||||
|
||||
tck_objects_list = ges_track_get_objects (track);
|
||||
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 */
|
||||
return_list = g_list_append (return_list, tmp->data);
|
||||
|
||||
} else
|
||||
g_object_unref (tmp->data);
|
||||
}
|
||||
|
|
|
@ -1749,6 +1749,7 @@ ges_timeline_object_ripple_end (GESTimelineObject * object, guint64 end)
|
|||
|
||||
/**
|
||||
* ges_timeline_object_roll_start:
|
||||
* @object: The #GESTimelineObject to roll
|
||||
* @start: The new start of @object in roll mode, it will also adapat
|
||||
* 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);
|
||||
|
||||
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) \
|
||||
(&GES_TIMELINE(timeline)->priv->pendingobjects_lock)
|
||||
#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) \
|
||||
(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
|
||||
*
|
||||
* The context aims at avoiding to recalculate values/objects on each call of the
|
||||
|
@ -90,7 +93,7 @@ struct _MoveContext
|
|||
/* Last snapping properties */
|
||||
GESTrackObject *last_snaped1;
|
||||
GESTrackObject *last_snaped2;
|
||||
GstClockTime last_snap_ts;
|
||||
GstClockTime *last_snap_ts;
|
||||
};
|
||||
|
||||
struct _GESTimelinePrivate
|
||||
|
@ -143,6 +146,7 @@ enum
|
|||
PROP_0,
|
||||
PROP_DURATION,
|
||||
PROP_SNAPPING_DISTANCE,
|
||||
PROP_UPDATE,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
|
@ -173,6 +177,26 @@ static void
|
|||
discoverer_discovered_cb (GstDiscoverer * discoverer,
|
||||
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*/
|
||||
static void
|
||||
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:
|
||||
g_value_set_uint64 (value, timeline->priv->snapping_distance);
|
||||
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:
|
||||
timeline->priv->snapping_distance = g_value_get_uint64 (value);
|
||||
break;
|
||||
case PROP_UPDATE:
|
||||
ges_timeline_enable_update_internal (timeline,
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
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,
|
||||
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
|
||||
* @timeline: the #GESTimeline
|
||||
|
@ -397,6 +449,9 @@ ges_timeline_init (GESTimeline * self)
|
|||
{
|
||||
GESTimelinePrivate *priv;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (ges_timeline_debug, "gestimeline",
|
||||
GST_DEBUG_FG_YELLOW, "ges timeline");
|
||||
|
||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||
GES_TYPE_TIMELINE, GESTimelinePrivate);
|
||||
|
||||
|
@ -449,6 +504,28 @@ sort_layers (gpointer a, gpointer b)
|
|||
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
|
||||
objects_start_compare (GESTrackObject * a, GESTrackObject * b)
|
||||
{
|
||||
|
@ -553,6 +630,7 @@ sort_starts_ends_end (GESTimeline * timeline, GESTrackObject * obj)
|
|||
*end = obj->start + obj->duration;
|
||||
|
||||
g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
|
||||
timeline_update_duration (timeline);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -567,6 +645,7 @@ sort_starts_ends_start (GESTimeline * timeline, GESTrackObject * obj)
|
|||
*start = obj->start;
|
||||
|
||||
g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
|
||||
timeline_update_duration (timeline);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -590,6 +669,7 @@ resort_all_starts_ends (GESTimeline * timeline)
|
|||
}
|
||||
|
||||
g_sequence_sort (priv->starts_ends, (GCompareDataFunc) compare_uint64, NULL);
|
||||
timeline_update_duration (timeline);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -610,7 +690,7 @@ init_movecontext (MoveContext * mv_ctx)
|
|||
mv_ctx->max_layer_prio = 0;
|
||||
mv_ctx->last_snaped1 = NULL;
|
||||
mv_ctx->last_snaped2 = NULL;
|
||||
mv_ctx->last_snap_ts = GST_CLOCK_TIME_NONE;
|
||||
mv_ctx->last_snap_ts = NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -645,7 +725,7 @@ stop_tracking_for_snapping (GESTimeline * timeline, GESTrackObject * tckobj)
|
|||
g_sequence_remove (iter_start);
|
||||
g_sequence_remove (iter_end);
|
||||
g_sequence_remove (tckobj_iter);
|
||||
|
||||
timeline_update_duration (timeline);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -672,6 +752,8 @@ start_tracking_track_obj (GESTimeline * timeline, GESTrackObject * tckobj)
|
|||
g_hash_table_insert (priv->by_object, pend, tckobj);
|
||||
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
|
||||
timeline_update_duration (timeline);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -680,11 +762,18 @@ ges_timeline_emit_snappig (GESTimeline * timeline, GESTrackObject * obj1,
|
|||
{
|
||||
GESTrackObject *obj2;
|
||||
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 (mv_ctx->last_snaped1 != NULL && mv_ctx->last_snaped2 != NULL) {
|
||||
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 */
|
||||
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);
|
||||
|
||||
if (mv_ctx->last_snap_ts != *timecode) {
|
||||
if (last_snap_ts != *timecode) {
|
||||
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 */
|
||||
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_snaped2 = obj2;
|
||||
mv_ctx->last_snap_ts = *timecode;
|
||||
mv_ctx->last_snap_ts = timecode;
|
||||
|
||||
g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0,
|
||||
obj1, obj2, *timecode);
|
||||
|
@ -724,8 +813,8 @@ ges_timeline_snap_position (GESTimeline * timeline, GESTrackObject * trackobj,
|
|||
GESTrackObject *tmp_tckobj;
|
||||
GESTimelineObject *tmp_tlobj, *tlobj;
|
||||
|
||||
GstClockTime *last_snap_ts = priv->movecontext.last_snap_ts;
|
||||
guint64 snap_distance = timeline->priv->snapping_distance;
|
||||
|
||||
guint64 *prev_tc, *next_tc, *ret = NULL, off = G_MAXUINT64, off1 =
|
||||
G_MAXUINT64;
|
||||
|
||||
|
@ -733,6 +822,16 @@ ges_timeline_snap_position (GESTimeline * timeline, GESTrackObject * trackobj,
|
|||
if (snap_distance == 0)
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (ret == NULL)
|
||||
off = G_MAXUINT64;
|
||||
|
||||
prev_iter = g_sequence_iter_prev (iter);
|
||||
while (!g_sequence_iter_is_begin (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);
|
||||
}
|
||||
|
||||
done:
|
||||
/* We emit the snapping signal only if we snapped with a different value
|
||||
* than the current one */
|
||||
if (emit) {
|
||||
|
@ -1272,11 +1375,12 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
|
|||
{
|
||||
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;
|
||||
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);
|
||||
if (snap_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) {
|
||||
position = position + *snap_end - 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) {
|
||||
position = position + *snap_st - position;
|
||||
ges_timeline_emit_snappig (timeline, object, snap_st);
|
||||
GST_DEBUG_OBJECT (timeline, "Real snap at %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (position));
|
||||
} else
|
||||
ges_timeline_emit_snappig (timeline, object, NULL);
|
||||
|
||||
|
@ -1437,6 +1537,7 @@ discoverer_discovered_cb (GstDiscoverer * discoverer,
|
|||
{
|
||||
GList *tmp;
|
||||
GList *stream_list;
|
||||
GESTimelineObject *tlobj;
|
||||
GESTrackType tfs_supportedformats;
|
||||
|
||||
gboolean found = FALSE;
|
||||
|
@ -1521,18 +1622,26 @@ discoverer_discovered_cb (GstDiscoverer * discoverer,
|
|||
|
||||
check_image:
|
||||
|
||||
tlobj = GES_TIMELINE_OBJECT (tfs);
|
||||
if (is_image) {
|
||||
/* don't set max-duration on still images */
|
||||
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 */
|
||||
add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs));
|
||||
|
||||
if (!is_image) {
|
||||
g_object_set (tfs, "max-duration",
|
||||
gst_discoverer_info_get_duration (info), NULL);
|
||||
}
|
||||
add_object_to_tracks (timeline, tlobj);
|
||||
|
||||
/* Remove the ref as the timeline file source is no longer needed here */
|
||||
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)) {
|
||||
GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
|
||||
" 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;
|
||||
}
|
||||
|
||||
|
@ -1649,7 +1757,12 @@ trackobj_start_changed_cb (GESTrackObject * child,
|
|||
sort_starts_ends_start (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;
|
||||
}
|
||||
|
||||
|
@ -1659,15 +1772,12 @@ trackobj_duration_changed_cb (GESTrackObject * child,
|
|||
{
|
||||
sort_starts_ends_end (timeline, child);
|
||||
|
||||
if (!timeline->priv->movecontext.ignore_needs_ctx)
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
trackobj_inpoint_changed_cb (GESTrackObject * child,
|
||||
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1683,8 +1793,6 @@ track_object_added_cb (GESTrack * track, GESTrackObject * object,
|
|||
G_CALLBACK (trackobj_start_changed_cb), timeline);
|
||||
g_signal_connect (GES_TRACK_OBJECT (object), "notify::duration",
|
||||
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);
|
||||
g_signal_handlers_disconnect_by_func (object, trackobj_duration_changed_cb,
|
||||
NULL);
|
||||
g_signal_handlers_disconnect_by_func (object, trackobj_inpoint_changed_cb,
|
||||
NULL);
|
||||
|
||||
/* Make sure to reinitialise the moving context next time */
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
|
@ -1708,32 +1814,72 @@ track_object_removed_cb (GESTrack * track, GESTrackObject * object,
|
|||
}
|
||||
|
||||
static void
|
||||
track_duration_cb (GstElement * track,
|
||||
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
|
||||
pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
|
||||
{
|
||||
guint64 duration, max_duration = 0;
|
||||
gchar *padname;
|
||||
gboolean no_more;
|
||||
GList *tmp;
|
||||
|
||||
for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
|
||||
TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
|
||||
g_object_get (tr_priv->track, "duration", &duration, NULL);
|
||||
GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
|
||||
|
||||
GST_DEBUG_OBJECT (track, "track duration : %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (duration));
|
||||
max_duration = MAX (duration, max_duration);
|
||||
if (G_UNLIKELY (tr_priv->pad)) {
|
||||
GST_WARNING ("We are already controlling a pad for this track");
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeline->priv->duration != max_duration) {
|
||||
GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (max_duration),
|
||||
GST_TIME_ARGS (timeline->priv->duration));
|
||||
/* Remember the pad */
|
||||
GST_OBJECT_LOCK (track);
|
||||
tr_priv->pad = pad;
|
||||
|
||||
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
|
||||
ges_timeline_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
|
@ -1777,6 +1923,7 @@ ges_timeline_change_state (GstElement * element, GstStateChange transition)
|
|||
|
||||
}
|
||||
|
||||
/**** API *****/
|
||||
/**
|
||||
* ges_timeline_new:
|
||||
*
|
||||
|
@ -2044,71 +2191,6 @@ ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
|
|||
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:
|
||||
* @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
|
||||
* 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 */
|
||||
g_signal_connect (G_OBJECT (track), "track-object-added",
|
||||
G_CALLBACK (track_object_added_cb), timeline);
|
||||
|
@ -2192,8 +2269,6 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
|
|||
g_list_free (objects);
|
||||
}
|
||||
|
||||
track_duration_cb (GST_ELEMENT (track), NULL, timeline);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -2246,8 +2321,6 @@ ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
|
|||
/* 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_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_removed_cb,
|
||||
timeline);
|
||||
|
@ -2382,18 +2455,27 @@ ges_timeline_is_updating (GESTimeline * timeline)
|
|||
gboolean
|
||||
ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
|
||||
{
|
||||
GList *tmp;
|
||||
gboolean res = TRUE;
|
||||
if (ges_timeline_enable_update_internal (timeline, enabled)) {
|
||||
g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_UPDATE]);
|
||||
|
||||
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;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Make sure we reset the context */
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
|
||||
return res;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_is_updating (GESTimeline * timeline);
|
||||
|
||||
GstClockTime ges_timeline_get_duration (GESTimeline *timeline);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _GES_TIMELINE */
|
||||
|
|
|
@ -1554,7 +1554,7 @@ ges_track_object_set_max_duration (GESTrackObject * object, guint64 maxduration)
|
|||
*
|
||||
* Copies @object
|
||||
*
|
||||
* Returns: The newly create #GESTrackObject, copied from @object
|
||||
* Returns: (transfer full): The newly create #GESTrackObject, copied from @object
|
||||
*
|
||||
* 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);
|
||||
|
||||
/* 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
|
||||
{
|
||||
/*< private > */
|
||||
GESTimeline *timeline;
|
||||
GList *trackobjects;
|
||||
GSequence *tckobjs_by_start;
|
||||
GList *gaps;
|
||||
|
||||
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 */
|
||||
|
||||
gboolean updating;
|
||||
|
||||
/* Virtual method to create GstElement that fill gaps */
|
||||
GESCreateElementForGapFunc create_element_for_gaps;
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -72,13 +87,329 @@ 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);
|
||||
|
||||
/* 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
|
||||
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,
|
||||
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
|
||||
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 * 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
|
||||
ges_track_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
|
@ -124,12 +455,11 @@ 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);
|
||||
}
|
||||
/* Remove all TrackObjects and drop our reference */
|
||||
g_sequence_foreach (track->priv->tckobjs_by_start,
|
||||
(GFunc) dispose_tckobjs_foreach, track);
|
||||
g_sequence_free (priv->tckobjs_by_start);
|
||||
g_list_free_full (priv->gaps, (GDestroyNotify) free_gap);
|
||||
|
||||
if (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);
|
||||
}
|
||||
|
||||
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
|
||||
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->dispose = ges_track_dispose;
|
||||
object_class->finalize = ges_track_finalize;
|
||||
object_class->constructed = ges_track_constructed;
|
||||
|
||||
/**
|
||||
* GESTrack:caps
|
||||
|
@ -286,6 +573,9 @@ ges_track_init (GESTrack * self)
|
|||
|
||||
self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
|
||||
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_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");
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -355,7 +649,11 @@ ges_track_audio_raw_new (void)
|
|||
GstCaps *caps = gst_caps_new_empty_simple ("audio/x-raw");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -372,12 +670,12 @@ ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
|
|||
GST_DEBUG ("track:%p, timeline:%p", track, timeline);
|
||||
|
||||
if (track->priv->timeline)
|
||||
g_signal_handlers_disconnect_by_func (track,
|
||||
timeline_duration_cb, track->priv->timeline);
|
||||
g_signal_handlers_disconnect_by_func (track->priv->timeline,
|
||||
timeline_duration_changed_cb, track);
|
||||
|
||||
if (timeline)
|
||||
g_signal_connect (G_OBJECT (timeline), "notify::duration",
|
||||
G_CALLBACK (timeline_duration_cb), track);
|
||||
g_signal_connect (timeline, "notify::duration",
|
||||
G_CALLBACK (timeline_duration_changed_cb), track);
|
||||
|
||||
track->priv->timeline = timeline;
|
||||
}
|
||||
|
@ -409,26 +707,6 @@ ges_track_set_caps (GESTrack * track, const GstCaps * caps)
|
|||
/* 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
|
||||
|
@ -471,9 +749,8 @@ ges_track_add_object (GESTrack * track, GESTrackObject * object)
|
|||
}
|
||||
|
||||
g_object_ref_sink (object);
|
||||
track->priv->trackobjects =
|
||||
g_list_insert_sorted (track->priv->trackobjects, object,
|
||||
(GCompareFunc) objects_start_compare);
|
||||
g_sequence_insert_sorted (track->priv->tckobjs_by_start, object,
|
||||
(GCompareDataFunc) objects_start_compare, NULL);
|
||||
|
||||
g_signal_emit (track, ges_track_signals[TRACK_OBJECT_ADDED], 0,
|
||||
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_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_CALLBACK (sort_track_objects_cb), track);
|
||||
|
||||
resort_and_fill_gaps (track);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -500,14 +782,11 @@ 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);
|
||||
}
|
||||
g_sequence_foreach (track->priv->tckobjs_by_start,
|
||||
(GFunc) add_trackobj_to_list_foreach, &ret);
|
||||
|
||||
ret = g_list_reverse (ret);
|
||||
return ret;
|
||||
|
@ -529,121 +808,25 @@ ges_track_get_objects (GESTrack * track)
|
|||
gboolean
|
||||
ges_track_remove_object (GESTrack * track, GESTrackObject * object)
|
||||
{
|
||||
GSequenceIter *it;
|
||||
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 (remove_object_internal (track, object) == TRUE) {
|
||||
it = g_sequence_lookup (priv->tckobjs_by_start, object,
|
||||
(GCompareDataFunc) objects_start_compare, NULL);
|
||||
g_sequence_remove (it);
|
||||
|
||||
resort_and_fill_gaps (track);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -702,6 +885,9 @@ ges_track_enable_update (GESTrack * track, gboolean enabled)
|
|||
|
||||
track->priv->updating = update;
|
||||
|
||||
if (update == TRUE)
|
||||
resort_and_fill_gaps (track);
|
||||
|
||||
return update == enabled;
|
||||
}
|
||||
|
||||
|
@ -720,3 +906,23 @@ ges_track_is_updating (GESTrack * track)
|
|||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* @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);
|
||||
|
||||
void
|
||||
ges_track_set_create_element_for_gap_func (GESTrack *track,
|
||||
GESCreateElementForGapFunc func);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _GES_TRACK */
|
||||
|
|
|
@ -206,6 +206,126 @@ GST_START_TEST (test_test_source_in_layer)
|
|||
|
||||
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 *
|
||||
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_properties);
|
||||
tcase_add_test (tc_chain, test_test_source_in_layer);
|
||||
tcase_add_test (tc_chain, test_gap_filling_basic);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue