Merge remote-tracking branch 'origin/master' into 0.11

Conflicts:
	bindings/python/Makefile.am
	bindings/python/ges-types.defs
	bindings/python/ges.defs
	bindings/python/ges.override
	bindings/python/gesmodule.c
	bindings/python/testsuite/test_textoverlay.py
This commit is contained in:
Tim-Philipp Müller 2012-03-12 15:25:49 +00:00
commit 5d02075d9b
39 changed files with 3871 additions and 329 deletions

View file

@ -193,6 +193,14 @@ AC_SUBST(GLIB_PREFIX)
AC_SUBST(GST_PREFIX)
AC_SUBST(GSTPB_PREFIX)
dnl pitivi formatter needs libxml
PKG_CHECK_MODULES(XML, libxml-2.0, HAVE_LIBXML="yes", HAVE_LIBXML="no")
if test "x$HAVE_LIBXML" != "xyes"; then
AC_ERROR([libxml2 is required])
fi
AC_SUBST(XML_LIBS)
AC_SUBST(XML_CFLAGS)
dnl GTK is optional and only used in examples
HAVE_GTK=no
HAVE_GTK_X11=no

View file

@ -88,6 +88,7 @@ platform as well as Windows. It is released under the GNU Library General Public
<title>Serialization Classes</title>
<xi:include href="xml/ges-formatter.xml"/>
<xi:include href="xml/ges-keyfile-formatter.xml"/>
<xi:include href="xml/ges-pitivi-formatter.xml"/>
</chapter>
<chapter id="ges-hierarchy">

View file

@ -4,6 +4,11 @@
<FILE>ges-common</FILE>
<TITLE>Initialization</TITLE>
ges_init
ges_version
GES_VERSION_MAJOR
GES_VERSION_MICRO
GES_VERSION_MINOR
GES_VERSION_NANO
<SUBSECTION Standard>
GES_PADDING
</SECTION>
@ -51,6 +56,8 @@ ges_track_add_object
ges_track_remove_object
ges_track_set_caps
ges_track_get_caps
ges_track_enable_update
ges_track_get_objects
<SUBSECTION Standard>
GESTrackClass
GESTrackPrivate
@ -236,11 +243,13 @@ ges_timeline_new
ges_timeline_new_audio_video
ges_timeline_new_from_uri
ges_timeline_add_layer
ges_timeline_append_layer
ges_timeline_remove_layer
ges_timeline_add_track
ges_timeline_remove_track
ges_timeline_load_from_uri
ges_timeline_save_to_uri
ges_timeline_enable_update
<SUBSECTION usage>
ges_timeline_get_tracks
ges_timeline_get_layers
@ -269,6 +278,8 @@ ges_timeline_layer_remove_object
ges_timeline_layer_set_priority
ges_timeline_layer_get_priority
ges_timeline_layer_get_objects
ges_timeline_layer_get_auto_transition
ges_timeline_layer_set_auto_transition
<SUBSECTION Standard>
GESTimelineLayerPrivate
ges_timeline_layer_set_timeline
@ -298,7 +309,11 @@ ges_timeline_object_find_track_object
ges_timeline_object_add_track_object
ges_timeline_object_get_top_effects
ges_timeline_object_get_top_effect_position
ges_timeline_object_move_to_layer
ges_timeline_object_set_top_effect_priority
ges_timeline_object_set_supported_formats
ges_timeline_object_get_supported_formats
ges_timeline_object_split
<SUBSECTION Standard>
GES_TIMELINE_OBJECT_DURATION
GES_TIMELINE_OBJECT_INPOINT
@ -308,10 +323,13 @@ GES_TIMELINE_OBJECT_HEIGHT
ges_timeline_object_create_track_objects
ges_timeline_object_create_track_object
ges_timeline_object_fill_track_object
ges_timeline_object_is_moving_from_layer
ges_timeline_object_release_track_object
ges_timeline_object_get_track_objects
ges_timeline_object_set_layer
ges_timeline_object_set_moving_from_layer
ges_timeline_object_set_priority
ges_timeline_object_lock_track_objects
GESTimelineObjectPrivate
GES_IS_TIMELINE_OBJECT
GES_IS_TIMELINE_OBJECT_CLASS
@ -331,6 +349,10 @@ ges_timeline_pipeline_new
ges_timeline_pipeline_add_timeline
ges_timeline_pipeline_set_mode
ges_timeline_pipeline_set_render_settings
ges_timeline_pipeline_preview_get_audio_sink
ges_timeline_pipeline_preview_get_video_sink
ges_timeline_pipeline_preview_set_audio_sink
ges_timeline_pipeline_preview_set_video_sink
ges_timeline_pipeline_get_thumbnail_buffer
ges_timeline_pipeline_get_thumbnail_rgb24
ges_timeline_pipeline_save_thumbnail
@ -732,12 +754,15 @@ GESFormatter
GESFormatterClass
GESFormatterLoadFromURIMethod
GESFormatterSaveToURIMethod
GESFormatterSourceMovedMethod
GESFormatterLoadedMethod
ges_default_formatter_new
ges_formatter_load_from_uri
ges_formatter_save_to_uri
ges_formatter_new_for_uri
ges_formatter_can_load_uri
ges_formatter_can_save_uri
ges_formatter_update_source_uri
<SUBSECTION Standard>
ges_formatter_get_type
GES_FORMATTER
@ -775,6 +800,26 @@ GES_TYPE_KEYFILE_FORMATTER
ges_keyfile_formatter_get_type
</SECTION>
<SECTION>
<FILE>ges-pitivi-formatter</FILE>
<TITLE>GESPitiviFormatter</TITLE>
GESPitiviFormatter
ges_pitivi_formatter_new
ges_pitivi_formatter_set_sources
ges_pitivi_formatter_get_sources
<SUBSECTION Standard>
GESPitiviFormatterClass
GESPitiviFormatterPrivate
GES_TYPE_PITIVI_FORMATTER
GES_IS_PITIVI_FORMATTER
GES_IS_PITIVI_FORMATTER_CLASS
GES_PITIVI_FORMATTER
GES_PITIVI_FORMATTER_CLASS
GES_PITIVI_FORMATTER_GET_CLASS
GES_TYPE_PITIVIFORMATTER
ges_pitivi_formatter_get_type
</SECTION>
<SECTION>
<FILE>ges-track-effect</FILE>
<TITLE>GESTrackEffect</TITLE>

View file

@ -46,6 +46,7 @@ libges_@GST_MAJORMINOR@_la_SOURCES = \
ges-screenshot.c \
ges-formatter.c \
ges-keyfile-formatter.c \
ges-pitivi-formatter.c \
ges-utils.c
libges_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/ges/
@ -89,13 +90,14 @@ libges_@GST_MAJORMINOR@include_HEADERS = \
ges-screenshot.h \
ges-formatter.h \
ges-keyfile-formatter.h \
ges-pitivi-formatter.h \
ges-utils.h
noinst_HEADERS = \
ges-internal.h
libges_@GST_MAJORMINOR@_la_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_VIDEO_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
libges_@GST_MAJORMINOR@_la_LIBADD = $(GST_PBUTILS_LIBS) $(GST_VIDEO_LIBS) $(GST_CONTROLLER_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS)
libges_@GST_MAJORMINOR@_la_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) $(GST_VIDEO_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(XML_CFLAGS)
libges_@GST_MAJORMINOR@_la_LIBADD = $(GST_PBUTILS_LIBS) $(GST_VIDEO_LIBS) $(GST_CONTROLLER_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) $(XML_LIBS)
libges_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS) -export-symbols-regex \^_*\(ges_\|GES_\).*
DISTCLEANFILE = $(CLEANFILES)

View file

@ -38,13 +38,19 @@
*
* Support for saving or loading new formats can be added by creating a subclass of
* #GESFormatter and implement the various vmethods of #GESFormatterClass.
*
* Note that subclasses should call project_loaded wen they are done loading
* a project.
**/
#include <gst/gst.h>
#include <stdlib.h>
#include "gesmarshal.h"
#include "ges-formatter.h"
#include "ges-keyfile-formatter.h"
#include "ges-internal.h"
#include "ges.h"
G_DEFINE_ABSTRACT_TYPE (GESFormatter, ges_formatter, G_TYPE_OBJECT);
@ -52,6 +58,10 @@ struct _GESFormatterPrivate
{
gchar *data;
gsize length;
/* Make sure not to emit several times "moved-source" when the user already
* provided the new source URI. */
GHashTable *uri_newuri_table;
};
static void ges_formatter_dispose (GObject * object);
@ -61,6 +71,20 @@ static gboolean save_to_uri (GESFormatter * formatter, GESTimeline *
timeline, const gchar * uri);
static gboolean default_can_load_uri (const gchar * uri);
static gboolean default_can_save_uri (const gchar * uri);
static void discovery_error_cb (GESTimeline * timeline,
GESTimelineFileSource * tfs, GError * error, GESFormatter * formatter);
static gboolean project_loaded (GESFormatter * formatter,
GESTimeline * timeline);
enum
{
SOURCE_MOVED_SIGNAL,
LOADED_SIGNAL,
LAST_SIGNAL
};
static guint ges_formatter_signals[LAST_SIGNAL] = { 0 };
static void
ges_formatter_class_init (GESFormatterClass * klass)
@ -69,12 +93,35 @@ ges_formatter_class_init (GESFormatterClass * klass)
g_type_class_add_private (klass, sizeof (GESFormatterPrivate));
/**
* GESFormatter::source-moved:
* @formatter: the #GESFormatter
* @source: The #GESTimelineFileSource that has an invalid URI. When this happens,
* you can call #ges_formatter_update_source_uri with the new URI of the source so
* the project can be loaded properly.
*/
ges_formatter_signals[SOURCE_MOVED_SIGNAL] =
g_signal_new ("source-moved", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE,
1, GES_TYPE_TIMELINE_FILE_SOURCE);
/**
* GESFormatter::loaded:
* @formatter: the #GESFormatter that is done loading a project.
*/
ges_formatter_signals[LOADED_SIGNAL] =
g_signal_new ("loaded", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE,
1, GES_TYPE_TIMELINE);
object_class->dispose = ges_formatter_dispose;
klass->can_load_uri = default_can_load_uri;
klass->can_save_uri = default_can_save_uri;
klass->load_from_uri = load_from_uri;
klass->save_to_uri = save_to_uri;
klass->update_source_uri = NULL;
klass->project_loaded = project_loaded;
}
static void
@ -82,6 +129,9 @@ ges_formatter_init (GESFormatter * object)
{
object->priv = G_TYPE_INSTANCE_GET_PRIVATE (object,
GES_TYPE_FORMATTER, GESFormatterPrivate);
object->priv->uri_newuri_table = g_hash_table_new_full (g_str_hash,
g_str_equal, g_free, g_free);
}
static void
@ -92,6 +142,7 @@ ges_formatter_dispose (GObject * object)
if (priv->data) {
g_free (priv->data);
}
g_hash_table_destroy (priv->uri_newuri_table);
}
/**
@ -144,7 +195,7 @@ default_can_save_uri (const gchar * uri)
/**
* ges_formatter_can_load_uri:
* @uri: a #gchar * pointing to the URI
*
*
* Checks if there is a #GESFormatter available which can load a #GESTimeline
* from the given URI.
*
@ -177,7 +228,7 @@ ges_formatter_can_load_uri (const gchar * uri)
/**
* ges_formatter_can_save_uri:
* @uri: a #gchar * pointing to a URI
*
*
* Returns TRUE if there is a #GESFormatter available which can save a
* #GESTimeline to the given URI.
*
@ -188,7 +239,7 @@ gboolean
ges_formatter_can_save_uri (const gchar * uri)
{
if (!(gst_uri_is_valid (uri))) {
GST_ERROR ("Invalid uri!");
GST_ERROR ("%s invalid uri!", uri);
return FALSE;
}
@ -237,7 +288,7 @@ ges_formatter_set_data (GESFormatter * formatter, void *data, gsize length)
*
* Lets you get the data @formatter used for loading.
*
* Returns: a pointer to the data.
* Returns: (transfer none): a pointer to the data.
*/
void *
ges_formatter_get_data (GESFormatter * formatter, gsize * length)
@ -334,7 +385,7 @@ ges_formatter_save (GESFormatter * formatter, GESTimeline * timeline)
* @formatter: a #GESFormatter
* @timeline: a #GESTimeline
* @uri: a #gchar * pointing to a URI
*
*
* Load data from the given URI into timeline.
*
* Returns: TRUE if the timeline data was successfully loaded from the URI,
@ -347,6 +398,11 @@ ges_formatter_load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
{
GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
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);
@ -451,3 +507,56 @@ save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
return ret;
}
gboolean
ges_formatter_update_source_uri (GESFormatter * formatter,
GESTimelineFileSource * source, gchar * new_uri)
{
GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
if (klass->update_source_uri) {
const gchar *uri = ges_timeline_filesource_get_uri (source);
gchar *cached_uri =
g_hash_table_lookup (formatter->priv->uri_newuri_table, uri);
if (!cached_uri) {
g_hash_table_insert (formatter->priv->uri_newuri_table, g_strdup (uri),
g_strdup (new_uri));
GST_DEBUG ("Adding %s to the new uri cache", new_uri);
}
return klass->update_source_uri (formatter, source, new_uri);
}
GST_ERROR ("not implemented!");
return FALSE;
}
static void
discovery_error_cb (GESTimeline * timeline,
GESTimelineFileSource * tfs, GError * error, GESFormatter * formatter)
{
if (error->domain == GST_RESOURCE_ERROR &&
error->code == GST_RESOURCE_ERROR_NOT_FOUND) {
const gchar *uri = ges_timeline_filesource_get_uri (tfs);
gchar *new_uri =
g_hash_table_lookup (formatter->priv->uri_newuri_table, uri);
if (new_uri) {
ges_formatter_update_source_uri (formatter, tfs, new_uri);
GST_DEBUG ("%s found in the cache, new uri: %s", uri, new_uri);
} else
g_signal_emit (formatter, ges_formatter_signals[SOURCE_MOVED_SIGNAL], 0,
tfs);
}
}
static gboolean
project_loaded (GESFormatter * formatter, GESTimeline * timeline)
{
g_signal_emit (formatter, ges_formatter_signals[LOADED_SIGNAL], 0, timeline);
return TRUE;
}

View file

