mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-21 07:46:38 +00:00
c7a984824f
API: ges_pitivi_formatter_new
1018 lines
32 KiB
C
1018 lines
32 KiB
C
#include <ges/ges.h>
|
|
#include <unistd.h>
|
|
#define GetCurrentDir getcwd
|
|
|
|
G_DEFINE_TYPE (GESPitiviFormatter, ges_pitivi_formatter, GES_TYPE_FORMATTER);
|
|
|
|
static gboolean save_pitivi_timeline_to_uri (GESFormatter * pitivi_formatter,
|
|
GESTimeline * timeline, const gchar * uri);
|
|
static gboolean load_pitivi_file_from_uri (GESFormatter * self,
|
|
GESTimeline * timeline, const gchar * uri);
|
|
|
|
static void ges_pitivi_formatter_finalize (GObject * object);
|
|
|
|
static xmlDocPtr create_doc (const gchar * uri);
|
|
|
|
static GHashTable *get_nodes_infos (xmlNodePtr nodes);
|
|
static gboolean create_tracks (GESFormatter * self);
|
|
static GHashTable *list_sources (GESFormatter * self);
|
|
static gboolean parse_track_objects (GESFormatter * self);
|
|
static gboolean parse_timeline_objects (GESFormatter * self);
|
|
|
|
static void save_tracks (GESTimeline * timeline, xmlTextWriterPtr writer,
|
|
GList * source_list);
|
|
static GList *save_sources (GESTimelineLayer * layer, xmlTextWriterPtr writer);
|
|
static void save_track_objects (xmlTextWriterPtr writer, GList * source_list,
|
|
gchar * res, gint * id);
|
|
static void save_timeline_objects (xmlTextWriterPtr writer, GList * list);
|
|
static void destroy_all (GList * list);
|
|
static void create_new_source_table (gchar * key, gchar * value,
|
|
GHashTable * table);
|
|
static gboolean make_timeline_objects (GESFormatter * self);
|
|
void set_properties (GObject * obj, GHashTable * props_table);
|
|
void make_source (GList * ref_list,
|
|
GHashTable * source_table, GESFormatter * self);
|
|
|
|
void layers_table_destroyer (gpointer data, gpointer data2, void *unused);
|
|
void list_table_destroyer (gpointer data, gpointer data2, void *unused);
|
|
void destroyer (gpointer data, gpointer data2, void *unused);
|
|
void ultimate_table_destroyer (gpointer data, gpointer data2, void *unused);
|
|
static void
|
|
track_object_added_cb (GESTimelineObject * object,
|
|
GESTrackObject * track_object, GHashTable * props_table);
|
|
|
|
struct _GESPitiviFormatterPrivate
|
|
{
|
|
gint not_done;
|
|
xmlXPathContextPtr xpathCtx;
|
|
GHashTable *source_table, *track_objects_table, *timeline_objects_table,
|
|
*layers_table;
|
|
GESTimeline *timeline;
|
|
gboolean parsed;
|
|
GESTrack *tracka, *trackv;
|
|
GESTimelineTestSource *background;
|
|
};
|
|
|
|
static void
|
|
ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass)
|
|
{
|
|
GESFormatterClass *formatter_klass;
|
|
GObjectClass *object_class;
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
formatter_klass = GES_FORMATTER_CLASS (klass);
|
|
g_type_class_add_private (klass, sizeof (GESPitiviFormatterPrivate));
|
|
|
|
formatter_klass->save_to_uri = save_pitivi_timeline_to_uri;
|
|
formatter_klass->load_from_uri = load_pitivi_file_from_uri;
|
|
object_class->finalize = ges_pitivi_formatter_finalize;
|
|
}
|
|
|
|
static void
|
|
ges_pitivi_formatter_init (GESPitiviFormatter * self)
|
|
{
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
|
GES_TYPE_PITIVI_FORMATTER, GESPitiviFormatterPrivate);
|
|
|
|
self->priv->not_done = 0;
|
|
|
|
self->priv->track_objects_table =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
|
|
|
self->priv->timeline_objects_table =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
|
|
|
self->priv->layers_table =
|
|
g_hash_table_new_full (g_int_hash, g_str_equal, NULL, NULL);
|
|
|
|
self->priv->parsed = FALSE;
|
|
}
|
|
|
|
static void
|
|
ges_pitivi_formatter_finalize (GObject * object)
|
|
{
|
|
GESPitiviFormatter *self = GES_PITIVI_FORMATTER (object);
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
g_hash_table_foreach (priv->source_table, (GHFunc) ultimate_table_destroyer,
|
|
NULL);
|
|
g_hash_table_destroy (priv->source_table);
|
|
|
|
g_hash_table_foreach (priv->timeline_objects_table,
|
|
(GHFunc) list_table_destroyer, NULL);
|
|
g_hash_table_destroy (priv->timeline_objects_table);
|
|
|
|
g_hash_table_foreach (priv->layers_table, (GHFunc) layers_table_destroyer,
|
|
NULL);
|
|
g_hash_table_destroy (priv->layers_table);
|
|
|
|
g_hash_table_foreach (priv->track_objects_table,
|
|
(GHFunc) ultimate_table_destroyer, NULL);
|
|
g_hash_table_destroy (priv->track_objects_table);
|
|
|
|
G_OBJECT_CLASS (ges_pitivi_formatter_parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
save_pitivi_timeline_to_uri (GESFormatter * pitivi_formatter,
|
|
GESTimeline * timeline, const gchar * uri)
|
|
{
|
|
xmlTextWriterPtr writer;
|
|
GList *list = NULL, *layers = NULL, *tmp = NULL;
|
|
writer = xmlNewTextWriterFilename (uri, 0);
|
|
|
|
xmlTextWriterSetIndent (writer, 1);
|
|
xmlTextWriterStartElement (writer, BAD_CAST "pitivi");
|
|
|
|
layers = ges_timeline_get_layers (timeline);
|
|
xmlTextWriterStartElement (writer, BAD_CAST "factories");
|
|
xmlTextWriterStartElement (writer, BAD_CAST "sources");
|
|
for (tmp = layers; tmp; tmp = tmp->next) {
|
|
|
|
/* 99 is the priority of the background source. */
|
|
if (ges_timeline_layer_get_priority (tmp->data) != 99) {
|
|
list = save_sources (tmp->data, writer);
|
|
}
|
|
}
|
|
xmlTextWriterEndElement (writer);
|
|
xmlTextWriterEndElement (writer);
|
|
save_tracks (timeline, writer, list);
|
|
save_timeline_objects (writer, list);
|
|
xmlTextWriterEndDocument (writer);
|
|
xmlFreeTextWriter (writer);
|
|
|
|
g_list_free (layers);
|
|
g_list_foreach (list, (GFunc) destroy_all, NULL);
|
|
g_list_free (list);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
load_pitivi_file_from_uri (GESFormatter * self,
|
|
GESTimeline * timeline, const gchar * uri)
|
|
{
|
|
xmlDocPtr doc;
|
|
GESTimelineLayer *layer;
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
|
|
gboolean ret = TRUE;
|
|
gint *prio = malloc (sizeof (gint));
|
|
|
|
*prio = 0;
|
|
layer = ges_timeline_layer_new ();
|
|
g_object_set (layer, "auto-transition", TRUE, NULL);
|
|
|
|
g_hash_table_insert (priv->layers_table, prio, layer);
|
|
priv->timeline = timeline;
|
|
g_object_set (layer, "priority", (gint32) 0, NULL);
|
|
|
|
if (!ges_timeline_add_layer (timeline, layer)) {
|
|
GST_ERROR ("Couldn't add layer");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(doc = create_doc (uri))) {
|
|
GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
|
|
uri);
|
|
return FALSE;
|
|
}
|
|
|
|
priv->xpathCtx = xmlXPathNewContext (doc);
|
|
|
|
if (!create_tracks (self)) {
|
|
GST_ERROR ("Couldn't create tracks");
|
|
ret = FALSE;
|
|
goto fail;
|
|
}
|
|
|
|
priv->source_table = list_sources (self);
|
|
|
|
if (!parse_timeline_objects (self)) {
|
|
GST_ERROR ("Couldn't find timeline objects markup in the xptv file");
|
|
ret = FALSE;
|
|
goto fail;
|
|
}
|
|
|
|
if (!parse_track_objects (self)) {
|
|
GST_ERROR ("Couldn't find track objects markup in the xptv file");
|
|
ret = FALSE;
|
|
}
|
|
|
|
if (!make_timeline_objects (self))
|
|
ret = FALSE;
|
|
|
|
fail:
|
|
xmlXPathFreeContext (priv->xpathCtx);
|
|
xmlFreeDoc (doc);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
save_timeline_objects (xmlTextWriterPtr writer, GList * list)
|
|
{
|
|
GList *tmp;
|
|
gint n_objects, i;
|
|
|
|
xmlTextWriterStartElement (writer, BAD_CAST "timeline-objects");
|
|
|
|
for (tmp = list; tmp; tmp = tmp->next) {
|
|
|
|
GList *elem;
|
|
xmlChar *cast;
|
|
|
|
xmlTextWriterStartElement (writer, BAD_CAST "timeline-object");
|
|
elem = tmp->data;
|
|
xmlTextWriterStartElement (writer, BAD_CAST "factory-ref");
|
|
cast = g_list_first (elem)->data;
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
|
|
xmlTextWriterEndElement (writer);
|
|
xmlTextWriterStartElement (writer, BAD_CAST "track-object-refs");
|
|
|
|
n_objects = g_list_length (elem) - 4;
|
|
for (i = 0; i < n_objects; i++) {
|
|
xmlTextWriterStartElement (writer, BAD_CAST "track-object-ref");
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "id",
|
|
BAD_CAST (gchar *) (g_list_nth (elem, (guint) 4 + i)->data));
|
|
xmlTextWriterEndElement (writer);
|
|
}
|
|
xmlTextWriterEndElement (writer);
|
|
xmlTextWriterEndElement (writer);
|
|
}
|
|
xmlTextWriterEndElement (writer);
|
|
}
|
|
|
|
static GList *
|
|
save_sources (GESTimelineLayer * layer, xmlTextWriterPtr writer)
|
|
{
|
|
GList *objects, *tmp;
|
|
GHashTable *source_table;
|
|
|
|
GList *source_list = NULL;
|
|
int id = 1;
|
|
objects = ges_timeline_layer_get_objects (layer);
|
|
source_table =
|
|
g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free);
|
|
|
|
for (tmp = objects; tmp; tmp = tmp->next) {
|
|
GList *ref_type_list = NULL;
|
|
GESTimelineObject *object;
|
|
gchar *tfs_uri;
|
|
xmlChar *cast;
|
|
object = tmp->data;
|
|
|
|
if GES_IS_TIMELINE_FILE_SOURCE
|
|
(object) {
|
|
|
|
tfs_uri = (gchar *) ges_timeline_filesource_get_uri
|
|
(GES_TIMELINE_FILE_SOURCE (object));
|
|
|
|
if (!g_hash_table_lookup (source_table, tfs_uri)) {
|
|
cast = xmlXPathCastNumberToString (id);
|
|
g_hash_table_insert (source_table, g_strdup (tfs_uri),
|
|
g_strdup ((gchar *) cast));
|
|
xmlFree (cast);
|
|
xmlTextWriterStartElement (writer, BAD_CAST "source");
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "filename",
|
|
BAD_CAST tfs_uri);
|
|
cast = xmlXPathCastNumberToString (id);
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
|
|
xmlFree (cast);
|
|
xmlTextWriterEndElement (writer);
|
|
id++;
|
|
}
|
|
|
|
ref_type_list =
|
|
g_list_append (ref_type_list,
|
|
g_strdup (g_hash_table_lookup (source_table, tfs_uri)));
|
|
ref_type_list = g_list_append (ref_type_list, object);
|
|
ref_type_list = g_list_append (ref_type_list, g_strdup ("simple"));
|
|
ref_type_list =
|
|
g_list_append (ref_type_list,
|
|
GINT_TO_POINTER (ges_timeline_layer_get_priority (layer)));
|
|
source_list = g_list_append (source_list, g_list_copy (ref_type_list));
|
|
g_list_free (ref_type_list);
|
|
}
|
|
}
|
|
|
|
g_object_unref (G_OBJECT (layer));
|
|
g_list_free (objects);
|
|
g_hash_table_destroy (source_table);
|
|
return source_list;
|
|
}
|
|
|
|
static void
|
|
save_tracks (GESTimeline * timeline, xmlTextWriterPtr writer,
|
|
GList * source_list)
|
|
{
|
|
GList *tracks, *tmp;
|
|
|
|
gint id = 0;
|
|
|
|
xmlTextWriterStartElement (writer, BAD_CAST "timeline");
|
|
xmlTextWriterStartElement (writer, BAD_CAST "tracks");
|
|
tracks = ges_timeline_get_tracks (timeline);
|
|
|
|
for (tmp = tracks; tmp; tmp = tmp->next) {
|
|
gchar *type, *caps, *res;
|
|
GESTrack *track;
|
|
GValue v = { 0 };
|
|
track = GES_TRACK (tmp->data);
|
|
xmlTextWriterStartElement (writer, BAD_CAST "track");
|
|
xmlTextWriterStartElement (writer, BAD_CAST "stream");
|
|
g_value_init (&v, GES_TYPE_TRACK_TYPE);
|
|
g_object_get_property (G_OBJECT (track), "track-type", &v);
|
|
type = gst_value_serialize (&v);
|
|
caps = gst_caps_to_string (ges_track_get_caps (track));
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "caps", BAD_CAST caps);
|
|
g_free (caps);
|
|
|
|
if (!g_strcmp0 (type, "GES_TRACK_TYPE_AUDIO")) {
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
|
|
BAD_CAST "pitivi.stream.AudioStream");
|
|
xmlTextWriterEndElement (writer);
|
|
res = (gchar *) "audio";
|
|
} else {
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "type",
|
|
BAD_CAST "pitivi.stream.VideoStream");
|
|
xmlTextWriterEndElement (writer);
|
|
res = (gchar *) "video";
|
|
}
|
|
g_free (type);
|
|
save_track_objects (writer, source_list, res, &id);
|
|
xmlTextWriterEndElement (writer);
|
|
}
|
|
|
|
g_list_free (tracks);
|
|
xmlTextWriterEndElement (writer);
|
|
}
|
|
|
|
static void
|
|
save_track_objects (xmlTextWriterPtr writer, GList * source_list, gchar * res,
|
|
gint * id)
|
|
{
|
|
GList *tmp, *tck_objs, *tmp_tck;
|
|
gchar *bin_desc;
|
|
xmlTextWriterStartElement (writer, BAD_CAST "track-objects");
|
|
|
|
for (tmp = source_list; tmp; tmp = tmp->next) {
|
|
GList *elem;
|
|
GESTimelineObject *object;
|
|
guint i, n, j;
|
|
|
|
elem = tmp->data;
|
|
object = g_list_next (elem)->data;
|
|
tck_objs = ges_timeline_object_get_track_objects (object);
|
|
|
|
for (tmp_tck = tck_objs; tmp_tck; tmp_tck = tmp_tck->next) {
|
|
GParamSpec **properties;
|
|
xmlChar *cast;
|
|
gchar *prio_str;
|
|
|
|
if (!ges_track_object_is_active (tmp_tck->data)) {
|
|
continue;
|
|
}
|
|
|
|
if (((ges_track_object_get_track (tmp_tck->data)->type ==
|
|
GES_TRACK_TYPE_VIDEO)
|
|
&& (!g_strcmp0 (res, (gchar *) "video")))
|
|
|| ((ges_track_object_get_track (tmp_tck->data)->type ==
|
|
GES_TRACK_TYPE_AUDIO)
|
|
&& (!g_strcmp0 (res, (gchar *) "audio")))) {
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
xmlTextWriterStartElement (writer, BAD_CAST "track-object");
|
|
cast =
|
|
xmlXPathCastNumberToString (GPOINTER_TO_INT (g_list_nth (elem,
|
|
(guint) 3)->data));
|
|
prio_str = g_strconcat ((gchar *) "(int)", (gchar *) cast, NULL);
|
|
xmlFree (cast);
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "priority",
|
|
BAD_CAST prio_str);
|
|
g_free (prio_str);
|
|
properties =
|
|
g_object_class_list_properties (G_OBJECT_GET_CLASS (tmp_tck->data),
|
|
&n);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
GParamSpec *p = properties[i];
|
|
GValue v = { 0 };
|
|
gchar *serialized, *concatenated;
|
|
|
|
if (!g_strcmp0 (p->name, (gchar *) "duration") ||
|
|
!g_strcmp0 (p->name, (gchar *) "start") ||
|
|
!g_strcmp0 (p->name, (gchar *) "in-point")) {
|
|
g_value_init (&v, p->value_type);
|
|
g_object_get_property (G_OBJECT (tmp_tck->data), p->name, &v);
|
|
serialized = gst_value_serialize (&v);
|
|
concatenated = g_strconcat ((gchar *) "(gint64)", serialized, NULL);
|
|
|
|
if (!g_strcmp0 (p->name, (gchar *) "in-point")) {
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST (gchar *) "in_point",
|
|
BAD_CAST concatenated);
|
|
} else {
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST p->name,
|
|
BAD_CAST concatenated);
|
|
}
|
|
g_free (concatenated);
|
|
g_free (serialized);
|
|
}
|
|
}
|
|
g_free (properties);
|
|
cast = xmlXPathCastNumberToString (*id);
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
|
|
xmlFree (cast);
|
|
|
|
if (GES_IS_TRACK_EFFECT (tmp_tck->data)) {
|
|
GParamSpec **pspecs, *spec;
|
|
gchar *serialized, *concatenated;
|
|
guint n_props = 0;
|
|
|
|
g_object_get (tmp_tck->data, "bin-description", &bin_desc, NULL);
|
|
xmlTextWriterStartElement (writer, BAD_CAST "effect");
|
|
xmlTextWriterStartElement (writer, BAD_CAST "factory");
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "name",
|
|
BAD_CAST bin_desc);
|
|
xmlTextWriterEndElement (writer);
|
|
xmlTextWriterStartElement (writer, BAD_CAST "gst-element-properties");
|
|
|
|
pspecs =
|
|
ges_track_object_list_children_properties (tmp_tck->data, &n_props);
|
|
|
|
j = 0;
|
|
|
|
while (j < n_props) {
|
|
GValue val = { 0 };
|
|
spec = pspecs[j];
|
|
g_value_init (&val, spec->value_type);
|
|
ges_track_object_get_child_property_by_pspec (tmp_tck->data, spec,
|
|
&val);
|
|
serialized = gst_value_serialize (&val);
|
|
if (!g_strcmp0 (spec->name, (gchar *) "preset")) {
|
|
concatenated =
|
|
g_strconcat ("(GEnum)",
|
|
xmlXPathCastNumberToString ((g_value_get_enum (&val))), NULL);
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST spec->name,
|
|
BAD_CAST concatenated);
|
|
} else {
|
|
concatenated =
|
|
g_strconcat ("(", g_type_name (spec->value_type), ")",
|
|
serialized, NULL);
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST spec->name,
|
|
BAD_CAST concatenated);
|
|
}
|
|
j++;
|
|
}
|
|
|
|
xmlTextWriterEndElement (writer);
|
|
|
|
for (i = 0; i < n_props; i++) {
|
|
g_param_spec_unref (pspecs[i]);
|
|
}
|
|
|
|
g_free (pspecs);
|
|
|
|
} else {
|
|
xmlTextWriterStartElement (writer, BAD_CAST "factory-ref");
|
|
cast = g_list_first (elem)->data;
|
|
xmlTextWriterWriteAttribute (writer, BAD_CAST "id", BAD_CAST cast);
|
|
}
|
|
xmlTextWriterEndElement (writer);
|
|
xmlTextWriterEndElement (writer);
|
|
|
|
if (GES_IS_TRACK_EFFECT (tmp_tck->data)) {
|
|
elem = g_list_append (elem, xmlXPathCastNumberToString (*id));
|
|
} else {
|
|
elem = g_list_insert (elem, xmlXPathCastNumberToString (*id), 4);
|
|
}
|
|
*id = *id + 1;
|
|
}
|
|
}
|
|
|
|
xmlTextWriterEndElement (writer);
|
|
}
|
|
|
|
static xmlDocPtr
|
|
create_doc (const gchar * uri)
|
|
{
|
|
xmlDocPtr doc;
|
|
doc = xmlParseFile (uri);
|
|
return doc;
|
|
}
|
|
|
|
static GHashTable *
|
|
list_sources (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
xmlXPathObjectPtr xpathObj;
|
|
GHashTable *table, *sources_table;
|
|
int size, j;
|
|
gchar *id;
|
|
xmlNodeSetPtr nodes;
|
|
|
|
sources_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
|
xpathObj = xmlXPathEvalExpression ((const xmlChar *)
|
|
"/pitivi/factories/sources/source", priv->xpathCtx);
|
|
nodes = xpathObj->nodesetval;
|
|
size = (nodes) ? nodes->nodeNr : 0;
|
|
for (j = 0; j < size; ++j) {
|
|
table = get_nodes_infos (nodes->nodeTab[j]);
|
|
id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
|
|
g_hash_table_insert (sources_table, g_strdup (id), table);
|
|
}
|
|
|
|
xmlXPathFreeObject (xpathObj);
|
|
return sources_table;
|
|
}
|
|
|
|
static gboolean
|
|
make_timeline_objects (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
GHashTable *source_table;
|
|
GESTimelineLayer *back_layer;
|
|
gint i;
|
|
gint *prio = malloc (sizeof (gint));
|
|
|
|
GList *keys = NULL, *tmp = NULL, *ref_list = NULL;
|
|
|
|
*prio = 0;
|
|
|
|
priv->background = ges_timeline_test_source_new ();
|
|
back_layer = ges_timeline_layer_new ();
|
|
ges_timeline_layer_set_priority (back_layer, 99);
|
|
if (!ges_timeline_add_layer (priv->timeline, back_layer)) {
|
|
GST_ERROR ("Couldn't add layer");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ges_timeline_layer_add_object (back_layer,
|
|
GES_TIMELINE_OBJECT (priv->background))) {
|
|
GST_ERROR ("Couldn't add background to the layer");
|
|
return FALSE;
|
|
}
|
|
|
|
keys = g_hash_table_get_keys (priv->timeline_objects_table);
|
|
|
|
for (tmp = keys, i = 1; tmp; tmp = tmp->next, i++) {
|
|
if (i == g_list_length (keys))
|
|
priv->parsed = TRUE;
|
|
ref_list =
|
|
g_hash_table_lookup (priv->timeline_objects_table, (gchar *) tmp->data);
|
|
source_table =
|
|
g_hash_table_lookup (priv->source_table, (gchar *) tmp->data);
|
|
make_source (ref_list, source_table, self);
|
|
}
|
|
g_hash_table_insert (priv->layers_table, prio, back_layer);
|
|
free (prio);
|
|
|
|
g_list_free (keys);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
make_source (GList * ref_list, GHashTable * source_table, GESFormatter * self)
|
|
{
|
|
GHashTable *props_table, *effect_table;
|
|
gchar **prio_array;
|
|
GESTimelineLayer *layer;
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
|
|
gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL;
|
|
GList *tmp = NULL, *keys, *tmp_key;
|
|
GESTimelineFileSource *src = NULL;
|
|
gint cast_prio = 0;
|
|
gint *prio = malloc (sizeof (gint));
|
|
gboolean a_avail = FALSE, v_avail = FALSE, video = FALSE;
|
|
|
|
for (tmp = ref_list; tmp; tmp = tmp->next) {
|
|
props_table =
|
|
g_hash_table_lookup (priv->track_objects_table, (gchar *) tmp->data);
|
|
prio_array =
|
|
g_strsplit ((gchar *) g_hash_table_lookup (props_table,
|
|
(gchar *) "priority"), ")", 0);
|
|
cast_prio = (gint) g_ascii_strtod (prio_array[1], NULL);
|
|
*prio = cast_prio;
|
|
fac_ref = (gchar *) g_hash_table_lookup (props_table, (gchar *) "fac_ref");
|
|
media_type =
|
|
(gchar *) g_hash_table_lookup (props_table, (gchar *) "media_type");
|
|
|
|
g_strfreev (prio_array);
|
|
|
|
if (!g_strcmp0 (media_type, (gchar *) "pitivi.stream.VideoStream"))
|
|
video = TRUE;
|
|
else
|
|
video = FALSE;
|
|
|
|
if (!(layer = g_hash_table_lookup (priv->layers_table, &cast_prio))) {
|
|
layer = ges_timeline_layer_new ();
|
|
g_object_set (layer, "auto-transition", TRUE, NULL);
|
|
ges_timeline_layer_set_priority (layer, cast_prio);
|
|
ges_timeline_add_layer (priv->timeline, layer);
|
|
g_hash_table_insert (priv->layers_table, prio, layer);
|
|
free (prio);
|
|
}
|
|
|
|
if (g_strcmp0 (fac_ref, (gchar *) "effect") && a_avail && (!video)) {
|
|
a_avail = FALSE;
|
|
g_signal_connect (src, "track-object-added",
|
|
G_CALLBACK (track_object_added_cb), props_table);
|
|
|
|
} else if (g_strcmp0 (fac_ref, (gchar *) "effect") && v_avail && (video)) {
|
|
v_avail = FALSE;
|
|
g_signal_connect (src, "track-object-added",
|
|
G_CALLBACK (track_object_added_cb), props_table);
|
|
|
|
} else if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
|
|
char cCurrentPath[FILENAME_MAX], *path;
|
|
|
|
if (a_avail) {
|
|
ges_timeline_filesource_set_supported_formats (src,
|
|
GES_TRACK_TYPE_VIDEO);
|
|
} else if (v_avail) {
|
|
ges_timeline_filesource_set_supported_formats (src,
|
|
GES_TRACK_TYPE_AUDIO);
|
|
}
|
|
|
|
filename =
|
|
(gchar *) g_hash_table_lookup (source_table, (gchar *) "filename");
|
|
path = GetCurrentDir (cCurrentPath, sizeof (cCurrentPath));
|
|
|
|
if (!g_strcmp0 (filename, (gchar *) "/DJ5r3oNFVeE.flv") ||
|
|
!g_strcmp0 (filename, (gchar *) "/a1Y73sPHKxw.flv")) {
|
|
filename = g_strconcat ("file://", path, filename, NULL);
|
|
src = ges_timeline_filesource_new (filename);
|
|
g_free (filename);
|
|
} else {
|
|
src = ges_timeline_filesource_new (filename);
|
|
}
|
|
|
|
if (!video) {
|
|
v_avail = TRUE;
|
|
a_avail = FALSE;
|
|
} else {
|
|
a_avail = TRUE;
|
|
v_avail = FALSE;
|
|
}
|
|
set_properties (G_OBJECT (src), props_table);
|
|
ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (src));
|
|
|
|
} else if (!g_strcmp0 (fac_ref, (gchar *) "effect")) {
|
|
GESTrackParseLaunchEffect *effect;
|
|
gchar *active = (gchar *)
|
|
g_hash_table_lookup (props_table, (gchar *) "active");
|
|
|
|
effect = ges_track_parse_launch_effect_new ((gchar *)
|
|
g_hash_table_lookup (props_table, (gchar *) "effect_name"));
|
|
effect_table =
|
|
g_hash_table_lookup (props_table, (gchar *) "effect_props");
|
|
|
|
ges_timeline_object_add_track_object (GES_TIMELINE_OBJECT (src),
|
|
GES_TRACK_OBJECT (effect));
|
|
|
|
if (!g_strcmp0 (active, (gchar *) "(bool)False"))
|
|
ges_track_object_set_active (GES_TRACK_OBJECT (effect), FALSE);
|
|
if (video)
|
|
ges_track_add_object (priv->trackv, GES_TRACK_OBJECT (effect));
|
|
else
|
|
ges_track_add_object (priv->tracka, GES_TRACK_OBJECT (effect));
|
|
keys = g_hash_table_get_keys (effect_table);
|
|
|
|
for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) {
|
|
gchar **value_array =
|
|
g_strsplit ((gchar *) g_hash_table_lookup (effect_table,
|
|
(gchar *) tmp_key->data),
|
|
(gchar *) ")", (gint) 0);
|
|
gchar *value = g_ascii_strdown (value_array[1], -1);
|
|
|
|
if (!g_strcmp0 (value, (gchar *) "true"))
|
|
ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
|
|
(gchar *) tmp_key->data, TRUE, NULL);
|
|
else if (!g_strcmp0 (value, (gchar *) "false"))
|
|
ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
|
|
(gchar *) tmp_key->data, FALSE, NULL);
|
|
else if (!g_strcmp0 (value, (gchar *) "effect"))
|
|
continue;
|
|
else if (!g_strcmp0 ((gchar *) "(guint", value_array[0])
|
|
|| !g_strcmp0 ((gchar *) "(GEnum", value_array[0])
|
|
|| !g_strcmp0 ((gchar *) "(gint", value_array[0]))
|
|
ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
|
|
(gchar *) tmp_key->data, g_ascii_strtoll (value, NULL, 0), NULL);
|
|
else
|
|
ges_track_object_set_child_property (GES_TRACK_OBJECT (effect),
|
|
(gchar *) tmp_key->data, g_ascii_strtod (value, NULL), NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (a_avail) {
|
|
ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_VIDEO);
|
|
} else if (v_avail) {
|
|
ges_timeline_filesource_set_supported_formats (src, GES_TRACK_TYPE_AUDIO);
|
|
}
|
|
free (prio);
|
|
}
|
|
|
|
void
|
|
set_properties (GObject * obj, GHashTable * props_table)
|
|
{
|
|
gint i;
|
|
gchar **prop_array;
|
|
gint64 prop_value;
|
|
|
|
gchar list[3][10] = { "duration", "in_point", "start" };
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
prop_array =
|
|
g_strsplit ((gchar *) g_hash_table_lookup (props_table, list[i]),
|
|
(gchar *) ")", (gint) 0);
|
|
prop_value = g_ascii_strtoll ((gchar *) prop_array[1], NULL, 0);
|
|
g_object_set (obj, list[i], prop_value, NULL);
|
|
g_strfreev (prop_array);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
parse_track_objects (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
xmlXPathObjectPtr xpathObj;
|
|
xmlNodeSetPtr nodes;
|
|
int size, j;
|
|
gchar *id, *fac_ref;
|
|
GHashTable *table = NULL, *new_table, *effect_table;
|
|
xmlNode *ref_node;
|
|
gchar *media_type;
|
|
|
|
xpathObj = xmlXPathEvalExpression ((const xmlChar *)
|
|
"/pitivi/timeline/tracks/track/track-objects/track-object",
|
|
priv->xpathCtx);
|
|
|
|
if (xpathObj == NULL) {
|
|
xmlXPathFreeObject (xpathObj);
|
|
return FALSE;
|
|
}
|
|
nodes = xpathObj->nodesetval;
|
|
size = (nodes) ? nodes->nodeNr : 0;
|
|
|
|
for (j = 0; j < size; ++j) {
|
|
GHashTable *new_effect_table = NULL;
|
|
table = get_nodes_infos (nodes->nodeTab[j]);
|
|
id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
|
|
ref_node = nodes->nodeTab[j]->children->next;
|
|
fac_ref = (gchar *) xmlGetProp (ref_node, (xmlChar *) "id");
|
|
new_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
|
|
|
if (!g_strcmp0 ((gchar *) ref_node->name, (gchar *) "effect")) {
|
|
fac_ref = (gchar *) "effect";
|
|
ref_node = ref_node->children->next;
|
|
new_effect_table =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
|
g_hash_table_insert (table, g_strdup ((gchar *) "effect_name"),
|
|
g_strdup ((gchar *) xmlGetProp (ref_node, (xmlChar *) "name")));
|
|
effect_table = get_nodes_infos (ref_node->next->next);
|
|
g_hash_table_foreach (effect_table, (GHFunc) create_new_source_table,
|
|
new_effect_table);
|
|
}
|
|
|
|
g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
|
|
g_strdup (fac_ref));
|
|
media_type =
|
|
(gchar *) xmlGetProp (nodes->nodeTab[j]->parent->prev->prev,
|
|
(xmlChar *) "type");
|
|
g_hash_table_insert (table, g_strdup ((gchar *) "media_type"),
|
|
g_strdup (media_type));
|
|
g_hash_table_foreach (table, (GHFunc) create_new_source_table, new_table);
|
|
if (new_effect_table) {
|
|
g_hash_table_insert (new_table, (gchar *) "effect_props",
|
|
new_effect_table);
|
|
}
|
|
g_hash_table_insert (priv->track_objects_table, g_strdup (id), new_table);
|
|
xmlFree (media_type);
|
|
if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
|
|
xmlFree (fac_ref);
|
|
}
|
|
g_hash_table_foreach (table, (GHFunc) destroyer, NULL);
|
|
g_hash_table_destroy (table);
|
|
}
|
|
|
|
xmlXPathFreeObject (xpathObj);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_timeline_objects (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
xmlXPathObjectPtr xpathObj;
|
|
xmlNodeSetPtr nodes;
|
|
int size, j;
|
|
gchar *id, *ref;
|
|
xmlChar *refxml;
|
|
|
|
GList *ref_list = NULL, *tmp_list = NULL, *tmp = NULL;
|
|
xmlNode *cur_node = NULL;
|
|
xpathObj = xmlXPathEvalExpression ((const xmlChar *)
|
|
"/pitivi/timeline/timeline-objects/timeline-object/factory-ref",
|
|
priv->xpathCtx);
|
|
|
|
if (xpathObj == NULL) {
|
|
xmlXPathFreeObject (xpathObj);
|
|
return FALSE;
|
|
}
|
|
|
|
nodes = xpathObj->nodesetval;
|
|
size = (nodes) ? nodes->nodeNr : 0;
|
|
|
|
for (j = 0; j < size; ++j) {
|
|
cur_node = nodes->nodeTab[j];
|
|
id = (gchar *) xmlGetProp (cur_node, (xmlChar *) "id");
|
|
cur_node = cur_node->next->next->children->next;
|
|
ref_list = NULL;
|
|
for (cur_node = cur_node; cur_node; cur_node = cur_node->next->next) {
|
|
|
|
refxml = xmlGetProp (cur_node, (xmlChar *) "id");
|
|
ref = (gchar *) refxml;
|
|
ref_list = g_list_append (ref_list, g_strdup (ref));
|
|
xmlFree (refxml);
|
|
}
|
|
tmp_list = g_hash_table_lookup (priv->timeline_objects_table, id);
|
|
if (tmp_list != NULL) {
|
|
for (tmp = tmp_list; tmp; tmp = tmp->next) {
|
|
ref_list = g_list_append (ref_list, tmp->data);
|
|
}
|
|
}
|
|
g_hash_table_insert (priv->timeline_objects_table, g_strdup (id),
|
|
g_list_copy (ref_list));
|
|
xmlFree (id);
|
|
g_list_free (ref_list);
|
|
g_list_free (tmp_list);
|
|
g_list_free (tmp);
|
|
}
|
|
xmlXPathFreeObject (xpathObj);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
create_new_source_table (gchar * key, gchar * value, GHashTable * table)
|
|
{
|
|
g_hash_table_insert (table, g_strdup (key), g_strdup (value));
|
|
}
|
|
|
|
static gboolean
|
|
create_tracks (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
GList *tracks = NULL;
|
|
|
|
tracks = ges_timeline_get_tracks (priv->timeline);
|
|
if (g_list_length (tracks)) {
|
|
GList *tmp = NULL;
|
|
GESTrack *track;
|
|
for (tmp = tracks; tmp; tmp = tmp->next) {
|
|
track = tmp->data;
|
|
if (track->type == GES_TRACK_TYPE_AUDIO) {
|
|
priv->tracka = track;
|
|
} else {
|
|
priv->trackv = track;
|
|
}
|
|
}
|
|
g_list_free (tracks);
|
|
return TRUE;
|
|
}
|
|
|
|
priv->tracka = ges_track_audio_raw_new ();
|
|
priv->trackv = ges_track_video_raw_new ();
|
|
|
|
if (!ges_timeline_add_track (priv->timeline, priv->trackv)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ges_timeline_add_track (priv->timeline, priv->tracka)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GHashTable *
|
|
get_nodes_infos (xmlNodePtr node)
|
|
{
|
|
xmlAttr *cur_attr;
|
|
GHashTable *props_table;
|
|
gchar *name, *value;
|
|
|
|
props_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
|
|
|
for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
|
|
name = (gchar *) cur_attr->name;
|
|
value = (gchar *) xmlGetProp (node, cur_attr->name);
|
|
g_hash_table_insert (props_table, g_strdup (name), g_strdup (value));
|
|
xmlFree (value);
|
|
}
|
|
|
|
return props_table;
|
|
}
|
|
|
|
static void
|
|
destroy_all (GList * list)
|
|
{
|
|
g_free (g_list_nth (list, (guint) 2)->data);
|
|
g_object_unref (G_OBJECT (g_list_nth (list, (guint) 1)->data));
|
|
g_free (g_list_nth (list, (guint) 3)->data);
|
|
|
|
if (g_list_length (list) == 6) {
|
|
g_free (g_list_nth (list, (guint) 5)->data);
|
|
}
|
|
|
|
g_free (g_list_nth (list, (guint) 0)->data);
|
|
g_list_free (list);
|
|
}
|
|
|
|
static void
|
|
track_object_added_cb (GESTimelineObject * object,
|
|
GESTrackObject * track_object, GHashTable * props_table)
|
|
{
|
|
gchar *media_type = NULL;
|
|
GList *tck_objs = NULL, *tmp = NULL;
|
|
GESTrack *object_track;
|
|
gint64 start, duration;
|
|
gboolean has_effect = FALSE;
|
|
gint type = 0;
|
|
tck_objs = ges_timeline_object_get_track_objects (object);
|
|
media_type =
|
|
(gchar *) g_hash_table_lookup (props_table, (gchar *) "media_type");
|
|
|
|
for (tmp = tck_objs; tmp; tmp = tmp->next) {
|
|
object_track = ges_track_object_get_track (tmp->data);
|
|
if (GES_IS_TRACK_PARSE_LAUNCH_EFFECT (tmp->data)) {
|
|
has_effect = TRUE;
|
|
continue;
|
|
}
|
|
if ((!g_strcmp0 (media_type, "pitivi.stream.VideoStream")
|
|
&& object_track->type == GES_TRACK_TYPE_VIDEO)
|
|
|| (!g_strcmp0 (media_type, "pitivi.stream.AudioStream")
|
|
&& object_track->type == GES_TRACK_TYPE_AUDIO)) {
|
|
ges_track_object_set_locked (tmp->data, FALSE);
|
|
set_properties (G_OBJECT (tmp->data), props_table);
|
|
ges_track_object_set_locked (tmp->data, TRUE);
|
|
type = object_track->type;
|
|
g_object_get (tmp->data, "start", &start, "duration", &duration, NULL);
|
|
}
|
|
}
|
|
|
|
if (has_effect) {
|
|
tck_objs = ges_timeline_object_get_track_objects (object);
|
|
for (tmp = tck_objs; tmp; tmp = tmp->next) {
|
|
object_track = ges_track_object_get_track (tmp->data);
|
|
if (GES_IS_TRACK_PARSE_LAUNCH_EFFECT (tmp->data)
|
|
&& (type == object_track->type)) {
|
|
ges_track_object_set_locked (tmp->data, FALSE);
|
|
g_object_set (tmp->data, "start", start, "duration", duration, NULL);
|
|
ges_track_object_set_locked (tmp->data, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ultimate_table_destroyer (gpointer data, gpointer data2, void *unused)
|
|
{
|
|
g_free (data);
|
|
g_hash_table_foreach (data2, (GHFunc) destroyer, NULL);
|
|
g_hash_table_destroy (data2);
|
|
}
|
|
|
|
void
|
|
layers_table_destroyer (gpointer data, gpointer data2, void *unused)
|
|
{
|
|
g_object_unref (data2);
|
|
g_free (data);
|
|
}
|
|
|
|
void
|
|
list_table_destroyer (gpointer data, gpointer data2, void *unused)
|
|
{
|
|
g_list_foreach (data2, (GFunc) g_free, NULL);
|
|
g_list_free (data2);
|
|
g_free (data);
|
|
}
|
|
|
|
void
|
|
destroyer (gpointer data, gpointer data2, void *unused)
|
|
{
|
|
if (!g_strcmp0 ((gchar *) data, (gchar *) "private")) {
|
|
} else if (!g_strcmp0 ((gchar *) data, (gchar *) "effect_props")) {
|
|
g_hash_table_foreach (data2, (GHFunc) destroyer, NULL);
|
|
g_hash_table_destroy (data2);
|
|
} else {
|
|
g_free (data2);
|
|
g_free (data);
|
|
}
|
|
}
|
|
|
|
GESPitiviFormatter *
|
|
ges_pitivi_formatter_new (void)
|
|
{
|
|
return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL);
|
|
}
|