1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
/** \page decoder_api Decoder API
\section decoder_api_introduction Introduction
This document is intended to make it easier to add support for new file
formats to MOC. It documents the requirements of decoder plugins and
some useful functions. It also says how to start.
\section decoder_plugin_idea The idea
Each file format is supported by code in a plugin. It's something similar to
plugins in popular audio players like XMMS with exception that there is no
separate library and dedicated header files to build such a plugin. It means
that to create a plugin you need to add it to MOC's source directory. It
will be built with the program and a file named like my_plugin.so will be
created and placed in the plugin directory during installation.
So why plugins? The idea is to avoid the dependency of the mocp binary on
various libraries used just to support some exotic formats. If a plugin
with an unresolved dependency is found at startup it's just not loaded.
\section decoder_plugin_how_to_add_source How to add the plugin code
Let's create a plugin named myplug. First create a directory in the
decoder_plugins directory named myplug; you will place the sources there.
Let's have 2 source files: myplug1.c and myplug2.c in your plugin's directory.
Now you must create a Makefile.am file there that contains the following lines:
\code
lib_LTLIBRARIES = libmyplug_decoder.la
libdir = $(plugindir)/$(DECODER_PLUGIN_DIR)
libmyplug_decoder_la_LDFLAGS = @PLUGIN_LDFLAGS@
libmyplug_decoder_la_LIBADD = $(MYPLUG_LIBS)
libmyplug_decoder_la_CFLAGS = $(MYPLUG_CFLAGS) -I$(top_srcdir)
libmyplug_decoder_la_SOURCES = myplug1.c myplug2.c
\endcode
Next you have to tell the build system to include your newly created directory
by editting the file Makefile.am in the decoder_plugins directory. You'll
see plenty of examples there, but your new entry will look like this:
\code
if BUILD_myplug
SUBDIRS += myplug
endif
\endcode
Almost done, now the hard part. As you may have already noticed,
the code in Makefile.am uses two useful variables: MYPLUG_CFLAGS and
MYPLUG_LIBS. They contain additional compiler and linker flags used to
compile your plugin. They are set by the configure script. You must
include a configure script fragment (myplug.m4 in this case) in your
myplug directory and modify decoder_plugins/decoders.m4 to include it.
The fragment needs to detect libraries and compiler flags needed to
build the plugin, add myplug to the DECODER_PLUGINS variable and set
things up so the plugin can be built. As an example, let's look at the
code detecting libFLAC which is using an autoconf macro provided by the
FLAC library and is very simple:
\code
AC_ARG_WITH(flac, AS_HELP_STRING([--without-flac],
[Compile without FLAC support]))
if test "x$with_flac" != "xno"
then
AM_PATH_LIBFLAC([AC_SUBST(LIBFLAC_LIBS)
AC_SUBST(LIBFLAC_CFLAGS)
$want_flac="yes"
DECODER_PLUGINS="$DECODER_PLUGINS flac"])
fi
AM_CONDITIONAL([BUILD_flac], [test "$want_flac"])
AC_CONFIG_FILES([decoder_plugins/flac/Makefile])
\endcode
If your decoder uses packages which don't have pkg-config configurations,
you can use something like this:
\code
AC_ARG_WITH(myplug, AS_HELP_STRING([--without-myplug],
[Compile without MyPlug support]))
if test "x$with_myplug" != "xno"
then
AC_CHECK_LIB(required_library, SomeFunction, [myplug_OK="yes"])
if test "x$myplug_OK" = "xyes"
then
MYPLUG_LIBS='-lrequired_library'
AC_SUBST(MYPLUG_LIBS)
MYPLUG_CFLAGS='-I/usr/include/required_library_includes'
AC_SUBST(MYPLUG_CFLAGS)
$want_myplug="yes"
DECODER_PLUGINS="$DECODER_PLUGINS myplug"
fi
fi
AM_CONDITIONAL([BUILD_myplug], [test "$want_flac"])
AC_CONFIG_FILES([decoder_plugins/myplug/Makefile])
\endcode
In the absence of any library dependencies, this reduces to:
\code
AC_ARG_WITH(myplug, AS_HELP_STRING([--without-myplug],
[Compile without MyPlug support]))
if test "x$with_myplug" != "xno"
then
MYPLUG_LIBS='-lrequired_library'
AC_SUBST(MYPLUG_LIBS)
MYPLUG_CFLAGS='-I/usr/include/required_library_includes'
AC_SUBST(MYPLUG_CFLAGS)
$want_myplug="yes"
DECODER_PLUGINS="$DECODER_PLUGINS myplug"
fi
AM_CONDITIONAL([BUILD_myplug], [test "$want_flac"])
AC_CONFIG_FILES([decoder_plugins/myplug/Makefile])
\endcode
Now you must recreate the configure script and all Makefile.in files:
run 'autoreconf [-i]' (it requires autoconf, automake and libtool -- the
newest versions of these tools are preferred; older versions may not work).
\section decoder_plugin_how_to_begin First code
The only public (not static) function that your plugin must contain is a
function named plugin_init. It returns a structure (struct decoder) with
pointers to functions provided by the plugin. Let's look at ogg's plugin
code:
\code
static struct decoder ogg_decoder = {
DECODER_API_VERSION,
ogg_open,
ogg_open_stream,
ogg_can_decode,
ogg_close,
ogg_decode,
ogg_seek,
ogg_info,
ogg_get_bitrate,
ogg_get_duration,
ogg_get_error,
ogg_our_format_ext,
ogg_our_mime,
ogg_get_name,
ogg_current_tags,
ogg_get_stream
};
struct decoder *plugin_init ()
{
return &ogg_decoder;
}
\endcode
This is something that every plugin must have.
Also, the first lines of code in each C source file should be:
\code
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
\endcode
You may place comments above them (for example, containing copyright
information).
Includes you need are:
\verbatim
#include "main.h" /* for xmalloc(), xstrdup() etc. */
#include "log.h" /* for logit() and debug() */
#include "decoder.h" /* required: provides decoder structure definition */
#include "io.h" /* if you use io_*() functions to access files. */
#include "audio.h" /* for sound_params structure */
\endverbatim
\section decoder_plugin_requirements Requirements
First thing you must remember is that your plugin is running in multi-thread
program. When it is asked to open a file it must return a pointer to the
structure containing your private data. This pointer will be passed to each
function associated with the stream. You must not use any global or static
(in function) variables, because more than one file your plugin handles may
be open at a time (for example, when precaching).
When you operate on a file it is preferred (but not required) that you use
io_*() functions from io.h instead of standard open(), read(), write()
etc. MOC's I/O functions provide sophisticated features: buffering in a
separate thread and reading from Internet streams. They were made to
be used like standard UNIX input/output functions: they take similar
parameters and return similar values.
\section decoder_plugin_useful_api The API
Now it's time to read the documentation and find out what all that
functions must do and what functions you may use to achieve that.
\li \ref decoder
\li \ref decoder_error_funcs
For debugging purposes you may use two macros: debug() and logit() from log.h.
The first one does nothing if you don't define DEBUG before including log.h,
and works like logit() if you do. logit() works like printf(), but it only
prints to the log file when the --debug command line option is used.
Also, logit() includes time and source file name of the
function name where it was invoked. You don't need to add the end of line
character to make lines, each invocation of logit() makes a new line.
It is also preferred that you use xmalloc(), xrealloc(), xstrdup() functions
from common.h instead of those without x. They check if allocation of memory
succeeds, so you don't need to do that.
\section decoder_plugin_useful_testing Testing and Debugging
There is a script in the 'tools' subdirectory which will help you to test
your decoder. It checks that the samples which arrive at MOC's player
code are the same as those produced by the library the decoder is using.
It does this by having MOC compute the MD5 sum of the audio file as it is
playing it and writing the result to the server's log file. The log file
is then read by the 'md5check.sh' tool and the MD5 sum checked against one
computed from the audio file using the library's native decoding program.
You will need to add a new function and case item to call it to the script
so it can recognise your new decoder's library. It is important to use
the program associated with the decoder library if one exists because
other programs may not produce exactly the same samples, particularly
for lossy formats. It is also possible that bugs in the library mean
the samples produced are not correct, but the tool is intended to test
the passage of the samples through the driver and not the fidelity of
the library used.
*/
|