#include "ges-formatter.h"
#include "ges-internal.h"
#include "ges.h"
+#ifdef HAS_PYTHON
+#include <Python.h>
+#include "ges-resources.h"
+#endif
+
+GST_DEBUG_CATEGORY_STATIC (ges_formatter_debug);
+#undef GST_CAT_DEFAULT
+#define GST_CAT_DEFAULT ges_formatter_debug
/* TODO Add a GCancellable somewhere in the API */
static void ges_extractable_interface_init (GESExtractableInterface * iface);
ges_meta_container_register_meta_string (container, GES_META_READ_WRITE,
GES_META_FORMAT_VERSION, NULL);
- g_clear_pointer (&fclass->name, g_free);
- g_clear_pointer (&fclass->description, g_free);
- g_clear_pointer (&fclass->extension, g_free);
- g_clear_pointer (&fclass->mimetype, g_free);
+ /* We are leaking the metadata but we don't really have choice here
+ * as calling ges_init() after deinit() is allowed.
+ */
return TRUE;
}
}
}
+static void
+load_python_formatters (void)
+{
+#ifdef HAS_PYTHON
+ PyGILState_STATE state = 0;
+ PyObject *main_module, *main_locals;
+ GError *err = NULL;
+ GResource *resource = ges_get_resource ();
+ GBytes *bytes =
+ g_resource_lookup_data (resource, "/ges/python/gesotioformatter.py",
+ G_RESOURCE_LOOKUP_FLAGS_NONE, &err);
+ PyObject *code = NULL, *res = NULL;
+ gboolean we_initialized = FALSE;
+ GModule *libpython;
+ gpointer has_python = NULL;
+
+ GST_LOG ("Checking to see if libpython is already loaded");
+ if (g_module_symbol (g_module_open (NULL, G_MODULE_BIND_LOCAL),
+ "_Py_NoneStruct", &has_python) && has_python) {
+ GST_LOG ("libpython is already loaded");
+ } else {
+ const gchar *libpython_path =
+ PY_LIB_LOC "/libpython" PYTHON_VERSION PY_ABI_FLAGS "." PY_LIB_SUFFIX;
+ GST_LOG ("loading libpython from '%s'", libpython_path);
+ libpython = g_module_open (libpython_path, 0);
+ if (!libpython) {
+ GST_ERROR ("Couldn't g_module_open libpython. Reason: %s",
+ g_module_error ());
+ return;
+ }
+ }
+
+ if (!Py_IsInitialized ()) {
+ GST_LOG ("python wasn't initialized");
+ /* set the correct plugin for registering stuff */
+ Py_Initialize ();
+ we_initialized = TRUE;
+ } else {
+ GST_LOG ("python was already initialized");
+ state = PyGILState_Ensure ();
+ }
+
+ if (!bytes) {
+ GST_DEBUG ("Could not load gesotioformatter: %s\n", err->message);
+
+ g_clear_error (&err);
+
+ goto done;
+ }
+
+ main_module = PyImport_AddModule ("__main__");
+ if (main_module == NULL) {
+ GST_WARNING ("Could not add main module");
+ PyErr_Print ();
+ PyErr_Clear ();
+ goto done;
+ }
+
+ main_locals = PyModule_GetDict (main_module);
+ /* Compiling the code ourself so it has a proper filename */
+ code =
+ Py_CompileString (g_bytes_get_data (bytes, NULL), "gesotioformatter.py",
+ Py_file_input);
+ if (PyErr_Occurred ()) {
+ PyErr_Print ();
+ PyErr_Clear ();
+ goto done;
+ }
+ res = PyEval_EvalCode ((gpointer) code, main_locals, main_locals);
+ Py_XDECREF (code);
+ Py_XDECREF (res);
+ if (PyErr_Occurred ()) {
+ PyErr_Print ();
+ PyErr_Clear ();
+ }
+
+done:
+ if (bytes)
+ g_bytes_unref (bytes);
+
+ if (we_initialized) {
+ PyEval_SaveThread ();
+ } else {
+ PyGILState_Release (state);
+ }
+#endif /* HAS_PYTHON */
+}
+
void
_init_formatter_assets (void)
{
GType *formatters;
guint n_formatters;
+ static gsize init_debug = 0;
+
+ if (g_once_init_enter (&init_debug)) {
+
+ GST_DEBUG_CATEGORY_INIT (ges_formatter_debug, "gesformatter",
+ GST_DEBUG_FG_YELLOW, "ges formatter");
+ g_once_init_leave (&init_debug, TRUE);
+ }
+
+ load_python_formatters ();
+
formatters = g_type_children (GES_TYPE_FORMATTER, &n_formatters);
_list_formatters (formatters, n_formatters);
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/ges/">
+ <file>python/gesotioformatter.py</file>
+ </gresource>
+</gresources>
command : [flex, '-Ppriv_ges_parse_yy', '--header-file=@OUTPUT1@', '-o', '@OUTPUT0@', '@INPUT@']
)
-libges = library('ges-1.0', ges_sources, parser,
+ges_resources = []
+if has_python
+ ges_resources = gnome.compile_resources(
+ 'ges-resources', 'ges.resource',
+ source_dir: '.',
+ c_name: 'ges'
+ )
+endif
+
+libges = library('ges-1.0', ges_sources, parser, ges_resources,
version : libversion,
soversion : soversion,
darwin_versions : osxversion,
--- /dev/null
+#!/usr/bin/env python
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+#
+# Copyright (C) 2019 Igalia S.L
+# Authors:
+# Thibault Saunier <tsaunier@igalia.com>
+#
+
+import sys
+
+import gi
+import tempfile
+gi.require_version("GES", "1.0")
+gi.require_version("Gst", "1.0")
+
+from gi.repository import GObject
+from gi.repository import Gst
+Gst.init(None)
+from gi.repository import GES
+from gi.repository import GLib
+from collections import OrderedDict
+
+try:
+ import opentimelineio as otio
+ otio.adapters.from_name('xges')
+except Exception as e:
+ Gst.info("Could not load OpenTimelineIO: %s" % e)
+ otio = None
+
+class GESOtioFormatter(GES.Formatter):
+ def do_save_to_uri(self, timeline, uri, overwrite):
+ if not Gst.uri_is_valid(uri) or Gst.uri_get_protocol(uri) != "file":
+ Gst.error("Protocol not supported for file: %s" % uri)
+ return False
+
+ with tempfile.NamedTemporaryFile(suffix=".xges") as tmpxges:
+ timeline.get_asset().save(timeline, "file://" + tmpxges.name, None, overwrite)
+
+ linker = otio.media_linker.MediaLinkingPolicy.ForceDefaultLinker
+ otio_timeline = otio.adapters.read_from_file(tmpxges.name, "xges", media_linker_name=linker)
+ location = Gst.uri_get_location(uri)
+ out_adapter = otio.adapters.from_filepath(location)
+ otio.adapters.write_to_file(otio_timeline, Gst.uri_get_location(uri), out_adapter.name)
+
+ return True
+
+ def do_can_load_uri(self, uri):
+ try:
+ if not Gst.uri_is_valid(uri) or Gst.uri_get_protocol(uri) != "file":
+ return False
+ except GLib.Error as e:
+ Gst.error(str(e))
+ return False
+
+ if uri.endswith(".xges"):
+ return False
+
+ try:
+ return otio.adapters.from_filepath(Gst.uri_get_location(uri)) is not None
+ except Exception as e:
+ Gst.info("Could not load %s -> %s" % (uri, e))
+ return False
+
+
+ def do_load_from_uri(self, timeline, uri):
+ location = Gst.uri_get_location(uri)
+ in_adapter = otio.adapters.from_filepath(location)
+ assert(in_adapter) # can_load_uri should have ensured it is loadable
+
+ linker = otio.media_linker.MediaLinkingPolicy.ForceDefaultLinker
+ otio_timeline = otio.adapters.read_from_file(
+ location,
+ in_adapter.name,
+ media_linker_name=linker
+ )
+
+ with tempfile.NamedTemporaryFile(suffix=".xges") as tmpxges:
+ otio.adapters.write_to_file(otio_timeline, tmpxges.name, "xges")
+ formatter = GES.Formatter.get_default().extract()
+ timeline.get_asset().add_formatter(formatter)
+ return formatter.load_from_uri(timeline, "file://" + tmpxges.name)
+
+if otio is not None:
+ GObject.type_register(GESOtioFormatter)
+ known_extensions_mimetype_map = [
+ ("otio", "xml", "fcpxml"),
+ ("application/otio", "application/xmeml", "application/fcpxml")
+ ]
+
+ extensions = []
+ for adapter in otio.plugins.ActiveManifest().adapters:
+ if adapter.name != 'xges':
+ extensions.extend(adapter.suffixes)
+
+ extensions_mimetype_map = [[], []]
+ for i, ext in enumerate(known_extensions_mimetype_map[0]):
+ if ext in extensions:
+ extensions_mimetype_map[0].append(ext)
+ extensions_mimetype_map[1].append(known_extensions_mimetype_map[1][i])
+ extensions.remove(ext)
+ extensions_mimetype_map[0].extend(extensions)
+
+ GES.FormatterClass.register_metas(GESOtioFormatter, "otioformatter",
+ "GES Formatter using OpenTimelineIO",
+ ','.join(extensions_mimetype_map[0]),
+ ';'.join(extensions_mimetype_map[1]), 0.1, Gst.Rank.SECONDARY)
cdata.set('HAVE_GST_VALIDATE', 1)
endif
-configure_file(output : 'config.h', configuration : cdata)
-
-
gir = find_program('g-ir-scanner', required : get_option('introspection'))
gnome = import('gnome')
'gst_init(NULL,NULL);' + \
'ges_init();', '--quiet']
+has_python = false
+if build_gir
+ pymod = import('python')
+ python = pymod.find_installation(required: get_option('python'))
+ python_dep = python.dependency(required : get_option('python'))
+ if python_dep.found()
+ python_abi_flags = python.get_variable('ABIFLAGS', '')
+ pylib_loc = get_option('libpython-dir')
+
+ error_msg = ''
+ if not cc.compiles('#include <Python.h>', dependencies: [python_dep])
+ error_msg = 'Could not compile a simple program against python'
+ elif pylib_loc == ''
+ check_path_exists = 'import os, sys; assert(os.path.exists(sys.argv[1]))'
+ pylib_loc = python.get_variable('LIBPL', '')
+ if host_machine.system() != 'windows'
+ pylib_ldlibrary = python.get_variable('LDLIBRARY', '')
+ if host_machine.system() == 'darwin'
+ # OSX is a pain. Python as shipped by apple installs libpython in /usr/lib
+ # so we hardcode that. Other systems can use -Dlibpythondir to
+ # override this.
+ pylib_loc = '/usr/lib'
+ else
+ if run_command(python, '-c', check_path_exists, join_paths(pylib_loc, pylib_ldlibrary)).returncode() != 0
+ # Workaround for Fedora
+ pylib_loc = python.get_variable('LIBDIR', '')
+ message('pylib_loc = @0@'.format(pylib_loc))
+ endif
+ endif
+
+ res = run_command(python, '-c', check_path_exists, join_paths(pylib_loc, pylib_ldlibrary))
+ if res.returncode() != 0
+ error_msg = '@0@ doesn\' exist, can\'t use python'.format(join_paths(pylib_loc, pylib_ldlibrary))
+ endif
+ endif
+ if error_msg == ''
+ pylib_suffix = 'so'
+ if host_machine.system() == 'windows'
+ pylib_suffix = 'dll'
+ elif host_machine.system() == 'darwin'
+ pylib_suffix = 'dylib'
+ endif
+
+ gmodule_dep = dependency('gmodule-2.0')
+ libges_deps = libges_deps + [python_dep, gmodule_dep]
+ has_python = true
+ message('python_abi_flags = @0@'.format(python_abi_flags))
+ message('pylib_loc = @0@'.format(pylib_loc))
+ cdata.set('HAS_PYTHON', true)
+ cdata.set('PY_LIB_LOC', '"@0@"'.format(pylib_loc))
+ cdata.set('PY_ABI_FLAGS', '"@0@"'.format(python_abi_flags))
+ cdata.set('PY_LIB_SUFFIX', '"@0@"'.format(pylib_suffix))
+ cdata.set('PYTHON_VERSION', '"@0@"'.format(python_dep.version()))
+ else
+ if get_option('python').enabled()
+ error(error_msg)
+ else
+ message(error_msg)
+ endif
+ endif
+ endif
+ endif
+endif
+
+configure_file(output : 'config.h', configuration : cdata)
+
ges_c_args = ['-DHAVE_CONFIG_H', '-DG_LOG_DOMAIN="GES"']
plugins_install_dir = '@0@/gstreamer-1.0'.format(get_option('libdir'))
description : 'Build the deprecated xptv formater')
option('doc', type : 'feature', value : 'auto', yield: true,
description: 'Enable documentation.')
+option('python', type : 'feature', value : 'auto', yield: true,
+ description: 'Enable python formatters.')
+option('libpython-dir', type : 'string', value : '',
+ description: 'Path to find libpythonXX.so')