#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);
{
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);
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);
+
+enum
+{
+ SOURCE_MOVED_SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint ges_formatter_signals[LAST_SIGNAL] = { 0 };
static void
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);
+
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;
}
static void
{
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
if (priv->data) {
g_free (priv->data);
}
+ g_hash_table_destroy (priv->uri_newuri_table);
}
/**
/**
* 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.
*
/**
* 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.
*
* @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,
{
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);
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);
+ }
+}
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);
+
/**
* GESFormatterClass:
* @parent_class: the parent class structure
* @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)
*
* GES Formatter class. Override the vmethods to implement the formatter functionnality.
*/
GESFormatterCanSaveURIMethod can_save_uri;
GESFormatterLoadFromURIMethod load_from_uri;
GESFormatterSaveToURIMethod save_to_uri;
+ GESFormatterSourceMovedMethod update_source_uri;
/*< private >*/
/* FIXME : formatter name */
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);