Plugins¶
- Plugins
- Using Plugins
- Creating Plugins
- Troubleshooting Windows Linker Problems
- How is core libavg code affected?
libavg supports loading of plugins at runtime that extend the player's functionality. Plugins are practically unrestricted: They can access all low-level functionality of libavg and extend the API exposed to python in arbitrary ways. For instance, they can add new node types by supplying the code necessary to render the Node, expose node functions to python and extend the XML DTD. Plugins have also been used to expose new input devices to libavg.
If your plugin defines a new node class, also have a look at NodeSubclassing.
Using Plugins¶
The pluginPath property¶
The player has a property called pluginPath
which is a list of directories separated by semicolons under windows and colons on other systems. When loadPlugin()
(see below) is called the player searches in each of these directories until it finds a file matching the name <modulename>.so
(or .dll
, if you're running windows). You may specify relative or absolute paths. Relative paths will be resolved when loadPlugin
is called, not when the pluginPath
property is set. This makes a difference when the working directory changes before the plugin is actually loaded. When the player starts up, it's pluginPath
property defaults to:
./plugin:$(AVG_PATH)/plugin
The loadPlugin()
method¶
Calling loadPlugin("module")
will cause the player to look for a file called module.so
(.dll
under windows) in each of the directories specified by pluginPath
(see above for details). Once the file is found, the player tries to load it as a shared object, extend the DTD and register the new Node type. If this succeeds - an exception is raised otherwise - the new node type can be used from now on as if it was a built-in type. For example you can use both forms of createNode()
(XML or key-value) to create such a node or you can load an XML file or string containing a node of the new type.
plugin.py
1player.pluginPath = "../test/plugin/.libs"
2player.loadPlugin("colorplugin")
3
4rootNode = canvas.getRootNode()
5node = colorplugin.ColorNode(fillcolor="7f7f00", parent=rootNode)
6node.fillcolor = "7f007f"
Note that to actually run this code, you need to change the plugin path to point to $(AVG_PATH)src/test/plugin/.libs
on your computer. Once it does run, it should display a pink window.
Logging¶
You can use the log category PLUGIN
to get diagnostic messages from the plugin loader. This is especially useful when a plugin fails to load.
What happens to the XML DTD?¶
Once a plugin is loaded, it is queried for a) it's supported attributes and b) valid parent node types which shall be allowed to contain nodes of this new type. Consequently, the DTD declarations for the parent nodes changes and a new XML node declaration is added to the DTD.
You may want to have a look at src/test/Plugintest.py
for a complete example of how to use a plugin.
Creating Plugins¶
Plugins are shared objects (DLLs on windows). Currently, the shared object must export one symbol that forms the interface between the PluginManager and the plugin itself:
AVG_PLUGIN_API void registerPlugin();
AVG_PLUGIN_API
is a macro defined in src/api.h
It has the effect of removing C++ name mangling from the symbol name and making sure the function is exported. To make this and the importing of symbols from libavg work properly, AVG_PLUGIN
needs to be defined in the complete plugin. This is done in the VS project file (for windows) or in the Makefile (for Linux/Mac).
In registerPlugin, a plugin will typically register one or more node types by calling registerNodeType()
:
1void Player::registerNodeType(NodeDefinition Def,
2 const char* pParentNames[] = 0);
The plugin also needs to create python bindings for any classes or functions that should be visible from python - see src/test/plugin/ColorNode.cpp
for a minimal but complete implementation of a Node plugin.
Building a plugin¶
Node plugins need to be build as a shared object (DLL on windows) and linked against avg.so. Use src/test/plugin/Makefile.am
as a starting point on Unix-like systems. On windows, clone the colornode project to get started. (You may copy the vcproj file and use search/replace to rename all occurrences of ColorNode and colornode)
Troubleshooting Windows Linker Problems¶
Unresolved externals in libavg are a typical issue when programming plugins or getting them to work under windows. Here are several tips that might help:
- If the linker can't find any libavg symbols, you're not linking to the correct libraries.
- Single missing symbols can be caused by missing
AVG_API
orAVG_TEMPLATE_API
macros for those symbols in libavg. - Make sure that
AVG_PLUGIN
is defined when compiling the plugin, either in the Makefile (for Linux and Mac) or in the Visual Studio project file (for Windows). It makes sure that theAVG_API
macros are defined correctly in the libavg header files when they get included. - If symbols aren't used in libavg itself, the linker will optimize those symbols away. This seems to be a bug in the Visual C++ linker (opt:NOREF doesn't solve it). A workaround is to
#include
the appropriate header files somewhere within libavg. Currently, these includes are atsrc/graphics/OGLImagingContext.cpp
.
How is core libavg code affected?¶
AVG_API, AVG_TEMPLATE_API and api.h
In order to make avg's functions and class methods available to a plugin, those functions need to be exported from avg.so
. While all symbols of a shared object are exported by default on Unix-like systems (at least those using ELF as their executable format and gcc as their build toolchain) the situation on Windows is different. Not only is exporting symbols not the default for the MS linker, it also lacks a switch to enable this behavior. In order to export symbols, each class definition needs to be decorated with __declspec(dllexport)
when exported and __declspec(dllimport)
in the plugin itself. In libavg code, the macros AVG_API
(for classes) and AVG_TEMPLATE_API
(for class templates) fulfill this task. They are defined in src/api.h
, which therefore must be included by each and every header file defining a class.