@ -100,6 +100,34 @@ typedef gboolean (*GESFormatterSaveMethod) (GESFormatter * formatter,
typedef gboolean (*GESFormatterLoadMethod) (GESFormatter * formatter,
GESTimeline * timeline);
/**
* GESFormatterSourceMovedMethod:
* @formatter: a #GESFormatter
* @tfs: a #GESTimelineFileSource
* @new_uri: the new URI of @tfs
*
* Virtual method for changing the URI of a #GESTimelineFileSource that has been
* moved between the saving and the loading of the timeline.
*
* This virtual method is not 100% necessary to be implemented as it is an
* extra feature.
*
* Returns: %TRUE if the source URI could be modified properly, %FALSE otherwize.
*/
typedef gboolean (*GESFormatterSourceMovedMethod) (GESFormatter *formatter,
GESTimelineFileSource *tfs, gchar *new_uri);
/**
* GESFormatterLoadedMethod
* @formatter: The #GESFormatter that is done loading
* @timeline: The #GESTimeline that has finnished to load
*
* This method should be called by sublcasses when they are done
* loading @timeline
*/
typedef gboolean (*GESFormatterLoadedMethod) (GESFormatter *formatter,
GESTimeline *timeline);
/**
* GESFormatterClass:
* @parent_class: the parent class structure
@ -107,6 +135,9 @@ typedef gboolean (*GESFormatterLoadMethod) (GESFormatter * formatter,
* @can_save_uri: Whether the URI can be saved
* @load_from_uri: class method to deserialize data from a URI
* @save_to_uri: class method to serialize data to a URI
* @update_source_uri: virtual method to specify that a source has moved, and thus its URI
* must be set to its new location (specified by the user)
* @project_loaded: Must be called by subclasses when done loading a project
*
* GES Formatter class. Override the vmethods to implement the formatter functionnality.
*/
@ -118,6 +149,8 @@ struct _GESFormatterClass {
GESFormatterCanSaveURIMethod can_save_uri;
GESFormatterLoadFromURIMethod load_from_uri;
GESFormatterSaveToURIMethod save_to_uri;
GESFormatterSourceMovedMethod update_source_uri;
GESFormatterLoadedMethod project_loaded;
/*< private >*/
/* FIXME : formatter name */
@ -148,6 +181,10 @@ gboolean ges_formatter_save_to_uri (GESFormatter * formatter,
GESTimeline *timeline,
const gchar *uri);
gboolean
ges_formatter_update_source_uri (GESFormatter * formatter,
GESTimelineFileSource * source, gchar * new_uri);
/* Non-standard methods. WILL BE DEPRECATED */
gboolean ges_formatter_load (GESFormatter * formatter,
GESTimeline * timeline);

1173
ges/ges-pitivi-formatter.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
#ifndef _GES_PITIVI_FORMATTER
#define _GES_PITIVI_FORMATTER
#define GES_TYPE_PITIVI_FORMATTER ges_pitivi_formatter_get_type()
#define GES_PITIVI_FORMATTER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatter))
#define GES_PITIVI_FORMATTER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterClass))
#define GES_IS_PITIVI_FORMATTER(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_PITIVI_FORMATTER))
#define GES_IS_PITIVI_FORMATTER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_PITIVI_FORMATTER))
#define GES_PITIVI_FORMATTER_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterClass))
typedef struct _GESPitiviFormatterPrivate GESPitiviFormatterPrivate;
/**
* GESPitiviFormatter:
*
* Serializes a #GESTimeline to a file using
*/
struct _GESPitiviFormatter {
GESFormatter parent;
/*< private >*/
GESPitiviFormatterPrivate *priv;
/* Padding for API extension */
gpointer _ges_reserved[GES_PADDING];
};
struct _GESPitiviFormatterClass {
/*< private >*/
GESFormatterClass parent_class;
/* Padding for API extension */
gpointer _ges_reserved[GES_PADDING];
};
GType ges_pitivi_formatter_get_type (void);
GESPitiviFormatter *ges_pitivi_formatter_new (void);
gboolean ges_pitivi_formatter_set_sources (GESPitiviFormatter * formatter, GList * infos);
GList * ges_pitivi_formatter_get_sources(GESPitiviFormatter * formatter);
#endif /* _GES_PITIVI_FORMATTER */

View file

@ -163,7 +163,7 @@ static void
gstl_recalculate (GESSimpleTimelineLayer * self)
{
GList *tmp;
GstClockTime pos = 0;
gint64 pos = 0;
gint priority = 0;
gint transition_priority = 0;
gint height;
@ -210,9 +210,11 @@ gstl_recalculate (GESSimpleTimelineLayer * self)
} else if (GES_IS_TIMELINE_TRANSITION (obj)) {
pos -= dur;
if (pos < 0)
pos = 0;
GST_LOG ("%p obj: height: %d: trans_priority %d", obj, height,
transition_priority);
GST_LOG ("%p obj: height: %d: trans_priority %d Position: %d, "
"duration %d", obj, height, transition_priority, pos);
g_assert (transition_priority != -1);

View file

@ -44,10 +44,6 @@ struct _GESTimelineFileSourcePrivate
gboolean is_image;
guint64 maxduration;
/* The formats supported by this filesource
* TODO : Could maybe be moved to a parent class */
GESTrackType supportedformats;
};
enum
@ -56,7 +52,6 @@ enum
PROP_URI,
PROP_MAX_DURATION,
PROP_MUTE,
PROP_SUPPORTED_FORMATS,
PROP_IS_IMAGE,
};
@ -64,6 +59,8 @@ enum
static GESTrackObject
* ges_timeline_filesource_create_track_object (GESTimelineObject * obj,
GESTrack * track);
void
ges_timeline_filesource_set_uri (GESTimelineFileSource * self, gchar * uri);
static void
ges_timeline_filesource_get_property (GObject * object, guint property_id,
@ -81,9 +78,6 @@ ges_timeline_filesource_get_property (GObject * object, guint property_id,
case PROP_MAX_DURATION:
g_value_set_uint64 (value, priv->maxduration);
break;
case PROP_SUPPORTED_FORMATS:
g_value_set_flags (value, priv->supportedformats);
break;
case PROP_IS_IMAGE:
g_value_set_boolean (value, priv->is_image);
break;
@ -100,7 +94,7 @@ ges_timeline_filesource_set_property (GObject * object, guint property_id,
switch (property_id) {
case PROP_URI:
tfs->priv->uri = g_value_dup_string (value);
ges_timeline_filesource_set_uri (tfs, g_value_dup_string (value));
break;
case PROP_MUTE:
ges_timeline_filesource_set_mute (tfs, g_value_get_boolean (value));
@ -109,10 +103,6 @@ ges_timeline_filesource_set_property (GObject * object, guint property_id,
ges_timeline_filesource_set_max_duration (tfs,
g_value_get_uint64 (value));
break;
case PROP_SUPPORTED_FORMATS:
ges_timeline_filesource_set_supported_formats (tfs,
g_value_get_flags (value));
break;
case PROP_IS_IMAGE:
ges_timeline_filesource_set_is_image (tfs, g_value_get_boolean (value));
break;
@ -151,7 +141,7 @@ ges_timeline_filesource_class_init (GESTimelineFileSourceClass * klass)
*/
g_object_class_install_property (object_class, PROP_URI,
g_param_spec_string ("uri", "URI", "uri of the resource",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
/**
* GESTimelineFileSource:max-duration:
@ -175,16 +165,6 @@ ges_timeline_filesource_class_init (GESTimelineFileSourceClass * klass)
g_param_spec_boolean ("mute", "Mute", "Mute audio track",
FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
/**
* GESTimelineFileSource:supported-formats:
*
* The formats supported by the filesource.
*/
g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
g_param_spec_flags ("supported-formats", "Supported formats",
"Formats supported by the file", GES_TYPE_TRACK_TYPE,
GES_TRACK_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
/**
* GESTimelineFileSource:is-image:
*
@ -255,6 +235,7 @@ ges_timeline_filesource_set_max_duration (GESTimelineFileSource * self,
guint64 maxduration)
{
GESTimelineObject *object = GES_TIMELINE_OBJECT (self);
GList *tmp, *tckobjs;
self->priv->maxduration = maxduration;
if (object->duration == GST_CLOCK_TIME_NONE || object->duration == 0) {
@ -262,6 +243,15 @@ ges_timeline_filesource_set_max_duration (GESTimelineFileSource * self,
g_object_set (self, "duration", self->priv->maxduration - object->inpoint,
NULL);
}
tckobjs = ges_timeline_object_get_track_objects (GES_TIMELINE_OBJECT (self));
for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
g_object_set (tmp->data, "max-duration", maxduration, NULL);
/* We free the list in the same loop */
g_object_unref (tmp->data);
g_list_free_1 (tmp);
}
}
/**
@ -276,7 +266,8 @@ void
ges_timeline_filesource_set_supported_formats (GESTimelineFileSource * self,
GESTrackType supportedformats)
{
self->priv->supportedformats = supportedformats;
ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (self),
supportedformats);
}
/**
@ -360,7 +351,7 @@ ges_timeline_filesource_get_uri (GESTimelineFileSource * self)
GESTrackType
ges_timeline_filesource_get_supported_formats (GESTimelineFileSource * self)
{
return self->priv->supportedformats;
return ges_timeline_object_get_supported_formats (GES_TIMELINE_OBJECT (self));
}
static GESTrackObject *
@ -370,15 +361,15 @@ ges_timeline_filesource_create_track_object (GESTimelineObject * obj,
GESTimelineFileSourcePrivate *priv = GES_TIMELINE_FILE_SOURCE (obj)->priv;
GESTrackObject *res;
if (!(priv->supportedformats & track->type)) {
if (!(ges_timeline_object_get_supported_formats (obj) & track->type)) {
GST_DEBUG ("We don't support this track format");
return NULL;
}
if (priv->is_image) {
if (track->type != GES_TRACK_TYPE_VIDEO) {
GST_DEBUG ("Object is still image, creating silent audio source");
res = (GESTrackObject *) ges_track_audio_test_source_new ();
GST_DEBUG ("Object is still image, not adding any audio source");
return NULL;
} else {
GST_DEBUG ("Creating a GESTrackImageSource");
res = (GESTrackObject *) ges_track_image_source_new (priv->uri);
@ -418,3 +409,20 @@ ges_timeline_filesource_new (gchar * uri)
return res;
}
void
ges_timeline_filesource_set_uri (GESTimelineFileSource * self, gchar * uri)
{
GESTimelineObject *tlobj = GES_TIMELINE_OBJECT (self);
GList *tckobjs = ges_timeline_object_get_track_objects (tlobj);
if (tckobjs) {
/* FIXME handle this case properly */
GST_WARNING_OBJECT (tlobj, "Can not change uri when already"
"containing TrackObjects");
return;
}
self->priv->uri = uri;
}

View file

@ -1,6 +1,7 @@
/* GStreamer Editing Services
* Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
* 2009 Nokia Corporation
* 2011 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -20,7 +21,7 @@
/**
* SECTION:ges-timeline-layer
* @short_description: Non-overlaping sequence of #GESTimelineObject
* @short_description: Non-overlapping sequence of #GESTimelineObject
*
* Responsible for the ordering of the various contained TimelineObject(s). A
* timeline layer has a "priority" property, which is used to manage the
@ -32,23 +33,48 @@
#include "gesmarshal.h"
#include "ges-timeline-layer.h"
#include "ges.h"
#include "ges-timeline-source.h"
#define LAYER_HEIGHT 1000
static void
track_object_removed_cb (GESTimelineObject * object,
GESTrackObject * track_object);
static void track_object_added_cb (GESTimelineObject * object,
GESTrackObject * track_object, GHashTable * signal_table);
static void track_object_changed_cb (GESTrackObject * track_object,
GParamSpec * arg G_GNUC_UNUSED);
static void calculate_transitions (GESTrackObject * track_object);
static void calculate_next_transition (GESTrackObject * track_object,
GESTimelineLayer * layer);
static void
timeline_object_height_changed_cb (GESTimelineObject * obj,
GESTrackEffect * tr_eff, GESTimelineObject * second_obj);
G_DEFINE_TYPE (GESTimelineLayer, ges_timeline_layer, G_TYPE_INITIALLY_UNOWNED);
struct _GESTimelineLayerPrivate
{
/*< private > */
GSList *objects_start; /* The TimelineObjects sorted by start and
GList *objects_start; /* The TimelineObjects sorted by start and
* priority */
guint32 priority; /* The priority of the layer within the
* containing timeline */
gboolean auto_transition;
GHashTable *signal_table;
};
enum
{
PROP_0,
PROP_PRIORITY,
PROP_AUTO_TRANSITION,
PROP_LAST
};
enum
@ -62,6 +88,11 @@ static guint ges_timeline_layer_signals[LAST_SIGNAL] = { 0 };
static gboolean ges_timeline_layer_resync_priorities (GESTimelineLayer * layer);
static GList *track_get_by_layer (GESTimelineLayer * layer, GESTrack * track);
static void compare (GList * compared, GESTrackObject * track_object,
gboolean ahead);
static void
ges_timeline_layer_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
@ -72,6 +103,9 @@ ges_timeline_layer_get_property (GObject * object, guint property_id,
case PROP_PRIORITY:
g_value_set_uint (value, layer->priv->priority);
break;
case PROP_AUTO_TRANSITION:
g_value_set_boolean (value, layer->priv->auto_transition);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -87,6 +121,10 @@ ges_timeline_layer_set_property (GObject * object, guint property_id,
case PROP_PRIORITY:
ges_timeline_layer_set_priority (layer, g_value_get_uint (value));
break;
case PROP_AUTO_TRANSITION:
ges_timeline_layer_set_auto_transition (layer,
g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -130,6 +168,15 @@ ges_timeline_layer_class_init (GESTimelineLayerClass * klass)
g_param_spec_uint ("priority", "Priority",
"The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
/**
* GESTimelineLayer:auto-transition
*
* Sets whether transitions are added automagically when timeline objects overlap.
*/
g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
g_param_spec_boolean ("auto-transition", "Auto-Transition",
"whether the transitions are added", FALSE, G_PARAM_READWRITE));
/**
* GESTimelineLayer::object-added
* @layer: the #GESTimelineLayer
@ -155,7 +202,6 @@ ges_timeline_layer_class_init (GESTimelineLayerClass * klass)
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass,
object_removed), NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
GES_TYPE_TIMELINE_OBJECT);
}
static void
@ -164,10 +210,13 @@ ges_timeline_layer_init (GESTimelineLayer * self)
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
GES_TYPE_TIMELINE_LAYER, GESTimelineLayerPrivate);
/* TODO : Keep those 3 values in sync */
self->priv->priority = 0;
self->priv->auto_transition = FALSE;
self->min_gnl_priority = 0;
self->max_gnl_priority = 9;
self->max_gnl_priority = LAYER_HEIGHT;
self->priv->signal_table =
g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref,
NULL);
}
/**
@ -209,6 +258,27 @@ objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
return 0;
}
static GList *
track_get_by_layer (GESTimelineLayer * layer, GESTrack * track)
{
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);
if (ges_timeline_object_get_layer (tl_obj) == layer) {
/* We steal the reference from tck_objects_list */
return_list = g_list_append (return_list, tmp->data);
} else
g_object_unref (tmp->data);
}
return return_list;
}
/**
* ges_timeline_layer_add_object:
* @layer: a #GESTimelineLayer
@ -222,12 +292,12 @@ objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
* Returns: TRUE if the object was properly added to the layer, or FALSE
* if the @layer refuses to add the object.
*/
gboolean
ges_timeline_layer_add_object (GESTimelineLayer * layer,
GESTimelineObject * object)
{
GESTimelineLayer *tl_obj_layer;
guint32 maxprio, minprio, prio;
GST_DEBUG ("layer:%p, object:%p", layer, object);
@ -243,9 +313,19 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer,
/* Take a reference to the object and store it stored by start/priority */
layer->priv->objects_start =
g_slist_insert_sorted (layer->priv->objects_start, object,
g_list_insert_sorted (layer->priv->objects_start, object,
(GCompareFunc) objects_start_compare);
/* We have to wait for the track objects to be created to calculate transitions */
if (layer->priv->auto_transition) {
if (GES_IS_TIMELINE_SOURCE (object)) {
g_signal_connect (G_OBJECT (object), "track-object-added",
G_CALLBACK (track_object_added_cb), layer->priv->signal_table);
g_signal_connect (G_OBJECT (object), "track-object-removed",
G_CALLBACK (track_object_removed_cb), NULL);
}
}
/* Inform the object it's now in this layer */
ges_timeline_object_set_layer (object, layer);
@ -254,13 +334,19 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer,
layer->min_gnl_priority, layer->max_gnl_priority);
/* Set the priority. */
if (GES_TIMELINE_OBJECT_PRIORITY (object) > (layer->max_gnl_priority)) {
ges_timeline_object_set_priority (object, layer->max_gnl_priority);
maxprio = layer->max_gnl_priority;
minprio = layer->min_gnl_priority;
prio = GES_TIMELINE_OBJECT_PRIORITY (object);
if (minprio + prio > (maxprio)) {
GST_WARNING ("%p is out of the layer %p space, setting its priority to "
"setting its priority %d to failthe maximum priority of the layer %d",
object, layer, prio, maxprio - minprio);
ges_timeline_object_set_priority (object, LAYER_HEIGHT - 1);
}
/* If the object has an acceptable priority, we just let it with its current
* priority */
else if (GES_TIMELINE_OBJECT_PRIORITY (object) < (layer->min_gnl_priority)) {
ges_timeline_object_set_priority (object, layer->min_gnl_priority);
}
ges_timeline_layer_resync_priorities (layer);
/* emit 'object-added' */
g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_ADDED], 0, object);
@ -268,6 +354,412 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer,
return TRUE;
}
static void
track_object_duration_cb (GESTrackObject * track_object,
GParamSpec * arg G_GNUC_UNUSED)
{
GESTimelineLayer *layer;
GESTimelineObject *tlobj;
tlobj = ges_track_object_get_timeline_object (track_object);
layer = ges_timeline_object_get_layer (tlobj);
if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
GST_DEBUG ("Here we should recalculate");
calculate_next_transition (track_object, layer);
}
static void
track_object_deleted_cb (GESTrack * track, GESTrackObject * track_object)
{
GList *track_objects, *tmp, *cur;
GESTimelineLayer *layer;
track_objects = ges_track_get_objects (track);
cur = g_list_find (track_objects, track_object);
for (tmp = cur->next; tmp; tmp = tmp->next) {
if (GES_IS_TRACK_SOURCE (tmp->data)) {
break;
}
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
layer =
ges_timeline_object_get_layer (ges_track_object_get_timeline_object
(tmp->data));
if (ges_timeline_layer_get_auto_transition (layer)) {
ges_track_enable_update (track, FALSE);
ges_timeline_layer_remove_object (layer,
ges_track_object_get_timeline_object (tmp->data));
ges_track_enable_update (track, TRUE);
}
g_object_unref (layer);
}
}
for (tmp = cur->prev; tmp; tmp = tmp->prev) {
if (GES_IS_TRACK_SOURCE (tmp->data)) {
break;
}
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
layer =
ges_timeline_object_get_layer (ges_track_object_get_timeline_object
(tmp->data));
if (ges_timeline_layer_get_auto_transition (layer)) {
ges_track_enable_update (track, FALSE);
ges_timeline_layer_remove_object (layer,
ges_track_object_get_timeline_object (tmp->data));
ges_track_enable_update (track, TRUE);
}
g_object_unref (layer);
}
}
g_object_unref (track_object);
}
static void
track_object_added_cb (GESTimelineObject * object,
GESTrackObject * track_object, GHashTable * signal_table)
{
GESTrack *track;
gint ptr;
if (GES_IS_TRACK_SOURCE (track_object)) {
g_signal_connect (G_OBJECT (track_object), "notify::start",
G_CALLBACK (track_object_changed_cb), NULL);
g_signal_connect (G_OBJECT (track_object), "notify::duration",
G_CALLBACK (track_object_duration_cb), NULL);
calculate_transitions (track_object);
}
track = ges_track_object_get_track (track_object);
if (!g_hash_table_lookup (signal_table, track)) {
ptr = g_signal_connect (track, "track-object-removed",
(GCallback) track_object_deleted_cb, NULL);
g_hash_table_insert (signal_table, track, GINT_TO_POINTER (ptr));
}
return;
}
static void
track_object_removed_cb (GESTimelineObject * object,
GESTrackObject * track_object)
{
return;
if (GES_IS_TRACK_SOURCE (track_object)) {
g_signal_handlers_disconnect_by_func (track_object, track_object_changed_cb,
object);
}
return;
}
static void
timeline_object_height_changed_cb (GESTimelineObject * obj,
GESTrackEffect * tr_eff, GESTimelineObject * second_obj)
{
gint priority, height;
g_object_get (obj, "height", &height, "priority", &priority, NULL);
g_object_set (second_obj, "priority", priority + height, NULL);
}
static void
track_object_changed_cb (GESTrackObject * track_object,
GParamSpec * arg G_GNUC_UNUSED)
{
if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
calculate_transitions (track_object);
}
static void
calculate_next_transition_with_list (GESTrackObject * track_object,
GList * tckobjs_in_layer, GESTimelineLayer * layer)
{
GList *compared;
if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
return;
if (compared == NULL)
/* This is the last TrackObject of the Track */
return;
do {
compared = compared->next;
if (compared == NULL)
return;
} while (!GES_IS_TRACK_SOURCE (compared->data));
compare (compared, track_object, FALSE);
}
static void
calculate_next_transition (GESTrackObject * track_object,
GESTimelineLayer * layer)
{
GESTrack *track = ges_track_object_get_track (track_object);
GList *tckobjs_in_layer = track_get_by_layer (layer, track);
if (ges_track_object_get_track (track_object)) {
calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
}
g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
g_list_free (tckobjs_in_layer);
}
static void
calculate_transitions (GESTrackObject * track_object)
{
GList *tckobjs_in_layer, *compared;
GESTrack *track = ges_track_object_get_track (track_object);
GESTimelineLayer *layer;
GESTimelineObject *tlobj;
tlobj = ges_track_object_get_timeline_object (track_object);
layer = ges_timeline_object_get_layer (tlobj);
tckobjs_in_layer = track_get_by_layer (layer, track);
if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
return;
do {
compared = compared->prev;
if (compared == NULL) {
/* Nothing before, let's check after */
calculate_next_transition_with_list (track_object, tckobjs_in_layer,
layer);
goto done;
}
} while (!GES_IS_TRACK_SOURCE (compared->data));
compare (compared, track_object, TRUE);
calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
done:
g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
g_list_free (tckobjs_in_layer);
}
/* Compare:
* @compared: The #GList of #GESTrackObjects that we compare with @track_object
* @track_object: The #GESTrackObject that serves as a reference
* @ahead: %TRUE if we are comparing frontward %FALSE if we are comparing
* backward*/
static void
compare (GList * compared, GESTrackObject * track_object, gboolean ahead)
{
GList *tmp;
gint64 start, duration, compared_start, compared_duration, end, compared_end,
tr_start, tr_duration;
GESTimelineStandardTransition *trans = NULL;
GESTrack *track;
GESTimelineLayer *layer;
GESTimelineObject *object, *compared_object, *first_object, *second_object;
gint priority;
g_return_if_fail (compared);
GST_DEBUG ("Recalculating transitions");
object = ges_track_object_get_timeline_object (track_object);
if (!object) {
GST_WARNING ("Trackobject not in a timeline object: "
"Can not calulate transitions");
return;
}
compared_object = ges_track_object_get_timeline_object (compared->data);
layer = ges_timeline_object_get_layer (object);
start = ges_track_object_get_start (track_object);
duration = ges_track_object_get_duration (track_object);
compared_start = ges_track_object_get_start (compared->data);
compared_duration = ges_track_object_get_duration (compared->data);
end = start + duration;
compared_end = compared_start + compared_duration;
if (ahead) {
/* Make sure we remove the last transition we created it is not needed
* FIXME make it a smarter way */
if (compared->prev && GES_IS_TRACK_TRANSITION (compared->prev->data)) {
trans = GES_TIMELINE_STANDARD_TRANSITION
(ges_track_object_get_timeline_object (compared->prev->data));
g_object_get (compared->prev->data, "start", &tr_start, "duration",
&tr_duration, NULL);
if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
trans = NULL;
}
for (tmp = compared->next; tmp; tmp = tmp->next) {
/* If we have a transitionmnmnm we recaluculuculate its values */
if (GES_IS_TRACK_TRANSITION (tmp->data)) {
g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
NULL);
if (tr_start + tr_duration == compared_start + compared_duration) {
GESTimelineObject *tlobj;
tlobj = ges_track_object_get_timeline_object (tmp->data);
trans = GES_TIMELINE_STANDARD_TRANSITION (tlobj);
break;
}
}
}
if (compared_end <= start) {
if (trans) {
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
g_object_get (compared_object, "priority", &priority, NULL);
g_object_set (object, "priority", priority, NULL);
}
goto clean;
} else if (start > compared_start && end < compared_end) {
if (trans) {
/* Transition not needed anymore */
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
}
goto clean;
} else if (start <= compared_start) {
if (trans) {
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
}
goto clean;
}
} else {
if (compared->next && GES_IS_TRACK_TRANSITION (compared->next->data)) {
trans = GES_TIMELINE_STANDARD_TRANSITION
(ges_track_object_get_timeline_object (compared->next->data));
g_object_get (compared->prev->data, "start", &tr_start, "duration",
&tr_duration, NULL);
if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
trans = NULL;
}
for (tmp = compared->prev; tmp; tmp = tmp->prev) {
if GES_IS_TRACK_TRANSITION
(tmp->data) {
g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
NULL);
if (tr_start == compared_start) {
trans = GES_TIMELINE_STANDARD_TRANSITION
(ges_track_object_get_timeline_object (tmp->data));
break;
}
}
}
if (start + duration <= compared_start) {
if (trans) {
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
g_object_get (object, "priority", &priority, NULL);
g_object_set (compared_object, "priority", priority, NULL);
}
goto clean;
} else if (start > compared_start) {
if (trans)
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
goto clean;
} else if (start < compared_start && end > compared_end) {
if (trans) {
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
}
goto clean;
}
}
if (!trans) {
gint height;
trans =
ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade");
track = ges_track_object_get_track (track_object);
ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (trans),
track->type);
ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (trans));
if (ahead) {
first_object = ges_track_object_get_timeline_object (compared->data);
second_object = object;
} else {
second_object = ges_track_object_get_timeline_object (compared->data);
first_object = object;
}
g_object_get (first_object, "priority", &priority, "height", &height, NULL);
g_object_set (second_object, "priority", priority + height, NULL);
g_signal_connect (first_object, "notify::height",
(GCallback) timeline_object_height_changed_cb, second_object);
}
if (ahead) {
g_object_set (trans, "start", start, "duration",
compared_duration + compared_start - start, NULL);
} else {
g_object_set (trans, "start", compared_start, "duration",
start + duration - compared_start, NULL);
}
clean:
g_object_unref (layer);
}
static void
look_for_transition (GESTrackObject * track_object, GESTimelineLayer * layer)
{
GESTrack *track;
GList *track_objects, *tmp, *cur;
track = ges_track_object_get_track (track_object);
track_objects = ges_track_get_objects (track);
cur = g_list_find (track_objects, track_object);
for (tmp = cur->next; tmp; tmp = tmp->next) {
if (GES_IS_TRACK_SOURCE (tmp->data)) {
break;
}
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
ges_timeline_layer_remove_object (layer,
ges_track_object_get_timeline_object (tmp->data));
}
}
for (tmp = cur->prev; tmp; tmp = tmp->prev) {
if (GES_IS_TRACK_SOURCE (tmp->data)) {
break;
}
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
ges_timeline_layer_remove_object (layer,
ges_track_object_get_timeline_object (tmp->data));
}
}
g_list_foreach (track_objects, (GFunc) g_object_unref, NULL);
g_list_free (track_objects);
}
static gboolean
disconnect_handlers (GESTrack * track, gpointer * ptr)
{
g_signal_handler_disconnect (track, GPOINTER_TO_INT (ptr));
return TRUE;
}
/**
* ges_timeline_layer_remove_object:
* @layer: a #GESTimelineLayer
@ -286,6 +778,7 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
GESTimelineObject * object)
{
GESTimelineLayer *tl_obj_layer;
GList *trackobjects, *tmp;
g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
@ -301,6 +794,20 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
}
g_object_unref (tl_obj_layer);
if (layer->priv->auto_transition) {
trackobjects = ges_timeline_object_get_track_objects (object);
for (tmp = trackobjects; tmp; tmp = tmp->next) {
look_for_transition (tmp->data, layer);
}
g_list_foreach (trackobjects, (GFunc) g_object_unref, NULL);
g_list_free (trackobjects);
}
g_hash_table_foreach_remove (layer->priv->signal_table,
(GHRFunc) disconnect_handlers, NULL);
/* emit 'object-removed' */
g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
@ -309,7 +816,7 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
/* Remove it from our list of controlled objects */
layer->priv->objects_start =
g_slist_remove (layer->priv->objects_start, object);
g_list_remove (layer->priv->objects_start, object);
/* Remove our reference to the object */
g_object_unref (object);
@ -327,18 +834,18 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
gboolean
ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
{
GSList *tmp;
GList *tmp;
GESTimelineObject *obj;
GST_DEBUG ("Resync priorities of %p", layer);
/* TODO : Inhibit composition updates while doing this.
* Ideally we want to do it from an even higher level, but here will
* do in the meantime. */
/* TODO : This is the dumb version where we put everything linearly,
* will need to be adjusted for more complex usages (like with
* transitions). */
for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
ges_timeline_object_set_priority ((GESTimelineObject *) tmp->data,
layer->min_gnl_priority);
obj = GES_TIMELINE_OBJECT (tmp->data);
ges_timeline_object_set_priority (obj, GES_TIMELINE_OBJECT_PRIORITY (obj));
}
return TRUE;
@ -361,14 +868,60 @@ ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority)
if (priority != layer->priv->priority) {
layer->priv->priority = priority;
layer->min_gnl_priority = (priority * 10);
layer->max_gnl_priority = ((priority + 1) * 10) - 1;
layer->min_gnl_priority = (priority * LAYER_HEIGHT);
layer->max_gnl_priority = ((priority + 1) * LAYER_HEIGHT) - 1;
/* FIXME : Update controlled object's gnl priority accordingly */
ges_timeline_layer_resync_priorities (layer);
}
}
/**
* ges_timeline_layer_get_auto_transition:
* @layer: a #GESTimelineLayer
*
* Gets whether transitions are automatically added when objects
* overlap or not.
*
* Returns: %TRUE if transitions are automatically added, else %FALSE.
*/
gboolean
ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer)
{
g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
return layer->priv->auto_transition;
}
/**
* ges_timeline_layer_set_auto_transition:
* @layer: a #GESTimelineLayer
* @auto_transition: whether the auto_transition is active
*
* Sets the layer to the given @auto_transition. See the documentation of the
* property auto_transition for more information.
*/
void
ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
gboolean auto_transition)
{
GList *tmp;
g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
if (auto_transition) {
for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
if (GES_IS_TIMELINE_SOURCE (tmp->data)) {
g_signal_connect (G_OBJECT (tmp->data), "track-object-added",
G_CALLBACK (track_object_added_cb), layer);
g_signal_connect (G_OBJECT (tmp->data), "track-object-removed",
G_CALLBACK (track_object_removed_cb), layer);
}
}
/* FIXME calculate all the transitions at that time */
}
layer->priv->auto_transition = auto_transition;
}
/**
* ges_timeline_layer_get_priority:
* @layer: a #GESTimelineLayer
@ -400,7 +953,7 @@ GList *
ges_timeline_layer_get_objects (GESTimelineLayer * layer)
{
GList *ret = NULL;
GSList *tmp;
GList *tmp;
GESTimelineLayerClass *klass;
g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);

View file

@ -102,7 +102,14 @@ gboolean ges_timeline_layer_remove_object (GESTimelineLayer * layer,
void ges_timeline_layer_set_priority (GESTimelineLayer * layer,
guint priority);
guint ges_timeline_layer_get_priority (GESTimelineLayer * layer);
guint ges_timeline_layer_get_priority (GESTimelineLayer * layer);
gboolean ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer);
void ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
gboolean auto_transition);
GList* ges_timeline_layer_get_objects (GESTimelineLayer * layer);
G_END_DECLS

View file

@ -56,8 +56,28 @@ track_object_priority_changed_cb (GESTrackObject * child,
GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
static void update_height (GESTimelineObject * object);
void
tck_object_added_cb (GESTimelineObject * object,
GESTrackObject * track_object, GList * track_objects);
static gint sort_track_effects (gpointer a, gpointer b,
GESTimelineObject * object);
static void
get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
guint32 * layer_max_gnl_prio);
static gboolean
ges_timeline_object_set_start_internal (GESTimelineObject * object,
guint64 start);
static gboolean ges_timeline_object_set_inpoint_internal (GESTimelineObject *
object, guint64 inpoint);
static gboolean ges_timeline_object_set_duration_internal (GESTimelineObject *
object, guint64 duration);
static gboolean ges_timeline_object_set_priority_internal (GESTimelineObject *
object, guint32 priority);
static GESTimelineObject *ges_timeline_object_copy (GESTimelineObject * object,
gboolean * deep);
G_DEFINE_ABSTRACT_TYPE (GESTimelineObject, ges_timeline_object,
G_TYPE_INITIALLY_UNOWNED);
@ -87,6 +107,8 @@ enum
{
EFFECT_ADDED,
EFFECT_REMOVED,
TRACK_OBJECT_ADDED,
TRACK_OBJECT_REMOVED,
LAST_SIGNAL
};
@ -106,10 +128,16 @@ struct _GESTimelineObjectPrivate
* properties so we don't end up in infinite property update loops
*/
gboolean ignore_notifies;
gboolean is_moving;
GList *mappings;
guint nb_effects;
GESTrackObject *initiated_move;
/* The formats supported by this TimelineObject */
GESTrackType supportedformats;
};
enum
@ -121,6 +149,7 @@ enum
PROP_PRIORITY,
PROP_HEIGHT,
PROP_LAYER,
PROP_SUPPORTED_FORMATS,
PROP_LAST
};
@ -151,6 +180,9 @@ ges_timeline_object_get_property (GObject * object, guint property_id,
case PROP_LAYER:
g_value_set_object (value, tobj->priv->layer);
break;
case PROP_SUPPORTED_FORMATS:
g_value_set_flags (value, tobj->priv->supportedformats);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -164,16 +196,23 @@ ges_timeline_object_set_property (GObject * object, guint property_id,
switch (property_id) {
case PROP_START:
ges_timeline_object_set_start (tobj, g_value_get_uint64 (value));
ges_timeline_object_set_start_internal (tobj, g_value_get_uint64 (value));
break;
case PROP_INPOINT:
ges_timeline_object_set_inpoint (tobj, g_value_get_uint64 (value));
ges_timeline_object_set_inpoint_internal (tobj,
g_value_get_uint64 (value));
break;
case PROP_DURATION:
ges_timeline_object_set_duration (tobj, g_value_get_uint64 (value));
ges_timeline_object_set_duration_internal (tobj,
g_value_get_uint64 (value));
break;
case PROP_PRIORITY:
ges_timeline_object_set_priority (tobj, g_value_get_uint (value));
ges_timeline_object_set_priority_internal (tobj,
g_value_get_uint (value));
break;
case PROP_SUPPORTED_FORMATS:
ges_timeline_object_set_supported_formats (tobj,
g_value_get_flags (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@ -251,6 +290,21 @@ ges_timeline_object_class_init (GESTimelineObjectClass * klass)
g_object_class_install_property (object_class, PROP_HEIGHT,
properties[PROP_HEIGHT]);
/**
* GESTimelineObject:supported-formats:
*
* The formats supported by the object.
*
* Since: 0.10.XX
*/
properties[PROP_SUPPORTED_FORMATS] = g_param_spec_flags ("supported-formats",
"Supported formats", "Formats supported by the file",
GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
properties[PROP_SUPPORTED_FORMATS]);
/**
* GESTimelineObject:layer
*
@ -290,6 +344,34 @@ ges_timeline_object_class_init (GESTimelineObjectClass * klass)
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GES_TYPE_TRACK_EFFECT);
/**
* GESTimelineObject::track-object-added
* @object: the #GESTimelineObject
* @tckobj: the #GESTrackObject that was added.
*
* Will be emitted after a track object was added to the object.
*
* Since: 0.10.2
*/
ges_timeline_object_signals[TRACK_OBJECT_ADDED] =
g_signal_new ("track-object-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
/**
* GESTimelineObject::track-object-removed
* @object: the #GESTimelineObject
* @tckobj: the #GESTrackObject that was removed.
*
* Will be emitted after a track object was removed from @object.
*
* Since: 0.10.2
*/
ges_timeline_object_signals[TRACK_OBJECT_REMOVED] =
g_signal_new ("track-object-removed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
klass->need_fill_track = TRUE;
}
@ -303,6 +385,7 @@ ges_timeline_object_init (GESTimelineObject * self)
self->priv->trackobjects = NULL;
self->priv->layer = NULL;
self->priv->nb_effects = 0;
self->priv->is_moving = FALSE;
}
/**
@ -325,6 +408,9 @@ ges_timeline_object_create_track_object (GESTimelineObject * object,
GESTimelineObjectClass *class;
GESTrackObject *res;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
class = GES_TIMELINE_OBJECT_GET_CLASS (object);
if (G_UNLIKELY (class->create_track_object == NULL)) {
@ -333,7 +419,6 @@ ges_timeline_object_create_track_object (GESTimelineObject * object,
}
res = class->create_track_object (object, track);
ges_timeline_object_add_track_object (object, res);
return res;
}
@ -358,13 +443,15 @@ ges_timeline_object_create_track_objects (GESTimelineObject * object,
{
GESTimelineObjectClass *klass;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
if (!(klass->create_track_objects)) {
GST_WARNING ("no GESTimelineObject::create_track_objects implentation");
return FALSE;
}
return klass->create_track_objects (object, track);
}
@ -376,13 +463,17 @@ ges_timeline_object_create_track_objects_func (GESTimelineObject * object,
GESTrack * track)
{
GESTrackObject *result;
gboolean ret;
result = ges_timeline_object_create_track_object (object, track);
if (!result) {
GST_WARNING ("couldn't create track object");
GST_DEBUG ("Did not create track object");
return FALSE;
}
return ges_track_add_object (track, result);
ges_track_object_set_timeline_object (result, object);
ret = ges_track_add_object (track, result);
ges_timeline_object_add_track_object (object, result);
return ret;
}
/**
@ -404,10 +495,14 @@ ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
{
ObjectMapping *mapping;
GList *tmp;
guint max_prio, min_prio;
GESTimelineObjectPrivate *priv = object->priv;
gboolean is_effect = GES_IS_TRACK_EFFECT (trobj);
GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
g_return_val_if_fail (GES_IS_TRACK_OBJECT (trobj), FALSE);
GST_LOG ("Got a TrackObject : %p , setting the timeline object as its"
"creator. Is a TrackEffect %i", trobj, is_effect);
@ -415,6 +510,7 @@ ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
return FALSE;
ges_track_object_set_timeline_object (trobj, object);
g_object_ref (trobj);
mapping = g_slice_new0 (ObjectMapping);
@ -451,10 +547,6 @@ ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
}
priv->nb_effects++;
/* emit 'effect-added' */
g_signal_emit (object, ges_timeline_object_signals[EFFECT_ADDED], 0,
GES_TRACK_EFFECT (trobj));
}
object->priv->trackobjects =
@ -487,10 +579,19 @@ ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
g_signal_connect (G_OBJECT (trobj), "notify::priority",
G_CALLBACK (track_object_priority_changed_cb), object);
ges_track_object_set_priority (trobj,
object->priority + mapping->priority_offset);
get_layer_priorities (priv->layer, &min_prio, &max_prio);
ges_track_object_set_priority (trobj, min_prio + object->priority
+ mapping->priority_offset);
GST_DEBUG ("Returning trobj:%p", trobj);
if (!GES_IS_TRACK_PARSE_LAUNCH_EFFECT (trobj)) {
g_signal_emit (object, ges_timeline_object_signals[TRACK_OBJECT_ADDED], 0,
GES_TRACK_OBJECT (trobj));
} else {
/* emit 'effect-added' */
g_signal_emit (object, ges_timeline_object_signals[EFFECT_ADDED], 0,
GES_TRACK_EFFECT (trobj));
}
return TRUE;
}
@ -512,6 +613,9 @@ ges_timeline_object_release_track_object (GESTimelineObject * object,
ObjectMapping *mapping = NULL;
GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
g_return_val_if_fail (GES_IS_TRACK_OBJECT (trackobject), FALSE);
GST_DEBUG ("object:%p, trackobject:%p", object, trackobject);
if (!(g_list_find (object->priv->trackobjects, trackobject))) {
@ -545,7 +649,9 @@ ges_timeline_object_release_track_object (GESTimelineObject * object,
/* emit 'object-removed' */
g_signal_emit (object, ges_timeline_object_signals[EFFECT_REMOVED], 0,
GES_TRACK_EFFECT (trackobject));
}
} else
g_signal_emit (object, ges_timeline_object_signals[TRACK_OBJECT_REMOVED], 0,
GES_TRACK_OBJECT (trackobject));
ges_track_object_set_timeline_object (trackobject, NULL);
@ -621,20 +727,16 @@ find_object_mapping (GESTimelineObject * object, GESTrackObject * child)
return NULL;
}
/**
* ges_timeline_object_set_start:
* @object: a #GESTimelineObject
* @start: the position in #GstClockTime
*
* Set the position of the object in its containing layer
*/
void
ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
static gboolean
ges_timeline_object_set_start_internal (GESTimelineObject * object,
guint64 start)
{
GList *tmp;
GESTrackObject *tr;
ObjectMapping *map;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
object, GST_TIME_ARGS (start));
@ -644,9 +746,17 @@ ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
tr = (GESTrackObject *) tmp->data;
map = find_object_mapping (object, tr);
if (ges_track_object_is_locked (tr)) {
if (ges_track_object_is_locked (tr) && tr != object->priv->initiated_move) {
gint64 new_start = start - map->start_offset;
/* Move the child... */
ges_track_object_set_start (tr, start + map->start_offset);
if (new_start < 0) {
GST_ERROR ("Trying to set start to a negative value %" GST_TIME_FORMAT,
GST_TIME_ARGS (-(start + map->start_offset)));
continue;
}
ges_track_object_set_start (tr, new_start);
} else {
/* ... or update the offset */
map->start_offset = start - tr->start;
@ -656,22 +766,36 @@ ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
object->priv->ignore_notifies = FALSE;
object->start = start;
return TRUE;
}
/**
* ges_timeline_object_set_inpoint:
* ges_timeline_object_set_start:
* @object: a #GESTimelineObject
* @inpoint: the in-point in #GstClockTime
* @start: the position in #GstClockTime
*
* Set the in-point, that is the moment at which the @object will start
* outputting data from its contents.
* Set the position of the object in its containing layer
*/
void
ges_timeline_object_set_inpoint (GESTimelineObject * object, guint64 inpoint)
ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
{
if (ges_timeline_object_set_start_internal (object, start))
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
#else
g_object_notify (G_OBJECT (object), "start");
#endif
}
static gboolean
ges_timeline_object_set_inpoint_internal (GESTimelineObject * object,
guint64 inpoint)
{
GList *tmp;
GESTrackObject *tr;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
object, GST_TIME_ARGS (inpoint));
@ -684,21 +808,37 @@ ges_timeline_object_set_inpoint (GESTimelineObject * object, guint64 inpoint)
}
object->inpoint = inpoint;
return TRUE;
}
/**
* ges_timeline_object_set_duration:
* ges_timeline_object_set_inpoint:
* @object: a #GESTimelineObject
* @duration: the duration in #GstClockTime
* @inpoint: the in-point in #GstClockTime
*
* Set the duration of the object
* Set the in-point, that is the moment at which the @object will start
* outputting data from its contents.
*/
void
ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
ges_timeline_object_set_inpoint (GESTimelineObject * object, guint64 inpoint)
{
if (ges_timeline_object_set_inpoint_internal (object, inpoint))
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
#else
g_object_notify (G_OBJECT (object), "in-point");
#endif
}
static gboolean
ges_timeline_object_set_duration_internal (GESTimelineObject * object,
guint64 duration)
{
GList *tmp;
GESTrackObject *tr;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
object, GST_TIME_ARGS (duration));
@ -711,6 +851,162 @@ ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
}
object->duration = duration;
return TRUE;
}
/**
* ges_timeline_object_set_duration:
* @object: a #GESTimelineObject
* @duration: the duration in #GstClockTime
*
* Set the duration of the object
*/
void
ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
{
if (ges_timeline_object_set_duration_internal (object, duration))
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
#else
g_object_notify (G_OBJECT (object), "duration");
#endif
}
static gboolean
ges_timeline_object_set_priority_internal (GESTimelineObject * object,
guint priority)
{
GList *tmp;
GESTrackObject *tr;
ObjectMapping *map;
GESTimelineObjectPrivate *priv;
guint32 layer_min_gnl_prio, layer_max_gnl_prio;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
priv = object->priv;
priv->ignore_notifies = TRUE;
object->priv->ignore_notifies = TRUE;
get_layer_priorities (priv->layer, &layer_min_gnl_prio, &layer_max_gnl_prio);
for (tmp = priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
tr = (GESTrackObject *) tmp->data;
map = find_object_mapping (object, tr);
if (ges_track_object_is_locked (tr)) {
guint32 real_tck_prio;
/* Move the child... */
real_tck_prio = layer_min_gnl_prio + priority + map->priority_offset;
if (real_tck_prio > layer_max_gnl_prio) {
GST_WARNING ("%p priority of %i, is outside of the its containing "
"layer space. (%d/%d) setting it to the maximum it can be", object,
priority, layer_min_gnl_prio, layer_max_gnl_prio);
real_tck_prio = layer_max_gnl_prio;
}
ges_track_object_set_priority (tr, real_tck_prio);
} else {
/* ... or update the offset */
map->priority_offset = tr->priority - layer_min_gnl_prio + priority;
}
}
priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
(GCompareDataFunc) sort_track_effects, object);
priv->ignore_notifies = FALSE;
object->priority = priority;
return TRUE;
}
/**
* ges_timeline_object_set_moving_from_layer:
* @object: a #GESTimelineObject
* @is_moving: %TRUE if you want to start moving @object to another layer
* %FALSE when you finished moving it.
*
* Sets the object in a moving to layer state. You might rather use the
* ges_timeline_object_move_to_layer function to move #GESTimelineObject-s
* from a layer to another.
**/
void
ges_timeline_object_set_moving_from_layer (GESTimelineObject * object,
gboolean is_moving)
{
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
object->priv->is_moving = is_moving;
}
/**
* ges_timeline_object_is_moving_from_layer:
* @object: a #GESTimelineObject
*
* Tells you if the object is currently moving from a layer to another.
* You might rather use the ges_timeline_object_move_to_layer function to
* move #GESTimelineObject-s from a layer to another.
*
*
* Returns: %TRUE if @object is currently moving from its current layer
* %FALSE otherwize
**/
gboolean
ges_timeline_object_is_moving_from_layer (GESTimelineObject * object)
{
return object->priv->is_moving;
}
/**
* ges_timeline_object_move_to_layer:
* @object: a #GESTimelineObject
* @layer: the new #GESTimelineLayer
*
* Moves @object to @layer. If @object is not in any layer, it adds it to
* @layer, else, it removes it from its current layer, and adds it to @layer.
*
* Returns: %TRUE if @object could be moved %FALSE otherwize
*/
gboolean
ges_timeline_object_move_to_layer (GESTimelineObject * object, GESTimelineLayer
* layer)
{
gboolean ret;
GESTimelineLayer *current_layer;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
current_layer = object->priv->layer;
if (current_layer == NULL) {
GST_DEBUG ("Not moving %p, only adding it to %p", object, layer);
return ges_timeline_layer_add_object (layer, object);
}
object->priv->is_moving = TRUE;
g_object_ref (object);
ret = ges_timeline_layer_remove_object (current_layer, object);
if (!ret) {
g_object_unref (object);
return FALSE;
}
ret = ges_timeline_layer_add_object (layer, object);
object->priv->is_moving = FALSE;
g_object_unref (object);
return ret;
}
/**
@ -723,33 +1019,12 @@ ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
void
ges_timeline_object_set_priority (GESTimelineObject * object, guint priority)
{
GList *tmp;
GESTrackObject *tr;
ObjectMapping *map;
GESTimelineObjectPrivate *priv = object->priv;
GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
priv->ignore_notifies = TRUE;
for (tmp = priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
tr = (GESTrackObject *) tmp->data;
map = find_object_mapping (object, tr);
if (ges_track_object_is_locked (tr)) {
/* Move the child... */
ges_track_object_set_priority (tr, priority + map->priority_offset);
} else {
/* ... or update the offset */
map->priority_offset = priority - tr->priority;
}
}
priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
(GCompareDataFunc) sort_track_effects, object);
priv->ignore_notifies = FALSE;
object->priority = priority;
if (ges_timeline_object_set_priority_internal (object, priority))
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
#else
g_object_notify (G_OBJECT (object), "priority");
#endif
}
/**
@ -889,12 +1164,15 @@ ges_timeline_object_get_top_effects (GESTimelineObject * object)
GList *tmp, *ret;
guint i;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
GST_DEBUG_OBJECT (object, "Getting the %i top effects",
object->priv->nb_effects);
ret = NULL;
for (tmp = object->priv->trackobjects, i = 0; i < object->priv->nb_effects;
tmp = tmp->next, i++) {
ret = g_list_append (ret, tmp->data);
g_object_ref (tmp->data);
ret = g_list_append (ret, g_object_ref (tmp->data));
}
return ret;
@ -915,6 +1193,8 @@ gint
ges_timeline_object_get_top_effect_position (GESTimelineObject * object,
GESTrackEffect * effect)
{
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), -1);
return find_object_mapping (object,
GES_TRACK_OBJECT (effect))->priority_offset;
}
@ -936,12 +1216,18 @@ gboolean
ges_timeline_object_set_top_effect_priority (GESTimelineObject * object,
GESTrackEffect * effect, guint newpriority)
{
GList *tmp;
gint inc;
GESTrackObject *tck_obj = GES_TRACK_OBJECT (effect);
GESTimelineObjectPrivate *priv = object->priv;
GList *tmp;
guint current_prio;
GESTrackObject *tck_obj;
GESTimelineObjectPrivate *priv;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
tck_obj = GES_TRACK_OBJECT (effect);
priv = object->priv;
current_prio = ges_track_object_get_priority (tck_obj);
guint current_prio = ges_track_object_get_priority (tck_obj);
/* We don't change the priority */
if (current_prio == newpriority ||
(G_UNLIKELY (ges_track_object_get_timeline_object (tck_obj) != object)))
@ -979,6 +1265,193 @@ ges_timeline_object_set_top_effect_priority (GESTimelineObject * object,
return TRUE;
}
void
tck_object_added_cb (GESTimelineObject * object,
GESTrackObject * track_object, GList * track_objects)
{
gint64 duration, start, inpoint, position;
GList *tmp;
gboolean locked;
ges_track_object_set_locked (track_object, FALSE);
g_object_get (object, "start", &position, NULL);
for (tmp = track_objects; tmp; tmp = tmp->next) {
if (ges_track_object_get_track (track_object)->type ==
ges_track_object_get_track (tmp->data)->type) {
locked = ges_track_object_is_locked (tmp->data);
ges_track_object_set_locked (tmp->data, FALSE);
g_object_get (tmp->data, "duration", &duration, "start", &start,
"in-point", &inpoint, NULL);
g_object_set (tmp->data, "duration",
duration - (duration + start - position), NULL);
g_object_set (track_object, "start", position, "in-point",
duration - (duration + start - inpoint - position), "duration",
duration + start - position, NULL);
ges_track_object_set_locked (tmp->data, locked);
ges_track_object_set_locked (track_object, locked);
}
}
}
/**
* ges_timeline_object_split:
* @object: the #GESTimelineObject to split
* @position: The position at which to split the @object (in nanosecond)
*
* The function modifies @object, and creates another #GESTimelineObject so
* we have two clips at the end, splitted at the time specified by @position.
*
* Returns: (transfer full): The newly created #GESTimelineObject resulting from
* the splitting
*
* Since: 0.10.XX
*/
GESTimelineObject *
ges_timeline_object_split (GESTimelineObject * object, gint64 position)
{
GList *track_objects, *tmp;
GESTimelineLayer *layer;
GESTimelineObject *new_object;
gint64 duration, start, inpoint;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
g_object_get (object, "duration", &duration, "start", &start, "in-point",
&inpoint, NULL);
track_objects = ges_timeline_object_get_track_objects (object);
layer = ges_timeline_object_get_layer (object);
new_object = ges_timeline_object_copy (object, FALSE);
if (g_list_length (track_objects) == 2) {
g_object_set (new_object, "start", position, NULL);
g_signal_connect (G_OBJECT (new_object), "track-object-added",
G_CALLBACK (tck_object_added_cb), track_objects);
} else {
for (tmp = track_objects; tmp; tmp = tmp->next) {
g_object_set (tmp->data, "duration",
duration - (duration + start - position), NULL);
g_object_set (new_object, "start", position, "in-point",
duration - (duration + start - position), "duration",
(duration + start - position), NULL);
g_object_set (object, "duration",
duration - (duration + start - position), NULL);
}
}
ges_timeline_layer_add_object (layer, new_object);
return new_object;
}
/* TODO implement the deep parameter, and make it public */
static GESTimelineObject *
ges_timeline_object_copy (GESTimelineObject * object, gboolean * deep)
{
GESTimelineObject *ret = NULL;
GParameter *params;
GParamSpec **specs;
guint n, n_specs, n_params;
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
specs =
g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &n_specs);
params = g_new0 (GParameter, n_specs);
n_params = 0;
for (n = 0; n < n_specs; ++n) {
if (strcmp (specs[n]->name, "parent") &&
(specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
params[n_params].name = g_intern_string (specs[n]->name);
g_value_init (&params[n_params].value, specs[n]->value_type);
g_object_get_property (G_OBJECT (object), specs[n]->name,
&params[n_params].value);
++n_params;
}
}
ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
if (GES_IS_TIMELINE_FILE_SOURCE (ret)) {
GList *tck_objects;
tck_objects = ges_timeline_object_get_track_objects (object);
if (g_list_length (tck_objects) == 1) {
GESTrackType type;
type = ges_track_object_get_track (tck_objects->data)->type;
ges_timeline_filesource_set_supported_formats (GES_TIMELINE_FILE_SOURCE
(ret), type);
}
g_list_free (tck_objects);
}
g_free (specs);
g_free (params);
return ret;
}
/**
* ges_timeline_object_set_supported_formats:
* @object: the #GESTimelineObject to set supported formats on
* @supportedformats: the #GESTrackType defining formats supported by @object
*
* Sets the formats supported by the file.
*
* Since: 0.10.XX
*/
void
ges_timeline_object_set_supported_formats (GESTimelineObject * object,
GESTrackType supportedformats)
{
g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
object->priv->supportedformats = supportedformats;
}
/**
* ges_timeline_object_get_supported_formats:
* @object: the #GESTimelineObject
*
* Get the formats supported by @object.
*
* Returns: The formats supported by @object.
*
* Since: 0.10.XX
*/
GESTrackType
ges_timeline_object_get_supported_formats (GESTimelineObject * object)
{
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object),
GES_TRACK_TYPE_UNKNOWN);
return object->priv->supportedformats;
}
/**
* ges_timeline_object_objects_set_locked:
* @object: the #GESTimelineObject
* @locked: whether the #GESTrackObject contained in @object are locked to it.
*
* Set the locking status of all the #GESTrackObject contained in @object to @locked.
* See the ges_track_object_set_locked documentation for more details.
*
* Since: 0.10.XX
*/
void
ges_timeline_object_objects_set_locked (GESTimelineObject * object,
gboolean locked)
{
GList *tmp;
g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
for (tmp = object->priv->mappings; tmp; tmp = g_list_next (tmp)) {
ges_track_object_set_locked (((ObjectMapping *) tmp->data)->object, locked);
}
}
static void
update_height (GESTimelineObject * object)
{
@ -1031,7 +1504,9 @@ track_object_start_changed_cb (GESTrackObject * child,
map->start_offset = object->start - child->start;
} else {
/* Or update the parent start */
object->priv->initiated_move = child;
ges_timeline_object_set_start (object, child->start + map->start_offset);
object->priv->initiated_move = NULL;
}
}
@ -1058,26 +1533,64 @@ track_object_priority_changed_cb (GESTrackObject * child,
GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
{
ObjectMapping *map;
guint32 layer_min_gnl_prio, layer_max_gnl_prio;
guint tck_priority = ges_track_object_get_priority (child);
GST_DEBUG ("Priority changed");
GST_DEBUG ("TrackObject %p priority changed to %i", child,
ges_track_object_get_priority (child));
if (object->priv->ignore_notifies)
return;
update_height (object);
map = find_object_mapping (object, child);
get_layer_priorities (object->priv->layer, &layer_min_gnl_prio,
&layer_max_gnl_prio);
if (G_UNLIKELY (map == NULL))
/* something massively screwed up if we get this */
return;
if (!ges_track_object_is_locked (child)) {
if (tck_priority < layer_min_gnl_prio || tck_priority > layer_max_gnl_prio) {
GST_WARNING ("%p priority of %i, is outside of its containing "
"layer space. (%d/%d). This is a bug in the program.", object,
tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
}
/* Update the internal priority_offset */
map->priority_offset = object->priority - tck_priority;
} else if (tck_priority < object->priority) {
map->priority_offset =
tck_priority - (layer_min_gnl_prio + object->priority);
} else if (tck_priority < layer_min_gnl_prio + object->priority) {
/* Or update the parent priority, the object priority is always the
* highest priority (smaller number) */
if (tck_priority < layer_min_gnl_prio || layer_max_gnl_prio < tck_priority) {
GST_WARNING ("%p priority of %i, is outside of its containing "
"layer space. (%d/%d). This is a bug in the program.", object,
tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
return;
}
ges_timeline_object_set_priority (object,
tck_priority + map->priority_offset);
tck_priority - layer_min_gnl_prio);
}
GST_DEBUG ("object %p priority %d child %p priority %d", object,
object->priority, child, ges_track_object_get_priority (child));
}
static void
get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
guint32 * layer_max_gnl_prio)
{
if (layer) {
*layer_min_gnl_prio = layer->min_gnl_priority;
*layer_max_gnl_prio = layer->max_gnl_priority;
} else {
*layer_min_gnl_prio = 0;
*layer_max_gnl_prio = G_MAXUINT32;
}
}

View file

@ -24,6 +24,7 @@
#include <glib-object.h>
#include <gst/gst.h>
#include <ges/ges-types.h>
#include <ges/ges-track.h>
G_BEGIN_DECLS
@ -210,57 +211,67 @@ struct _GESTimelineObjectClass {
gpointer _ges_reserved[GES_PADDING - 2];
};
GType ges_timeline_object_get_type (void);
GType ges_timeline_object_get_type (void);
/* Setters */
void ges_timeline_object_set_start (GESTimelineObject * object,
void ges_timeline_object_set_start (GESTimelineObject * object,
guint64 start);
void ges_timeline_object_set_inpoint (GESTimelineObject * object,
void ges_timeline_object_set_inpoint (GESTimelineObject * object,
guint64 inpoint);
void ges_timeline_object_set_duration (GESTimelineObject * object,
void ges_timeline_object_set_duration (GESTimelineObject * object,
guint64 duration);
void ges_timeline_object_set_priority (GESTimelineObject * object,
void ges_timeline_object_set_priority (GESTimelineObject * object,
guint priority);
void ges_timeline_object_set_layer (GESTimelineObject * object,
void ges_timeline_object_set_layer (GESTimelineObject * object,
GESTimelineLayer * layer);
/* TrackObject handling */
GESTrackObject *
ges_timeline_object_create_track_object (GESTimelineObject * object,
ges_timeline_object_create_track_object (GESTimelineObject * object,
GESTrack * track);
gboolean
ges_timeline_object_create_track_objects (GESTimelineObject * object,
ges_timeline_object_create_track_objects (GESTimelineObject * object,
GESTrack * track);
gboolean
ges_timeline_object_release_track_object (GESTimelineObject * object,
ges_timeline_object_release_track_object (GESTimelineObject * object,
GESTrackObject * trackobject);
gboolean
ges_timeline_object_fill_track_object (GESTimelineObject * object,
GESTrackObject * trackobj,
GstElement * gnlobj);
ges_timeline_object_fill_track_object (GESTimelineObject * object,
GESTrackObject * trackobj, GstElement * gnlobj);
GESTrackObject *
ges_timeline_object_find_track_object (GESTimelineObject * object,
ges_timeline_object_find_track_object (GESTimelineObject * object,
GESTrack * track, GType type);
GList *
ges_timeline_object_get_track_objects (GESTimelineObject *object);
ges_timeline_object_get_track_objects (GESTimelineObject *object);
gboolean
ges_timeline_object_add_track_object (GESTimelineObject *object,
ges_timeline_object_add_track_object (GESTimelineObject *object,
GESTrackObject *trobj);
/* Layer */
GESTimelineLayer *
ges_timeline_object_get_layer (GESTimelineObject *object);
ges_timeline_object_get_layer (GESTimelineObject *object);
gboolean
ges_timeline_object_move_to_layer (GESTimelineObject *object,
GESTimelineLayer *layer);
gboolean
ges_timeline_object_is_moving_from_layer (GESTimelineObject *object);
void
ges_timeline_object_set_moving_from_layer (GESTimelineObject * object,
gboolean is_moving);
/* Effects */
GList *
ges_timeline_object_get_top_effects (GESTimelineObject *object);
ges_timeline_object_get_top_effects (GESTimelineObject *object);
gint
ges_timeline_object_get_top_effect_position (GESTimelineObject *object,
@ -271,6 +282,18 @@ ges_timeline_object_set_top_effect_priority (GESTimelineObject *object,
GESTrackEffect *effect,
guint newpriority);
GESTrackType
ges_timeline_object_get_supported_formats (GESTimelineObject * object);
void
ges_timeline_object_set_supported_formats (GESTimelineObject * object,
GESTrackType supportedformats);
GESTimelineObject *
ges_timeline_object_split (GESTimelineObject * object, gint64 position);
void
ges_timeline_object_objects_set_locked (GESTimelineObject * object, gboolean locked);
G_END_DECLS
#endif /* _GES_TIMELINE_OBJECT */

View file

@ -44,6 +44,7 @@ typedef struct
GstPad *srcpad; /* Timeline source pad */
GstPad *playsinkpad;
GstPad *encodebinpad;
GstPad *blocked_pad;
} OutputChain;
G_DEFINE_TYPE (GESTimelinePipeline, ges_timeline_pipeline, GST_TYPE_PIPELINE);
@ -380,6 +381,13 @@ no_pad:
}
}
static void
pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
{
/* no nothing */
GST_DEBUG_OBJECT (pad, "blocked callback, blocked: %d", blocked);
}
static void
pad_added_cb (GstElement * timeline, GstPad * pad, GESTimelinePipeline * self)
{
@ -470,7 +478,9 @@ pad_added_cb (GstElement * timeline, GstPad * pad, GESTimelinePipeline * self)
gst_object_unref (tmppad);
goto error;
}
gst_object_unref (tmppad);
chain->blocked_pad = tmppad;
GST_DEBUG ("blocking pad %" GST_PTR_FORMAT, tmppad);
gst_pad_set_blocked_async (tmppad, TRUE, pad_blocked, NULL);
GST_DEBUG ("Reconfiguring playsink");
@ -574,6 +584,13 @@ pad_removed_cb (GstElement * timeline, GstPad * pad, GESTimelinePipeline * self)
gst_object_unref (chain->playsinkpad);
}
if (chain->blocked_pad) {
GST_DEBUG ("unblocking pad %" GST_PTR_FORMAT, chain->blocked_pad);
gst_pad_set_blocked_async (chain->blocked_pad, FALSE, pad_blocked, NULL);
gst_object_unref (chain->blocked_pad);
chain->blocked_pad = NULL;
}
/* Unlike/remove tee */
peer = gst_element_get_static_pad (chain->tee, "sink");
gst_pad_unlink (pad, peer);
@ -587,6 +604,22 @@ pad_removed_cb (GstElement * timeline, GstPad * pad, GESTimelinePipeline * self)
GST_DEBUG ("done");
}
static void
no_more_pads_cb (GstElement * timeline, GESTimelinePipeline * self)
{
GList *tmp;
GST_DEBUG ("received no-more-pads");
for (tmp = self->priv->chains; tmp; tmp = g_list_next (tmp)) {
OutputChain *chain = (OutputChain *) tmp->data;
if (chain->blocked_pad) {
GST_DEBUG ("unblocking pad %" GST_PTR_FORMAT, chain->blocked_pad);
gst_pad_set_blocked_async (chain->blocked_pad, FALSE, pad_blocked, NULL);
}
}
}
/**
* ges_timeline_pipeline_add_timeline:
* @pipeline: a #GESTimelinePipeline
@ -618,6 +651,8 @@ ges_timeline_pipeline_add_timeline (GESTimelinePipeline * pipeline,
g_signal_connect (timeline, "pad-added", (GCallback) pad_added_cb, pipeline);
g_signal_connect (timeline, "pad-removed", (GCallback) pad_removed_cb,
pipeline);
g_signal_connect (timeline, "no-more-pads", (GCallback) no_more_pads_cb,
pipeline);
return TRUE;
}

View file

@ -192,16 +192,21 @@ ges_tl_transition_create_track_object (GESTimelineObject * obj,
GESTimelineStandardTransition *transition =
(GESTimelineStandardTransition *) obj;
GESTrackObject *res;
GESTrackType supportedformats;
GST_DEBUG ("Creating a GESTrackTransition");
if (track->type == GES_TRACK_TYPE_VIDEO) {
supportedformats = ges_timeline_object_get_supported_formats (obj);
if (track->type == GES_TRACK_TYPE_VIDEO &&
(supportedformats == GES_TRACK_TYPE_UNKNOWN ||
supportedformats & GES_TRACK_TYPE_VIDEO)) {
res = GES_TRACK_OBJECT (ges_track_video_transition_new ());
ges_track_video_transition_set_transition_type ((GESTrackVideoTransition *)
res, transition->vtype);
}
else if (track->type == GES_TRACK_TYPE_AUDIO) {
} else if (track->type == GES_TRACK_TYPE_AUDIO &&
(supportedformats == GES_TRACK_TYPE_UNKNOWN ||
supportedformats & GES_TRACK_TYPE_AUDIO)) {
res = GES_TRACK_OBJECT (ges_track_audio_transition_new ());
}

View file

@ -49,8 +49,8 @@ struct _GESTimelineTitleSourcePrivate
gboolean mute;
gchar *text;
gchar *font_desc;
GESTextVAlign halign;
GESTextHAlign valign;
GESTextHAlign halign;
GESTextVAlign valign;
GSList *track_titles;
guint32 color;
gdouble xpos;

View file

@ -43,18 +43,32 @@
#include "ges-timeline-layer.h"
#include "ges.h"
static void track_duration_cb (GstElement * track,
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline);
G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
#define GES_TIMELINE_PENDINGOBJS_GET_LOCK(timeline) \
(GES_TIMELINE(timeline)->priv->pendingobjects_lock)
#define GES_TIMELINE_PENDINGOBJS_LOCK(timeline) \
(g_mutex_lock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
#define GES_TIMELINE_PENDINGOBJS_UNLOCK(timeline) \
(g_mutex_unlock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
struct _GESTimelinePrivate
{
GList *layers; /* A list of GESTimelineLayer sorted by priority */
GList *tracks; /* A list of private track data */
/* The duration of the timeline */
gint64 duration;
/* discoverer used for virgin sources */
GstDiscoverer *discoverer;
/* Objects that are being discovered FIXME : LOCK ! */
GList *pendingobjects;
/* lock to avoid discovery of objects that will be removed */
GMutex *pendingobjects_lock;
/* Whether we are changing state asynchronously or not */
gboolean async_pending;
};
@ -69,12 +83,22 @@ typedef struct
GstPad *ghostpad;
} TrackPrivate;
enum
{
PROP_0,
PROP_DURATION,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
enum
{
TRACK_ADDED,
TRACK_REMOVED,
LAYER_ADDED,
LAYER_REMOVED,
DISCOVERY_ERROR,
LAST_SIGNAL
};
@ -91,13 +115,23 @@ static void
discoverer_discovered_cb (GstDiscoverer * discoverer,
GstDiscovererInfo * info, GError * err, GESTimeline * timeline);
void look_for_transition (GESTrackObject * track_object,
GESTimelineLayer * layer);
void track_object_removed_cb (GESTrack * track, GESTrackObject * track_object,
GESTimeline * timeline);
static void
ges_timeline_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
GESTimeline *timeline = GES_TIMELINE (object);
switch (property_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
case PROP_DURATION:
g_value_set_uint64 (value, timeline->priv->duration);
break;
}
}
@ -143,6 +177,10 @@ ges_timeline_dispose (GObject * object)
static void
ges_timeline_finalize (GObject * object)
{
GESTimeline *timeline = GES_TIMELINE (object);
g_mutex_free (timeline->priv->pendingobjects_lock);
G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
}
@ -163,6 +201,20 @@ ges_timeline_class_init (GESTimelineClass * klass)
object_class->dispose = ges_timeline_dispose;
object_class->finalize = ges_timeline_finalize;
/**
* GESTimelineObject:duration
*
* Current duration (in nanoseconds) of the #GESTimeline
*
* Default value: 0
*/
properties[PROP_DURATION] =
g_param_spec_uint64 ("duration", "Duration",
"The duration of the timeline", 0, G_MAXUINT64,
GST_CLOCK_TIME_NONE, G_PARAM_READABLE);
g_object_class_install_property (object_class, PROP_DURATION,
properties[PROP_DURATION]);
/**
* GESTimeline::track-added
* @timeline: the #GESTimeline
@ -211,6 +263,18 @@ ges_timeline_class_init (GESTimelineClass * klass)
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
GES_TYPE_TIMELINE_LAYER);
/**
* GESTimeline::discovery-error:
* @formatter: the #GESFormatter
* @source: The #GESTimelineFileSource that could not be discovered properly
* @error: (type GLib.Error): #GError, which will be non-NULL if an error
* occurred during discovery
*/
ges_timeline_signals[DISCOVERY_ERROR] =
g_signal_new ("discovery-error", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, gst_marshal_VOID__OBJECT_BOXED,
G_TYPE_NONE, 2, GES_TYPE_TIMELINE_FILE_SOURCE, GST_TYPE_G_ERROR);
}
static void
@ -222,7 +286,9 @@ ges_timeline_init (GESTimeline * self)
self->priv->layers = NULL;
self->priv->tracks = NULL;
self->priv->duration = 0;
self->priv->pendingobjects_lock = g_mutex_new ();
/* New discoverer with a 15s timeout */
self->priv->discoverer = gst_discoverer_new (15 * GST_SECOND, NULL);
g_signal_connect (self->priv->discoverer, "finished",
@ -232,6 +298,26 @@ ges_timeline_init (GESTimeline * self)
gst_discoverer_start (self->priv->discoverer);
}
static gint
sort_layers (gpointer a, gpointer b)
{
GESTimelineLayer *layer_a, *layer_b;
guint prio_a, prio_b;
layer_a = GES_TIMELINE_LAYER (a);
layer_b = GES_TIMELINE_LAYER (b);
prio_a = ges_timeline_layer_get_priority (layer_a);
prio_b = ges_timeline_layer_get_priority (layer_b);
if ((gint) prio_a > (guint) prio_b)
return 1;
if ((guint) prio_a < (guint) prio_b)
return -1;
return 0;
}
/**
* ges_timeline_new:
*
@ -435,13 +521,16 @@ discoverer_discovered_cb (GstDiscoverer * discoverer,
GstDiscovererInfo * info, GError * err, GESTimeline * timeline)
{
GList *tmp;
GList *stream_list;
GESTrackType tfs_supportedformats;
gboolean found = FALSE;
gboolean is_image = FALSE;
GESTimelineFileSource *tfs = NULL;
GESTimelinePrivate *priv = timeline->priv;
const gchar *uri = gst_discoverer_info_get_uri (info);
GST_DEBUG ("Discovered uri %s", uri);
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
/* Find corresponding TimelineFileSource in the sources */
for (tmp = priv->pendingobjects; tmp; tmp = tmp->next) {
@ -453,57 +542,85 @@ discoverer_discovered_cb (GstDiscoverer * discoverer,
}
}
if (found) {
GList *stream_list;
GESTrackType tfs_supportedformats;
if (!found) {
GST_WARNING ("Discovered %s, that seems not to be in the list of sources"
"to discover", uri);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
return;
}
if (err) {
GError *propagate_error = NULL;
/* Remove object from list */
priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
GST_WARNING ("Error while discovering %s: %s", uri, err->message);
/* FIXME : Handle errors in discovery */
stream_list = gst_discoverer_info_get_stream_list (info);
g_propagate_error (&propagate_error, err);
g_signal_emit (timeline, ges_timeline_signals[DISCOVERY_ERROR], 0, tfs,
propagate_error);
/* Update timelinefilesource properties based on info */
for (tmp = stream_list; tmp; tmp = tmp->next) {
GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
return;
}
tfs_supportedformats =
ges_timeline_filesource_get_supported_formats (tfs);
/* Everything went fine... let's do our job! */
GST_DEBUG ("Discovered uri %s", uri);
if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
/* The timeline file source will be updated with discovered information
* so it needs to not be finalized during this process */
g_object_ref (tfs);
/* Remove object from list */
priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
/* FIXME : Handle errors in discovery */
stream_list = gst_discoverer_info_get_stream_list (info);
tfs_supportedformats = ges_timeline_filesource_get_supported_formats (tfs);
if (tfs_supportedformats != GES_TRACK_TYPE_UNKNOWN)
goto check_image;
/* Update timelinefilesource properties based on info */
for (tmp = stream_list; tmp; tmp = tmp->next) {
GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
} else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_VIDEO;
ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
ges_timeline_filesource_set_supported_formats (tfs,
tfs_supportedformats);
} else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_VIDEO;
ges_timeline_filesource_set_supported_formats (tfs,
tfs_supportedformats);
if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
ges_timeline_filesource_set_supported_formats (tfs,
tfs_supportedformats);
is_image = TRUE;
}
is_image = TRUE;
}
}
if (stream_list)
gst_discoverer_stream_info_list_free (stream_list);
if (is_image) {
/* don't set max-duration on still images */
g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
}
else {
g_object_set (tfs, "max-duration",
gst_discoverer_info_get_duration (info), NULL);
}
/* Continue the processing on tfs */
add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs));
}
if (stream_list)
gst_discoverer_stream_info_list_free (stream_list);
check_image:
if (is_image) {
/* don't set max-duration on still images */
g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
}
/* 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);
}
/* Remove the ref as the timeline file source is no longer needed here */
g_object_unref (tfs);
}
static GstStateChangeReturn
@ -514,9 +631,13 @@ ges_timeline_change_state (GstElement * element, GstStateChange transition)
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
if (timeline->priv->pendingobjects) {
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
do_async_start (timeline);
ret = GST_STATE_CHANGE_ASYNC;
} else {
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
}
break;
default:
@ -549,6 +670,12 @@ static void
layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
GESTimeline * timeline)
{
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);
return;
}
GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
@ -567,8 +694,12 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
tfs_maxdur == GST_CLOCK_TIME_NONE || object->duration == 0) {
GST_LOG ("Incomplete TimelineFileSource, discovering it");
tfs_uri = ges_timeline_filesource_get_uri (tfs);
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
timeline->priv->pendingobjects =
g_list_append (timeline->priv->pendingobjects, object);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
gst_discoverer_discover_uri_async (timeline->priv->discoverer, tfs_uri);
} else
add_object_to_tracks (timeline, object);
@ -579,12 +710,25 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
GST_DEBUG ("done");
}
static void
layer_priority_changed_cb (GESTimelineLayer * layer,
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
{
timeline->priv->layers = g_list_sort (timeline->priv->layers, (GCompareFunc)
sort_layers);
}
static void
layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
GESTimeline * timeline)
{
GList *tmp, *trackobjects;
GList *trackobjects, *tmp;
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);
return;
}
GST_DEBUG ("TimelineObject %p removed from layer %p", object, layer);
@ -604,15 +748,50 @@ layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
ges_timeline_object_release_track_object (object, trobj);
}
/* removing the reference added by _get_track_objects() */
g_object_unref (trobj);
}
g_list_free (trackobjects);
/* if the object is a timeline file source that has not yet been discovered,
* it no longer needs to be discovered so remove it from the pendingobjects
* list if it belongs to this layer */
if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
timeline->priv->pendingobjects =
g_list_remove_all (timeline->priv->pendingobjects, object);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
}
GST_DEBUG ("Done");
}
/**
* ges_timeline_append_layer:
* @timeline: a #GESTimeline
* @layer: the #GESTimelineLayer to add
*
* Convenience method to append @layer to @timeline which means that the
* priority of @layer is changed to correspond to the last layer of @timeline.
* The reference to the @layer will be stolen by @timeline.
*
* Returns: TRUE if the layer was properly added, else FALSE.
*/
gboolean
ges_timeline_append_layer (GESTimeline * timeline, GESTimelineLayer * layer)
{
guint32 priority;
GESTimelinePrivate *priv = timeline->priv;
GST_DEBUG ("Appending layer to layer:%p, timeline:%p", timeline, layer);
priority = g_list_length (priv->layers);
ges_timeline_layer_set_priority (layer, priority);
return ges_timeline_add_layer (timeline, layer);
}
/**
* ges_timeline_add_layer:
* @timeline: a #GESTimeline
@ -644,7 +823,8 @@ ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
}
g_object_ref_sink (layer);
priv->layers = g_list_append (priv->layers, layer);
priv->layers = g_list_insert_sorted (priv->layers, layer,
(GCompareFunc) sort_layers);
/* Inform the layer that it belongs to a new timeline */
ges_timeline_layer_set_timeline (layer, timeline);
@ -654,6 +834,8 @@ ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
timeline);
g_signal_connect (layer, "object-removed",
G_CALLBACK (layer_object_removed_cb), timeline);
g_signal_connect (layer, "notify::priority",
G_CALLBACK (layer_priority_changed_cb), timeline);
GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
@ -726,7 +908,8 @@ 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));
@ -736,8 +919,20 @@ pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
}
/* 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);
@ -745,6 +940,11 @@ pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
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
@ -849,6 +1049,12 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
g_list_free (objects);
}
/* 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);
track_duration_cb (GST_ELEMENT (track), NULL, timeline);
return TRUE;
}
@ -901,6 +1107,8 @@ 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);
/* Signal track removal to all layers/objects */
g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
@ -978,7 +1186,7 @@ ges_timeline_get_tracks (GESTimeline * timeline)
* Get the list of #GESTimelineLayer present in the Timeline.
*
* Returns: (transfer full) (element-type GESTimelineLayer): the list of
* #GESTimelineLayer present in the Timeline.
* #GESTimelineLayer present in the Timeline sorted by priority.
* The caller should unref each Layer once he is done with them.
*/
GList *
@ -987,8 +1195,71 @@ ges_timeline_get_layers (GESTimeline * timeline)
GList *tmp, *res = NULL;
for (tmp = timeline->priv->layers; tmp; tmp = g_list_next (tmp)) {
res = g_list_append (res, g_object_ref (tmp->data));
res = g_list_insert_sorted (res, g_object_ref (tmp->data),
(GCompareFunc) sort_layers);
}
return res;
}
/**
* ges_timeline_enable_update:
* @timeline: a #GESTimeline
* @enabled: Whether the timeline should update on every change or not.
*
* Control whether the timeline is updated for every change happening within.
*
* Users will want to use this method with %FALSE before doing lots of changes,
* and then call again with %TRUE for the changes to take effect in one go.
*
* Returns: %TRUE if the update status could be changed, else %FALSE.
*/
gboolean
ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
{
GList *tmp, *tracks;
gboolean res = TRUE;
GST_DEBUG_OBJECT (timeline, "%s updates", enabled ? "Enabling" : "Disabling");
tracks = ges_timeline_get_tracks (timeline);
for (tmp = tracks; tmp; tmp = tmp->next) {
if (!ges_track_enable_update (tmp->data, enabled)) {
res = FALSE;
}
}
g_list_free (tracks);
return res;
}
static void
track_duration_cb (GstElement * track,
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
{
guint64 duration, max_duration = 0;
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 duration : %" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
max_duration = MAX (duration, max_duration);
}
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));
timeline->priv->duration = max_duration;
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
#else
g_object_notify (G_OBJECT (timeline), "duration");
#endif
}
}

