h264decoder: Enable low-latency bumping in case of pic_order_cnt_type 2
[platform/upstream/gstreamer.git] / subprojects / gst-editing-services / 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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:gesformatter
23  * @title: GESFormatter
24  * @short_description: Timeline saving and loading.
25  *
26  **/
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <gst/gst.h>
32 #include <gio/gio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "ges-formatter.h"
37 #include "ges-internal.h"
38 #include "ges.h"
39 #ifndef DISABLE_XPTV
40 #include "ges-pitivi-formatter.h"
41 #endif
42
43 #ifdef HAS_PYTHON
44 #include <Python.h>
45 #include "ges-resources.h"
46 #endif
47
48 GST_DEBUG_CATEGORY_STATIC (ges_formatter_debug);
49 #undef GST_CAT_DEFAULT
50 #define GST_CAT_DEFAULT ges_formatter_debug
51 static gboolean initialized = FALSE;
52
53 /* TODO Add a GCancellable somewhere in the API */
54 static void ges_extractable_interface_init (GESExtractableInterface * iface);
55
56 struct _GESFormatterPrivate
57 {
58   gpointer nothing;
59 };
60
61 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESFormatter, ges_formatter,
62     G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (GESFormatter)
63     G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
64         ges_extractable_interface_init));
65
66 static void ges_formatter_dispose (GObject * object);
67 static gboolean default_can_load_uri (GESFormatter * dummy_instance,
68     const gchar * uri, GError ** error);
69
70 /* GESExtractable implementation */
71 static gchar *
72 extractable_check_id (GType type, const gchar * id)
73 {
74   GESFormatterClass *class;
75
76   if (id)
77     return g_strdup (id);
78
79   class = g_type_class_peek (type);
80   return g_strdup (class->name);
81 }
82
83 static gchar *
84 extractable_get_id (GESExtractable * self)
85 {
86   GESAsset *asset;
87
88   if (!(asset = ges_extractable_get_asset (self)))
89     return NULL;
90
91   return g_strdup (ges_asset_get_id (asset));
92 }
93
94 static gboolean
95 _register_metas (GESExtractableInterface * iface, GObjectClass * class,
96     GESAsset * asset)
97 {
98   GESFormatterClass *fclass = GES_FORMATTER_CLASS (class);
99   GESMetaContainer *container = GES_META_CONTAINER (asset);
100
101   ges_meta_container_register_meta_string (container, GES_META_READABLE,
102       GES_META_FORMATTER_NAME, fclass->name);
103   ges_meta_container_register_meta_string (container, GES_META_READABLE,
104       GES_META_DESCRIPTION, fclass->description);
105   ges_meta_container_register_meta_string (container, GES_META_READABLE,
106       GES_META_FORMATTER_MIMETYPE, fclass->mimetype);
107   ges_meta_container_register_meta_string (container, GES_META_READABLE,
108       GES_META_FORMATTER_EXTENSION, fclass->extension);
109   ges_meta_container_register_meta_double (container, GES_META_READABLE,
110       GES_META_FORMATTER_VERSION, fclass->version);
111   ges_meta_container_register_meta_uint (container, GES_META_READABLE,
112       GES_META_FORMATTER_RANK, fclass->rank);
113   ges_meta_container_register_meta_string (container, GES_META_READ_WRITE,
114       GES_META_FORMAT_VERSION, NULL);
115
116   /* We are leaking the metadata but we don't really have choice here
117    * as calling ges_init() after deinit() is allowed.
118    */
119
120   return TRUE;
121 }
122
123 static void
124 ges_extractable_interface_init (GESExtractableInterface * iface)
125 {
126   iface->check_id = (GESExtractableCheckId) extractable_check_id;
127   iface->get_id = extractable_get_id;
128   iface->asset_type = GES_TYPE_ASSET;
129   iface->register_metas = _register_metas;
130 }
131
132 static void
133 ges_formatter_class_init (GESFormatterClass * klass)
134 {
135   GObjectClass *object_class = G_OBJECT_CLASS (klass);
136
137   object_class->dispose = ges_formatter_dispose;
138
139   klass->can_load_uri = default_can_load_uri;
140   klass->load_from_uri = NULL;
141   klass->save_to_uri = NULL;
142
143   /* We set dummy  metas */
144   klass->name = g_strdup ("base-formatter");
145   klass->extension = g_strdup ("noextension");
146   klass->description = g_strdup ("Formatter base class, you should give"
147       " a name to your formatter");
148   klass->mimetype = g_strdup ("No mimetype");
149   klass->version = 0.0;
150   klass->rank = GST_RANK_NONE;
151 }
152
153 static void
154 ges_formatter_init (GESFormatter * object)
155 {
156   object->priv = ges_formatter_get_instance_private (object);
157   object->project = NULL;
158 }
159
160 static void
161 ges_formatter_dispose (GObject * object)
162 {
163   ges_formatter_set_project (GES_FORMATTER (object), NULL);
164
165   G_OBJECT_CLASS (ges_formatter_parent_class)->dispose (object);
166 }
167
168 static gboolean
169 default_can_load_uri (GESFormatter * dummy_instance, const gchar * uri,
170     GError ** error)
171 {
172   GST_DEBUG ("%s: no 'can_load_uri' vmethod implementation",
173       G_OBJECT_TYPE_NAME (dummy_instance));
174
175   return FALSE;
176 }
177
178 static gchar *
179 _get_extension (const gchar * uri)
180 {
181   gchar *result;
182   gsize len;
183   gint find;
184
185   GST_DEBUG ("finding extension of %s", uri);
186
187   if (uri == NULL)
188     goto no_uri;
189
190   /* find the extension on the uri, this is everything after a '.' */
191   len = strlen (uri);
192   find = len - 1;
193
194   while (find >= 0) {
195     if (uri[find] == '.')
196       break;
197     find--;
198   }
199   if (find < 0)
200     goto no_extension;
201
202   result = g_strdup (&uri[find + 1]);
203
204   GST_DEBUG ("found extension %s", result);
205
206   return result;
207
208   /* ERRORS */
209 no_uri:
210   {
211     GST_WARNING ("could not parse the peer uri");
212     return NULL;
213   }
214 no_extension:
215   {
216     GST_WARNING ("could not find uri extension in %s", uri);
217     return NULL;
218   }
219 }
220
221 /**
222  * ges_formatter_can_load_uri:
223  * @uri: a #gchar * pointing to the URI
224  * @error: A #GError that will be set in case of error
225  *
226  * Checks if there is a #GESFormatter available which can load a #GESTimeline
227  * from the given URI.
228  *
229  * Returns: TRUE if there is a #GESFormatter that can support the given uri
230  * or FALSE if not.
231  */
232
233 gboolean
234 ges_formatter_can_load_uri (const gchar * uri, GError ** error)
235 {
236   gboolean ret = FALSE;
237   gchar *extension;
238   GList *formatter_assets, *tmp;
239   GESFormatterClass *class = NULL;
240
241   if (!(gst_uri_is_valid (uri))) {
242     GST_ERROR ("Invalid uri!");
243     return FALSE;
244   }
245
246   extension = _get_extension (uri);
247
248   formatter_assets = ges_list_assets (GES_TYPE_FORMATTER);
249   for (tmp = formatter_assets; tmp; tmp = tmp->next) {
250     GESAsset *asset = GES_ASSET (tmp->data);
251     GESFormatter *dummy_instance;
252     gchar **valid_exts =
253         g_strsplit (ges_meta_container_get_string (GES_META_CONTAINER (asset),
254             GES_META_FORMATTER_EXTENSION), ",", -1);
255     gint i;
256
257     if (extension) {
258       gboolean found = FALSE;
259
260       for (i = 0; valid_exts[i]; i++) {
261         if (!g_strcmp0 (extension, valid_exts[i])) {
262           found = TRUE;
263           break;
264         }
265       }
266
267       if (!found)
268         goto next;
269     }
270
271     class = g_type_class_ref (ges_asset_get_extractable_type (asset));
272     dummy_instance =
273         g_object_ref_sink (g_object_new (ges_asset_get_extractable_type (asset),
274             NULL));
275     if (class->can_load_uri (dummy_instance, uri, error)) {
276       g_type_class_unref (class);
277       gst_object_unref (dummy_instance);
278       ret = TRUE;
279       break;
280     }
281     g_type_class_unref (class);
282     gst_object_unref (dummy_instance);
283   next:
284     g_strfreev (valid_exts);
285   }
286   g_free (extension);
287
288   g_list_free (formatter_assets);
289   return ret;
290 }
291
292 /**
293  * ges_formatter_can_save_uri:
294  * @uri: a #gchar * pointing to a URI
295  * @error: A #GError that will be set in case of error
296  *
297  * Returns TRUE if there is a #GESFormatter available which can save a
298  * #GESTimeline to the given URI.
299  *
300  * Returns: TRUE if the given @uri is supported, else FALSE.
301  */
302
303 gboolean
304 ges_formatter_can_save_uri (const gchar * uri, GError ** error)
305 {
306   GFile *file = NULL;
307   GFile *dir = NULL;
308   gboolean ret = TRUE;
309   GFileInfo *info = NULL;
310
311   if (!(gst_uri_is_valid (uri))) {
312     GST_ERROR ("%s invalid uri!", uri);
313     return FALSE;
314   }
315
316   if (!(gst_uri_has_protocol (uri, "file"))) {
317     gchar *proto = gst_uri_get_protocol (uri);
318     GST_ERROR ("Unsupported protocol '%s'", proto);
319     g_free (proto);
320     return FALSE;
321   }
322
323   /* Check if URI or parent directory is writeable */
324   file = g_file_new_for_uri (uri);
325   if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL)
326       == G_FILE_TYPE_DIRECTORY) {
327     dir = g_object_ref (file);
328   } else {
329     dir = g_file_get_parent (file);
330
331     if (dir == NULL)
332       goto error;
333   }
334
335   info = g_file_query_info (dir, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
336       G_FILE_QUERY_INFO_NONE, NULL, error);
337
338   if (error && *error != NULL) {
339     GST_ERROR ("Unable to write to directory: %s", (*error)->message);
340
341     goto error;
342   } else {
343     gboolean writeable = g_file_info_get_attribute_boolean (info,
344         G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
345     if (!writeable) {
346       GST_ERROR ("Unable to write to directory");
347       goto error;
348     }
349   }
350
351 done:
352   if (file)
353     g_object_unref (file);
354
355   if (dir)
356     g_object_unref (dir);
357
358   if (info)
359     g_object_unref (info);
360
361   /* TODO: implement file format registry */
362   /* TODO: search through the registry and chose a GESFormatter class that can
363    * handle the URI.*/
364
365   return ret;
366 error:
367   ret = FALSE;
368   goto done;
369 }
370
371 /**
372  * ges_formatter_load_from_uri:
373  * @formatter: a #GESFormatter
374  * @timeline: a #GESTimeline
375  * @uri: a #gchar * pointing to a URI
376  * @error: A #GError that will be set in case of error
377  *
378  * Load data from the given URI into timeline.
379  *
380  * Returns: TRUE if the timeline data was successfully loaded from the URI,
381  * else FALSE.
382  *
383  * Deprecated: 1.18: Use @ges_timeline_load_from_uri
384  */
385
386 gboolean
387 ges_formatter_load_from_uri (GESFormatter * formatter,
388     GESTimeline * timeline, const gchar * uri, GError ** error)
389 {
390   gboolean ret = FALSE;
391   GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
392
393   g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
394   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
395
396   if (klass->load_from_uri) {
397     formatter->timeline = timeline;
398     ret = klass->load_from_uri (formatter, timeline, uri, error);
399   }
400
401   return ret;
402 }
403
404 /**
405  * ges_formatter_save_to_uri:
406  * @formatter: a #GESFormatter
407  * @timeline: a #GESTimeline
408  * @uri: a #gchar * pointing to a URI
409  * @overwrite: %TRUE to overwrite file if it exists
410  * @error: A #GError that will be set in case of error
411  *
412  * Save data from timeline to the given URI.
413  *
414  * Returns: TRUE if the timeline data was successfully saved to the URI
415  * else FALSE.
416  *
417  * Deprecated: 1.18: Use @ges_timeline_save_to_uri
418  */
419
420 gboolean
421 ges_formatter_save_to_uri (GESFormatter * formatter, GESTimeline *
422     timeline, const gchar * uri, gboolean overwrite, GError ** error)
423 {
424   GError *lerr = NULL;
425   gboolean ret = FALSE;
426   GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
427
428   GST_DEBUG_OBJECT (formatter, "Saving %" GST_PTR_FORMAT " to %s",
429       timeline, uri);
430   if (klass->save_to_uri)
431     ret = klass->save_to_uri (formatter, timeline, uri, overwrite, &lerr);
432   else
433     GST_ERROR_OBJECT (formatter, "save_to_uri not implemented!");
434
435   if (lerr) {
436     GST_WARNING_OBJECT (formatter, "%" GST_PTR_FORMAT
437         " not saved to %s error: %s", timeline, uri, lerr->message);
438     g_propagate_error (error, lerr);
439   } else
440     GST_INFO_OBJECT (formatter, "%" GST_PTR_FORMAT
441         " saved to %s", timeline, uri);
442
443   return ret;
444 }
445
446 /**
447  * ges_formatter_get_default:
448  *
449  * Get the default #GESAsset to use as formatter. It will return
450  * the asset for the #GESFormatter that has the highest @rank
451  *
452  * Returns: (transfer none): The #GESAsset for the formatter with highest @rank
453  */
454 GESAsset *
455 ges_formatter_get_default (void)
456 {
457   GList *assets, *tmp;
458   GESAsset *ret = NULL;
459   guint tmprank, rank = GST_RANK_NONE;
460
461   assets = ges_list_assets (GES_TYPE_FORMATTER);
462   for (tmp = assets; tmp; tmp = tmp->next) {
463     tmprank = GST_RANK_NONE;
464     ges_meta_container_get_uint (GES_META_CONTAINER (tmp->data),
465         GES_META_FORMATTER_RANK, &tmprank);
466
467     if (tmprank > rank) {
468       rank = tmprank;
469       ret = GES_ASSET (tmp->data);
470     }
471   }
472   g_list_free (assets);
473
474   return ret;
475 }
476
477 /**
478  * ges_formatter_class_register_metas:
479  * @klass: The class to register metas on
480  * @name: The name of the formatter
481  * @description: The formatter description
482  * @extensions: A list of coma separated file extensions handled
483  * by the formatter. The order of the extensions should match the
484  * list of the structures inside @caps
485  * @caps: The caps the formatter handled, they should match what
486  * gstreamer typefind mechanism will report for the files the formatter
487  * handles.
488  * @version: The version of the formatter
489  * @rank: The rank of the formatter
490  */
491 void
492 ges_formatter_class_register_metas (GESFormatterClass * klass,
493     const gchar * name, const gchar * description, const gchar * extensions,
494     const gchar * caps, gdouble version, GstRank rank)
495 {
496   g_return_if_fail (klass->name);
497   klass->name = g_strdup (name);
498   klass->description = g_strdup (description);
499   klass->extension = g_strdup (extensions);
500   klass->mimetype = g_strdup (caps);
501   klass->version = version;
502   klass->rank = rank;
503
504   if (g_atomic_int_get (&initialized)
505       && g_type_class_peek (G_OBJECT_CLASS_TYPE (klass)))
506     gst_object_unref (ges_asset_request (G_OBJECT_CLASS_TYPE (klass), NULL,
507             NULL));
508 }
509
510 /* Main Formatter methods */
511
512 /*< protected >*/
513 void
514 ges_formatter_set_project (GESFormatter * formatter, GESProject * project)
515 {
516   formatter->project = project;
517 }
518
519 GESProject *
520 ges_formatter_get_project (GESFormatter * formatter)
521 {
522   return formatter->project;
523 }
524
525 static void
526 _list_formatters (GType * formatters, guint n_formatters)
527 {
528   GType *tmptypes, type;
529   guint tmp_n_types, i;
530
531   for (i = 0; i < n_formatters; i++) {
532     type = formatters[i];
533     tmptypes = g_type_children (type, &tmp_n_types);
534     if (tmp_n_types) {
535       /* Recurse as g_type_children does not */
536       _list_formatters (tmptypes, tmp_n_types);
537     }
538     g_free (tmptypes);
539
540     if (G_TYPE_IS_ABSTRACT (type)) {
541       GST_DEBUG ("%s is abstract, not using", g_type_name (type));
542     } else {
543       gst_object_unref (ges_asset_request (type, NULL, NULL));
544     }
545   }
546 }
547
548 static void
549 load_python_formatters (void)
550 {
551 #ifdef HAS_PYTHON
552   PyGILState_STATE state = 0;
553   PyObject *main_module, *main_locals;
554   GError *err = NULL;
555   GResource *resource = ges_get_resource ();
556   GBytes *bytes =
557       g_resource_lookup_data (resource, "/ges/python/gesotioformatter.py",
558       G_RESOURCE_LOOKUP_FLAGS_NONE, &err);
559   PyObject *code = NULL, *res = NULL;
560   gboolean we_initialized = FALSE;
561   GModule *libpython;
562   gpointer has_python = NULL;
563
564   GST_LOG ("Checking to see if libpython is already loaded");
565   if (g_module_symbol (g_module_open (NULL, G_MODULE_BIND_LOCAL),
566           "_Py_NoneStruct", &has_python) && has_python) {
567     GST_LOG ("libpython is already loaded");
568   } else {
569     const gchar *libpython_path =
570         PY_LIB_LOC "/libpython" PYTHON_VERSION PY_ABI_FLAGS "." PY_LIB_SUFFIX;
571     GST_LOG ("loading libpython from '%s'", libpython_path);
572     libpython = g_module_open (libpython_path, 0);
573     if (!libpython) {
574       GST_ERROR ("Couldn't g_module_open libpython. Reason: %s",
575           g_module_error ());
576       return;
577     }
578   }
579
580   if (!Py_IsInitialized ()) {
581     GST_LOG ("python wasn't initialized");
582     /* set the correct plugin for registering stuff */
583     Py_Initialize ();
584     we_initialized = TRUE;
585   } else {
586     GST_LOG ("python was already initialized");
587     state = PyGILState_Ensure ();
588   }
589
590   if (!bytes) {
591     GST_DEBUG ("Could not load gesotioformatter: %s\n", err->message);
592
593     g_clear_error (&err);
594
595     goto done;
596   }
597
598   main_module = PyImport_AddModule ("__main__");
599   if (main_module == NULL) {
600     GST_WARNING ("Could not add main module");
601     PyErr_Print ();
602     PyErr_Clear ();
603     goto done;
604   }
605
606   main_locals = PyModule_GetDict (main_module);
607   /* Compiling the code ourself so it has a proper filename */
608   code =
609       Py_CompileString (g_bytes_get_data (bytes, NULL), "gesotioformatter.py",
610       Py_file_input);
611   if (PyErr_Occurred ()) {
612     PyErr_Print ();
613     PyErr_Clear ();
614     goto done;
615   }
616   res = PyEval_EvalCode ((gpointer) code, main_locals, main_locals);
617   Py_XDECREF (code);
618   Py_XDECREF (res);
619   if (PyErr_Occurred ()) {
620     PyObject *exception_backtrace;
621     PyObject *exception_type;
622     PyObject *exception_value, *exception_value_repr, *exception_value_str;
623
624     PyErr_Fetch (&exception_type, &exception_value, &exception_backtrace);
625     PyErr_NormalizeException (&exception_type, &exception_value,
626         &exception_backtrace);
627
628     exception_value_repr = PyObject_Repr (exception_value);
629     exception_value_str =
630         PyUnicode_AsEncodedString (exception_value_repr, "utf-8", "Error ~");
631     GST_INFO ("Could not load OpenTimelineIO formatter: %s",
632         PyBytes_AS_STRING (exception_value_str));
633
634     Py_XDECREF (exception_type);
635     Py_XDECREF (exception_value);
636     Py_XDECREF (exception_backtrace);
637
638     Py_XDECREF (exception_value_repr);
639     Py_XDECREF (exception_value_str);
640     PyErr_Clear ();
641   }
642
643 done:
644   if (bytes)
645     g_bytes_unref (bytes);
646
647   if (we_initialized) {
648     PyEval_SaveThread ();
649   } else {
650     PyGILState_Release (state);
651   }
652 #endif /* HAS_PYTHON */
653 }
654
655 void
656 _init_formatter_assets (void)
657 {
658   GType *formatters;
659   guint n_formatters;
660   static gsize init_debug = 0;
661
662   if (g_once_init_enter (&init_debug)) {
663
664     GST_DEBUG_CATEGORY_INIT (ges_formatter_debug, "gesformatter",
665         GST_DEBUG_FG_YELLOW, "ges formatter");
666     g_once_init_leave (&init_debug, TRUE);
667   }
668
669   if (g_atomic_int_compare_and_exchange (&initialized, FALSE, TRUE)) {
670     /* register formatter types with the system */
671 #ifndef DISABLE_XPTV
672     g_type_class_ref (GES_TYPE_PITIVI_FORMATTER);
673 #endif
674     g_type_class_ref (GES_TYPE_COMMAND_LINE_FORMATTER);
675     g_type_class_ref (GES_TYPE_XML_FORMATTER);
676
677     load_python_formatters ();
678
679     formatters = g_type_children (GES_TYPE_FORMATTER, &n_formatters);
680     _list_formatters (formatters, n_formatters);
681     g_free (formatters);
682   }
683 }
684
685 void
686 _deinit_formatter_assets (void)
687 {
688   if (g_atomic_int_compare_and_exchange (&initialized, TRUE, FALSE)) {
689
690 #ifndef DISABLE_XPTV
691     g_type_class_unref (g_type_class_peek (GES_TYPE_PITIVI_FORMATTER));
692 #endif
693
694     g_type_class_unref (g_type_class_peek (GES_TYPE_COMMAND_LINE_FORMATTER));
695     g_type_class_unref (g_type_class_peek (GES_TYPE_XML_FORMATTER));
696   }
697 }
698
699 static gint
700 _sort_formatters (GESAsset * asset, GESAsset * asset1)
701 {
702   GESFormatterClass *class =
703       g_type_class_peek (ges_asset_get_extractable_type (asset));
704   GESFormatterClass *class1 =
705       g_type_class_peek (ges_asset_get_extractable_type (asset1));
706
707   /* We want the highest ranks to be first! */
708   if (class->rank > class1->rank)
709     return -1;
710   else if (class->rank < class1->rank)
711     return 1;
712
713   return 0;
714 }
715
716 GESAsset *
717 _find_formatter_asset_for_id (const gchar * id)
718 {
719   GESFormatterClass *class = NULL;
720   GList *formatter_assets, *tmp;
721   GESAsset *asset = NULL;
722
723   formatter_assets = g_list_sort (ges_list_assets (GES_TYPE_FORMATTER),
724       (GCompareFunc) _sort_formatters);
725   for (tmp = formatter_assets; tmp; tmp = tmp->next) {
726     GESFormatter *dummy_instance;
727
728     asset = GES_ASSET (tmp->data);
729     class = g_type_class_ref (ges_asset_get_extractable_type (asset));
730     dummy_instance =
731         g_object_ref_sink (g_object_new (ges_asset_get_extractable_type (asset),
732             NULL));
733     if (class->can_load_uri (dummy_instance, id, NULL)) {
734       g_type_class_unref (class);
735       asset = gst_object_ref (asset);
736       gst_object_unref (dummy_instance);
737       break;
738     }
739
740     asset = NULL;
741     g_type_class_unref (class);
742     gst_object_unref (dummy_instance);
743   }
744
745   g_list_free (formatter_assets);
746
747   return asset;
748 }
749
750 /**
751  * ges_find_formatter_for_uri:
752  *
753  * Get the best formatter for @uri. It tries to find a formatter
754  * compatible with @uri extension, if none is found, it returns the default
755  * formatter asset.
756  *
757  * Returns: (transfer none): The #GESAsset for the best formatter to save to @uri
758  *
759  * Since: 1.18
760  */
761 GESAsset *
762 ges_find_formatter_for_uri (const gchar * uri)
763 {
764   GList *formatter_assets, *tmp;
765   GESAsset *asset = NULL;
766
767   gchar *extension = _get_extension (uri);
768   if (!extension)
769     return ges_formatter_get_default ();
770
771   formatter_assets = g_list_sort (ges_list_assets (GES_TYPE_FORMATTER),
772       (GCompareFunc) _sort_formatters);
773
774   for (tmp = formatter_assets; tmp; tmp = tmp->next) {
775     gint i;
776     gchar **valid_exts =
777         g_strsplit (ges_meta_container_get_string (GES_META_CONTAINER
778             (tmp->data),
779             GES_META_FORMATTER_EXTENSION), ",", -1);
780
781     for (i = 0; valid_exts[i]; i++) {
782       if (!g_strcmp0 (extension, valid_exts[i])) {
783         asset = GES_ASSET (tmp->data);
784         break;
785       }
786     }
787
788     g_strfreev (valid_exts);
789     if (asset)
790       break;
791   }
792   g_free (extension);
793   g_list_free (formatter_assets);
794
795   if (asset) {
796     GST_INFO_OBJECT (asset, "Using for URI %s", uri);
797     return asset;
798   }
799
800   return ges_formatter_get_default ();
801 }