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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:gesformatter
23 * @title: GESFormatter
24 * @short_description: Timeline saving and loading.
36 #include "ges-formatter.h"
37 #include "ges-internal.h"
40 #include "ges-pitivi-formatter.h"
45 #include "ges-resources.h"
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;
53 /* TODO Add a GCancellable somewhere in the API */
54 static void ges_extractable_interface_init (GESExtractableInterface * iface);
56 struct _GESFormatterPrivate
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));
66 static void ges_formatter_dispose (GObject * object);
67 static gboolean default_can_load_uri (GESFormatter * dummy_instance,
68 const gchar * uri, GError ** error);
70 /* GESExtractable implementation */
72 extractable_check_id (GType type, const gchar * id)
74 GESFormatterClass *class;
79 class = g_type_class_peek (type);
80 return g_strdup (class->name);
84 extractable_get_id (GESExtractable * self)
88 if (!(asset = ges_extractable_get_asset (self)))
91 return g_strdup (ges_asset_get_id (asset));
95 _register_metas (GESExtractableInterface * iface, GObjectClass * class,
98 GESFormatterClass *fclass = GES_FORMATTER_CLASS (class);
99 GESMetaContainer *container = GES_META_CONTAINER (asset);
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);
116 /* We are leaking the metadata but we don't really have choice here
117 * as calling ges_init() after deinit() is allowed.
124 ges_extractable_interface_init (GESExtractableInterface * iface)
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;
133 ges_formatter_class_init (GESFormatterClass * klass)
135 GObjectClass *object_class = G_OBJECT_CLASS (klass);
137 object_class->dispose = ges_formatter_dispose;
139 klass->can_load_uri = default_can_load_uri;
140 klass->load_from_uri = NULL;
141 klass->save_to_uri = NULL;
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;
154 ges_formatter_init (GESFormatter * object)
156 object->priv = ges_formatter_get_instance_private (object);
157 object->project = NULL;
161 ges_formatter_dispose (GObject * object)
163 ges_formatter_set_project (GES_FORMATTER (object), NULL);
165 G_OBJECT_CLASS (ges_formatter_parent_class)->dispose (object);
169 default_can_load_uri (GESFormatter * dummy_instance, const gchar * uri,
172 GST_DEBUG ("%s: no 'can_load_uri' vmethod implementation",
173 G_OBJECT_TYPE_NAME (dummy_instance));
179 _get_extension (const gchar * uri)
185 GST_DEBUG ("finding extension of %s", uri);
190 /* find the extension on the uri, this is everything after a '.' */
195 if (uri[find] == '.')
202 result = g_strdup (&uri[find + 1]);
204 GST_DEBUG ("found extension %s", result);
211 GST_WARNING ("could not parse the peer uri");
216 GST_WARNING ("could not find uri extension in %s", uri);
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
226 * Checks if there is a #GESFormatter available which can load a #GESTimeline
227 * from the given URI.
229 * Returns: TRUE if there is a #GESFormatter that can support the given uri
234 ges_formatter_can_load_uri (const gchar * uri, GError ** error)
236 gboolean ret = FALSE;
238 GList *formatter_assets, *tmp;
239 GESFormatterClass *class = NULL;
241 if (!(gst_uri_is_valid (uri))) {
242 GST_ERROR ("Invalid uri!");
246 extension = _get_extension (uri);
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;
253 g_strsplit (ges_meta_container_get_string (GES_META_CONTAINER (asset),
254 GES_META_FORMATTER_EXTENSION), ",", -1);
258 gboolean found = FALSE;
260 for (i = 0; valid_exts[i]; i++) {
261 if (!g_strcmp0 (extension, valid_exts[i])) {
271 class = g_type_class_ref (ges_asset_get_extractable_type (asset));
273 g_object_ref_sink (g_object_new (ges_asset_get_extractable_type (asset),
275 if (class->can_load_uri (dummy_instance, uri, error)) {
276 g_type_class_unref (class);
277 gst_object_unref (dummy_instance);
281 g_type_class_unref (class);
282 gst_object_unref (dummy_instance);
284 g_strfreev (valid_exts);
288 g_list_free (formatter_assets);
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
297 * Returns TRUE if there is a #GESFormatter available which can save a
298 * #GESTimeline to the given URI.
300 * Returns: TRUE if the given @uri is supported, else FALSE.
304 ges_formatter_can_save_uri (const gchar * uri, GError ** error)
309 GFileInfo *info = NULL;
311 if (!(gst_uri_is_valid (uri))) {
312 GST_ERROR ("%s invalid uri!", uri);
316 if (!(gst_uri_has_protocol (uri, "file"))) {
317 gchar *proto = gst_uri_get_protocol (uri);
318 GST_ERROR ("Unsupported protocol '%s'", proto);
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);
329 dir = g_file_get_parent (file);
335 info = g_file_query_info (dir, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
336 G_FILE_QUERY_INFO_NONE, NULL, error);
338 if (error && *error != NULL) {
339 GST_ERROR ("Unable to write to directory: %s", (*error)->message);
343 gboolean writeable = g_file_info_get_attribute_boolean (info,
344 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
346 GST_ERROR ("Unable to write to directory");
353 g_object_unref (file);
356 g_object_unref (dir);
359 g_object_unref (info);
361 /* TODO: implement file format registry */
362 /* TODO: search through the registry and chose a GESFormatter class that can
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
378 * Load data from the given URI into timeline.
380 * Returns: TRUE if the timeline data was successfully loaded from the URI,
383 * Deprecated: 1.18: Use @ges_timeline_load_from_uri
387 ges_formatter_load_from_uri (GESFormatter * formatter,
388 GESTimeline * timeline, const gchar * uri, GError ** error)
390 gboolean ret = FALSE;
391 GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
393 g_return_val_if_fail (GES_IS_FORMATTER (formatter), FALSE);
394 g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
396 if (klass->load_from_uri) {
397 formatter->timeline = timeline;
398 ret = klass->load_from_uri (formatter, timeline, uri, error);
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
412 * Save data from timeline to the given URI.
414 * Returns: TRUE if the timeline data was successfully saved to the URI
417 * Deprecated: 1.18: Use @ges_timeline_save_to_uri
421 ges_formatter_save_to_uri (GESFormatter * formatter, GESTimeline *
422 timeline, const gchar * uri, gboolean overwrite, GError ** error)
425 gboolean ret = FALSE;
426 GESFormatterClass *klass = GES_FORMATTER_GET_CLASS (formatter);
428 GST_DEBUG_OBJECT (formatter, "Saving %" GST_PTR_FORMAT " to %s",
430 if (klass->save_to_uri)
431 ret = klass->save_to_uri (formatter, timeline, uri, overwrite, &lerr);
433 GST_ERROR_OBJECT (formatter, "save_to_uri not implemented!");
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);
440 GST_INFO_OBJECT (formatter, "%" GST_PTR_FORMAT
441 " saved to %s", timeline, uri);
447 * ges_formatter_get_default:
449 * Get the default #GESAsset to use as formatter. It will return
450 * the asset for the #GESFormatter that has the highest @rank
452 * Returns: (transfer none): The #GESAsset for the formatter with highest @rank
455 ges_formatter_get_default (void)
458 GESAsset *ret = NULL;
459 guint tmprank, rank = GST_RANK_NONE;
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);
467 if (tmprank > rank) {
469 ret = GES_ASSET (tmp->data);
472 g_list_free (assets);
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
488 * @version: The version of the formatter
489 * @rank: The rank of the formatter
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)
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;
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,
510 /* Main Formatter methods */
514 ges_formatter_set_project (GESFormatter * formatter, GESProject * project)
516 formatter->project = project;
520 ges_formatter_get_project (GESFormatter * formatter)
522 return formatter->project;
526 _list_formatters (GType * formatters, guint n_formatters)
528 GType *tmptypes, type;
529 guint tmp_n_types, i;
531 for (i = 0; i < n_formatters; i++) {
532 type = formatters[i];
533 tmptypes = g_type_children (type, &tmp_n_types);
535 /* Recurse as g_type_children does not */
536 _list_formatters (tmptypes, tmp_n_types);
540 if (G_TYPE_IS_ABSTRACT (type)) {
541 GST_DEBUG ("%s is abstract, not using", g_type_name (type));
543 gst_object_unref (ges_asset_request (type, NULL, NULL));
549 load_python_formatters (void)
552 PyGILState_STATE state = 0;
553 PyObject *main_module, *main_locals;
555 GResource *resource = ges_get_resource ();
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;
562 gpointer has_python = NULL;
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");
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);
574 GST_ERROR ("Couldn't g_module_open libpython. Reason: %s",
580 if (!Py_IsInitialized ()) {
581 GST_LOG ("python wasn't initialized");
582 /* set the correct plugin for registering stuff */
584 we_initialized = TRUE;
586 GST_LOG ("python was already initialized");
587 state = PyGILState_Ensure ();
591 GST_DEBUG ("Could not load gesotioformatter: %s\n", err->message);
593 g_clear_error (&err);
598 main_module = PyImport_AddModule ("__main__");
599 if (main_module == NULL) {
600 GST_WARNING ("Could not add main module");
606 main_locals = PyModule_GetDict (main_module);
607 /* Compiling the code ourself so it has a proper filename */
609 Py_CompileString (g_bytes_get_data (bytes, NULL), "gesotioformatter.py",
611 if (PyErr_Occurred ()) {
616 res = PyEval_EvalCode ((gpointer) code, main_locals, main_locals);
619 if (PyErr_Occurred ()) {
620 PyObject *exception_backtrace;
621 PyObject *exception_type;
622 PyObject *exception_value, *exception_value_repr, *exception_value_str;
624 PyErr_Fetch (&exception_type, &exception_value, &exception_backtrace);
625 PyErr_NormalizeException (&exception_type, &exception_value,
626 &exception_backtrace);
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));
634 Py_XDECREF (exception_type);
635 Py_XDECREF (exception_value);
636 Py_XDECREF (exception_backtrace);
638 Py_XDECREF (exception_value_repr);
639 Py_XDECREF (exception_value_str);
645 g_bytes_unref (bytes);
647 if (we_initialized) {
648 PyEval_SaveThread ();
650 PyGILState_Release (state);
652 #endif /* HAS_PYTHON */
656 _init_formatter_assets (void)
660 static gsize init_debug = 0;
662 if (g_once_init_enter (&init_debug)) {
664 GST_DEBUG_CATEGORY_INIT (ges_formatter_debug, "gesformatter",
665 GST_DEBUG_FG_YELLOW, "ges formatter");
666 g_once_init_leave (&init_debug, TRUE);
669 if (g_atomic_int_compare_and_exchange (&initialized, FALSE, TRUE)) {
670 /* register formatter types with the system */
672 g_type_class_ref (GES_TYPE_PITIVI_FORMATTER);
674 g_type_class_ref (GES_TYPE_COMMAND_LINE_FORMATTER);
675 g_type_class_ref (GES_TYPE_XML_FORMATTER);
677 load_python_formatters ();
679 formatters = g_type_children (GES_TYPE_FORMATTER, &n_formatters);
680 _list_formatters (formatters, n_formatters);
686 _deinit_formatter_assets (void)
688 if (g_atomic_int_compare_and_exchange (&initialized, TRUE, FALSE)) {
691 g_type_class_unref (g_type_class_peek (GES_TYPE_PITIVI_FORMATTER));
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));
700 _sort_formatters (GESAsset * asset, GESAsset * asset1)
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));
707 /* We want the highest ranks to be first! */
708 if (class->rank > class1->rank)
710 else if (class->rank < class1->rank)
717 _find_formatter_asset_for_id (const gchar * id)
719 GESFormatterClass *class = NULL;
720 GList *formatter_assets, *tmp;
721 GESAsset *asset = NULL;
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;
728 asset = GES_ASSET (tmp->data);
729 class = g_type_class_ref (ges_asset_get_extractable_type (asset));
731 g_object_ref_sink (g_object_new (ges_asset_get_extractable_type (asset),
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);
741 g_type_class_unref (class);
742 gst_object_unref (dummy_instance);
745 g_list_free (formatter_assets);
751 * ges_find_formatter_for_uri:
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
757 * Returns: (transfer none): The #GESAsset for the best formatter to save to @uri
762 ges_find_formatter_for_uri (const gchar * uri)
764 GList *formatter_assets, *tmp;
765 GESAsset *asset = NULL;
767 gchar *extension = _get_extension (uri);
769 return ges_formatter_get_default ();
771 formatter_assets = g_list_sort (ges_list_assets (GES_TYPE_FORMATTER),
772 (GCompareFunc) _sort_formatters);
774 for (tmp = formatter_assets; tmp; tmp = tmp->next) {
777 g_strsplit (ges_meta_container_get_string (GES_META_CONTAINER
779 GES_META_FORMATTER_EXTENSION), ",", -1);
781 for (i = 0; valid_exts[i]; i++) {
782 if (!g_strcmp0 (extension, valid_exts[i])) {
783 asset = GES_ASSET (tmp->data);
788 g_strfreev (valid_exts);
793 g_list_free (formatter_assets);
796 GST_INFO_OBJECT (asset, "Using for URI %s", uri);
800 return ges_formatter_get_default ();