View file

@ -89,6 +89,7 @@ gboolean ges_timeline_load_from_uri (GESTimeline *timeline, const gchar *uri);
gboolean ges_timeline_save_to_uri (GESTimeline *timeline, const gchar *uri);
gboolean ges_timeline_add_layer (GESTimeline *timeline, GESTimelineLayer *layer);
gboolean ges_timeline_append_layer (GESTimeline * timeline, GESTimelineLayer * layer);
gboolean ges_timeline_remove_layer (GESTimeline *timeline, GESTimelineLayer *layer);
GList* ges_timeline_get_layers (GESTimeline *timeline);
@ -98,6 +99,8 @@ gboolean ges_timeline_remove_track (GESTimeline *timeline, GESTrack *track);
GESTrack * ges_timeline_get_track_for_pad (GESTimeline *timeline, GstPad *pad);
GList *ges_timeline_get_tracks (GESTimeline *timeline);
gboolean ges_timeline_enable_update(GESTimeline * timeline, gboolean enabled);
G_END_DECLS
#endif /* _GES_TIMELINE */

View file

@ -34,14 +34,14 @@ G_DEFINE_TYPE (GESTrackFileSource, ges_track_filesource, GES_TYPE_TRACK_SOURCE);
struct _GESTrackFileSourcePrivate
{
/* Dummy variable */
void *nothing;
guint64 maxduration;
};
enum
{
PROP_0,
PROP_URI
PROP_URI,
PROP_MAX_DURATION
};
static void
@ -54,6 +54,9 @@ ges_track_filesource_get_property (GObject * object, guint property_id,
case PROP_URI:
g_value_set_string (value, tfs->uri);
break;
case PROP_MAX_DURATION:
g_value_set_uint64 (value, tfs->priv->maxduration);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -69,6 +72,9 @@ ges_track_filesource_set_property (GObject * object, guint property_id,
case PROP_URI:
tfs->uri = g_value_dup_string (value);
break;
case PROP_MAX_DURATION:
tfs->priv->maxduration = g_value_get_uint64 (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -108,6 +114,19 @@ ges_track_filesource_class_init (GESTrackFileSourceClass * klass)
object_class->set_property = ges_track_filesource_set_property;
object_class->dispose = ges_track_filesource_dispose;
/**
* GESTrackFileSource:max-duration:
*
* The maximum duration (in nanoseconds) of the file.
*
* If not set before adding the object to a layer, it will be discovered
* asynchronously. Connect to 'notify::max-duration' to be notified of it.
*/
g_object_class_install_property (object_class, PROP_MAX_DURATION,
g_param_spec_uint64 ("max-duration", "Maximum duration",
"The duration of the file", 0, G_MAXUINT64, GST_CLOCK_TIME_NONE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
/**
* GESTrackFileSource:uri
*

View file

@ -73,6 +73,7 @@ enum
PROP_DURATION,
PROP_PRIORITY,
PROP_ACTIVE,
PROP_LOCKED,
PROP_LAST
};
@ -118,6 +119,8 @@ static inline gboolean ges_track_object_set_duration_internal (GESTrackObject *
object, guint64 duration);
static inline gboolean ges_track_object_set_priority_internal (GESTrackObject *
object, guint32 priority);
static inline void
ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked);
static GParamSpec **default_list_children_properties (GESTrackObject * object,
guint * n_properties);
@ -144,6 +147,9 @@ ges_track_object_get_property (GObject * object, guint property_id,
case PROP_ACTIVE:
g_value_set_boolean (value, ges_track_object_is_active (tobj));
break;
case PROP_LOCKED:
g_value_set_boolean (value, ges_track_object_is_locked (tobj));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -171,6 +177,9 @@ ges_track_object_set_property (GObject * object, guint property_id,
case PROP_ACTIVE:
ges_track_object_set_active (tobj, g_value_get_boolean (value));
break;
case PROP_LOCKED:
ges_track_object_set_locked_internal (tobj, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -270,6 +279,18 @@ ges_track_object_class_init (GESTrackObjectClass * klass)
g_object_class_install_property (object_class, PROP_ACTIVE,
properties[PROP_ACTIVE]);
/**
* GESTrackObject:locked
*
* If %TRUE, then moves in sync with its controlling #GESTimelineObject
*/
properties[PROP_LOCKED] =
g_param_spec_boolean ("locked", "Locked",
"Moves in sync with its controling TimelineObject", TRUE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_LOCKED,
properties[PROP_LOCKED]);
/**
* GESTrackObject::deep-notify:
* @track_object: a #GESTrackObject
@ -876,6 +897,12 @@ ges_track_object_get_element (GESTrackObject * object)
return object->priv->element;
}
static inline void
ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked)
{
object->priv->locked = locked;
}
/**
* ges_track_object_set_locked:
* @object: a #GESTrackObject
@ -888,7 +915,17 @@ ges_track_object_get_element (GESTrackObject * object)
void
ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
{
object->priv->locked = locked;
g_return_if_fail (GES_IS_TRACK_OBJECT (object));
GST_DEBUG_OBJECT (object, "%s object", locked ? "Locking" : "Unlocking");
ges_track_object_set_locked_internal (object, locked);
#if GLIB_CHECK_VERSION(2,26,0)
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_LOCKED]);
#else
g_object_notify (G_OBJECT (object), "locked");
#endif
}
/**
@ -1051,8 +1088,7 @@ ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
if (classename == NULL ||
g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
GST_DEBUG ("The %s property from %s has been found in %s", name,
classename, GST_OBJECT_NAME (GST_OBJECT (element)));
GST_DEBUG ("The %s property from %s has been found", name, classename);
if (element)
*element = g_object_ref (value);

View file

@ -132,80 +132,96 @@ struct _GESTrackObjectClass {
/*< private >*/
/* signals (currently unused) */
void (*changed) (GESTrackObject * object);
void (*changed) (GESTrackObject * object);
/*< public >*/
/* virtual methods for subclasses */
GHashTable* (*get_props_hastable) (GESTrackObject * object);
GParamSpec** (*list_children_properties) (GESTrackObject * object,
guint *n_properties);
guint *n_properties);
/*< private >*/
/* Padding for API extension */
gpointer _ges_reserved[GES_PADDING - 2];
};
GType ges_track_object_get_type (void);
GType ges_track_object_get_type (void);
gboolean ges_track_object_set_track (GESTrackObject * object,
GESTrack * track);
GESTrack* ges_track_object_get_track (GESTrackObject * object);
gboolean ges_track_object_set_track (GESTrackObject * object,
GESTrack * track);
GESTrack* ges_track_object_get_track (GESTrackObject * object);
void ges_track_object_set_timeline_object (GESTrackObject * object,
GESTimelineObject * tlobject);
void ges_track_object_set_timeline_object (GESTrackObject * object,
GESTimelineObject * tlobject);
GESTimelineObject *
ges_track_object_get_timeline_object (GESTrackObject* object);
ges_track_object_get_timeline_object (GESTrackObject* object);
GstElement * ges_track_object_get_gnlobject (GESTrackObject * object);
GstElement * ges_track_object_get_element (GESTrackObject * object);
GstElement * ges_track_object_get_gnlobject (GESTrackObject * object);
void ges_track_object_set_locked (GESTrackObject * object,
gboolean locked);
gboolean ges_track_object_is_locked (GESTrackObject * object);
GstElement * ges_track_object_get_element (GESTrackObject * object);
void ges_track_object_set_start (GESTrackObject * object,
guint64 start);
void ges_track_object_set_inpoint (GESTrackObject * object,
guint64 inpoint);
void ges_track_object_set_duration (GESTrackObject * object,
guint64 duration);
void ges_track_object_set_priority (GESTrackObject * object,
guint32 priority);
gboolean ges_track_object_set_active (GESTrackObject * object,
gboolean active);
void ges_track_object_set_locked (GESTrackObject * object,
gboolean locked);
guint64 ges_track_object_get_start (GESTrackObject * object);
guint64 ges_track_object_get_inpoint (GESTrackObject * object);
guint64 ges_track_object_get_duration (GESTrackObject * object);
guint32 ges_track_object_get_priority (GESTrackObject * object);
gboolean ges_track_object_is_active (GESTrackObject * object);
gboolean ges_track_object_is_locked (GESTrackObject * object);
void ges_track_object_set_start (GESTrackObject * object,
guint64 start);
void ges_track_object_set_inpoint (GESTrackObject * object,
guint64 inpoint);
void ges_track_object_set_duration (GESTrackObject * object,
guint64 duration);
void ges_track_object_set_priority (GESTrackObject * object,
guint32 priority);
gboolean ges_track_object_set_active (GESTrackObject * object,
gboolean active);
guint64 ges_track_object_get_start (GESTrackObject * object);
guint64 ges_track_object_get_inpoint (GESTrackObject * object);
guint64 ges_track_object_get_duration (GESTrackObject * object);
guint32 ges_track_object_get_priority (GESTrackObject * object);
gboolean ges_track_object_is_active (GESTrackObject * object);
GParamSpec **
ges_track_object_list_children_properties (GESTrackObject *object,
guint *n_properties);
gboolean ges_track_object_lookup_child (GESTrackObject *object,
const gchar *prop_name,
GstElement **element,
GParamSpec **pspec);
ges_track_object_list_children_properties (GESTrackObject *object,
guint *n_properties);
void ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
GParamSpec * pspec,
GValue * value);
void ges_track_object_get_child_property_valist (GESTrackObject * object,
const gchar * first_property_name,
va_list var_args);
void ges_track_object_get_child_property (GESTrackObject *object,
const gchar * first_property_name,
...) G_GNUC_NULL_TERMINATED;
gboolean ges_track_object_lookup_child (GESTrackObject *object,
const gchar *prop_name,
GstElement **element,
GParamSpec **pspec);
void
ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
GParamSpec * pspec,
GValue * value);
void
ges_track_object_get_child_property_valist (GESTrackObject * object,
const gchar * first_property_name,
va_list var_args);
void ges_track_object_get_child_property (GESTrackObject *object,
const gchar * first_property_name,
...) G_GNUC_NULL_TERMINATED;
void
ges_track_object_set_child_property_valist (GESTrackObject * object,
const gchar * first_property_name,
va_list var_args);
void
ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
GParamSpec * pspec,
GValue * value);
void ges_track_object_set_child_property (GESTrackObject * object,
const gchar * first_property_name,
...) G_GNUC_NULL_TERMINATED;
void ges_track_object_set_child_property_valist (GESTrackObject * object,
const gchar * first_property_name,
va_list var_args);
void ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
GParamSpec * pspec,
GValue * value);
void ges_track_object_set_child_property (GESTrackObject * object,
const gchar * first_property_name,
...) G_GNUC_NULL_TERMINATED;
G_END_DECLS
#endif /* _GES_TRACK_OBJECT */

