mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 20:51:13 +00:00
a7347ca8f7
Export GES library API in headers when we're building the library itself, otherwise import the API from the headers. This fixes linker warnings on Windows when building with MSVC. Fix up some missing config.h includes when building the lib which is needed to get the export api define from config.h Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/42
773 lines
22 KiB
C
773 lines
22 KiB
C
/* GStreamer Editing Services Pitivi Formatter
|
|
* Copyright (C) 2011-2012 Mathieu Duponchelle <seeed@laposte.net>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION: gespitiviformatter
|
|
* @title: GESPitiviFormatter
|
|
* @short_description: A formatter for the obsolete Pitivi xptv project file format
|
|
*
|
|
* This is a legacy format and you should avoid to use it. The formatter
|
|
* is really not in good shape and is deprecated.
|
|
*
|
|
* Deprecated: 1.0
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#undef VERSION
|
|
#endif
|
|
|
|
#include <libxml/xmlreader.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/xpathInternals.h>
|
|
#include <libxml/encoding.h>
|
|
#include <libxml/xmlwriter.h>
|
|
|
|
#include "ges-internal.h"
|
|
#include <ges/ges-pitivi-formatter.h>
|
|
#include <ges/ges.h>
|
|
|
|
/* The Pitivi etree formatter is 0.1 we set GES one to 0.2 */
|
|
//#define VERSION "0.2"
|
|
#define DOUBLE_VERSION 0.2
|
|
|
|
#undef GST_CAT_DEFAULT
|
|
GST_DEBUG_CATEGORY_STATIC (ges_pitivi_formatter_debug);
|
|
#define GST_CAT_DEFAULT ges_pitivi_formatter_debug
|
|
|
|
typedef struct SrcMapping
|
|
{
|
|
gchar *id;
|
|
GESClip *clip;
|
|
guint priority;
|
|
GList *track_element_ids;
|
|
} SrcMapping;
|
|
|
|
struct _GESPitiviFormatterPrivate
|
|
{
|
|
xmlXPathContextPtr xpathCtx;
|
|
|
|
/* {"sourceId" : {"prop": "value"}} */
|
|
GHashTable *sources_table;
|
|
|
|
/* Used as a set of the uris */
|
|
GHashTable *source_uris;
|
|
|
|
/* {trackId: {"factory_ref": factoryId, ""}
|
|
* if effect:
|
|
* {"factory_ref": "effect",
|
|
* "effect_name": name
|
|
* "effect_props": {"propname": value}}}
|
|
*/
|
|
GHashTable *track_elements_table;
|
|
|
|
/* {factory-ref: [track-object-ref-id,...]} */
|
|
GHashTable *clips_table;
|
|
|
|
/* {layerPriority: layer} */
|
|
GHashTable *layers_table;
|
|
|
|
GESTimeline *timeline;
|
|
|
|
GESTrack *tracka, *trackv;
|
|
|
|
/* List the Clip that haven't been loaded yet */
|
|
GList *sources_to_load;
|
|
|
|
/* Saving context */
|
|
/* {factory_id: uri} */
|
|
GHashTable *saving_source_table;
|
|
guint nb_sources;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GESPitiviFormatter, ges_pitivi_formatter,
|
|
GES_TYPE_FORMATTER);
|
|
|
|
|
|
static void
|
|
list_table_destroyer (gpointer key, gpointer value, void *unused)
|
|
{
|
|
g_list_foreach (value, (GFunc) g_free, NULL);
|
|
g_list_free (value);
|
|
}
|
|
|
|
static gboolean
|
|
pitivi_can_load_uri (GESFormatter * dummy_instance, const gchar * uri,
|
|
GError ** error)
|
|
{
|
|
xmlDocPtr doc;
|
|
gboolean ret = TRUE;
|
|
xmlXPathObjectPtr xpathObj;
|
|
xmlXPathContextPtr xpathCtx;
|
|
gchar *filename = g_filename_from_uri (uri, NULL, NULL);
|
|
|
|
if (!filename || !g_file_test (filename, G_FILE_TEST_EXISTS)) {
|
|
g_free (filename);
|
|
return FALSE;
|
|
}
|
|
|
|
g_free (filename);
|
|
|
|
if (!(doc = xmlParseFile (uri))) {
|
|
GST_ERROR ("The xptv file for uri %s was badly formed", uri);
|
|
return FALSE;
|
|
}
|
|
|
|
xpathCtx = xmlXPathNewContext (doc);
|
|
xpathObj = xmlXPathEvalExpression ((const xmlChar *) "/pitivi", xpathCtx);
|
|
if (!xpathObj || !xpathObj->nodesetval || xpathObj->nodesetval->nodeNr == 0)
|
|
ret = FALSE;
|
|
|
|
xmlFreeDoc (doc);
|
|
xmlXPathFreeObject (xpathObj);
|
|
xmlXPathFreeContext (xpathCtx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Project loading functions */
|
|
|
|
/* Return: a GHashTable containing:
|
|
* {attr: value}
|
|
*/
|
|
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 gboolean
|
|
create_tracks (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
GList *tracks = NULL;
|
|
|
|
tracks = ges_timeline_get_tracks (self->timeline);
|
|
|
|
GST_DEBUG ("Creating tracks, current number of tracks %d",
|
|
g_list_length (tracks));
|
|
|
|
if (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_foreach (tracks, (GFunc) gst_object_unref, NULL);
|
|
g_list_free (tracks);
|
|
return TRUE;
|
|
}
|
|
|
|
priv->tracka = GES_TRACK (ges_audio_track_new ());
|
|
priv->trackv = GES_TRACK (ges_video_track_new ());
|
|
|
|
if (!ges_timeline_add_track (self->timeline, priv->trackv)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ges_timeline_add_track (self->timeline, priv->tracka)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
parse_metadatas (GESFormatter * self)
|
|
{
|
|
guint i, size;
|
|
xmlNodePtr node;
|
|
xmlAttr *cur_attr;
|
|
xmlNodeSetPtr nodes;
|
|
xmlXPathObjectPtr xpathObj;
|
|
GESMetaContainer *metacontainer = GES_META_CONTAINER (self->project);
|
|
|
|
xpathObj = xmlXPathEvalExpression ((const xmlChar *)
|
|
"/pitivi/metadata", GES_PITIVI_FORMATTER (self)->priv->xpathCtx);
|
|
nodes = xpathObj->nodesetval;
|
|
|
|
size = (nodes) ? nodes->nodeNr : 0;
|
|
for (i = 0; i < size; i++) {
|
|
node = nodes->nodeTab[i];
|
|
for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next) {
|
|
ges_meta_container_set_string (metacontainer, (gchar *) cur_attr->name,
|
|
(gchar *) xmlGetProp (node, cur_attr->name));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
list_sources (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
xmlXPathObjectPtr xpathObj;
|
|
GHashTable *table;
|
|
int size, j;
|
|
gchar *id, *filename;
|
|
xmlNodeSetPtr nodes;
|
|
|
|
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");
|
|
filename = (gchar *) g_hash_table_lookup (table, (gchar *) "filename");
|
|
g_hash_table_insert (priv->sources_table, g_strdup (id), table);
|
|
g_hash_table_insert (priv->source_uris, g_strdup (filename),
|
|
g_strdup (filename));
|
|
if (self->project)
|
|
ges_project_create_asset (self->project, filename, GES_TYPE_URI_CLIP);
|
|
}
|
|
|
|
xmlXPathFreeObject (xpathObj);
|
|
}
|
|
|
|
static gboolean
|
|
parse_track_elements (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
xmlXPathObjectPtr xpathObj;
|
|
xmlNodeSetPtr nodes;
|
|
int size, j;
|
|
gchar *id, *fac_ref;
|
|
GHashTable *table = NULL, *effect_table = NULL;
|
|
xmlNode *first_child;
|
|
gchar *media_type;
|
|
|
|
/* FIXME Make this whole function cleaner starting from
|
|
* "/pitivi/timeline/tracks/track/stream" and descending
|
|
* into the children. */
|
|
xpathObj = xmlXPathEvalExpression ((const xmlChar *)
|
|
"/pitivi/timeline/tracks/track/track-objects/track-object",
|
|
priv->xpathCtx);
|
|
|
|
if (xpathObj == NULL) {
|
|
GST_DEBUG ("No track object found");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
nodes = xpathObj->nodesetval;
|
|
size = (nodes) ? nodes->nodeNr : 0;
|
|
|
|
for (j = 0; j < size; ++j) {
|
|
xmlNodePtr node = nodes->nodeTab[j];
|
|
|
|
table = get_nodes_infos (nodes->nodeTab[j]);
|
|
id = (gchar *) g_hash_table_lookup (table, (gchar *) "id");
|
|
first_child = nodes->nodeTab[j]->children->next;
|
|
fac_ref = (gchar *) xmlGetProp (first_child, (xmlChar *) "id");
|
|
|
|
/* We check if the first child is "effect" */
|
|
if (!g_strcmp0 ((gchar *) first_child->name, (gchar *) "effect")) {
|
|
xmlChar *effect_name;
|
|
xmlNodePtr fact_node = first_child->children->next;
|
|
|
|
/* We have a node called "text" in between thus ->next->next */
|
|
xmlNodePtr elem_props_node = fact_node->next->next;
|
|
|
|
effect_name = xmlGetProp (fact_node, (xmlChar *) "name");
|
|
g_hash_table_insert (table, g_strdup ((gchar *) "effect_name"),
|
|
g_strdup ((gchar *) effect_name));
|
|
xmlFree (effect_name);
|
|
|
|
/* We put the effects properties in an hacktable (Lapsus is on :) */
|
|
effect_table = get_nodes_infos (elem_props_node);
|
|
|
|
g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
|
|
g_strdup ("effect"));
|
|
|
|
xmlFree (fac_ref);
|
|
} else {
|
|
|
|
g_hash_table_insert (table, g_strdup ((gchar *) "fac_ref"),
|
|
g_strdup (fac_ref));
|
|
xmlFree (fac_ref);
|
|
}
|
|
|
|
/* Same as before, we got a text node in between, thus the 2 prev
|
|
* node->parent is <track-objects>, the one before is <stream>
|
|
*/
|
|
media_type = (gchar *) xmlGetProp (node->parent->prev->prev,
|
|
(const xmlChar *) "type");
|
|
g_hash_table_insert (table, g_strdup ((gchar *) "media_type"),
|
|
g_strdup (media_type));
|
|
xmlFree (media_type);
|
|
|
|
|
|
if (effect_table)
|
|
g_hash_table_insert (table, g_strdup ("effect_props"), effect_table);
|
|
|
|
g_hash_table_insert (priv->track_elements_table, g_strdup (id), table);
|
|
}
|
|
|
|
xmlXPathFreeObject (xpathObj);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_clips (GESFormatter * self)
|
|
{
|
|
int size, j;
|
|
xmlNodeSetPtr nodes;
|
|
xmlXPathObjectPtr xpathObj;
|
|
xmlNodePtr clip_nd, tmp_nd, tmp_nd2;
|
|
xmlChar *trackelementrefId, *facrefId = NULL;
|
|
|
|
GList *reflist = NULL;
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
GHashTable *clips_table = priv->clips_table;
|
|
|
|
xpathObj = xmlXPathEvalExpression ((const xmlChar *)
|
|
"/pitivi/timeline/timeline-objects/timeline-object", priv->xpathCtx);
|
|
|
|
if (xpathObj == NULL) {
|
|
xmlXPathFreeObject (xpathObj);
|
|
return FALSE;
|
|
}
|
|
|
|
nodes = xpathObj->nodesetval;
|
|
size = (nodes) ? nodes->nodeNr : 0;
|
|
|
|
for (j = 0; j < size; j++) {
|
|
clip_nd = nodes->nodeTab[j];
|
|
|
|
for (tmp_nd = clip_nd->children; tmp_nd; tmp_nd = tmp_nd->next) {
|
|
/* We assume that factory-ref is always before the tckobjs-ref */
|
|
if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "factory-ref")) {
|
|
facrefId = xmlGetProp (tmp_nd, (xmlChar *) "id");
|
|
|
|
} else if (!xmlStrcmp (tmp_nd->name, (xmlChar *) "track-object-refs")) {
|
|
|
|
for (tmp_nd2 = tmp_nd->children; tmp_nd2; tmp_nd2 = tmp_nd2->next) {
|
|
if (!xmlStrcmp (tmp_nd2->name, (xmlChar *) "track-object-ref")) {
|
|
/* We add the track object ref ID to the list of the current
|
|
* Clip tracks, this way we can merge 2
|
|
* Clip-s into 1 when we have unlinked TrackElement-s */
|
|
reflist = g_hash_table_lookup (clips_table, facrefId);
|
|
trackelementrefId = xmlGetProp (tmp_nd2, (xmlChar *) "id");
|
|
reflist =
|
|
g_list_append (reflist, g_strdup ((gchar *) trackelementrefId));
|
|
g_hash_table_insert (clips_table, g_strdup ((gchar *) facrefId),
|
|
reflist);
|
|
|
|
xmlFree (trackelementrefId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
xmlXPathFreeObject (xpathObj);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
set_properties (GObject * obj, GHashTable * props_table)
|
|
{
|
|
gint i;
|
|
gchar **prop_array, *valuestr;
|
|
gint64 value;
|
|
|
|
gchar props[3][10] = { "duration", "in_point", "start" };
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
valuestr = g_hash_table_lookup (props_table, props[i]);
|
|
prop_array = g_strsplit (valuestr, ")", 0);
|
|
value = g_ascii_strtoll (prop_array[1], NULL, 0);
|
|
g_object_set (obj, props[i], value, NULL);
|
|
|
|
g_strfreev (prop_array);
|
|
}
|
|
}
|
|
|
|
static void
|
|
track_element_added_cb (GESClip * clip,
|
|
GESTrackElement * track_element, GHashTable * props_table)
|
|
{
|
|
GESPitiviFormatter *formatter;
|
|
|
|
formatter = GES_PITIVI_FORMATTER (g_hash_table_lookup (props_table,
|
|
"current-formatter"));
|
|
if (formatter) {
|
|
GESPitiviFormatterPrivate *priv = formatter->priv;
|
|
|
|
/* Make sure the hack to get a ref to the formatter
|
|
* doesn't break everything */
|
|
g_hash_table_steal (props_table, "current-formatter");
|
|
|
|
priv->sources_to_load = g_list_remove (priv->sources_to_load, clip);
|
|
if (!priv->sources_to_load && GES_FORMATTER (formatter)->project)
|
|
ges_project_set_loaded (GES_FORMATTER (formatter)->project,
|
|
GES_FORMATTER (formatter));
|
|
}
|
|
|
|
/* Disconnect the signal */
|
|
g_signal_handlers_disconnect_by_func (clip, track_element_added_cb,
|
|
props_table);
|
|
}
|
|
|
|
static void
|
|
make_source (GESFormatter * self, GList * reflist, GHashTable * source_table)
|
|
{
|
|
GHashTable *props_table, *effect_table;
|
|
gchar **prio_array;
|
|
GESLayer *layer;
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
|
|
gchar *fac_ref = NULL, *media_type = NULL, *filename = NULL, *prio_str;
|
|
GList *tmp = NULL, *keys, *tmp_key;
|
|
GESUriClip *src = NULL;
|
|
gint prio;
|
|
gboolean a_avail = FALSE, v_avail = FALSE, video;
|
|
GHashTable *trackelement_table = priv->track_elements_table;
|
|
|
|
for (tmp = reflist; tmp; tmp = tmp->next) {
|
|
|
|
/* Get the layer */
|
|
props_table = g_hash_table_lookup (trackelement_table, (gchar *) tmp->data);
|
|
prio_str = (gchar *) g_hash_table_lookup (props_table, "priority");
|
|
prio_array = g_strsplit (prio_str, ")", 0);
|
|
prio = (gint) g_ascii_strtod (prio_array[1], NULL);
|
|
g_strfreev (prio_array);
|
|
|
|
/* If we do not have any layer with this priority, create it */
|
|
if (!(layer = g_hash_table_lookup (priv->layers_table, &prio))) {
|
|
layer = ges_layer_new ();
|
|
g_object_set (layer, "auto-transition", TRUE, "priority", prio, NULL);
|
|
ges_timeline_add_layer (self->timeline, layer);
|
|
g_hash_table_insert (priv->layers_table, g_memdup (&prio,
|
|
sizeof (guint64)), layer);
|
|
}
|
|
|
|
fac_ref = (gchar *) g_hash_table_lookup (props_table, "fac_ref");
|
|
media_type = (gchar *) g_hash_table_lookup (props_table, "media_type");
|
|
|
|
if (!g_strcmp0 (media_type, "pitivi.stream.VideoStream"))
|
|
video = TRUE;
|
|
else
|
|
video = FALSE;
|
|
|
|
/* FIXME I am sure we could reimplement this whole part
|
|
* in a simpler way */
|
|
|
|
if (g_strcmp0 (fac_ref, (gchar *) "effect")) {
|
|
/* FIXME this is a hack to get a ref to the formatter when receiving
|
|
* child-added */
|
|
g_hash_table_insert (props_table, (gchar *) "current-formatter", self);
|
|
if (a_avail && (!video)) {
|
|
a_avail = FALSE;
|
|
} else if (v_avail && (video)) {
|
|
v_avail = FALSE;
|
|
} else {
|
|
|
|
/* If we only have audio or only video in the previous source,
|
|
* set it has such */
|
|
if (a_avail) {
|
|
ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
|
|
} else if (v_avail) {
|
|
ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
|
|
}
|
|
|
|
filename = (gchar *) g_hash_table_lookup (source_table, "filename");
|
|
|
|
src = ges_uri_clip_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_layer_add_clip (layer, GES_CLIP (src));
|
|
|
|
g_signal_connect (src, "child-added",
|
|
G_CALLBACK (track_element_added_cb), props_table);
|
|
|
|
priv->sources_to_load = g_list_prepend (priv->sources_to_load, src);
|
|
}
|
|
|
|
} else {
|
|
GESEffect *effect;
|
|
gchar *active = (gchar *) g_hash_table_lookup (props_table, "active");
|
|
|
|
effect = ges_effect_new ((gchar *)
|
|
g_hash_table_lookup (props_table, (gchar *) "effect_name"));
|
|
ges_track_element_set_track_type (GES_TRACK_ELEMENT (effect),
|
|
(video ? GES_TRACK_TYPE_VIDEO : GES_TRACK_TYPE_AUDIO));
|
|
effect_table =
|
|
g_hash_table_lookup (props_table, (gchar *) "effect_props");
|
|
|
|
ges_container_add (GES_CONTAINER (src), GES_TIMELINE_ELEMENT (effect));
|
|
|
|
if (!g_strcmp0 (active, (gchar *) "(bool)False"))
|
|
ges_track_element_set_active (GES_TRACK_ELEMENT (effect), FALSE);
|
|
|
|
/* Set effect properties */
|
|
keys = g_hash_table_get_keys (effect_table);
|
|
for (tmp_key = keys; tmp_key; tmp_key = tmp_key->next) {
|
|
GstStructure *structure;
|
|
const GValue *value;
|
|
GParamSpec *spec;
|
|
GstCaps *caps;
|
|
gchar *prop_val;
|
|
|
|
prop_val = (gchar *) g_hash_table_lookup (effect_table,
|
|
(gchar *) tmp_key->data);
|
|
|
|
if (g_strstr_len (prop_val, -1, "(GEnum)")) {
|
|
gchar **val = g_strsplit (prop_val, ")", 2);
|
|
|
|
ges_track_element_set_child_properties (GES_TRACK_ELEMENT (effect),
|
|
(gchar *) tmp_key->data, atoi (val[1]), NULL);
|
|
g_strfreev (val);
|
|
|
|
} else if (ges_track_element_lookup_child (GES_TRACK_ELEMENT (effect),
|
|
(gchar *) tmp->data, NULL, &spec)) {
|
|
gchar *caps_str = g_strdup_printf ("structure1, property1=%s;",
|
|
prop_val);
|
|
|
|
caps = gst_caps_from_string (caps_str);
|
|
g_free (caps_str);
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
value = gst_structure_get_value (structure, "property1");
|
|
|
|
ges_track_element_set_child_property_by_pspec (GES_TRACK_ELEMENT
|
|
(effect), spec, (GValue *) value);
|
|
gst_caps_unref (caps);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (a_avail) {
|
|
ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_VIDEO);
|
|
} else if (v_avail) {
|
|
ges_clip_set_supported_formats (GES_CLIP (src), GES_TRACK_TYPE_AUDIO);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
make_clips (GESFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
GHashTable *source_table;
|
|
|
|
GList *keys = NULL, *tmp = NULL, *reflist = NULL;
|
|
|
|
keys = g_hash_table_get_keys (priv->clips_table);
|
|
|
|
for (tmp = keys; tmp; tmp = tmp->next) {
|
|
gchar *fac_id = (gchar *) tmp->data;
|
|
|
|
reflist = g_hash_table_lookup (priv->clips_table, fac_id);
|
|
source_table = g_hash_table_lookup (priv->sources_table, fac_id);
|
|
make_source (self, reflist, source_table);
|
|
}
|
|
|
|
g_list_free (keys);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
load_pitivi_file_from_uri (GESFormatter * self,
|
|
GESTimeline * timeline, const gchar * uri, GError ** error)
|
|
{
|
|
xmlDocPtr doc;
|
|
GESLayer *layer;
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
|
|
gboolean ret = TRUE;
|
|
gint *prio = malloc (sizeof (gint));
|
|
|
|
*prio = 0;
|
|
layer = ges_layer_new ();
|
|
g_object_set (layer, "auto-transition", TRUE, NULL);
|
|
|
|
g_hash_table_insert (priv->layers_table, prio, layer);
|
|
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 = xmlParseFile (uri))) {
|
|
GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
|
|
uri);
|
|
return FALSE;
|
|
}
|
|
|
|
priv->xpathCtx = xmlXPathNewContext (doc);
|
|
|
|
if (self->project)
|
|
parse_metadatas (self);
|
|
|
|
if (!create_tracks (self)) {
|
|
GST_ERROR ("Couldn't create tracks");
|
|
return FALSE;
|
|
}
|
|
|
|
list_sources (self);
|
|
|
|
if (!parse_clips (self)) {
|
|
GST_ERROR ("Couldn't find clips markup in the xptv file");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!parse_track_elements (self)) {
|
|
GST_ERROR ("Couldn't find track objects markup in the xptv file");
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/* If there are no clips to load we should emit
|
|
* 'project-loaded' signal.
|
|
*/
|
|
if (!g_hash_table_size (priv->clips_table) && GES_FORMATTER (self)->project) {
|
|
ges_project_set_loaded (GES_FORMATTER (self)->project,
|
|
GES_FORMATTER (self));
|
|
} else {
|
|
if (!make_clips (self)) {
|
|
GST_ERROR ("Couldn't deserialise the project properly");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
xmlXPathFreeContext (priv->xpathCtx);
|
|
xmlFreeDoc (doc);
|
|
return ret;
|
|
}
|
|
|
|
/* Object functions */
|
|
static void
|
|
ges_pitivi_formatter_finalize (GObject * object)
|
|
{
|
|
GESPitiviFormatter *self = GES_PITIVI_FORMATTER (object);
|
|
GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;
|
|
|
|
g_hash_table_destroy (priv->sources_table);
|
|
g_hash_table_destroy (priv->source_uris);
|
|
|
|
g_hash_table_destroy (priv->saving_source_table);
|
|
g_list_free (priv->sources_to_load);
|
|
|
|
if (priv->clips_table != NULL) {
|
|
g_hash_table_foreach (priv->clips_table,
|
|
(GHFunc) list_table_destroyer, NULL);
|
|
g_hash_table_destroy (priv->clips_table);
|
|
}
|
|
|
|
if (priv->layers_table != NULL)
|
|
g_hash_table_destroy (priv->layers_table);
|
|
|
|
if (priv->track_elements_table != NULL) {
|
|
g_hash_table_destroy (priv->track_elements_table);
|
|
}
|
|
|
|
G_OBJECT_CLASS (ges_pitivi_formatter_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
ges_pitivi_formatter_class_init (GESPitiviFormatterClass * klass)
|
|
{
|
|
GESFormatterClass *formatter_klass;
|
|
GObjectClass *object_class;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (ges_pitivi_formatter_debug, "ges_pitivi_formatter",
|
|
GST_DEBUG_FG_YELLOW, "ges pitivi formatter");
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
formatter_klass = GES_FORMATTER_CLASS (klass);
|
|
|
|
formatter_klass->can_load_uri = pitivi_can_load_uri;
|
|
formatter_klass->save_to_uri = NULL;
|
|
formatter_klass->load_from_uri = load_pitivi_file_from_uri;
|
|
object_class->finalize = ges_pitivi_formatter_finalize;
|
|
|
|
ges_formatter_class_register_metas (formatter_klass, "pitivi",
|
|
"Legacy Pitivi project files", "xptv", "text/x-xptv",
|
|
DOUBLE_VERSION, GST_RANK_MARGINAL);
|
|
}
|
|
|
|
static void
|
|
ges_pitivi_formatter_init (GESPitiviFormatter * self)
|
|
{
|
|
GESPitiviFormatterPrivate *priv;
|
|
|
|
self->priv = ges_pitivi_formatter_get_instance_private (self);
|
|
|
|
priv = self->priv;
|
|
|
|
priv->track_elements_table =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
|
(GDestroyNotify) g_hash_table_destroy);
|
|
|
|
priv->clips_table =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
|
|
priv->layers_table =
|
|
g_hash_table_new_full (g_int_hash, g_str_equal, g_free, gst_object_unref);
|
|
|
|
priv->sources_table =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
|
(GDestroyNotify) g_hash_table_destroy);
|
|
|
|
priv->source_uris =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
|
|
priv->sources_to_load = NULL;
|
|
|
|
/* Saving context */
|
|
priv->saving_source_table =
|
|
g_hash_table_new_full (g_str_hash, g_int_equal, g_free, g_free);
|
|
priv->nb_sources = 1;
|
|
}
|
|
|
|
GESPitiviFormatter *
|
|
ges_pitivi_formatter_new (void)
|
|
{
|
|
return g_object_new (GES_TYPE_PITIVI_FORMATTER, NULL);
|
|
}
|