1 /* GStreamer Editing Services
2 * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
3 * 2010 Nokia Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:ges-formatter
23 * @short_description: Timeline saving and loading.
25 * The #GESFormatter is the object responsible for loading and/or saving the contents
26 * of a #GESTimeline to/from various formats.
28 * In order to save a #GESTimeline, you can either let GES pick a default formatter by
29 * using ges_timeline_save_to_uri(), or pick your own formatter and use
30 * ges_formatter_save_to_uri().
32 * To load a #GESTimeline, you might want to be able to track the progress of the loading,
33 * in which case you should create an empty #GESTimeline, connect to the relevant signals
34 * and call ges_formatter_load_from_uri().
36 * If you do not care about tracking the loading progress, you can use the convenience
37 * ges_timeline_new_from_uri() method.
39 * Support for saving or loading new formats can be added by creating a subclass of
40 * #GESFormatter and implement the various vmethods of #GESFormatterClass.
42 * Note that subclasses should call project_loaded wen they are done loading
50 #include "gesmarshal.h"
51 #include "ges-formatter.h"
52 #include "ges-keyfile-formatter.h"
53 #include "ges-internal.h"
56 G_DEFINE_ABSTRACT_TYPE (GESFormatter, ges_formatter, G_TYPE_OBJECT);
58 struct _GESFormatterPrivate
63 /* Make sure not to emit several times "moved-source" when the user already
64 * provided the new source URI. */
65 GHashTable *uri_newuri_table;
66 GHashTable *parent_newparent_table;
69 static void ges_formatter_dispose (GObject * object);
70 static gboolean load_from_uri (GESFormatter * formatter, GESTimeline *
71 timeline, const gchar * uri);
72 static gboolean save_to_uri (GESFormatter * formatter, GESTimeline *
73 timeline, const gchar * uri);
74 static gboolean default_can_load_uri (const gchar * uri);
75 static gboolean default_can_save_uri (const gchar * uri);
76 static void discovery_error_cb (GESTimeline * timeline,
77 GESTimelineFileSource * tfs, GError * error, GESFormatter * formatter);
79 static gboolean project_loaded (GESFormatter * formatter,
80 GESTimeline * timeline);
89 static guint ges_formatter_signals[LAST_SIGNAL] = { 0 };
92 ges_formatter_class_init (GESFormatterClass * klass)
94 GObjectClass *object_class = G_OBJECT_CLASS (klass);
96 g_type_class_add_private (klass, sizeof (GESFormatterPrivate));
99 * GESFormatter::source-moved:
100 * @formatter: the #GESFormatter
101 * @source: The #GESTimelineFileSource that has an invalid URI. When this happens,
102 * you can call #ges_formatter_update_source_uri with the new URI of the source so
103 * the project can be loaded properly.
105 ges_formatter_signals[SOURCE_MOVED_SIGNAL] =
106 g_signal_new ("source-moved", G_TYPE_FROM_CLASS (klass),
107 G_SIGNAL_RUN_LAST, 0, NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE,
108 1, GES_TYPE_TIMELINE_FILE_SOURCE);
111 * GESFormatter::loaded:
112 * @formatter: the #GESFormatter that is done loading a project.
114 ges_formatter_signals[LOADED_SIGNAL] =
115 g_signal_new ("loaded", G_TYPE_FROM_CLASS (klass),
116 G_SIGNAL_RUN_LAST, 0, NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE,
117 1, GES_TYPE_TIMELINE);
119 object_class->dispose = ges_formatter_dispose;
121 klass->can_load_uri = default_can_load_uri;
122 klass->can_save_uri = default_can_save_uri;
123 klass->load_from_uri = load_from_uri;
124 klass->save_to_uri = save_to_uri;
125 klass->update_source_uri = NULL;
126 klass->project_loaded = project_loaded;
130 ges_formatter_init (GESFormatter * object)
132 object->priv = G_TYPE_INSTANCE_GET_PRIVATE (object,
133 GES_TYPE_FORMATTER, GESFormatterPrivate);
135 object->priv->uri_newuri_table = g_hash_table_new_full (g_str_hash,
136 g_str_equal, g_free, g_free);
137 object->priv->parent_newparent_table = g_hash_table_new_full (g_file_hash,
138 (GEqualFunc) g_file_equal, g_object_unref, g_object_unref);
142 ges_formatter_dispose (GObject * object)
144 GESFormatterPrivate *priv = GES_FORMATTER (object)->priv;
149 g_hash_table_destroy (priv->uri_newuri_table);
150 g_hash_table_destroy (priv->parent_newparent_table);
154 * ges_formatter_new_for_uri:
155 * @uri: a #gchar * pointing to the uri
157 * Creates a #GESFormatter that can handle the given URI.
159 * Returns: A GESFormatter that can load the given uri, or NULL if
160 * the uri is not supported.
164 ges_formatter_new_for_uri (const gchar * uri)
166 if (ges_formatter_can_load_uri (uri))
167 return GES_FORMATTER (ges_keyfile_formatter_new ());
172 * ges_default_formatter_new:
174 * Creates a new instance of the default GESFormatter type on this system
175 * (currently #GESKeyfileFormatter).
177 * Returns: (transfer full): a #GESFormatter instance or %NULL
181 ges_default_formatter_new (void)
183 return GES_FORMATTER (ges_keyfile_formatter_new ());
187 default_can_load_uri (const gchar * uri)
189 GST_ERROR ("No 'can_load_uri' vmethod implementation");
194 default_can_save_uri (const gchar * uri)
196 GST_ERROR ("No 'can_save_uri' vmethod implementation");
201 * ges_formatter_can_load_uri:
202 * @uri: a #gchar * pointing to the URI
204 * Checks if there is a #GESFormatter available which can load a #GESTimeline
205 * from the given URI.
207 * Returns: TRUE if there is a #GESFormatter that can support the given uri
212 ges_formatter_can_load_uri (const gchar * uri)
214 if (!(gst_uri_is_valid (uri))) {
215 GST_ERROR ("Invalid uri!");
219 if (!(gst_uri_has_protocol (uri, "file"))) {
220 gchar *proto = gst_uri_get_protocol (uri);
221 GST_ERROR ("Unspported protocol '%s'", proto);
226 /* TODO: implement file format registry */
227 /* TODO: search through the registry and chose a GESFormatter class that can
234 * ges_formatter_can_save_uri:
235 * @uri: a #gchar * pointing to a URI
237 * Returns TRUE if there is a #GESFormatter available which can save a
238 * #GESTimeline to the given URI.
240 * Returns: TRUE if the given @uri is supported, else FALSE.
244 ges_formatter_can_save_uri (const gchar * uri)
246 if (!(gst_uri_is_valid (uri))) {
247 GST_ERROR ("%s invalid uri!", uri);
251 if (!(gst_uri_has_protocol (uri, "file"))) {
252 gchar *proto = gst_uri_get_protocol (uri);
253 GST_ERROR ("Unspported protocol '%s'", proto);
258 /* TODO: implement file format registry */
259 /* TODO: search through the registry and chose a GESFormatter class that can
266 * ges_formatter_set_data:
267 * @formatter: a #GESFormatter
268 * @data: the data to be set on the formatter
269 * @length: the length of the data in bytes
271 * Set the data that this formatter will use for loading. The formatter will
272 * takes ownership of the data and will free the data if
273 * @ges_formatter_set_data is called again or when the formatter itself is
274 * disposed. You should call @ges_formatter_clear_data () if you do not wish
279 ges_formatter_set_data (GESFormatter * formatter, void *data, gsize length)
281 GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
286 priv->length = length;
290 * ges_formatter_get_data:
291 * @formatter: a #GESFormatter
292 * @length: location into which to store the size of the data in bytes.
294 * Lets you get the data @formatter used for loading.
296 * Returns: (transfer none): a pointer to the data.
299 ges_formatter_get_data (GESFormatter * formatter, gsize * length)
301 GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
303 *length = priv->length;
309 * ges_formatter_clear_data:
310 * @formatter: a #GESFormatter
312 * clears the data from a #GESFormatter without freeing it. You should call
313 * this before disposing or setting data on a #GESFormatter if the current data
314 * pointer should not be freed.
318 ges_formatter_clear_data (GESFormatter * formatter)
320 GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
327 * ges_formatter_load:
328 * @formatter: a #GESFormatter
329 * @timeline: a #GESTimeline
331 * Loads data from formatter to into timeline. You should first call
332 * ges_formatter_set_data() with the location and size of a block of data
333 * from which to read.
335 * Returns: TRUE if the data was successfully loaded into timeline
336 * or FALSE if an error occured during loading.
340 ges_formatter_load (GESFormatter * formatter, GESTimeline * timeline)
342 GESFormatterClass *klass;
344 klass = GES_FORMATTER_GET_CLASS (formatter);
347 return klass->load (formatter, timeline);
348 GST_ERROR ("not implemented!");
353 * ges_formatter_save:
354 * @formatter: a #GESFormatter
355 * @timeline: a #GESTimeline
357 * Save data from timeline into a block of data. You can retrieve the location
358 * and size of this data with ges_formatter_get_data().
360 * Returns: TRUE if the timeline data was successfully saved for FALSE if
361 * an error occured during saving.
365 ges_formatter_save (GESFormatter * formatter, GESTimeline * timeline)
367 GESFormatterClass *klass;
370 /* Saving an empty timeline is not allowed */
371 /* FIXME : Having a ges_timeline_is_empty() would be more efficient maybe */
372 layers = ges_timeline_get_layers (timeline);
374 g_return_val_if_fail (layers != NULL, FALSE);
375 g_list_foreach (layers, (GFunc) g_object_unref, NULL);
376 g_list_free (layers);
378 klass = GES_FORMATTER_GET_CLASS (formatter);
381 return klass->save (formatter, timeline);
383 GST_ERROR ("not implemented!");
389 * ges_formatter_load_from_uri:
390 * @formatter: a #GESFormatter
391 * @timeline: a #GESTimeline
392 * @uri: a #gchar * pointing to a URI
394 * Load data from the given URI into timeline.
396 * Returns: TRUE if the timeline data was successfully loaded from the URI,
401 ges_formatter_load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
404 GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
406 g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
407 g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
409 g_signal_connect (timeline, "discovery-error",
410 G_CALLBACK (discovery_error_cb), formatter);
411 if (klass->load_from_uri)
412 return klass->load_from_uri (formatter, timeline, uri);
418 load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
424 GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
428 GST_ERROR ("formatter already has data! please set data to NULL");
431 if (!(location = gst_uri_get_location (uri))) {
435 if (g_file_get_contents (location, &priv->data, &priv->length, &e)) {
436 if (!ges_formatter_load (formatter, timeline)) {
437 GST_ERROR ("couldn't deserialize formatter");
441 GST_ERROR ("couldn't read file '%s': %s", location, e->message);
453 * ges_formatter_save_to_uri:
454 * @formatter: a #GESFormatter
455 * @timeline: a #GESTimeline
456 * @uri: a #gchar * pointing to a URI
458 * Save data from timeline to the given URI.
460 * Returns: TRUE if the timeline data was successfully saved to the URI
465 ges_formatter_save_to_uri (GESFormatter * formatter, GESTimeline *
466 timeline, const gchar * uri)
468 GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
471 /* Saving an empty timeline is not allowed */
472 /* FIXME : Having a ges_timeline_is_empty() would be more efficient maybe */
473 layers = ges_timeline_get_layers (timeline);
475 g_return_val_if_fail (layers != NULL, FALSE);
476 g_list_foreach (layers, (GFunc) g_object_unref, NULL);
477 g_list_free (layers);
479 if (klass->save_to_uri)
480 return klass->save_to_uri (formatter, timeline, uri);
482 GST_ERROR ("not implemented!");
488 save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
494 GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
496 if (!(location = g_filename_from_uri (uri, NULL, NULL))) {
500 if (!ges_formatter_save (formatter, timeline)) {
501 GST_ERROR ("couldn't serialize formatter");
503 if (!g_file_set_contents (location, priv->data, priv->length, &e)) {
504 GST_ERROR ("couldn't write file '%s': %s", location, e->message);
517 ges_formatter_update_source_uri (GESFormatter * formatter,
518 GESTimelineFileSource * source, gchar * new_uri)
520 GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
522 if (klass->update_source_uri) {
523 const gchar *uri = ges_timeline_filesource_get_uri (source);
525 g_hash_table_lookup (formatter->priv->uri_newuri_table, uri);
528 GFile *parent, *new_parent, *new_file = g_file_new_for_uri (new_uri),
529 *file = g_file_new_for_uri (uri);
531 parent = g_file_get_parent (file);
532 new_parent = g_file_get_parent (new_file);
533 g_hash_table_insert (formatter->priv->uri_newuri_table, g_strdup (uri),
536 g_hash_table_insert (formatter->priv->parent_newparent_table,
539 GST_DEBUG ("Adding %s and its parent to the new uri cache", new_uri);
541 g_object_unref (file);
542 g_object_unref (new_file);
545 return klass->update_source_uri (formatter, source, new_uri);
548 GST_ERROR ("not implemented!");
554 discovery_error_cb (GESTimeline * timeline,
555 GESTimelineFileSource * tfs, GError * error, GESFormatter * formatter)
557 if (error->domain == GST_RESOURCE_ERROR &&
558 error->code == GST_RESOURCE_ERROR_NOT_FOUND) {
559 const gchar *uri = ges_timeline_filesource_get_uri (tfs);
560 gchar *new_uri = g_hash_table_lookup (formatter->priv->uri_newuri_table,
563 /* We didn't find this exact URI, trying to find its parent new directory */
565 GFile *parent, *file = g_file_new_for_uri (uri);
567 /* Check if we have the new parent in cache */
568 parent = g_file_get_parent (file);
571 g_hash_table_lookup (formatter->priv->parent_newparent_table,
575 gchar *basename = g_file_get_basename (file);
576 GFile *new_file = g_file_get_child (new_parent, basename);
578 new_uri = g_file_get_uri (new_file);
580 g_object_unref (new_file);
581 g_object_unref (parent);
586 g_object_unref (file);
590 ges_formatter_update_source_uri (formatter, tfs, new_uri);
591 GST_DEBUG ("%s found in the cache, new uri: %s", uri, new_uri);
594 g_signal_emit (formatter, ges_formatter_signals[SOURCE_MOVED_SIGNAL], 0,
601 project_loaded (GESFormatter * formatter, GESTimeline * timeline)
603 g_signal_emit (formatter, ges_formatter_signals[LOADED_SIGNAL], 0, timeline);