View file

@ -32,6 +32,7 @@
#include "ges-internal.h"
#include "ges-track.h"
#include "ges-track-object.h"
#include "gesmarshal.h"
G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
@ -45,6 +46,7 @@ struct _GESTrackPrivate
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 */
};
@ -54,9 +56,14 @@ enum
ARG_CAPS,
ARG_TYPE,
ARG_DURATION,
ARG_LAST
ARG_LAST,
TRACK_OBJECT_ADDED,
TRACK_OBJECT_REMOVED,
LAST_SIGNAL
};
static guint ges_track_signals[LAST_SIGNAL] = { 0 };
static GParamSpec *properties[ARG_LAST];
static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track);
@ -64,6 +71,12 @@ static void
pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
static void composition_duration_cb (GstElement * composition, GParamSpec * arg
G_GNUC_UNUSED, GESTrack * obj);
static void
sort_track_objects_cb (GESTrackObject * child,
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
static void timeline_duration_cb (GESTimeline * timeline,
GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
static void
ges_track_get_property (GObject * object, guint property_id,
@ -130,6 +143,48 @@ 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)
{
@ -147,6 +202,7 @@ 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
@ -191,6 +247,34 @@ ges_track_class_init (GESTrackClass * klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, ARG_TYPE,
properties[ARG_TYPE]);
/**
* GESTrack::track-object-added
* @object: the #GESTrack
* @effect: the #GESTrackObject that was added.
*
* Will be emitted after a track object was added to the track.
*
* Since: 0.10.2
*/
ges_track_signals[TRACK_OBJECT_ADDED] =
g_signal_new ("track-object-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
/**
* GESTrack::track-object-removed
* @object: the #GESTrack
* @effect: the #GESTrackObject that was removed.
*
* Will be emitted after a track object was removed from the track.
*
* Since: 0.10.2
*/
ges_track_signals[TRACK_OBJECT_REMOVED] =
g_signal_new ("track-object-removed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
}
static void
@ -285,6 +369,14 @@ 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);
if (timeline)
g_signal_connect (G_OBJECT (timeline), "notify::duration",
G_CALLBACK (timeline_duration_cb), track);
track->priv->timeline = timeline;
}
@ -315,6 +407,26 @@ 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
@ -354,7 +466,9 @@ ges_track_add_object (GESTrack * track, GESTrackObject * object)
return FALSE;
}
GST_DEBUG ("Adding object to ourself");
GST_DEBUG ("Adding object %s to ourself %s",
GST_OBJECT_NAME (ges_track_object_get_gnlobject (object)),
GST_OBJECT_NAME (track->priv->composition));
if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->priv->composition),
ges_track_object_get_gnlobject (object)))) {
@ -363,12 +477,48 @@ 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);
track->priv->trackobjects = g_list_append (track->priv->trackobjects, object);
g_signal_emit (track, ges_track_signals[TRACK_OBJECT_ADDED], 0,
GES_TRACK_OBJECT (object));
g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
G_CALLBACK (sort_track_objects_cb), track);
g_signal_connect (GES_TRACK_OBJECT (object), "notify::priority",
G_CALLBACK (sort_track_objects_cb), track);
return TRUE;
}
/**
* ges_track_get_objects:
* @track: a #GESTrack
*
* Gets the #GESTrackObject contained in @track
*
* Returns: (transfer full) (element-type GESTrackObject): the list of
* #GESTrackObject present in the Track sorted by priority and start.
*/
GList *
ges_track_get_objects (GESTrack * track)
{
GList *ret = NULL;
GList *tmp;
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
for (tmp = track->priv->trackobjects; tmp; tmp = tmp->next) {
ret = g_list_prepend (ret, tmp->data);
g_object_ref (tmp->data);
}
ret = g_list_reverse (ret);
return ret;
}
/**
* ges_track_remove_object:
* @track: a #GESTrack
@ -409,7 +559,13 @@ ges_track_remove_object (GESTrack * track, GESTrackObject * object)
}
}
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);
@ -474,6 +630,29 @@ composition_duration_cb (GstElement * composition,
}
}
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));
}
/**
* ges_track_get_caps:
* @track: a #GESTrack
@ -505,3 +684,31 @@ ges_track_get_timeline (GESTrack * track)
return track->priv->timeline;
}
/**
* ges_track_enable_update:
* @track: a #GESTrack
* @enabled: Whether the track should update on every change or not.
*
* Control whether the track is updated for every change happening within.
*
* Users will want to use this method with %FALSE before doing lots of changes,
* and then call again with %TRUE for the changes to take effect in one go.
*
* Returns: %TRUE if the update status could be changed, else %FALSE.
*/
gboolean
ges_track_enable_update (GESTrack * track, gboolean enabled)
{
gboolean update;
g_object_set (track->priv->composition, "update", enabled, NULL);
g_object_get (track->priv->composition, "update", &update, NULL);
if (update == enabled) {
return TRUE;
} else {
return FALSE;
}
}

