File: decoder_api.doxy

package info (click to toggle)
moc 1%3A2.6.0~svn-r3005-7
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 2,904 kB
  • sloc: ansic: 31,748; sh: 929; cpp: 487; makefile: 240
file content (237 lines) | stat: -rw-r--r-- 8,860 bytes parent folder | download | duplicates (6)
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.

*/