Updated The VDR Plugin System (markdown)

Manuel Reimer 2023-09-19 10:41:48 +02:00
parent 64bbb2f651
commit 5f09756fc3

@ -17,9 +17,9 @@ separate from the core VDR source, without the need of patching the original
VDR code (and all the problems of correlating various patches).
<p>
This document is divided into two parts, the first one describing the
<a href="#Part I - The External Interface"><i>external</i> interface</a>
<a href="#part-i---the-external-interface"><i>external</i> interface</a>
of the plugin system, and the second one describing the
<a href="#Part II - The Internal Interface"><i>internal</i> interface</a>.
<a href="#part-ii---the-internal-interface"><i>internal</i> interface</a>.
The <i>external</i> interface handles everything necessary for a plugin to get hooked into the core
VDR program and present itself to the user.
The <i>internal</i> interface provides the plugin code access to VDR's internal data
@ -28,53 +28,53 @@ structures and allows it to hook itself into specific areas to perform special a
<hr>
<h1>Table Of Contents</h1>
<ul>
<li><a href="#Part I - The External Interface">Part I - The External Interface</a>
<li><a href="#part-i---the-external-interface">Part I - The External Interface</a>
<ul>
<li><a href="#Quick start">Quick start</a>
<li><a href="#The name of the plugin">The name of the plugin</a>
<li><a href="#The plugin directory structure">The plugin directory structure</a>
<li><a href="#Initializing a new plugin directory">Initializing a new plugin directory</a>
<li><a href="#The actual implementation">The actual implementation</a>
<li><a href="#Construction and Destruction">Construction and Destruction</a>
<li><a href="#Version number">Version number</a>
<li><a href="#Description">Description</a>
<li><a href="#Command line arguments">Command line arguments</a>
<li><a href="#Command line help">Command line help</a>
<li><a href="#Getting started">Getting started</a>
<li><a href="#Shutting down">Shutting down</a>
<li><a href="#Logging">Logging</a>
<li><a href="#Main menu entry">Main menu entry</a>
<li><a href="#User interaction">User interaction</a>
<li><a href="#Housekeeping">Housekeeping</a>
<li><a href="#Main thread hook">Main thread hook</a>
<li><a href="#Activity">Activity</a>
<li><a href="#Wakeup">Wakeup</a>
<li><a href="#Setup parameters">Setup parameters</a>
<li><a href="#The Setup menu">The Setup menu</a>
<li><a href="#Additional files">Additional files</a>
<li><a href="#Internationalization">Internationalization</a>
<li><a href="#Custom services">Custom services</a>
<li><a href="#SVDRP commands">SVDRP commands</a>
<li><a href="#Locking">Locking</a>
<li><a href="#Loading plugins into VDR">Loading plugins into VDR</a>
<li><a href="#Building the distribution package">Building the distribution package</a>
<li><a href="#quick-start">Quick start</a>
<li><a href="#the-name-of-the-plugin">The name of the plugin</a>
<li><a href="#the-plugin-directory-structure">The plugin directory structure</a>
<li><a href="#initializing-a-new-plugin-directory">Initializing a new plugin directory</a>
<li><a href="#the-actual-implementation">The actual implementation</a>
<li><a href="#construction-and-destruction">Construction and Destruction</a>
<li><a href="#version-number">Version number</a>
<li><a href="#description">Description</a>
<li><a href="#command-line-arguments">Command line arguments</a>
<li><a href="#command-line-help">Command line help</a>
<li><a href="#getting-started">Getting started</a>
<li><a href="#shutting-down">Shutting down</a>
<li><a href="#logging">Logging</a>
<li><a href="#main-menu-entry">Main menu entry</a>
<li><a href="#user-interaction">User interaction</a>
<li><a href="#housekeeping">Housekeeping</a>
<li><a href="#main-thread-hook">Main thread hook</a>
<li><a href="#activity">Activity</a>
<li><a href="#wakeup">Wakeup</a>
<li><a href="#setup-parameters">Setup parameters</a>
<li><a href="#the-setup-menu">The Setup menu</a>
<li><a href="#additional-files">Additional files</a>
<li><a href="#internationalization">Internationalization</a>
<li><a href="#custom-services">Custom services</a>
<li><a href="#svdrp-commands">SVDRP commands</a>
<li><a href="#locking">Locking</a>
<li><a href="#loading-plugins-into-vdr">Loading plugins into VDR</a>
<li><a href="#building-the-distribution-package">Building the distribution package</a>
</ul>
<li><a href="#Part II - The Internal Interface">Part II - The Internal Interface</a>
<li><a href="#part-ii---the-internal-interface">Part II - The Internal Interface</a>
<ul>
<li><a href="#Status monitor">Status monitor</a>
<li><a href="#Players">Players</a>
<li><a href="#Receivers">Receivers</a>
<li><a href="#Filters">Filters</a>
<li><a href="#The On Screen Display">The On Screen Display</a>
<li><a href="#Skins">Skins</a>
<li><a href="#Themes">Themes</a>
<li><a href="#Devices">Devices</a>
<li><a href="#Positioners">Positioners</a>
<li><a href="#Audio">Audio</a>
<li><a href="#Remote Control">Remote Control</a>
<li><a href="#Conditional Access">Conditional Access</a>
<li><a href="#Electronic Program Guide">Electronic Program Guide</a>
<li><a href="#The video directory">The video directory</a>
<li><a href="#status-monitor">Status monitor</a>
<li><a href="#players">Players</a>
<li><a href="#receivers">Receivers</a>
<li><a href="#filters">Filters</a>
<li><a href="#the-on-screen-display">The On Screen Display</a>
<li><a href="#skins">Skins</a>
<li><a href="#themes">Themes</a>
<li><a href="#devices">Devices</a>
<li><a href="#positioners">Positioners</a>
<li><a href="#audio">Audio</a>
<li><a href="#remote-control">Remote Control</a>
<li><a href="#conditional-access">Conditional Access</a>
<li><a href="#electronic-program-guide">Electronic Program Guide</a>
<li><a href="#the-video-directory">The video directory</a>
</ul>
</ul>
@ -150,7 +150,7 @@ that there is a <tt>Makefile</tt> that provides the targets <tt>all</tt>, <tt>in
loadable library file for that plugin (we'll get to the details later).
The dynamically loadable library file for the plugin shall be located directly under
the plugin's source directory.
See the section <a href="#Initializing a new plugin directory">Initializing a new plugin directory</a>
See the section <a href="#initializing-a-new-plugin-directory">Initializing a new plugin directory</a>
for how to generate an example Makefile.
<p>
The <tt>lib</tt> directory contains the dynamically loadable libraries of all
@ -177,7 +177,7 @@ The VDR <tt>Makefile</tt> contains the target <tt>plugins</tt>, which calls
plus the target <tt>clean-plugins</tt>, which calls <tt>make clean</tt> in
each of these directories.
<p>
If you download a plugin <a href="#Building the distribution package">package</a>
If you download a plugin <a href="#building-the-distribution-package">package</a>
from the web, it will typically have a name like
<p>
<tt>vdr-hello-0.0.1.tgz</tt>
@ -234,7 +234,7 @@ necessary. Don't forget to adapt the <tt>Makefile</tt> appropriately.
<div>Use the source, Luke!</div><p>
A newly initialized plugin doesn't really do very much yet.
If you <a href="#Loading plugins into VDR">load it into VDR</a> you will find a new
If you <a href="#loading-plugins-into-vdr">load it into VDR</a> you will find a new
entry in the main menu, with the same name as your plugin (where the first character
has been converted to uppercase). There will also be a new entry named "Plugins" in
the "Setup" menu, which will bring up a list of all loaded plugins, through which you
@ -291,7 +291,7 @@ up to you. You could, for instance, prepend the macro with a <tt>'P'</tt>, as in
<tt>P__I18N_H</tt>, or leave out the trailing <tt>_H</tt>, as in <tt>__I18N</tt>,
or use a completely different way to make sure a header file is included only once.
<p>
The 'hello' example that comes with VDR makes use of <a href="#Internationalization">internationalization</a>
The 'hello' example that comes with VDR makes use of <a href="#internationalization">internationalization</a>
and implements a file named <tt>i18n.h</tt>. To make sure it won't clash with VDR's
<tt>i18n.h</tt> it uses the macro <tt>_I18N__H</tt> (one underline at the beginning
and two replacing the dot).
@ -311,8 +311,8 @@ The <b>constructor</b> shall initialize any member variables the plugin defines,
<b>must not access any global structures of VDR</b>.
It also must not create any threads or other large data structures. These things
are done in the
<a href="#Getting started"><tt>Initialize()</tt></a> or
<a href="#Getting started"><tt>Start()</tt></a>
<a href="#getting-started"><tt>Initialize()</tt></a> or
<a href="#getting-started"><tt>Start()</tt></a>
function later.
Constructing a plugin object shall not have any side effects or produce any output,
since VDR, for instance, has to create the plugin objects in order to get their
@ -320,7 +320,7 @@ command line help - and after that immediately destroys them again.
<p>
The <b>destructor</b> has to clean up any data created by the plugin.
Any threads the plugin may have created shall be stopped in the
<a href="#Shutting down"><tt>Stop()</tt></a> function.
<a href="#shutting-down"><tt>Stop()</tt></a> function.
<p>
Of course, if your plugin doesn't define any member variables that need to be
initialized (and deleted), you don't need to implement either of these functions.
@ -393,7 +393,7 @@ virtual const char *Description(void)
</pre>
Note the <tt>tr()</tt> around the <tt>DESCRIPTION</tt>, which allows the description
to be <a href="#Internationalization">internationalized</a>.
to be <a href="#internationalization">internationalized</a>.
<hr><h2>Command line arguments</h2>
@ -483,7 +483,7 @@ be shorter than 80 characters.
<div>Let's get ready to rumble!</div><p>
If a plugin implements a function that runs in the background (presumably in a
thread of its own), or wants to make use of <a href="#Internationalization">internationalization</a>,
thread of its own), or wants to make use of <a href="#internationalization">internationalization</a>,
it needs to implement one of the functions
<pre>
@ -529,9 +529,9 @@ virtual void Stop(void);
in which it shall stop them.
<p>
The <tt>Stop()</tt> function will only be called if a previous call to the
<a href="#Getting started"><tt>Start()</tt></a> function of that plugin has
<a href="#getting-started"><tt>Start()</tt></a> function of that plugin has
returned <i>true</i>. The <tt>Stop()</tt> functions are called in the reverse order
as the <a href="#Getting started"><tt>Start()</tt></a> functions were called.
as the <a href="#getting-started"><tt>Start()</tt></a> functions were called.
<hr><h2>Logging</h2>
@ -625,7 +625,7 @@ which can do one of three things:
eventually start a custom player to replay a file other than a VDR recording.
<li>Return a pointer to a <tt>cOsdObject</tt> object which will be displayed
instead of the normal menu. The derived <tt>cOsdObject</tt> can open a
<a href="#The On Screen Display">raw OSD</a> from within its <tt>Show()</tt>
<a href="#the-on-screen-display">raw OSD</a> from within its <tt>Show()</tt>
function (it should not attempt to do so from within its constructor, since
at that time the OSD is still in use by the main menu).
See the 'osddemo' example that comes with VDR for a demonstration of how this
@ -670,7 +670,7 @@ the plugin should launch a separate thread to do this.
<div>Pushing in...</div><p>
Normally a plugin only reacts on user input if directly called through its
<a href="#Main menu entry">main menu entry</a>, or performs some background
<a href="#main-menu-entry">main menu entry</a>, or performs some background
activity in a separate thread. However, sometimes a plugin may need to do
something in the context of the main program thread, without being explicitly
called up by the user. In such a case it can implement the function
@ -767,7 +767,7 @@ virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
</pre>
The <tt>SetupMenu()</tt> function shall return the plugin's <a href="#The Setup menu"><i>Setup</i> menu</a>
The <tt>SetupMenu()</tt> function shall return the plugin's <a href="#the-setup-menu"><i>Setup</i> menu</a>
page, where the user can adjust all the parameters known to this plugin.
<p>
<tt>SetupParse()</tt> will be called for each parameter the plugin has
@ -789,7 +789,7 @@ bool cPluginHello::SetupParse(const char *Name, const char *Value)
</pre>
It is important to make sure that the parameter names are exactly the same as
used in the <a href="#The Setup menu"><i>Setup</i> menu</a>'s <tt>Store()</tt> function.
used in the <a href="#the-setup-menu"><i>Setup</i> menu</a>'s <tt>Store()</tt> function.
<p>
The plugin's setup parameters are stored in the same file as VDR's parameters.
In order to allow each plugin (and VDR itself) to have its own set of parameters,
@ -895,7 +895,7 @@ free to store such files anywhere it sees fit, it might be a good idea to put th
place, preferably where such data already exists.
<p>
<i>configuration files</i>, maybe for data that can't be stored in the simple
<a href="#Setup parameters">setup parameters</a> of VDR, or maybe because it needs to
<a href="#setup-parameters">setup parameters</a> of VDR, or maybe because it needs to
launch other programs that simply need a separate configuration file.
<p>
<i>cache files</i>, to store data so that future requests for that data can be served faster. The data
@ -1158,7 +1158,7 @@ when presenting them to the caller, and the continuation character ('<tt>-</tt>'
will be set for all but the last one.
<p>
<b>The SVDRP functions are called from the separate "SVDRP server handler" thread.
Therefore the plugin needs to take care of proper <a href="#Locking">locking</a> if it accesses any
Therefore the plugin needs to take care of proper <a href="#locking">locking</a> if it accesses any
global data.</b>
<hr><h2>Locking</h2>
@ -1248,7 +1248,7 @@ There can be any number of <b><tt>-L</tt></b> options, and each of them will app
When started with the <b><tt>-h</tt></b> or <b><tt>-V</tt></b> option (for <i>help</i>
or <i>version</i> information, respectively), VDR will automatically load all plugins
in the default or given directory that match the VDR plugin
<a href="#The plugin directory structure">naming convention</a>,
<a href="#the-plugin-directory-structure">naming convention</a>,
and display their help and/or version information in addition to its own output.
<hr><h2>Building the distribution package</h2>
@ -1258,7 +1258,7 @@ and display their help and/or version information in addition to its own output.
If you want to make your plugin available to other VDR users, you'll need to
make a package that can be easily distributed.
The <tt>Makefile</tt> that has been created by the call to
<a href="#Initializing a new plugin directory"><tt>newplugin</tt></a>
<a href="#initializing-a-new-plugin-directory"><tt>newplugin</tt></a>
provides the target <tt>dist</tt>, which does this for you.
<p>
Simply change into your source directory and execute <tt>make dist</tt>:
@ -1769,7 +1769,7 @@ public:
See the comments in <tt>VDR/skins.h</tt> for details. <tt>VDR/skinclassic.[hc]</tt>
can be used as an example for how to implement all the necessary classes and
functions to compose a complete skin. See also the chapter about <a href="#Themes">themes</a>
functions to compose a complete skin. See also the chapter about <a href="#themes">themes</a>
if you want to make the colors used by your skin configurable.
<p>
To add your new skin to the list of skins available to the user in Setup/OSD/Skin,
@ -1779,7 +1779,7 @@ all you need to do is create a new object of your skin class, as in
new cMySkin;
</pre>
in the <a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
in the <a href="#getting-started"><tt>Start()</tt></a> function of your plugin.
Do not delete this object, it will be automatically deleted when the program ends.
<p>
In order to be able to easily identify plugins that implement a skin it is recommended
@ -1795,7 +1795,7 @@ where <tt>xyz</tt> is the actual name of the skin.
<div>Eye of the beholder...</div><p>
A <i>theme</i> is a collection of colors that can be used by a <a href="#Skins">skin</a>.
A <i>theme</i> is a collection of colors that can be used by a <a href="#skins">skin</a>.
Since every skin most likely has its own idea about what parts of it can be
<i>themed</i>, and different skins may have completely different numbers of
"themeable" parts, a particular theme can only be used with the skin it was designed
@ -1969,7 +1969,7 @@ StartSectionHandler();
from its constructor.
<p>
See <a href="#Filters">Filters</a> on how to set up actual filters that can
See <a href="#filters">Filters</a> on how to set up actual filters that can
handle section data.
<p>
@ -2029,9 +2029,9 @@ See <tt>VDR/dvbdevice.c</tt> for the implementation of the <tt>cDvbDevice</tt>
initialize function.
<p>
A plugin that adds devices to a VDR instance shall call this
function from its <a href="#Getting started"><tt>Initialize()</tt></a> function
function from its <a href="#getting-started"><tt>Initialize()</tt></a> function
to make sure other plugins that may need to have access to all available devices
will see them in their <a href="#Getting started"><tt>Start()</tt></a> function.
will see them in their <a href="#getting-started"><tt>Start()</tt></a> function.
<p>
Nothing needs to be done to shut down the devices. VDR will automatically
shut down (delete) all devices when the program terminates. It is therefore
@ -2124,7 +2124,7 @@ public:
See the implementation of <tt>cDiseqcPositioner</tt> in <tt>diseqc.c</tt> for details.
<p>
You should create your derived positioner object in the
<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
<a href="#getting-started"><tt>Start()</tt></a> function of your plugin.
Note that the object has to be created on the heap (using <tt>new</tt>),
and you shall not delete it at any point (it will be deleted automatically
when the program ends).
@ -2156,7 +2156,7 @@ public:
</pre>
You should create your derived audio object in the
<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
<a href="#getting-started"><tt>Start()</tt></a> function of your plugin.
Note that the object has to be created on the heap (using <tt>new</tt>),
and you shall not delete it at any point (it will be deleted automatically
when the program ends).
@ -2214,7 +2214,7 @@ class to work, but typically you may want to have a separate thread running that
collects the input and delivers it to the <tt>cRemote</tt> base class.
<p>
You should create your derived remote control object in the
<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
<a href="#getting-started"><tt>Start()</tt></a> function of your plugin.
Note that the object has to be created on the heap (using <tt>new</tt>),
and you shall not delete it at any point (it will be deleted automatically
when the program ends).
@ -2392,7 +2392,7 @@ public:
See the description in <tt>videodir.h</tt> for details.
<p>
You should create your derived video directory object in the
<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
<a href="#getting-started"><tt>Start()</tt></a> function of your plugin.
Note that the object has to be created on the heap (using <tt>new</tt>),
and you shall not delete it at any point (it will be deleted automatically
when the program ends).