View file

@ -79,24 +79,32 @@ struct _GESTrackClass {
gpointer _ges_reserved[GES_PADDING];
};
GType ges_track_get_type (void);
GType ges_track_get_type (void);
GESTrack* ges_track_new (GESTrackType type, GstCaps * caps);
GESTrack* ges_track_new (GESTrackType type, GstCaps * caps);
void ges_track_set_timeline (GESTrack * track,
GESTimeline *timeline);
void ges_track_set_caps (GESTrack * track,
const GstCaps * caps);
const GstCaps * ges_track_get_caps (GESTrack *track);
void ges_track_set_timeline (GESTrack * track,
GESTimeline *timeline);
void ges_track_set_caps (GESTrack * track,
const GstCaps * caps);
const GstCaps * ges_track_get_caps (GESTrack *track);
const GESTimeline *ges_track_get_timeline (GESTrack *track);
gboolean ges_track_add_object (GESTrack * track,
GESTrackObject * object);
gboolean ges_track_remove_object (GESTrack * track,
GESTrackObject * object);
gboolean ges_track_add_object (GESTrack * track,
GESTrackObject * object);
GESTrack *ges_track_video_raw_new (void);
GESTrack *ges_track_audio_raw_new (void);
gboolean ges_track_remove_object (GESTrack * track,
GESTrackObject * object);
GESTrack *ges_track_video_raw_new (void);
GESTrack *ges_track_audio_raw_new (void);
gboolean ges_track_enable_update (GESTrack * track, gboolean enabled);
GList* ges_track_get_objects (GESTrack *track);
G_END_DECLS

