From 367d281ab6f2bcc0727aab0661d7075be4c09c11 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 6 Feb 2014 16:17:03 +0100 Subject: [PATCH] Reimplement gstpython plugin on top of PyGobject --- Makefile.am | 2 +- acinclude.m4 | 14 ++- configure.ac | 19 +++ plugin/Makefile.am | 13 ++ plugin/gstpythonplugin.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 349 insertions(+), 3 deletions(-) create mode 100644 plugin/Makefile.am create mode 100644 plugin/gstpythonplugin.c diff --git a/Makefile.am b/Makefile.am index a71f705..e444968 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = common gi +SUBDIRS = common gi plugin # Examples and testsuite need to be ported to 1.0 #examples testsuite diff --git a/acinclude.m4 b/acinclude.m4 index 4f93951..c0d1a5e 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -47,6 +47,11 @@ py_prefix=`$PYTHON -c "import sys; print(sys.prefix)"` py_exec_prefix=`$PYTHON -c "import sys; print(sys.exec_prefix)"` if $PYTHON-config --help 1>/dev/null 2>/dev/null; then PYTHON_INCLUDES=`$PYTHON-config --includes 2>/dev/null` + if $PYTHON-config --abiflags 1>/dev/null 2>/dev/null; then + PYTHON_ABI_FLAGS=`$PYTHON-config --abiflags 2>/dev/null` + else + PYTHON_ABI_FLAGS= + fi else PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" if test "$py_prefix" != "$py_exec_prefix"; then @@ -86,8 +91,12 @@ if $PYTHON-config --help 1>/dev/null 2>/dev/null; then # default to prefix/lib for distros that don't have a link in # .../pythonX.Y/config/ - if test ! -e $PYTHON_LIB_LOC/libpython${PYTHON_VERSION}.so; then - PYTHON_LIB_LOC=${py_prefix}/lib + if test ! -e $PYTHON_LIB_LOC/libpython${PYTHON_VERSION}${PYTHON_ABI_FLAGS}.so; then + if test -e ${py_prefix}/lib64/libpython${PYTHON_VERSION}${PYTHON_ABI_FLAGS}.so; then + PYTHON_LIB_LOC=${py_prefix}/lib64 + else + PYTHON_LIB_LOC=${py_prefix}/lib + fi fi fi else @@ -111,6 +120,7 @@ fi AC_SUBST(PYTHON_LIBS) AC_SUBST(PYTHON_LIB_LOC) +AC_SUBST(PYTHON_ABI_FLAGS) AC_SUBST(PYTHON_LIB_SUFFIX) dnl check if the headers exist: save_LIBS="$LIBS" diff --git a/configure.ac b/configure.ac index 924c834..ef1cb0c 100644 --- a/configure.ac +++ b/configure.ac @@ -64,7 +64,21 @@ if sys.version_info < minver: sys.exit(1) sys.exit(0)" +dnl check for GStreamer +GST_API_VERSION=1.0 +AC_SUBST(GST_API_VERSION) +PKG_CHECK_MODULES(GST, gstreamer-$GST_API_VERSION >= $GST_REQ) +AC_DEFINE_UNQUOTED(GST_API_VERSION, "$GST_API_VERSION", [Gst API version]) +GST_CFLAGS="$GST_CFLAGS $GLIB_EXTRA_CFLAGS" +AC_SUBST(GST_CFLAGS) +AC_SUBST(GST_LIBS) + +dnl check for pygobject +PKG_CHECK_MODULES(PYGOBJECT, pygobject-3.0 >= $PYGOBJECT_REQ) +AC_SUBST(PYGOBJECT_CFLAGS) + +dnl check for python if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC then AC_MSG_RESULT(okay) @@ -109,6 +123,8 @@ GST_CFLAGS="$GST_CFLAGS $GLIB_EXTRA_CFLAGS" AC_SUBST(GST_CFLAGS) AC_SUBST(GST_LIBS) +AG_GST_SET_PLUGINDIR + dnl check for pygobject PKG_CHECK_MODULES(PYGOBJECT, pygobject-3.0 >= $PYGOBJECT_REQ) AC_SUBST(PYGOBJECT_CFLAGS) @@ -117,6 +133,8 @@ dnl and set the override directory AC_ARG_WITH([pygi_overrides_dir], AC_HELP_STRING([--with-pygi-overrides-dir], [Path to pygobject overrides directory])) +AM_CHECK_PYTHON_LIBS(, AC_MSG_ERROR([Python libs not found. Windows requires Python modules to be explicitly linked to libpython.])) + AG_GST_VALGRIND_CHECK dnl set release date/time @@ -133,5 +151,6 @@ AC_OUTPUT([ common/Makefile common/m4/Makefile gi/Makefile + plugin/Makefile gi/overrides/Makefile ]) diff --git a/plugin/Makefile.am b/plugin/Makefile.am new file mode 100644 index 0000000..234ea4e --- /dev/null +++ b/plugin/Makefile.am @@ -0,0 +1,13 @@ +plugin_LTLIBRARIES = libgstpythonplugin.la + +INCLUDES = $(PYGOBJECT_CFLAGS) $(GST_CFLAGS)\ +-DPYTHON_VERSION=\"$(PYTHON_VERSION)\" \ + -DPY_LIB_LOC="\"$(PYTHON_LIB_LOC)\"" \ + -DPY_ABI_FLAGS="\"$(PYTHON_ABI_FLAGS)\"" \ + -DPY_LIB_SUFFIX=$(PYTHON_LIB_SUFFIX) \ + $(PYTHON_INCLUDES) + +libgstpythonplugin_la_SOURCES = gstpythonplugin.c +libgstpythonplugin_la_LDFLAGS = +libgstpythonplugin_la_LIBADD = $(PYTHON_LIBS) $(PYGOBJECT_LIBS) $(GST_LIBS) +libgstpythonplugin_la_CFLAGS = $(GST_CFLAGS) $(PYGOBJECT_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(PYTHON_INCLUDES) diff --git a/plugin/gstpythonplugin.c b/plugin/gstpythonplugin.c new file mode 100644 index 0000000..17c857b --- /dev/null +++ b/plugin/gstpythonplugin.c @@ -0,0 +1,304 @@ +/* gst-python + * Copyright (C) 2009 Edward Hervey + * 2005 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* include this first, before NO_IMPORT_PYGOBJECT is defined */ +#include +#include +#include +#include + +void *_PyGstElement_Type; + +GST_DEBUG_CATEGORY_STATIC (pyplugindebug); +#define GST_CAT_DEFAULT pyplugindebug + +#define GST_ORIGIN "http://gstreamer.freedesktop.org" + +static PyObject *element; + +static gboolean +gst_python_plugin_load_file (GstPlugin * plugin, const char *name) +{ + PyObject *main_module, *main_locals; + PyObject *elementfactory; + PyObject *module; + const gchar *facname; + guint rank; + PyObject *class; + + GST_DEBUG ("loading plugin %s", name); + + main_module = PyImport_AddModule ("__main__"); + if (main_module == NULL) { + GST_WARNING ("Could not get __main__, ignoring plugin %s", name); + PyErr_Print (); + PyErr_Clear (); + return FALSE; + } + + main_locals = PyModule_GetDict (main_module); + module = + PyImport_ImportModuleEx ((char *) name, main_locals, main_locals, NULL); + if (!module) { + GST_DEBUG ("Could not load module, ignoring plugin %s", name); + PyErr_Print (); + PyErr_Clear (); + return FALSE; + } + + /* Get __gstelementfactory__ from file */ + elementfactory = PyObject_GetAttrString (module, "__gstelementfactory__"); + if (!elementfactory) { + GST_DEBUG ("python file doesn't contain __gstelementfactory__"); + PyErr_Clear (); + return FALSE; + } + + /* parse tuple : name, rank, gst.ElementClass */ + if (!PyArg_ParseTuple (elementfactory, "sIO", &facname, &rank, &class)) { + GST_WARNING ("__gstelementfactory__ isn't correctly formatted"); + PyErr_Print (); + PyErr_Clear (); + Py_DECREF (elementfactory); + return FALSE; + } + + if (!PyObject_IsSubclass (class, (PyObject *) & PyGObject_Type)) { + GST_WARNING ("the class provided isn't a subclass of GObject.Object"); + PyErr_Print (); + PyErr_Clear (); + Py_DECREF (elementfactory); + Py_DECREF (class); + return FALSE; + } + + if (!g_type_is_a (pyg_type_from_object (class), GST_TYPE_ELEMENT)) { + GST_WARNING ("the class provided isn't a subclass of Gst.Element"); + PyErr_Print (); + PyErr_Clear (); + Py_DECREF (elementfactory); + Py_DECREF (class); + return FALSE; + } + + GST_INFO ("Valid plugin"); + Py_DECREF (elementfactory); + + return gst_element_register (plugin, facname, rank, + pyg_type_from_object (class)); +} + +static gboolean +gst_python_load_directory (GstPlugin * plugin, gchar * path) +{ + GDir *dir; + const gchar *file; + GError *error = NULL; + gboolean ret = TRUE; + + dir = g_dir_open (path, 0, &error); + if (!dir) { + /*retval should probably be depending on error, but since we ignore it... */ + GST_DEBUG ("Couldn't open Python plugin dir: %s", error->message); + g_error_free (error); + return FALSE; + } + while ((file = g_dir_read_name (dir))) { + /* FIXME : go down in subdirectories */ + if (g_str_has_suffix (file, ".py")) { + gsize len = strlen (file) - 3; + gchar *name = g_strndup (file, len); + ret &= gst_python_plugin_load_file (plugin, name); + g_free (name); + } + } + return TRUE; +} + +static gboolean +gst_python_plugin_load (GstPlugin * plugin) +{ + PyObject *sys_path; + const gchar *plugin_path; + gboolean ret = TRUE; + + sys_path = PySys_GetObject ("path"); + + /* Mimic the order in which the registry is checked in core */ + + /* 1. check env_variable GST_PLUGIN_PATH */ + plugin_path = g_getenv ("GST_PLUGIN_PATH"); + if (plugin_path) { + char **list; + int i; + + GST_DEBUG ("GST_PLUGIN_PATH set to %s", plugin_path); + list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0); + for (i = 0; list[i]; i++) { + gchar *sysdir = g_build_filename (list[i], "python", NULL); + PyList_Insert (sys_path, 0, PyUnicode_FromString (sysdir)); + gst_python_load_directory (plugin, sysdir); + g_free (sysdir); + } + + g_strfreev (list); + } + + /* 2. Check for GST_PLUGIN_SYSTEM_PATH */ + plugin_path = g_getenv ("GST_PLUGIN_SYSTEM_PATH"); + if (plugin_path == NULL) { + char *home_plugins; + + /* 2.a. Scan user and system-wide plugin directory */ + GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH not set"); + + /* plugins in the user's home directory take precedence over + * system-installed ones */ + home_plugins = g_build_filename (g_get_home_dir (), + ".gstreamer-" GST_API_VERSION, "plugins", "python", NULL); + PyList_Insert (sys_path, 0, PyUnicode_FromString (home_plugins)); + gst_python_load_directory (plugin, home_plugins); + g_free (home_plugins); + + /* add the main (installed) library path */ + PyList_Insert (sys_path, 0, PyUnicode_FromString (PLUGINDIR "/python")); + gst_python_load_directory (plugin, PLUGINDIR "/python"); + } else { + gchar **list; + gint i; + + /* 2.b. Scan GST_PLUGIN_SYSTEM_PATH */ + GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH set to %s", plugin_path, plugin_path); + list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0); + for (i = 0; list[i]; i++) { + gchar *sysdir; + + sysdir = g_build_filename (list[i], "python", NULL); + + PyList_Insert (sys_path, 0, PyUnicode_FromString (sysdir)); + gst_python_load_directory (plugin, sysdir); + g_free (sysdir); + } + g_strfreev (list); + } + + + return ret; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + PyGILState_STATE state; + PyObject *gst, *dict, *pyplugin; + gboolean we_initialized = FALSE; + GModule *libpython; + gpointer has_python = NULL; + PyObject *seq, *list; + int i, len; + + GST_DEBUG_CATEGORY_INIT (pyplugindebug, "pyplugin", 0, + "Python plugin loader"); + + gst_plugin_add_dependency_simple (plugin, + "HOME/.gstreamer-" GST_API_VERSION + "/plugins/python:GST_PLUGIN_SYSTEM_PATH/python:GST_PLUGIN_PATH/python", + PLUGINDIR "/python:HOME/.gstreamer-" GST_API_VERSION "/plugins/python:" + "GST_PLUGIN_SYSTEM_PATH/python:GST_PLUGIN_PATH/python", NULL, + GST_PLUGIN_DEPENDENCY_FLAG_NONE); + + GST_LOG ("Checking to see if libpython is already loaded"); + g_module_symbol (g_module_open (NULL, G_MODULE_BIND_LOCAL), "_Py_NoneStruct", + &has_python); + if (has_python) { + GST_LOG ("libpython is already loaded"); + } else { + GST_LOG ("loading libpython"); + libpython = + g_module_open (PY_LIB_LOC "/libpython" PYTHON_VERSION PY_ABI_FLAGS + "." PY_LIB_SUFFIX, 0); + if (!libpython) { + GST_WARNING ("Couldn't g_module_open libpython. Reason: %s", + g_module_error ()); + return FALSE; + } + } + + 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 (); + } + + GST_LOG ("initializing pygobject"); + if (!pygobject_init (3, 0, 0)) { + GST_WARNING ("pygobject initialization failed"); + return FALSE; + } + + gst = PyImport_ImportModule ("gi.repository.Gst"); + if (we_initialized) { + PyObject *tmp; + + dict = PyModule_GetDict (gst); + if (!dict) { + GST_ERROR ("no dict?!"); + } + + + tmp = + PyObject_GetAttr (PyMapping_GetItemString (dict, + "_introspection_module"), PyUnicode_FromString ("__dict__")); + + _PyGstElement_Type = PyMapping_GetItemString (tmp, "Element"); + + if (!_PyGstElement_Type) { + g_error ("Could not get Gst.Element"); + Py_DECREF (pyplugin); + } + pyplugin = pygobject_new (G_OBJECT (plugin)); + if (!pyplugin || PyModule_AddObject (gst, "__plugin__", pyplugin) != 0) { + g_warning ("Couldn't set plugin"); + Py_DECREF (pyplugin); + } + } + + gst_python_plugin_load (plugin); + + if (we_initialized) { + /* We need to release the GIL since we're going back to C land */ + PyEval_SaveThread (); + } else + PyGILState_Release (state); + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, python, + "loader for plugins written in python", + plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_ORIGIN) -- 2.7.4