Plugin Architecture Discussion
This topic discusses a possible future plugin interface/architecture of Motion. MotionRearchitectureDiscussion
contains a lot of good ideas, such as adding xvid output, combining Motion with Gstreamer etc. Kenneth has rounded off that topic with the suggestion that Motion should be fitted with a generic plugin interface, with which a variety of plugins of different flavours could be written. For example, we could have one ffmpeg plugin, one xvid plugin, one Gstreamer plugin etc.
There are a number of questions that need to be answered:
- Which plugin types should we have?
- Which mechanism should we use for loading plugins?
- What should the plugin API look like?
The important thing is to have a generic API to not limit plugins too much. Some API considerations:
- Future extensions to the API must/should not break existing plugins.
- Alternatively, the Motion version could be exposed through the API, so plugins can "lock" on a certain Motion version (or range of version), much like Firefox extensions.
- The Motion configuration needs to be accessible from the plugins.
Speaking about configuration, we of course need to find a consistent and easy-to-use way to specify configuration options for plugins. Configuration options should all be kept in the same place (the config file(s)), and the current way of handling options is very static.
Motion of course needs to be shipped with a number of standard plugins. These should include a netcam plugin, a v4l plugin, an ffmpeg plugin, a mysql plugin, a postgresql plugin etc. Jpeg output could be either a plugin or a native part of Motion.
I'll just keep the introduction brief. Feel free to add proposals, thoughts and comments below.
- 27 Aug 2005
Just for the fun of it, I put together a little implementation proposal. It defines two plugin types, one for multiplication and one for division (to be replaced with input, output, event, etc. of course), and a corresponding API for each type. There are also two plugins, one of each type. The test app loads the specified plugin, calls it via the API and the unloads it.
For the plugin loading mechanism, I chose to use the dl functions that are in libdl under Linux (libc under FreeBSD
). I though about using glib, but that would add yet another dependency to Motion, and the dl functions are simple enough.
The plugin functions are thread-safe (using a mutex), but that's just because the
function isn't re-entrant. It must be used to check for error from
, so it made sense to protect all plugin functions.
To test the proposal, do as follows:
- Download the attached file dl_plugin_test.tar.gz and unpack it.
- Cd into
make to build the two plugins.
- Cd back to
make to build the test application.
I hope the test app builds on FreeBSD
- I added a specific test for it, as the dl functions are in libc instead of in libdl. If it doesn't work, simply make sure that
Amyway, run the test application by typing:
./testapp <path to plugin .so file> <int 1> <int 2>
The test app will print plugin info, test for plugin type and then perform an action accordingly.
All source files are pretty well-commented, so it should be obvious what everything does.
- I don't know if the dl functions exist on all platforms, but as the two main Motion platforms are Linux and FreeBSD, it should be pretty safe.
- As I've coded it, a plugin can only be of one type (since the APIs are in a union - check the code to see what I mean). Perhaps we should allow one single plugin to be of many types at the same time.
- I've defined the APIs as structs of function pointers, which is not very extensible. Perhaps it's better to define an API in terms of just a list of symbols (i.e., function names) that need to exist in a plugin file for it to be a valid plugin.
- There may be limitations of the dl functions that I've overlooked.
Regarding plugin types, we should have at least input plugins, output plugins and event plugins. Anything else?
Comments are welcome!
- 27 Aug 2005
After a bit of searching, I found a short description of plugin architectures which I think would be a useful guideline for us: http://developer.apple.com/documentation/Cocoa/Conceptual/LoadingCode/Concepts/Plugins.html
. I would suggest we follow the suggestions for a C language implementation, i.e. entry point and callback. I'd further suggest that we minimise the entry points - my preference would be 2 (one for 'control' messages, e.g. inititialisation, end-of-run cleanup, etc., and one for all callback messages).
I'm working on more details, and will expand on this in the near future.
- 06 Sep 2005
The test app ran just fine under FreeBSD
- 12 Sep 2005
In order to be able to discuss plugin implementation more thoroughly, I decided the best way would be to try to begin implementing a plugin for motion's video input. I started out with the files provided by Per, and planned to modify video.c (and any modules calling routines within it). I also tried to draw out the existing motion architecture (see MotionCurrentDesign
), with the intent to modify that diagram as the new implementation progresses. My current progress is not yet stable enough for me to produce the next drawing, and I am not yet ready to provide a complete tarball for others to try out, but the current state of my development can be viewed at http://bbhk.bbrack.org:8080/ver4-motion
(sorry about the port - my ISP blocks port 80). I will try to keep that directory in a "runnable" state (as it is now, except when I'm recompiling/testing).
Note that I am not strongly advocating any of the implementation details which I describe in the following paragraphs. For me, the easiest way to gain an understanding of how to do things is to implement some code doing it. My implementation is very "dynamic" - at each step along the way, as new facts and experiences come to light, previous code gets re-written and some design "decisions" get changed.
My first step was to enhance the layout of the directory containing the project files. I created two subdirectories (named "src" and "plugins"), and moved all of the existing C modules and headers into src
The second step was one which I really don't like, but feel is necessary for longer-term development - I changed over to using the gnu auto* tools, particularly including automake
and it's associated libtool
(see the files Makefile.am in the top directory as well as the src
directories). At the moment, this much seems to be working alright.
The third step was to take video.c and video.h out of the src
directory into the plugins
directory. Simultaneously, I renamed them as v4lPlugin.c and v4lPlugin.h. I also moved all of the netcam modules out of src
, but have not yet started any work on them.
When looking at how to convert video.c into a plugin, the first problem encountered was it's use of certain routines which are a part of the motion program, e.g. mymalloc, myrealloc, motion_log, etc. I want to try to make our plugins as "independent" as possible; by this I mean that I would like to compile/produce a plugin which does not have to be re-compiled and re-linked anytime there is an update / new release of motion. On the other hand, there is certainly a very close relationship between the plugin and motion. This led me to decide that the way to best "connect" them (plugin and motion) was through the motion context
structure. Accordingly, within the motion initial startup, I created a structure (named motionCallbacks) to contain pointers to all the motion routines (like mymalloc, etc.) which we want to be "exposed" to the plugin (giving them what I consider "nice" names like motionAlloc, motionReAlloc, motionLog, etc.), and put a pointer to that structure (which I named "callbacks") within the motion context structure. Then, within v4lPlugin.c, I changed (for example) all the calls to mymalloc(...) to become cnt->callbacks->motionAlloc(...). Note that this approach requires that any routine within the plugin which wants to use these callbacks must
have a pointer to the motion context. For this reason, I changed several of the routine prototypes to include a context pointer.
The second problem I encountered was that I needed to come up with a reasonably "clean" method to allow routines within motion to (transparently) call routines which are within the plugin. My solution for this was, first, to look at exactly which routines were involved. For the general video input, the list included vid_init, vid_start, vid_next, vid_close and vid_cleanup. For the existing video.c, there was also vid_startpipe and vid_putpipe. So, I created a structure (which, strangely enough, I named motionVideoInput) to contain pointers to those routines, simultaneously giving them "nice" names like videoInit, videoStart, etc.). The intention is to define other similar structures when other "plugin points" are created. Then, within v4lPlugin.c, this structure is created and it's elements "filled in" with pointers to the corresponding routines located within the plugin. My current implementation has the plugin initialisation routine return a pointer to this structure; one could equally make the structure a global symbol and have the plugin loader find it.
Finally, I decided to try an approach with a single global symbol within the plugin. This is something similar to what is used within gstreamer. The symbol is a static structure containing the details of the plugin (name, author, etc.), and a single pointer within that structure to an initialisation routine.
I have not yet implemented the actual (dynamic) loading of the plugin, which needs to be based upon the configuration file (Note: I fully agree with, and support, previous comments about the need to re-do Motion's configuration methods. However, I don't want that to distract from the plugin work). For the moment, I have put in some "hard code" to always load the v4lPlugin during motion initialisation so that I can test the plugin code.
There is still lots more work to be done, but I wanted to update this Topic with my current state. I would certainly appreciate others' comments and suggestions, and will periodically add further comments as work progresses.
- 13 Sep 2005
Progress is slow (but steady!). My current state can now be viewed at http://bbrack.org/viewcvs
. I have converted both the v4l code and the netcam code to be plugins, and (for testing only, not
for production) have implemented an additional configuration parameter "use_plugin" (which currently must
be present in the main configuration file, and may optionally be present in the individual thread files). Most of the new code is reasonably well commented, and compiles and runs mostly ok (shutdown is still not good). The main area which still needs considerable work is in deciding at precisely which points within the Motion main code we want to call plugin code, and in agreeing the structures containing the entry points located within the plugins (c.f. struct context, element videoCtxt in my experimental code).
- 02 Oct 2005
Please note that the CVS mentioned above is not an official Motion CVS. The actual plugin design may follow the principles but the MotionPluginRequirements
etc needs to be worked more on so we can get a clear direction and can split up the tasks so we all can contribute.
- 02 Oct 2005