View file

@ -134,5 +134,7 @@ typedef struct _GESFormatterClass GESFormatterClass;
typedef struct _GESKeyfileFormatter GESKeyfileFormatter;
typedef struct _GESKeyfileFormatterClass GESKeyfileFormatterClass;
typedef struct _GESPitiviFormatter GESPitiviFormatter;
typedef struct _GESPitiviFormatterClass GESPitiviFormatterClass;
#endif /* __GES_TYPES_H__ */

View file

@ -80,3 +80,27 @@ ges_init (void)
return TRUE;
}
/**
* ges_version:
* @major: (out): pointer to a guint to store the major version number
* @minor: (out): pointer to a guint to store the minor version number
* @micro: (out): pointer to a guint to store the micro version number
* @nano: (out): pointer to a guint to store the nano version number
*
* Gets the version number of the GStreamer Editing Services library.
*/
void
ges_version (guint * major, guint * minor, guint * micro, guint * nano)
{
g_return_if_fail (major);
g_return_if_fail (minor);
g_return_if_fail (micro);
g_return_if_fail (nano);
*major = GES_VERSION_MAJOR;
*minor = GES_VERSION_MINOR;
*micro = GES_VERSION_MICRO;
*nano = GES_VERSION_NANO;
}

View file

@ -20,7 +20,6 @@
#ifndef __GES_H__
#define __GES_H__
#include <glib.h>
#include <gst/gst.h>
@ -63,15 +62,22 @@
#include <ges/ges-track-audio-transition.h>
#include <ges/ges-track-effect.h>
#include <ges/ges-track-parse-launch-effect.h>
#include <ges/ges-formatter.h>
#include <ges/ges-keyfile-formatter.h>
#include <ges/ges-pitivi-formatter.h>
#include <ges/ges-utils.h>
G_BEGIN_DECLS
gboolean ges_init (void);
#define GES_VERSION_MAJOR (0)
#define GES_VERSION_MINOR (10)
#define GES_VERSION_MICRO (1)
#define GES_VERSION_NANO (1)
gboolean ges_init (void);
void ges_version (guint * major, guint * minor, guint * micro,
guint * nano);
G_END_DECLS

