timeline-object: Add TrackObject to the Track after the TimelineObject
[platform/upstream/gstreamer.git] / ges / ges-formatter.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
3  *               2010 Nokia Corporation
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:ges-formatter
23  * @short_description: Timeline saving and loading.
24  *
25  * The #GESFormatter is the object responsible for loading and/or saving the contents
26  * of a #GESTimeline to/from various formats.
27  *
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().
31  *
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().
35  *
36  * If you do not care about tracking the loading progress, you can use the convenience
37  * ges_timeline_new_from_uri() method.
38  *
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.
41  *
42  * Note that subclasses should call project_loaded wen they are done loading
43  * a project.
44  **/
45
46 #include <gst/gst.h>
47 #include <gio/gio.h>
48 #include <stdlib.h>
49
50 #include "gesmarshal.h"
51 #include "ges-formatter.h"
52 #include "ges-keyfile-formatter.h"
53 #include "ges-internal.h"
54 #include "ges.h"
55
56 G_DEFINE_ABSTRACT_TYPE (GESFormatter, ges_formatter, G_TYPE_OBJECT);
57
58 struct _GESFormatterPrivate
59 {
60   gchar *data;
61   gsize length;
62
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;
67 };
68
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);
78
79 static gboolean project_loaded (GESFormatter * formatter,
80     GESTimeline * timeline);
81
82 enum
83 {
84   SOURCE_MOVED_SIGNAL,
85   LOADED_SIGNAL,
86   LAST_SIGNAL
87 };
88
89 static guint ges_formatter_signals[LAST_SIGNAL] = { 0 };
90
91 static void
92 ges_formatter_class_init (GESFormatterClass * klass)
93 {
94   GObjectClass *object_class = G_OBJECT_CLASS (klass);
95
96   g_type_class_add_private (klass, sizeof (GESFormatterPrivate));
97
98   /**
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.
104    */
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);
109
110   /**
111    * GESFormatter::loaded:
112    * @formatter: the #GESFormatter that is done loading a project.
113    */
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);
118
119   object_class->dispose = ges_formatter_dispose;
120
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;
127 }
128
129 static void
130 ges_formatter_init (GESFormatter * object)
131 {
132   object->priv = G_TYPE_INSTANCE_GET_PRIVATE (object,
133       GES_TYPE_FORMATTER, GESFormatterPrivate);
134
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);
139 }
140
141 static void
142 ges_formatter_dispose (GObject * object)
143 {
144   GESFormatterPrivate *priv = GES_FORMATTER (object)->priv;
145
146   if (priv->data) {
147     g_free (priv->data);
148   }
149   g_hash_table_destroy (priv->uri_newuri_table);
150   g_hash_table_destroy (priv->parent_newparent_table);
151 }
152
153 /**
154  * ges_formatter_new_for_uri:
155  * @uri: a #gchar * pointing to the uri
156  *
157  * Creates a #GESFormatter that can handle the given URI.
158  *
159  * Returns: A GESFormatter that can load the given uri, or NULL if
160  * the uri is not supported.
161  */
162
163 GESFormatter *
164 ges_formatter_new_for_uri (const gchar * uri)
165 {
166   if (ges_formatter_can_load_uri (uri))
167     return GES_FORMATTER (ges_keyfile_formatter_new ());
168   return NULL;
169 }
170
171 /**
172  * ges_default_formatter_new:
173  *
174  * Creates a new instance of the default GESFormatter type on this system
175  * (currently #GESKeyfileFormatter).
176  *
177  * Returns: (transfer full): a #GESFormatter instance or %NULL
178  */
179
180 GESFormatter *
181 ges_default_formatter_new (void)
182 {
183   return GES_FORMATTER (ges_keyfile_formatter_new ());
184 }
185
186 static gboolean
187 default_can_load_uri (const gchar * uri)
188 {
189   GST_ERROR ("No 'can_load_uri' vmethod implementation");
190   return FALSE;
191 }
192
193 static gboolean
194 default_can_save_uri (const gchar * uri)
195 {
196   GST_ERROR ("No 'can_save_uri' vmethod implementation");
197   return FALSE;
198 }
199
200 /**
201  * ges_formatter_can_load_uri:
202  * @uri: a #gchar * pointing to the URI
203  *
204  * Checks if there is a #GESFormatter available which can load a #GESTimeline
205  * from the given URI.
206  *
207  * Returns: TRUE if there is a #GESFormatter that can support the given uri
208  * or FALSE if not.
209  */
210
211 gboolean
212 ges_formatter_can_load_uri (const gchar * uri)
213 {
214   if (!(gst_uri_is_valid (uri))) {
215     GST_ERROR ("Invalid uri!");
216     return FALSE;
217   }
218
219   if (!(gst_uri_has_protocol (uri, "file"))) {
220     gchar *proto = gst_uri_get_protocol (uri);
221     GST_ERROR ("Unspported protocol '%s'", proto);
222     g_free (proto);
223     return FALSE;
224   }
225
226   /* TODO: implement file format registry */
227   /* TODO: search through the registry and chose a GESFormatter class that can
228    * handle the URI.*/
229
230   return TRUE;
231 }
232
233 /**
234  * ges_formatter_can_save_uri:
235  * @uri: a #gchar * pointing to a URI
236  *
237  * Returns TRUE if there is a #GESFormatter available which can save a
238  * #GESTimeline to the given URI.
239  *
240  * Returns: TRUE if the given @uri is supported, else FALSE.
241  */
242
243 gboolean
244 ges_formatter_can_save_uri (const gchar * uri)
245 {
246   if (!(gst_uri_is_valid (uri))) {
247     GST_ERROR ("%s invalid uri!", uri);
248     return FALSE;
249   }
250
251   if (!(gst_uri_has_protocol (uri, "file"))) {
252     gchar *proto = gst_uri_get_protocol (uri);
253     GST_ERROR ("Unspported protocol '%s'", proto);
254     g_free (proto);
255     return FALSE;
256   }
257
258   /* TODO: implement file format registry */
259   /* TODO: search through the registry and chose a GESFormatter class that can
260    * handle the URI.*/
261
262   return TRUE;
263 }
264
265 /**
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
270  *
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
275  * this to happen.
276  */
277
278 void
279 ges_formatter_set_data (GESFormatter * formatter, void *data, gsize length)
280 {
281   GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
282
283   if (priv->data)
284     g_free (priv->data);
285   priv->data = data;
286   priv->length = length;
287 }
288
289 /**
290  * ges_formatter_get_data:
291  * @formatter: a #GESFormatter
292  * @length: location into which to store the size of the data in bytes.
293  *
294  * Lets you get the data @formatter used for loading.
295  *
296  * Returns: (transfer none): a pointer to the data.
297  */
298 void *
299 ges_formatter_get_data (GESFormatter * formatter, gsize * length)
300 {
301   GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
302
303   *length = priv->length;
304
305   return priv->data;
306 }
307
308 /**
309  * ges_formatter_clear_data:
310  * @formatter: a #GESFormatter
311  *
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.
315  */
316
317 void
318 ges_formatter_clear_data (GESFormatter * formatter)
319 {
320   GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
321
322   priv->data = NULL;
323   priv->length = 0;
324 }
325
326 /**
327  * ges_formatter_load:
328  * @formatter: a #GESFormatter
329  * @timeline: a #GESTimeline
330  *
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.
334  *
335  * Returns: TRUE if the data was successfully loaded into timeline
336  * or FALSE if an error occured during loading.
337  */
338
339 gboolean
340 ges_formatter_load (GESFormatter * formatter, GESTimeline * timeline)
341 {
342   GESFormatterClass *klass;
343
344   klass = GES_FORMATTER_GET_CLASS (formatter);
345
346   if (klass->load)
347     return klass->load (formatter, timeline);
348   GST_ERROR ("not implemented!");
349   return FALSE;
350 }
351
352 /**
353  * ges_formatter_save:
354  * @formatter: a #GESFormatter
355  * @timeline: a #GESTimeline
356  *
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().
359  *
360  * Returns: TRUE if the timeline data was successfully saved for FALSE if
361  * an error occured during saving.
362  */
363
364 gboolean
365 ges_formatter_save (GESFormatter * formatter, GESTimeline * timeline)
366 {
367   GESFormatterClass *klass;
368   GList *layers;
369
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);
373
374   g_return_val_if_fail (layers != NULL, FALSE);
375   g_list_foreach (layers, (GFunc) g_object_unref, NULL);
376   g_list_free (layers);
377
378   klass = GES_FORMATTER_GET_CLASS (formatter);
379
380   if (klass->save)
381     return klass->save (formatter, timeline);
382
383   GST_ERROR ("not implemented!");
384
385   return FALSE;
386 }
387
388 /**
389  * ges_formatter_load_from_uri:
390  * @formatter: a #GESFormatter
391  * @timeline: a #GESTimeline
392  * @uri: a #gchar * pointing to a URI
393  *
394  * Load data from the given URI into timeline.
395  *
396  * Returns: TRUE if the timeline data was successfully loaded from the URI,
397  * else FALSE.
398  */
399
400 gboolean
401 ges_formatter_load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
402     const gchar * uri)
403 {
404   GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
405
406   g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
407   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
408
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);
413
414   return FALSE;
415 }
416
417 static gboolean
418 load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
419     const gchar * uri)
420 {
421   gchar *location;
422   GError *e = NULL;
423   gboolean ret = TRUE;
424   GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
425
426
427   if (priv->data) {
428     GST_ERROR ("formatter already has data! please set data to NULL");
429   }
430
431   if (!(location = gst_uri_get_location (uri))) {
432     return FALSE;
433   }
434
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");
438       ret = FALSE;
439     }
440   } else {
441     GST_ERROR ("couldn't read file '%s': %s", location, e->message);
442     ret = FALSE;
443   }
444
445   if (e)
446     g_error_free (e);
447   g_free (location);
448
449   return ret;
450 }
451
452 /**
453  * ges_formatter_save_to_uri:
454  * @formatter: a #GESFormatter
455  * @timeline: a #GESTimeline
456  * @uri: a #gchar * pointing to a URI
457  *
458  * Save data from timeline to the given URI.
459  *
460  * Returns: TRUE if the timeline data was successfully saved to the URI
461  * else FALSE.
462  */
463
464 gboolean
465 ges_formatter_save_to_uri (GESFormatter * formatter, GESTimeline *
466     timeline, const gchar * uri)
467 {
468   GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
469   GList *layers;
470
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);
474
475   g_return_val_if_fail (layers != NULL, FALSE);
476   g_list_foreach (layers, (GFunc) g_object_unref, NULL);
477   g_list_free (layers);
478
479   if (klass->save_to_uri)
480     return klass->save_to_uri (formatter, timeline, uri);
481
482   GST_ERROR ("not implemented!");
483
484   return FALSE;
485 }
486
487 static gboolean
488 save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
489     const gchar * uri)
490 {
491   gchar *location;
492   GError *e = NULL;
493   gboolean ret = TRUE;
494   GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;
495
496   if (!(location = g_filename_from_uri (uri, NULL, NULL))) {
497     return FALSE;
498   }
499
500   if (!ges_formatter_save (formatter, timeline)) {
501     GST_ERROR ("couldn't serialize formatter");
502   } else {
503     if (!g_file_set_contents (location, priv->data, priv->length, &e)) {
504       GST_ERROR ("couldn't write file '%s': %s", location, e->message);
505       ret = FALSE;
506     }
507   }
508
509   if (e)
510     g_error_free (e);
511   g_free (location);
512
513   return ret;
514 }
515
516 gboolean
517 ges_formatter_update_source_uri (GESFormatter * formatter,
518     GESTimelineFileSource * source, gchar * new_uri)
519 {
520   GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
521
522   if (klass->update_source_uri) {
523     const gchar *uri = ges_timeline_filesource_get_uri (source);
524     gchar *cached_uri =
525         g_hash_table_lookup (formatter->priv->uri_newuri_table, uri);
526
527     if (!cached_uri) {
528       GFile *parent, *new_parent, *new_file = g_file_new_for_uri (new_uri),
529           *file = g_file_new_for_uri (uri);
530
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),
534           g_strdup (new_uri));
535
536       g_hash_table_insert (formatter->priv->parent_newparent_table,
537           parent, new_parent);
538
539       GST_DEBUG ("Adding %s and its parent to the new uri cache", new_uri);
540
541       g_object_unref (file);
542       g_object_unref (new_file);
543     }
544
545     return klass->update_source_uri (formatter, source, new_uri);
546   }
547
548   GST_ERROR ("not implemented!");
549
550   return FALSE;
551 }
552
553 static void
554 discovery_error_cb (GESTimeline * timeline,
555     GESTimelineFileSource * tfs, GError * error, GESFormatter * formatter)
556 {
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,
561         uri);
562
563     /* We didn't find this exact URI, trying to find its parent new directory */
564     if (!new_uri) {
565       GFile *parent, *file = g_file_new_for_uri (uri);
566
567       /* Check if we have the new parent in cache */
568       parent = g_file_get_parent (file);
569       if (parent) {
570         GFile *new_parent =
571             g_hash_table_lookup (formatter->priv->parent_newparent_table,
572             parent);
573
574         if (new_parent) {
575           gchar *basename = g_file_get_basename (file);
576           GFile *new_file = g_file_get_child (new_parent, basename);
577
578           new_uri = g_file_get_uri (new_file);
579
580           g_object_unref (new_file);
581           g_object_unref (parent);
582           g_free (basename);
583         }
584       }
585
586       g_object_unref (file);
587     }
588
589     if (new_uri) {
590       ges_formatter_update_source_uri (formatter, tfs, new_uri);
591       GST_DEBUG ("%s found in the cache, new uri: %s", uri, new_uri);
592
593     } else {
594       g_signal_emit (formatter, ges_formatter_signals[SOURCE_MOVED_SIGNAL], 0,
595           tfs);
596     }
597   }
598 }
599
600 static gboolean
601 project_loaded (GESFormatter * formatter, GESTimeline * timeline)
602 {
603   g_signal_emit (formatter, ges_formatter_signals[LOADED_SIGNAL], 0, timeline);
604
605   return TRUE;
606 }