View file

@ -1,12 +1,12 @@
# the standard variables don't make sense for an uninstalled copy
prefix=
exec_prefix=
libdir=${pcfiledir}/../ges
includedir=${pcfiledir}/..
libdir=@abs_top_builddir@/ges
includedir=@abs_top_builddir@
Name: gst-editing-services
Description: GStreamer Editing Services
Version: @VERSION@
Requires: gstreamer-@GST_MAJORMINOR@ gstreamer-base-@GST_MAJORMINOR@ gstreamer-controller-@GST_MAJORMINOR@ gstreamer-pbutils-@GST_MAJORMINOR@
Libs: ${libdir}/libges-@GST_MAJORMINOR@
Cflags: -I${includedir} -I@srcdir@/..
Libs: @abs_top_builddir@/ges/libges-@GST_MAJORMINOR@.la
Cflags: -I@abs_top_srcdir@ -I@abs_top_builddir@

View file

@ -72,6 +72,7 @@ GST_START_TEST (test_test_source_properties)
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));
@ -169,10 +170,12 @@ GST_START_TEST (test_test_source_in_layer)
assert_equals_float (volume, 0);
freq = ges_track_audio_test_source_get_freq (
GES_TRACK_AUDIO_TEST_SOURCE (trobj));
volume = ges_track_audio_test_source_get_volume (
GES_TRACK_AUDIO_TEST_SOURCE (trobj));
freq =
ges_track_audio_test_source_get_freq (GES_TRACK_AUDIO_TEST_SOURCE
(trobj));
volume =
ges_track_audio_test_source_get_volume (GES_TRACK_AUDIO_TEST_SOURCE
(trobj));
g_assert (freq == 440);
g_assert (volume == 0);
@ -183,10 +186,12 @@ GST_START_TEST (test_test_source_in_layer)
assert_equals_float (freq, 2000);
assert_equals_float (volume, 0.5);
freq = ges_track_audio_test_source_get_freq (
GES_TRACK_AUDIO_TEST_SOURCE (trobj));
volume = ges_track_audio_test_source_get_volume (
GES_TRACK_AUDIO_TEST_SOURCE (trobj));
freq =
ges_track_audio_test_source_get_freq (GES_TRACK_AUDIO_TEST_SOURCE
(trobj));
volume =
ges_track_audio_test_source_get_volume (GES_TRACK_AUDIO_TEST_SOURCE
(trobj));
g_assert (freq == 2000);
g_assert (volume == 0.5);

View file

@ -55,6 +55,8 @@ GST_START_TEST (test_filesource_basic)
trackobject =
ges_timeline_object_create_track_object (GES_TIMELINE_OBJECT (source),
track);
ges_timeline_object_add_track_object (GES_TIMELINE_OBJECT (source),
trackobject);
fail_unless (trackobject != NULL);
/* The track holds a reference to the object
@ -110,6 +112,7 @@ GST_START_TEST (test_filesource_properties)
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));
@ -174,6 +177,7 @@ GST_START_TEST (test_filesource_images)
/* the returned track object should be an image source */
trobj = ges_timeline_object_create_track_object (tlobj, v);
ges_timeline_object_add_track_object (tlobj, trobj);
fail_unless (GES_IS_TRACK_IMAGE_SOURCE (trobj));
/* The track holds a reference to the object
@ -183,19 +187,9 @@ GST_START_TEST (test_filesource_images)
ges_track_remove_object (v, trobj);
ges_timeline_object_release_track_object (tlobj, trobj);
/* the timeline object should create an audio test source when the is_image
* property is set true */
/* the timeline object should not create any TrackObject in the audio track */
trobj = ges_timeline_object_create_track_object (tlobj, a);
fail_unless (GES_IS_TRACK_AUDIO_TEST_SOURCE (trobj));
/* The track holds a reference to the object
* And the timelineobject holds a reference to the object */
ASSERT_OBJECT_REFCOUNT (trobj, "Audio Track Object", 2);
ges_track_remove_object (v, trobj);
ges_timeline_object_release_track_object (tlobj, trobj);
fail_unless (trobj == NULL);
g_object_unref (a);
g_object_unref (v);

View file

@ -20,6 +20,8 @@
#include <ges/ges.h>
#include <gst/check/gstcheck.h>
#define LAYER_HEIGHT 1000
static gboolean
my_fill_track_func (GESTimelineObject * object,
GESTrackObject * trobject, GstElement * gnlobj, gpointer user_data)
@ -116,16 +118,16 @@ GST_START_TEST (test_layer_properties)
/* Change the priority of the layer */
g_object_set (layer, "priority", 1, NULL);
assert_equals_int (ges_timeline_layer_get_priority (layer), 1);
assert_equals_uint64 (GES_TIMELINE_OBJECT_PRIORITY (object), 10);
assert_equals_uint64 (GES_TIMELINE_OBJECT_PRIORITY (object), 0);
gnl_object_check (ges_track_object_get_gnlobject (trackobject), 42, 51, 12,
51, 10, TRUE);
51, LAYER_HEIGHT, TRUE);
/* Change it to an insanely high value */
g_object_set (layer, "priority", 1000000, NULL);
assert_equals_int (ges_timeline_layer_get_priority (layer), 1000000);
assert_equals_uint64 (GES_TIMELINE_OBJECT_PRIORITY (object), 10000000);
g_object_set (layer, "priority", 31, NULL);
assert_equals_int (ges_timeline_layer_get_priority (layer), 31);
assert_equals_uint64 (GES_TIMELINE_OBJECT_PRIORITY (object), 0);
gnl_object_check (ges_track_object_get_gnlobject (trackobject), 42, 51, 12,
51, 10000000, TRUE);
51, LAYER_HEIGHT * 31, TRUE);
/* and back to 0 */
g_object_set (layer, "priority", 0, NULL);
@ -143,6 +145,192 @@ GST_START_TEST (test_layer_properties)
GST_END_TEST;
GST_START_TEST (test_layer_priorities)
{
GESTrack *track;
GESTimeline *timeline;
GESTimelineLayer *layer1, *layer2, *layer3;
GESTrackObject *tckobj1, *tckobj2, *tckobj3;
GESTimelineObject *object1, *object2, *object3;
GstElement *gnlobj1, *gnlobj2, *gnlobj3;
guint prio1, prio2, prio3;
GList *objs, *tmp;
ges_init ();
/* Timeline and 3 Layer */
timeline = ges_timeline_new ();
layer1 = (GESTimelineLayer *) ges_timeline_layer_new ();
layer2 = (GESTimelineLayer *) ges_timeline_layer_new ();
layer3 = (GESTimelineLayer *) ges_timeline_layer_new ();
ges_timeline_layer_set_priority (layer2, 1);
ges_timeline_layer_set_priority (layer3, 2);
fail_unless (ges_timeline_add_layer (timeline, layer1));
fail_unless (ges_timeline_add_layer (timeline, layer2));
fail_unless (ges_timeline_add_layer (timeline, layer3));
fail_unless_equals_int (ges_timeline_layer_get_priority (layer1), 0);
fail_unless_equals_int (ges_timeline_layer_get_priority (layer2), 1);
fail_unless_equals_int (ges_timeline_layer_get_priority (layer3), 2);
track = ges_track_video_raw_new ();
fail_unless (track != NULL);
fail_unless (ges_timeline_add_track (timeline, track));
object1 =
GES_TIMELINE_OBJECT (ges_custom_timeline_source_new (my_fill_track_func,
NULL));
object2 =
GES_TIMELINE_OBJECT (ges_custom_timeline_source_new (my_fill_track_func,
NULL));
object3 =
GES_TIMELINE_OBJECT (ges_custom_timeline_source_new (my_fill_track_func,
NULL));
fail_unless (object1 != NULL);
fail_unless (object2 != NULL);
fail_unless (object3 != NULL);
/* Set priorities on the objects */
g_object_set (object1, "priority", 0, NULL);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object1), 0);
g_object_set (object2, "priority", 1, NULL);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object2), 1);
g_object_set (object3, "priority", LAYER_HEIGHT + 1, NULL);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object3), LAYER_HEIGHT + 1);
/* Add objects to the timeline */
fail_unless (ges_timeline_layer_add_object (layer1, object1));
tckobj1 = ges_timeline_object_find_track_object (object1, track, G_TYPE_NONE);
fail_unless (tckobj1 != NULL);
fail_unless (ges_timeline_layer_add_object (layer2, object2));
tckobj2 = ges_timeline_object_find_track_object (object2, track, G_TYPE_NONE);
fail_unless (tckobj2 != NULL);
fail_unless (ges_timeline_layer_add_object (layer3, object3));
tckobj3 = ges_timeline_object_find_track_object (object3, track, G_TYPE_NONE);
fail_unless (tckobj3 != NULL);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object1), 0);
gnlobj1 = ges_track_object_get_gnlobject (tckobj1);
fail_unless (gnlobj1 != NULL);
g_object_get (gnlobj1, "priority", &prio1, NULL);
assert_equals_int (prio1, 0);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object2), 1);
gnlobj2 = ges_track_object_get_gnlobject (tckobj2);
fail_unless (gnlobj2 != NULL);
g_object_get (gnlobj2, "priority", &prio2, NULL);
/* object2 is on the second layer and has a priority of 1 */
assert_equals_int (prio2, LAYER_HEIGHT + 1);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object3), LAYER_HEIGHT - 1);
gnlobj3 = ges_track_object_get_gnlobject (tckobj3);
fail_unless (gnlobj3 != NULL);
/* object3 is on the third layer and has a priority of LAYER_HEIGHT + 1
* it priority must have the maximum priority of this layer*/
g_object_get (gnlobj3, "priority", &prio3, NULL);
assert_equals_int (prio3, LAYER_HEIGHT * 3 - 1);
/* Move layers around */
g_object_set (layer1, "priority", 2, NULL);
g_object_set (layer2, "priority", 0, NULL);
g_object_set (layer3, "priority", 1, NULL);
/* And check the new priorities */
assert_equals_int (ges_timeline_layer_get_priority (layer1), 2);
assert_equals_int (ges_timeline_layer_get_priority (layer2), 0);
assert_equals_int (ges_timeline_layer_get_priority (layer3), 1);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object1), 0);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object2), 1);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object3), LAYER_HEIGHT - 1);
g_object_get (gnlobj1, "priority", &prio1, NULL);
g_object_get (gnlobj2, "priority", &prio2, NULL);
g_object_get (gnlobj3, "priority", &prio3, NULL);
assert_equals_int (prio1, 2 * LAYER_HEIGHT);
assert_equals_int (prio2, 1);
assert_equals_int (prio3, LAYER_HEIGHT * 2 - 1);
/* And move objects around */
fail_unless (ges_timeline_object_move_to_layer (object2, layer1));
fail_unless (ges_timeline_object_move_to_layer (object3, layer1));
objs = ges_timeline_layer_get_objects (layer1);
assert_equals_int (g_list_length (objs), 3);
fail_unless (ges_timeline_layer_get_objects (layer2) == NULL);
fail_unless (ges_timeline_layer_get_objects (layer3) == NULL);
for (tmp = objs; tmp; tmp = g_list_next (tmp)) {
g_object_unref (tmp->data);
}
g_list_free (objs);
/* Check their priorities (layer1 priority is now 2) */
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object1), 0);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object2), 1);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object3), LAYER_HEIGHT - 1);
g_object_get (gnlobj1, "priority", &prio1, NULL);
g_object_get (gnlobj2, "priority", &prio2, NULL);
g_object_get (gnlobj3, "priority", &prio3, NULL);
assert_equals_int (prio1, 2 * LAYER_HEIGHT);
assert_equals_int (prio2, 2 * LAYER_HEIGHT + 1);
assert_equals_int (prio3, LAYER_HEIGHT * 3 - 1);
/* And change TrackObject-s priorities and check that changes are well
* refected on it containing TimelineObject */
ges_track_object_set_priority (tckobj3, LAYER_HEIGHT * 2);
g_object_get (gnlobj3, "priority", &prio3, NULL);
assert_equals_int (prio3, 2 * LAYER_HEIGHT);
assert_equals_int (GES_TIMELINE_OBJECT_PRIORITY (object3), 0);
g_object_unref (tckobj1);
g_object_unref (tckobj2);
g_object_unref (tckobj3);
g_object_unref (timeline);
}
GST_END_TEST;
GST_START_TEST (test_layer_automatic_transition)
{
GESTimeline *timeline;
GESTimelineLayer *layer;
GESTimelineTestSource *src, *srcbis;
GList *objects = NULL, *tmp = NULL;
gboolean res = FALSE;
ges_init ();
timeline = ges_timeline_new_audio_video ();
layer = ges_timeline_layer_new ();
ges_timeline_add_layer (timeline, layer);
g_object_set (layer, "auto-transition", TRUE, NULL);
src = ges_timeline_test_source_new ();
srcbis = ges_timeline_test_source_new ();
g_object_set (srcbis, "start", (gint64) 5000, "duration", (gint64) 10000LL,
NULL);
g_object_set (src, "start", (gint64) 0, "duration", (gint64) 10000LL, NULL);
ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (src));
ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (srcbis));
objects = ges_timeline_layer_get_objects (layer);
for (tmp = objects; tmp; tmp = tmp->next) {
if (GES_IS_TIMELINE_STANDARD_TRANSITION (tmp->data)) {
res = TRUE;
}
}
fail_unless (res == TRUE);
}
GST_END_TEST;
static Suite *
ges_suite (void)
{
@ -152,6 +340,8 @@ ges_suite (void)
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_layer_properties);
tcase_add_test (tc_chain, test_layer_priorities);
tcase_add_test (tc_chain, test_layer_automatic_transition);
return s;
}

View file

@ -71,6 +71,7 @@ GST_START_TEST (test_overlay_properties)
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));

View file

@ -20,6 +20,8 @@
#include <ges/ges.h>
#include <gst/check/gstcheck.h>
#include <string.h>
#include <unistd.h>
#define GetCurrentDir getcwd
#include <gst/audio/audio.h>
@ -121,6 +123,7 @@ GST_START_TEST (test_keyfile_save)
KEY ("Object0", "in-point", "0");
KEY ("Object0", "duration", "2000000000");
KEY ("Object0", "priority", "2");
KEY ("Object0", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
KEY ("Object0", "mute", "false");
KEY ("Object0", "vpattern", "100% Black");
KEY ("Object0", "freq", "440");
@ -140,6 +143,7 @@ GST_START_TEST (test_keyfile_save)
KEY ("Object1", "in-point", "0");
KEY ("Object1", "duration", "500000000");
KEY ("Object1", "priority", "1");
KEY ("Object1", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
KEY ("Object1", "vtype", "A bar moves from left to right");
COMPARE;
@ -154,6 +158,7 @@ GST_START_TEST (test_keyfile_save)
KEY ("Object2", "in-point", "0");
KEY ("Object2", "duration", "2000000000");
KEY ("Object2", "priority", "3");
KEY ("Object2", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
KEY ("Object2", "mute", "false");
KEY ("Object2", "vpattern", "100% Black");
KEY ("Object2", "freq", "440");
@ -183,8 +188,8 @@ GST_START_TEST (test_keyfile_save)
KEY ("Object3", "start", "5000000000");
KEY ("Object3", "in-point", "0");
KEY ("Object3", "duration", "1000000000");
/* The second layer's minimum priority will be 10 */
KEY ("Object3", "priority", "10");
KEY ("Object3", "priority", "0");
KEY ("Object3", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
KEY ("Object3", "mute", "false");
KEY ("Object3", "text", "\"the\\\\ quick\\\\ brown\\\\ fox\"");
KEY ("Object3", "font-desc", "\"Serif\\\\ 36\"");
@ -626,6 +631,53 @@ GST_START_TEST (test_keyfile_load)
GST_END_TEST;
GST_START_TEST (test_pitivi_file_load)
{
GESFormatter *formatter;
GESTimeline *timeline, *expected;
GMainLoop *mainloop;
char cCurrentPath[FILENAME_MAX];
char *a;
gchar *uri, *save_uri;
/*create the expected timeline */
timeline = ges_timeline_new ();
mainloop = g_main_loop_new (NULL, FALSE);
expected = ges_timeline_new ();
/* create the timeline from formatter */
formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
a = GetCurrentDir (cCurrentPath, sizeof (cCurrentPath));
uri = g_strconcat (a, "/test.xptv", NULL);
save_uri = g_strconcat (a, "/testsave.xptv", NULL);
if (g_file_test (uri, G_FILE_TEST_EXISTS) == FALSE) {
GST_ERROR ("Could not test GESPitiviFormatter as no project file found");
return;
}
ges_formatter_load_from_uri (formatter, timeline, uri);
g_timeout_add (1000, (GSourceFunc) g_main_loop_quit, mainloop);
g_main_loop_run (mainloop);
formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
ges_formatter_save_to_uri (formatter, timeline, save_uri);
formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
ges_formatter_load_from_uri (formatter, expected, uri);
g_timeout_add (1000, (GSourceFunc) g_main_loop_quit, mainloop);
g_main_loop_run (mainloop);
/* compare the two timelines and fail test if they are different */
TIMELINE_COMPARE (expected, timeline);
g_free (uri);
g_free (save_uri);
g_main_loop_unref (mainloop);
g_object_unref (formatter);
g_object_unref (timeline);
g_object_unref (expected);
}
GST_END_TEST;
GST_START_TEST (test_keyfile_identity)
{
@ -679,9 +731,11 @@ GST_START_TEST (test_keyfile_identity)
"freq", (gdouble) 600,
"volume", 1.0, "vpattern", GES_VIDEO_TEST_PATTERN_RED);
} LAYER_END;
}
LAYER_END;
} TIMELINE_END;
}
TIMELINE_END;
serialized = ges_timeline_new ();
@ -708,6 +762,7 @@ ges_suite (void)
tcase_add_test (tc_chain, test_keyfile_save);
tcase_add_test (tc_chain, test_keyfile_load);
tcase_add_test (tc_chain, test_keyfile_identity);
tcase_add_test (tc_chain, test_pitivi_file_load);
return s;
}

View file

@ -79,6 +79,7 @@ GST_START_TEST (test_object_properties)
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));
@ -146,6 +147,7 @@ GST_START_TEST (test_object_properties_unlocked)
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));

View file

@ -72,6 +72,7 @@ GST_START_TEST (test_title_source_properties)
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));

View file

@ -49,6 +49,7 @@ GST_START_TEST (test_transition_basic)
trackobject =
ges_timeline_object_create_track_object (GES_TIMELINE_OBJECT (tr2),
track);
ges_timeline_object_add_track_object (GES_TIMELINE_OBJECT (tr2), trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_video_transition_get_transition_type
@ -103,6 +104,7 @@ GST_START_TEST (test_transition_properties)
assert_equals_uint64 (GES_TIMELINE_OBJECT_INPOINT (object), 12);
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));
@ -157,6 +159,7 @@ GST_START_TEST (test_transition_properties)
GST_DEBUG ("creating track object");
trackobject = ges_timeline_object_create_track_object (object, track);
ges_timeline_object_add_track_object (object, trackobject);
fail_unless (trackobject != NULL);
fail_unless (ges_track_object_set_track (trackobject, track));

View file

@ -112,6 +112,7 @@ gboolean window_delete_event_cb (GtkObject * window, GdkEvent * event,
void new_activate_cb (GtkMenuItem * item, App * app);
void open_activate_cb (GtkMenuItem * item, App * app);
void save_as_activate_cb (GtkMenuItem * item, App * app);
void launch_pitivi_project_activate_cb (GtkMenuItem * item, App * app);
void quit_item_activate_cb (GtkMenuItem * item, App * app);
void delete_activate_cb (GtkAction * item, App * app);
void play_activate_cb (GtkAction * item, App * app);
@ -451,6 +452,24 @@ pipeline_state_changed_cb (App * app)
gtk_widget_set_sensitive (app->properties, !playing_or_paused);
}
static void
project_bus_message_cb (GstBus * bus, GstMessage * message,
GMainLoop * mainloop)
{
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR:
g_printerr ("ERROR\n");
g_main_loop_quit (mainloop);
break;
case GST_MESSAGE_EOS:
g_printerr ("Done\n");
g_main_loop_quit (mainloop);
break;
default:
break;
}
}
static void
bus_message_cb (GstBus * bus, GstMessage * message, App * app)
{
@ -1148,6 +1167,33 @@ app_add_file (App * app, gchar * uri)
obj, -1);
}
static void
app_launch_project (App * app, gchar * uri)
{
GESTimeline *timeline;
GMainLoop *mainloop;
GESTimelinePipeline *pipeline;
GstBus *bus;
GESFormatter *formatter;
uri = g_strsplit (uri, "//", 2)[1];
printf ("we will launch this uri : %s\n", uri);
formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
timeline = ges_timeline_new ();
pipeline = ges_timeline_pipeline_new ();
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
mainloop = g_main_loop_new (NULL, FALSE);
ges_timeline_pipeline_add_timeline (pipeline, timeline);
ges_formatter_load_from_uri (formatter, timeline, uri);
ges_timeline_pipeline_set_mode (pipeline, TIMELINE_MODE_PREVIEW_VIDEO);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message", G_CALLBACK (project_bus_message_cb),
mainloop);
g_main_loop_run (mainloop);
}
static void
app_add_title (App * app)
{
@ -1371,6 +1417,35 @@ new_activate_cb (GtkMenuItem * item, App * app)
app_new ();
}
void
launch_pitivi_project_activate_cb (GtkMenuItem * item, App * app)
{
GtkFileChooserDialog *dlg;
GtkFileFilter *filter;
GST_DEBUG ("add file signal handler");
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, "pitivi projects");
gtk_file_filter_add_pattern (filter, "*.xptv");
dlg = (GtkFileChooserDialog *)
gtk_file_chooser_dialog_new ("Preview Project...",
GTK_WINDOW (app->main_window),
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), filter);
g_object_set (G_OBJECT (dlg), "select-multiple", FALSE, NULL);
if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
gchar *uri;
uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg));
gtk_widget_destroy ((GtkWidget *) dlg);
app_launch_project (app, uri);
}
}
void
open_activate_cb (GtkMenuItem * item, App * app)
{

View file

@ -90,6 +90,16 @@
<signal name="activate" handler="save_as_activate_cb"/>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="launch_pitivi_project">
<property name="label" translatable="yes">Preview Pitivi Project</property>
<property name="visible">True</property>
<property name="tooltip_text" translatable="yes">Launches a .xptv project</property>
<property name="image">image1</property>
<property name="use_stock">False</property>
<signal name="activate" handler="launch_pitivi_project_activate_cb"/>
</object>
</child>
</object>
</child>
</object>
@ -271,8 +281,8 @@
<child>
<object class="GtkToolButton" id="play_button">
<property name="visible">True</property>
<property name="related_action">play</property>
<property name="use_action_appearance">True</property>
<property name="related_action">play</property>
<property name="label" translatable="yes">toolbutton1</property>
<property name="use_underline">True</property>
</object>
@ -284,8 +294,8 @@
<child>
<object class="GtkToolButton" id="toolbutton6">
<property name="visible">True</property>
<property name="related_action">stop</property>
<property name="use_action_appearance">True</property>
<property name="related_action">stop</property>
<property name="label" translatable="yes">toolbutton6</property>
<property name="use_underline">True</property>
</object>
@ -306,8 +316,8 @@
<child>
<object class="GtkToolButton" id="delete_button">
<property name="visible">True</property>
<property name="related_action">delete</property>
<property name="use_action_appearance">True</property>
<property name="related_action">delete</property>
<property name="label" translatable="yes">Delete</property>
<property name="use_underline">True</property>
</object>
@ -328,8 +338,8 @@
<child>
<object class="GtkToolButton" id="add_button">
<property name="visible">True</property>
<property name="related_action">add_file</property>
<property name="use_action_appearance">True</property>
<property name="related_action">add_file</property>
<property name="label" translatable="yes">toolbutton1</property>
<property name="use_underline">True</property>
</object>
@ -341,8 +351,8 @@
<child>
<object class="GtkToolButton" id="toolbutton2">
<property name="visible">True</property>
<property name="related_action">add_text</property>
<property name="use_action_appearance">True</property>
<property name="related_action">add_text</property>
<property name="label" translatable="yes">toolbutton2</property>
<property name="use_underline">True</property>
</object>
@ -354,8 +364,8 @@
<child>
<object class="GtkToolButton" id="toolbutton4">
<property name="visible">True</property>
<property name="related_action">add_test</property>
<property name="use_action_appearance">True</property>
<property name="related_action">add_test</property>
<property name="label" translatable="yes">toolbutton4</property>
<property name="use_underline">True</property>
</object>
@ -367,8 +377,8 @@
<child>
<object class="GtkToolButton" id="toolbutton5">
<property name="visible">True</property>
<property name="related_action">add_transition</property>
<property name="use_action_appearance">True</property>
<property name="related_action">add_transition</property>
<property name="label" translatable="yes">toolbutton5</property>
<property name="use_underline">True</property>
</object>
@ -402,8 +412,8 @@
<child>
<object class="GtkToolButton" id="toolbutton8">
<property name="visible">True</property>
<property name="related_action">move_up</property>
<property name="use_action_appearance">True</property>
<property name="related_action">move_up</property>
<property name="label" translatable="yes">toolbutton8</property>
<property name="use_underline">True</property>
</object>
@ -415,8 +425,8 @@
<child>
<object class="GtkToolButton" id="toolbutton9">
<property name="visible">True</property>
<property name="related_action">move_down</property>
<property name="use_action_appearance">True</property>
<property name="related_action">move_down</property>
<property name="label" translatable="yes">toolbutton9</property>
<property name="use_underline">True</property>
</object>
@ -437,8 +447,8 @@
<child>
<object class="GtkToggleToolButton" id="toolbutton11">
<property name="visible">True</property>
<property name="related_action">audio_track</property>
<property name="use_action_appearance">True</property>
<property name="related_action">audio_track</property>
<property name="label" translatable="yes">toolbutton11</property>
<property name="use_underline">True</property>
</object>
@ -450,8 +460,8 @@
<child>
<object class="GtkToggleToolButton" id="toolbutton12">
<property name="visible">True</property>
<property name="related_action">video_track</property>
<property name="use_action_appearance">True</property>
<property name="related_action">video_track</property>
<property name="label" translatable="yes">toolbutton12</property>
<property name="use_underline">True</property>
</object>
@ -1013,6 +1023,10 @@
<action-widget response="0">button1</action-widget>
</action-widgets>
</object>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-floppy</property>
</object>
<object class="GtkSizeGroup" id="LabelSizeGroup">
<widgets>
<widget name="label4"/>

View file

@ -35,6 +35,8 @@ static guint repeat = 0;
static GESTimelinePipeline *pipeline = NULL;
static gboolean seenerrors = FALSE;
void load_project (gchar * uri);
static gchar *
ensure_uri (gchar * location)
{
@ -396,6 +398,31 @@ print_pattern_list (void)
print_enum (GES_VIDEO_TEST_PATTERN_TYPE);
}
void
load_project (gchar * uri)
{
GESFormatter *formatter;
GESTimeline *timeline;
GMainLoop *mainloop;
GESTimelinePipeline *pipeline;
GstBus *bus;
formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
timeline = ges_timeline_new ();
pipeline = ges_timeline_pipeline_new ();
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
mainloop = g_main_loop_new (NULL, FALSE);
ges_timeline_pipeline_add_timeline (pipeline, timeline);
ges_formatter_load_from_uri (formatter, timeline, uri);
ges_timeline_pipeline_set_mode (pipeline, TIMELINE_MODE_PREVIEW_VIDEO);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);
g_main_loop_run (mainloop);
}
int
main (int argc, gchar ** argv)
{
@ -416,6 +443,7 @@ main (int argc, gchar ** argv)
static gboolean verbose = FALSE;
gchar *save_path = NULL;
gchar *load_path = NULL;
gchar *project_path = NULL;
GOptionEntry options[] = {
{"thumbnail", 'm', 0.0, G_OPTION_ARG_DOUBLE, &thumbinterval,
"Take thumbnails every n seconds (saved in current directory)", "N"},
@ -451,6 +479,8 @@ main (int argc, gchar ** argv)
"Output status information and property notifications", NULL},
{"exclude", 'X', 0, G_OPTION_ARG_NONE, &exclude_args,
"Do not output status information of TYPE", "TYPE1,TYPE2,..."},
{"load-xptv", 'y', 0, G_OPTION_ARG_STRING, &project_path,
"Load xptv project from file for previewing", "<path>"},
{NULL}
};
GOptionContext *ctx;
@ -499,6 +529,10 @@ main (int argc, gchar ** argv)
exit (0);
}
if (project_path) {
load_project (project_path);
exit (0);
}
if (((!load_path && (argc < 4))) || (outputuri && (!render && !smartrender))) {
g_printf ("%s", g_option_context_get_help (ctx, TRUE, NULL));
g_option_context_free (ctx);