diff options
Diffstat (limited to 'doc/hackersguide/hackersguide.html')
-rw-r--r-- | doc/hackersguide/hackersguide.html | 5071 |
1 files changed, 5071 insertions, 0 deletions
diff --git a/doc/hackersguide/hackersguide.html b/doc/hackersguide/hackersguide.html new file mode 100644 index 000000000..5b3add969 --- /dev/null +++ b/doc/hackersguide/hackersguide.html @@ -0,0 +1,5071 @@ +<HTML +><HEAD +><TITLE +>The xine hacker's guide</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.7"></HEAD +><BODY +CLASS="BOOK" +><DIV +CLASS="BOOK" +><A +NAME="AEN1" +></A +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="AEN2" +></A +>The xine hacker's guide</H1 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN6" +></A +>Günter Bartsch</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN9" +></A +>Heiko Schäfer</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN12" +></A +>Richard Wareham</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN15" +></A +>Miguel Freitas</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN18" +></A +>James Courtier-Dutton</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN21" +></A +>Siggi Langauf</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN24" +></A +>Marco Zühlke</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN27" +></A +>Mike Melanson</H3 +><H3 +CLASS="AUTHOR" +><A +NAME="AEN30" +></A +>Michael Roitzsch</H3 +><P +CLASS="COPYRIGHT" +>Copyright © 2001-2003 the xine project team</P +><DIV +><DIV +CLASS="ABSTRACT" +><A +NAME="AEN36" +></A +><P +></P +><P +> This document should help xine hackers to find their way through + xine's architecture and source code. It's a pretty free-form document + containing a loose collection of articles describing various aspects + of xine's internals. + </P +><P +></P +></DIV +></DIV +><HR></DIV +><DIV +CLASS="TOC" +><DL +><DT +><B +>Table of Contents</B +></DT +><DT +>1. <A +HREF="#INTRO" +>Introduction</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN40" +>Where am I?</A +></DT +><DT +><A +HREF="#AEN44" +>What does this text do?</A +></DT +><DT +><A +HREF="#AEN47" +>New versions of this document</A +></DT +><DT +><A +HREF="#AEN54" +>Feedback</A +></DT +></DL +></DD +><DT +>2. <A +HREF="#XINE-LIBRARY" +>Using the xine library</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN61" +>xine architecture as visible to libxine clients</A +></DT +><DT +><A +HREF="#AEN75" +>Writing a new frontend to xine</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN78" +>Source code of a simple X11 frontend</A +></DT +></DL +></DD +></DL +></DD +><DT +>3. <A +HREF="#OVERVIEW" +>xine code overview</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN83" +>Walking the source tree</A +></DT +><DT +><A +HREF="#AEN385" +>Object oriented programming in C</A +></DT +><DT +><A +HREF="#AEN396" +>Coding style and guidelines</A +></DT +><DT +><A +HREF="#AEN415" +>The xine logging system</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN418" +>xine_log</A +></DT +><DT +><A +HREF="#AEN424" +>xprintf</A +></DT +><DT +><A +HREF="#AEN430" +>lprintf/llprintf</A +></DT +></DL +></DD +><DT +><A +HREF="#AEN443" +>How to contribute</A +></DT +></DL +></DD +><DT +>4. <A +HREF="#INTERNALS" +>xine internals</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN448" +>Engine architecture and data flow</A +></DT +><DT +><A +HREF="#AEN461" +>Plugin system</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN480" +>Plugin location and filesystem layout</A +></DT +><DT +><A +HREF="#AEN490" +>Plugin Content: What's inside the .so?</A +></DT +></DL +></DD +><DT +><A +HREF="#AEN529" +>What is this metronom thingy?</A +></DT +><DT +><A +HREF="#AEN542" +>How does xine synchronize audio and video?</A +></DT +><DT +><A +HREF="#OSD" +>Overlays and OSD</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN571" +>Overlay Manager</A +></DT +><DT +><A +HREF="#AEN575" +>OSD Renderer</A +></DT +></DL +></DD +><DT +><A +HREF="#AEN610" +>MRLs</A +></DT +></DL +></DD +><DT +>5. <A +HREF="#STREAM" +>xine's stream layer</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN620" +>Input layer</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN632" +>Writing a xine input plugin</A +></DT +></DL +></DD +><DT +><A +HREF="#AEN676" +>Demuxer layer</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN679" +>Introduction to demuxer theory</A +></DT +><DT +><A +HREF="#AEN683" +>Input considerations</A +></DT +><DT +><A +HREF="#AEN686" +>Seeking Policy</A +></DT +><DT +><A +HREF="#AEN693" +>Writing a xine demuxer</A +></DT +><DT +><A +HREF="#AEN733" +>Buffer types</A +></DT +></DL +></DD +><DT +><A +HREF="#AEN747" +>Decoder layer</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN750" +>Audio and video decoders</A +></DT +><DT +><A +HREF="#AEN754" +>Video output formats</A +></DT +><DT +><A +HREF="#AEN757" +>Audio output formats</A +></DT +><DT +><A +HREF="#AEN760" +>Writing a xine decoder</A +></DT +><DT +><A +HREF="#AEN821" +>SPU decoder</A +></DT +></DL +></DD +></DL +></DD +><DT +>6. <A +HREF="#OUTPUT" +>xine's output layer</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN836" +>Video output</A +></DT +><DD +><DL +><DT +><A +HREF="#AEN850" +>Writing a xine video out plugin</A +></DT +></DL +></DD +></DL +></DD +></DL +></DIV +><DIV +CLASS="CHAPTER" +><HR><H1 +><A +NAME="INTRO" +></A +>Chapter 1. Introduction</H1 +><DIV +CLASS="SECT1" +><H2 +CLASS="SECT1" +><A +NAME="AEN40" +></A +>Where am I?</H2 +><P +> You are currently looking at a piece of documentation for xine. + xine is a free video player. It lives on + <A +HREF="http://xinehq.de/" +TARGET="_top" +>http://xinehq.de/</A +>. Specifically + this document goes under the moniker of the "xine Hackers' Guide". + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN44" +></A +>What does this text do?</H2 +><P +> This document should help xine hackers to find their way through + xine's architecture and source code. It's a pretty free-form document + containing a loose collection of articles describing various aspects + of xine's internals. It has been written by a number of people who work + on xine themselves and is intended to provide the important concepts and + methods used within xine. Readers should not consider this document to be + an exhausative description of the internals of xine. As with all projects + which provide access, the source-code should be considered the definitive + source of information. + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN47" +></A +>New versions of this document</H2 +><P +> This document is being developed in the xine-lib cvs repository within + the directory <TT +CLASS="FILENAME" +>doc/hackersguide/</TT +>. If you are + unsure what to do with the stuff in that directory, please read the + <TT +CLASS="FILENAME" +>README</TT +> file located there. + </P +><P +> New versions of this document can also be obtained from the xine web site: + <A +HREF="http://xinehq.de/" +TARGET="_top" +>http://xinehq.de/</A +>. + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN54" +></A +>Feedback</H2 +><P +> All comments, error reports, additional information and criticism + concerning this document should be directed to the xine documentations + mailing list <TT +CLASS="EMAIL" +><<A +HREF="mailto:xine-docs@lists.sourceforge.net" +>xine-docs@lists.sourceforge.net</A +>></TT +>. + Questions about xine hacking in general should be sent to the + developer mailing list <TT +CLASS="EMAIL" +><<A +HREF="mailto:xine-devel@lists.sourceforge.net" +>xine-devel@lists.sourceforge.net</A +>></TT +>. + </P +></DIV +></DIV +><DIV +CLASS="CHAPTER" +><HR><H1 +><A +NAME="XINE-LIBRARY" +></A +>Chapter 2. Using the xine library</H1 +><DIV +CLASS="SECT1" +><H2 +CLASS="SECT1" +><A +NAME="AEN61" +></A +>xine architecture as visible to libxine clients</H2 +><P +> The following drawing shows the components of xine as outside applications + see them. For every component, the functions for creating and destroying it + are given. Every other function works in the context it is enclosed in. + Functions that facilitate the connection of the individual components are + also given. + </P +><DIV +CLASS="MEDIAOBJECT" +><P +><IMG +SRC="library.png"><DIV +CLASS="CAPTION" +><P +>outside view on xine components</P +></DIV +></P +></DIV +><P +> The function are named just to give you an overview of what is actually + there. It is all thoroughly documented in the plublic header + <TT +CLASS="FILENAME" +>xine.h</TT +>, which is the main and preferably the only xine + header, clients should include. (xine/xineutils.h and the XML parser might + make an exception.) + </P +><P +> Details on the OSD feature can be found in the <A +HREF="#OSD" +>OSD section</A +>. + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN75" +></A +>Writing a new frontend to xine</H2 +><P +> The best way to explain this seems to be actual code. Below you + will find a very easy and hopefully self-explaining xine frontend + to give you a start. + </P +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN78" +></A +>Source code of a simple X11 frontend</H3 +><TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +>/* +** Copyright (C) 2003 Daniel Caujolle-Bert <segfault@club-internet.fr> +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +*/ + +/* + * compile-command: "gcc -Wall -O2 `xine-config --cflags` `xine-config --libs` -lX11 -lm -o xinimin xinimin.c" + */ + +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/extensions/XShm.h> + +#include <xine.h> +#include <xine/xineutils.h> + + +#define MWM_HINTS_DECORATIONS (1L << 1) +#define PROP_MWM_HINTS_ELEMENTS 5 +typedef struct { + uint32_t flags; + uint32_t functions; + uint32_t decorations; + int32_t input_mode; + uint32_t status; +} MWMHints; + +static xine_t *xine; +static xine_stream_t *stream; +static xine_video_port_t *vo_port; +static xine_audio_port_t *ao_port; +static xine_event_queue_t *event_queue; + +static Display *display; +static int screen; +static Window window[2]; +static int xpos, ypos, width, height, fullscreen; +static double pixel_aspect; + +static int running = 0; + + +/* this will be called by xine, if it wants to know the target size of a frame */ +static void dest_size_cb(void *data, int video_width, int video_height, double video_pixel_aspect, + int *dest_width, int *dest_height, double *dest_pixel_aspect) { + + if(!running) + return; + + *dest_width = width; + *dest_height = height; + *dest_pixel_aspect = pixel_aspect; +} + +/* this will be called by xine when it's about to draw the frame */ +static void frame_output_cb(void *data, int video_width, int video_height, + double video_pixel_aspect, int *dest_x, int *dest_y, + int *dest_width, int *dest_height, + double *dest_pixel_aspect, int *win_x, int *win_y) { + if(!running) + return; + + *dest_x = 0; + *dest_y = 0; + *win_x = xpos; + *win_y = ypos; + *dest_width = width; + *dest_height = height; + *dest_pixel_aspect = pixel_aspect; +} + +static void event_listener(void *user_data, const xine_event_t *event) { + switch(event->type) { + case XINE_EVENT_UI_PLAYBACK_FINISHED: + running = 0; + break; + + case XINE_EVENT_PROGRESS: + { + xine_progress_data_t *pevent = (xine_progress_data_t *) event->data; + + printf("%s [%d%%]\n", pevent->description, pevent->percent); + } + break; + + /* you can handle a lot of other interesting events here */ + } +} + +int main(int argc, char **argv) { + char configfile[2048]; + x11_visual_t vis; + double res_h, res_v; + char *vo_driver = "auto"; + char *ao_driver = "auto"; + char *mrl = NULL; + int i; + Atom XA_NO_BORDER; + MWMHints mwmhints; + + /* parsing command line */ + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-vo") == 0) { + vo_driver = argv[++i]; + } + else if (strcmp(argv[i], "-ao") == 0) { + ao_driver = argv[++i]; + } + else + mrl = argv[i]; + } + + if (!mrl) { + printf("specify an mrl\n"); + return 1; + } + printf("mrl: '%s'\n", mrl); + + if (!XInitThreads()) { + printf("XInitThreads() failed\n"); + return 1; + } + + /* load xine config file and init xine */ + xine = xine_new(); + sprintf(configfile, "%s%s", xine_get_homedir(), "/.xine/config"); + xine_config_load(xine, configfile); + xine_init(xine); + + display = XOpenDisplay(NULL); + screen = XDefaultScreen(display); + xpos = 0; + ypos = 0; + width = 320; + height = 200; + + /* some initalization for the X11 Window we will be showing video in */ + XLockDisplay(display); + fullscreen = 0; + window[0] = XCreateSimpleWindow(display, XDefaultRootWindow(display), + xpos, ypos, width, height, 1, 0, 0); + + window[1] = XCreateSimpleWindow(display, XDefaultRootWindow(display), + 0, 0, (DisplayWidth(display, screen)), + (DisplayHeight(display, screen)), 0, 0, 0); + + XSelectInput(display, window[0], (ExposureMask | ButtonPressMask | KeyPressMask | + ButtonMotionMask | StructureNotifyMask | + PropertyChangeMask | PointerMotionMask)); + + XSelectInput(display, window[1], (ExposureMask | ButtonPressMask | KeyPressMask | + ButtonMotionMask | StructureNotifyMask | + PropertyChangeMask | PointerMotionMask)); + + XA_NO_BORDER = XInternAtom(display, "_MOTIF_WM_HINTS", False); + mwmhints.flags = MWM_HINTS_DECORATIONS; + mwmhints.decorations = 0; + XChangeProperty(display, window[1], + XA_NO_BORDER, XA_NO_BORDER, 32, PropModeReplace, (unsigned char *) &mwmhints, + PROP_MWM_HINTS_ELEMENTS); + + XMapRaised(display, window[fullscreen]); + + res_h = (DisplayWidth(display, screen) * 1000 / DisplayWidthMM(display, screen)); + res_v = (DisplayHeight(display, screen) * 1000 / DisplayHeightMM(display, screen)); + XSync(display, False); + XUnlockDisplay(display); + + /* filling in the xine visual struct */ + vis.display = display; + vis.screen = screen; + vis.d = window[fullscreen]; + vis.dest_size_cb = dest_size_cb; + vis.frame_output_cb = frame_output_cb; + vis.user_data = NULL; + pixel_aspect = res_v / res_h; + + /* opening xine output ports */ + vo_port = xine_open_video_driver(xine, vo_driver, XINE_VISUAL_TYPE_X11, (void *)&vis); + ao_port = xine_open_audio_driver(xine , ao_driver, NULL); + + /* open a xine stream connected to these ports */ + stream = xine_stream_new(xine, ao_port, vo_port); + /* hook our event handler into the streams events */ + event_queue = xine_event_new_queue(stream); + xine_event_create_listener_thread(event_queue, event_listener, NULL); + + /* make the video window visible to xine */ + xine_port_send_gui_data(vo_port, XINE_GUI_SEND_DRAWABLE_CHANGED, (void *) window[fullscreen]); + xine_port_send_gui_data(vo_port, XINE_GUI_SEND_VIDEOWIN_VISIBLE, (void *) 1); + + /* start playback */ + if (!xine_open(stream, mrl) || !xine_play(stream, 0, 0)) { + printf("Unable to open mrl '%s'\n", mrl); + return 1; + } + + running = 1; + + while (running) { + XEvent xevent; + + XNextEvent(display, &xevent); + + switch(xevent.type) { + + case KeyPress: + { + XKeyEvent kevent; + KeySym ksym; + char kbuf[256]; + int len; + + kevent = xevent.xkey; + + XLockDisplay(display); + len = XLookupString(&kevent, kbuf, sizeof(kbuf), &ksym, NULL); + XUnlockDisplay(display); + + switch (ksym) { + + case XK_q: + case XK_Q: + /* user pressed q => quit */ + running = 0; + break; + + case XK_f: + case XK_F: + { + /* user pressed f => toggle fullscreen */ + Window tmp_win; + + XLockDisplay(display); + XUnmapWindow(display, window[fullscreen]); + fullscreen = !fullscreen; + XMapRaised(display, window[fullscreen]); + XSync(display, False); + XTranslateCoordinates(display, window[fullscreen], + DefaultRootWindow(display), + 0, 0, &xpos, &ypos, &tmp_win); + XUnlockDisplay(display); + + xine_port_send_gui_data(vo_port, XINE_GUI_SEND_DRAWABLE_CHANGED, + (void*) window[fullscreen]); + } + break; + + case XK_Up: + /* cursor up => increase volume */ + xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME, + (xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) + 1)); + break; + + case XK_Down: + /* cursor down => decrease volume */ + xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME, + (xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) - 1)); + break; + + case XK_plus: + /* plus => next audio channel */ + xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, + (xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) + 1)); + break; + + case XK_minus: + /* minus => previous audio channel */ + xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, + (xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) - 1)); + break; + + case XK_space: + /* space => toggle pause mode */ + if (xine_get_param(stream, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE) + xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); + else + xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); + break; + + } + } + break; + + case Expose: + /* this handles (partial) occlusion of our video window */ + if (xevent.xexpose.count != 0) + break; + xine_port_send_gui_data(vo_port, XINE_GUI_SEND_EXPOSE_EVENT, &xevent); + break; + + case ConfigureNotify: + { + XConfigureEvent *cev = (XConfigureEvent *) &xevent; + Window tmp_win; + + width = cev->width; + height = cev->height; + + if ((cev->x == 0) && (cev->y == 0)) { + XLockDisplay(display); + XTranslateCoordinates(display, cev->window, + DefaultRootWindow(cev->display), + 0, 0, &xpos, &ypos, &tmp_win); + XUnlockDisplay(display); + } else { + xpos = cev->x; + ypos = cev->y; + } + } + break; + + } + } + + /* cleanup */ + xine_close(stream); + xine_event_dispose_queue(event_queue); + xine_dispose(stream); + xine_close_audio_driver(xine, ao_port); + xine_close_video_driver(xine, vo_port); + xine_exit(xine); + + XLockDisplay(display); + XUnmapWindow(display, window[fullscreen]); + XDestroyWindow(display, window[0]); + XDestroyWindow(display, window[1]); + XUnlockDisplay(display); + + XCloseDisplay (display); + + return 0; +}</PRE +></TD +></TR +></TABLE +></DIV +></DIV +></DIV +><DIV +CLASS="CHAPTER" +><HR><H1 +><A +NAME="OVERVIEW" +></A +>Chapter 3. xine code overview</H1 +><DIV +CLASS="SECT1" +><H2 +CLASS="SECT1" +><A +NAME="AEN83" +></A +>Walking the source tree</H2 +><P +> The <TT +CLASS="FILENAME" +>src/</TT +> directory in xine-lib contains several + modules, this should give you a quick overview on where + to find what sources. + </P +><P +> Directories marked with "(imported)" contain + code that is copied from an external project into xine-lib. + Everything below such a directory is up to this project. When modifying + code there, be sure to send the patches on. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>audio_out</TT +></DT +><DD +><P +> Audio output plugins. These provide a thin abstraction layer + around different types of audio output architectures or platforms. + Basically an audio output plugin provides functions to query and setup + the audio hardware and output audio data (e.g. PCM samples). + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>demuxers</TT +></DT +><DD +><P +> Demuxer plugins that handle various system layer file formats + like avi, asf or mpeg. The ideal demuxer know nothing about where the + data comes from and who decodes it. It should basically just unpack + it into chunks the rest of the engine can eat. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>dxr3</TT +></DT +><DD +><P +> Code to support the DXR3 / hollywood+ hardware mpeg decoder. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>input</TT +></DT +><DD +><P +> Input plugins encapsulate the origin of the data. Data sources like + ordinary files, DVDs, CDA or streaming media are handled here. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>dvb</TT +></DT +><DD +><P +> Some headers for Digital Video Broadcast. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libdvdnav</TT +> (imported)</DT +><DD +><P +> The libdvdnav library for DVD navigation is used + by xine's DVD input plugin. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libreal</TT +>, <TT +CLASS="FILENAME" +>librtsp</TT +></DT +><DD +><P +> Support for RealMedia streaming as used by the RTSP input plugin. + </P +><P +></P +></DD +></DL +></DIV +> + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>liba52</TT +> (imported)</DT +><DD +><P +> A52 (aka AC3, aka Dolby Digital) audio decoder library and xine plugin. + </P +><P +> We maintain some small integration improving differences between the + original liba52 and our copy in the file + <TT +CLASS="FILENAME" +>diff_against_release.patch</TT +>. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libdivx4</TT +></DT +><DD +><P +> Video decoder plugin using libdivx4linux if it is installed. + Currently unmaintained and soon to be discontinued if noone cares to take over. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libdts</TT +></DT +><DD +><P +> Audio decoder plugin that does currently nothing but passing through + DTS (AC5) data to the audio output plugin. This is only usefull + when using an external hardware DTS decoder. James has started to + work on software DTS decoding, but has not succeeded so far. Anyone + giving him a hand? + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libfaad</TT +> (imported)</DT +><DD +><P +> The Free AAC Decoder library and xine plugin. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libffmpeg</TT +></DT +><DD +><P +> A xine decoder plugin using various audio and video decoders from the + ffmpeg decoder pack libavcodec. Their MPEG encoder is also for the DXR3. + </P +><P +> To optimize the integration of libavcodec and the xine engine, we maintain + some differences between the original ffmpeg and our copy in the file + <TT +CLASS="FILENAME" +>diff_to_ffmpeg_cvs.txt</TT +>. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>libavcodec</TT +> (imported)</DT +><DD +><P +> The libavcodec decoder pack as used by xine's ffmpeg plugin. + </P +><P +></P +></DD +></DL +></DIV +> + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libflac</TT +></DT +><DD +><P +> A xine demuxer and decoder plugin for the Free Lossless Audio Codec library, + which has to be installed separately. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>liblpcm</TT +></DT +><DD +><P +> Audio decoder plugin that "decodes" raw PCM data; most notably + endianess-conversions are done here. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libmad</TT +> (imported)</DT +><DD +><P +> Mpeg audio decoder plugin (i.e. mp2 and mp3 decoding). + ISO/IEC compliant decoder using fixed point math. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libmpeg2</TT +> (imported)</DT +><DD +><P +> Most important MPEG video decoder plugin, provides fast and + high-precision MPEG-1/2 video decoding. + </P +><P +> Although this is an imported library, we have heavily modified + our internal copy to blend it as seamlessly as possible into + the xine engine in order to get the maximum MPEG decoding + performance. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libmpeg2new</TT +></DT +><DD +><P +> James started an effort to bring a recent and unmodified version + of libmpeg2 into xine to one day replace our current internal + modified libmpeg2 with one closer to the original. But since + the full feature catalog has not yet been achieved with the new + one, it is still disabled. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>include</TT +>, <TT +CLASS="FILENAME" +>libmpeg2</TT +> (imported)</DT +><DD +><P +> The code of the imported new libmpeg2. + </P +><P +></P +></DD +></DL +></DIV +> + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libmpg123</TT +> (imported)</DT +><DD +><P +> An MPEG audio decoder plugin baseg on mpg123 code. This plugin is disabled + because it is unmaintained. Some people said, it was faster than the libmad + decoder. But if noone starts to fix it, it will disappear soon. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libreal</TT +></DT +><DD +><P +> A thin wrapper around Real's binary codecs from the Linux RealPlayer to + use them as a xine plugin. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libspeex</TT +></DT +><DD +><P +> A xine decoder plugin for the speex library, + which has to be installed separately. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libspucc</TT +></DT +><DD +><P +> Closed caption subtitle decoder plugin. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libspudec</TT +></DT +><DD +><P +> DVD SPU subtitle decoder plugin. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libsputext</TT +></DT +><DD +><P +> Plain text subtitle decoder plugins. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libtheora</TT +></DT +><DD +><P +> A xine decoder plugin for the theora library, + which has to be installed separately. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libvorbis</TT +></DT +><DD +><P +> A xine decoder plugin for the ogg/vorbis library, + which has to be installed separately. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libw32dll</TT +></DT +><DD +><P +> Video and audio decoder plugins that exploit some wine code + to use win32 (media player and Quicktime) codecs in xine. + Works on x86 platforms only. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>DirectShow</TT +>, <TT +CLASS="FILENAME" +>dmo</TT +>, + <TT +CLASS="FILENAME" +>qtx</TT +>, <TT +CLASS="FILENAME" +>wine</TT +> (imported)</DT +><DD +><P +> Stripped down version of wine to support Video for Windows DLLs + and additional code to use DirectShow, DMO and QuickTime DLLs. + </P +><P +></P +></DD +></DL +></DIV +> + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libxineadec</TT +></DT +><DD +><P +> xine's decoder pack of additional audio decoders. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>gsm610</TT +> (imported)</DT +><DD +><P +> The gsm610 audio decoder library as used by the related xine plugin. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>nosefart</TT +> (imported)</DT +><DD +><P +> The nosefart audio decoder library as used by the related xine plugin. + </P +><P +></P +></DD +></DL +></DIV +> + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libxinevdec</TT +></DT +><DD +><P +> xine's decoder pack of additional video decoders. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>libxvid</TT +></DT +><DD +><P +> A xine decoder plugin for the xvid library, + which has to be installed separately. This plugin is + unmaintained and unless someone cares to update it, it will + be moved to the attic soon. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>post</TT +></DT +><DD +><P +> Video and audio post effect plugins live here. Post plugins + modify streams of video frames or audio buffers as they leave + the decoder to provide conversion or effects. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>deinterlace</TT +> (imported)</DT +><DD +><P +> The tvtime deinterlacer as a xine video filter post. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>goom</TT +> (imported)</DT +><DD +><P +> The goom audio visualizer as a xine visualizer post. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>mosaico</TT +></DT +><DD +><P +> Some post plugins merging multiple frames into one. For example + picture in picture can be done with this. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>planar</TT +></DT +><DD +><P +> Some simple 2D video effects as xine video filter posts. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>visualizations</TT +></DT +><DD +><P +> Audio visualization post plugins. + </P +><P +></P +></DD +></DL +></DIV +> + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>video_out</TT +></DT +><DD +><P +> Contains various video output driver plugins. Video output drivers + are thin abstraction layers over various video output platforms + (e.g. X11, directfb, directX,...). Video output driver plugins + provide functions like frame allocation and drawing and handle + stuff like hardware acceleration, scaling and colorspace conversion + if necessary. They do not handle a/v sync since this is done + in the xine-engine already. + </P +><P +> <P +></P +><DIV +CLASS="VARIABLELIST" +><DL +><DT +><TT +CLASS="FILENAME" +>libdha</TT +> (imported)</DT +><DD +><P +> A library for direct hardware access to the graphics card + as used by the vidix video out plugin. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>vidix</TT +> (imported)</DT +><DD +><P +> The vidix system for high performance video output + as used by the vidix video out plugin. + </P +><P +></P +></DD +></DL +></DIV +> + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>xine-engine</TT +></DT +><DD +><P +> The heart of xine - it's engine. Contains code to + load and handle all the plugins, the configuration repository + as well as the generic decoding loops and code for synchronized output. + A lot of helper functions for plugins to use live here as well. + What's in the individual files should be guessable by the files' + names. This document is not going to explain the source, because + it simply changes too often. A look at the architectural drawing + in the <A +HREF="#INTERNALS" +>internals section</A +> should + give you a pretty good idea, what to expect in this directory. + Basically, everything in this picture that is not called "plugin" + lives here. + </P +><P +></P +></DD +><DT +><TT +CLASS="FILENAME" +>xine-utils</TT +></DT +><DD +><P +> Collection of utility functions and platform abstractions. + Also contains a simple XML parser for frontend playlist handling. + </P +><P +></P +></DD +></DL +></DIV +> + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN385" +></A +>Object oriented programming in C</H2 +><P +> xine uses a lot of design principles normally found in + object oriented designs. As xine is written in c, a few + basic principles shall be explained here on how xine + is object oriented anyway. + </P +><P +> Classes are structs containing function pointers and public member data. + Example: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> typedef struct my_stack_s my_class_t; + + struct my_stack_s { + /* method "push" with one parameter and no return value */ + void (*push)(my_stack_t *this, int i); + + /* method "add" with no parameters and no return value */ + void (*add)(my_stack_t *this); + + /* method "pop" with no parameters (except "this") and a return value */ + int (*pop) (my_stack_t *this); + }; + + /* constructor */ + my_class_t *new_my_stack(void);</PRE +></TD +></TR +></TABLE +> + </P +><P +> To derive from such a class, private member variables can be added: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> typedef struct { + my_stack_t stack; /* public part */ + + /* private part follows here */ + int values[MAX_STACK_SIZE]; + int stack_size; + } intstack_t;</PRE +></TD +></TR +></TABLE +> + Each method is implemented as a static method (static to prevent + namespace pollution). The "this" pointer needs to be cast to the + private pointer type to gain access to the private member variables. + </P +><P +> Implementation of the "push" method follows: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> static void push (my_stack_t *this_gen, int i) { + intstack_t *this = (intstack_t *)this_gen; + this->values[MAX_STACK_SIZE - ++this->stack_size] = i; + }</PRE +></TD +></TR +></TABLE +> + </P +><P +> Finally the contructor malloc()s the data struct (private variant) + and fills in function pointers and default values. Usually the + constructor is the only public (i.e. non-static) function in the module: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> my_stack_t *new_my_stack(void) { + intstack_t *this; + + /* alloc memory */ + this = malloc(sizeof(intstack_t)); + + /* fill in methods */ + this->push = push; + this->add = add; + this->pop = pop; + + /* init data fields */ + this->stack_size = 0; + + /* return public part */ + return &this->stack; + }</PRE +></TD +></TR +></TABLE +> + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN396" +></A +>Coding style and guidelines</H2 +><P +> This section contains some guidelines for writing xine-code. + These are really just guidelines, no strict rules. + Contributions will not be rejected if they do not meet these + rules but they will be even more appreciated if they do. + <P +></P +><UL +><LI +><P +> Comment your interfaces directly in the header files. + No doxygen comments, ordinary C comments will do. + </P +></LI +><LI +><P +> Use C-style comments (/* */), not C++-style (//). + </P +></LI +><LI +><P +> When in doubt, use lower case. BTW: This thing is called xine, never Xine. + </P +></LI +><LI +><P +> Use expressive variable and function identifiers on all public interfaces. + Use underscores to seperate words in identifiers, not uppercase + letters (my_function_name is ok, myFunctionName is not ok). + </P +></LI +><LI +><P +> Avoid macros unless they are really useful. Avoid gotos. + </P +></LI +><LI +><P +> use something like + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="90%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> printf("module: ..."[,...]);</PRE +></TD +></TR +></TABLE +> + for console output. All console output goes to stdout and + must be prefixed by the module name which generates the + output (see example above). + </P +></LI +><LI +><P +> Refer to emac's C-mode for all questions of proper indentiation. + That first of all means: indent with two spaces. + </P +></LI +></UL +> + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN415" +></A +>The xine logging system</H2 +><P +> xine offers a wide range of possibilities to display + strings. This section should describe when to use + which way and how to do it right. + </P +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN418" +></A +>xine_log</H3 +><P +> Output which is done thru this function will be + displayed for the end user by the frontend. + If <TT +CLASS="VARNAME" +>xine->verbosity</TT +> is not 0 the messages will also + be displayed on the console. Ideally these strings + are translated. + This function is for information which the user should + read always. + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> xine_log(xine_t *xine, int buf, const char *format, ...);</PRE +></TD +></TR +></TABLE +> + <TT +CLASS="VARNAME" +>buf</TT +> is either XINE_LOG_MSG for general messages or + XINE_LOG_PLUGIN for messages about plugins. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN424" +></A +>xprintf</H3 +><P +> This macro uses the <TT +CLASS="VARNAME" +>xine->verbosity</TT +> value to decide + if the string should be printed to the console. Possible + values are XINE_VERBOSITY_NONE, XINE_VERBOSITY_LOG or + XINE_VERBOSITY_DEBUG. By default nothing is printed. + When you use xine-ui you can enable this output with + the <TT +CLASS="PARAMETER" +><I +>--verbose=[1,2]</I +></TT +> options. + This function should be used for information which the + user should only read up on request. + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> xprintf(xine_t *xine, int verbosity, const char *format, ...);</PRE +></TD +></TR +></TABLE +> + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN430" +></A +>lprintf/llprintf</H3 +><P +> These macros are for debugging purpose only. Under normal + circumstances it is disabled. And can only be enabled by changing + a define statement and a recompilation. It has to be enabled for these + files that are of interest. + It should only be used for information which is intended for developers. + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> lprintf(const char *format, ...); + llprintf(bool, const char *format, ...);</PRE +></TD +></TR +></TABLE +> + <TT +CLASS="VARNAME" +>bool</TT +> is a flag which enables or disables this logging. + </P +><P +> <TT +CLASS="FUNCTION" +>lprintf</TT +> can be enabled by defining LOG at the top of the source file. + <TT +CLASS="FUNCTION" +>llprintf</TT +> can be used for more than one categorie + per file by using diffent lables: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> #define LOG_LOAD 1 + #define LOG_SAVE 0 + + llprintf(LOG_LOAD, "loading was successful\n"); + llprintf(LOG_SAVE, "could not save to file %s\n", filename);</PRE +></TD +></TR +></TABLE +> + </P +><P +> In this case only the first messages is printed. To enable/disable change the defines. + </P +><P +> LOG_MODULE should be used to set the modulename for xprintf/lprintf/llprintf. + Each output line will start with "modulename: ". + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> #define LOG_MODULE "modulename"</PRE +></TD +></TR +></TABLE +> + </P +><P +> LOG_VERBOSE can be defined to enable the logging of functionname and linenumbers. + Then the output will be: "modulename: (function_name:42) message". + </P +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN443" +></A +>How to contribute</H2 +><P +> Make sure you send your patches in unified diff format to + the xine-devel mailing list. You'll have to subscribe first, + otherwise you're not allowed to post. Please do not send + patches to individual developers unless instructed otherwise + because your patch is more likely to get lost in an overfull + INBOX in that case. Please be patient, it may take 1-2 weeks + before you hear any comments on your work (developers may be + working on other parts of the code or are simply busy at + the moment). + </P +></DIV +></DIV +><DIV +CLASS="CHAPTER" +><HR><H1 +><A +NAME="INTERNALS" +></A +>Chapter 4. xine internals</H1 +><DIV +CLASS="SECT1" +><H2 +CLASS="SECT1" +><A +NAME="AEN448" +></A +>Engine architecture and data flow</H2 +><DIV +CLASS="MEDIAOBJECT" +><P +><IMG +SRC="architecture.png"><DIV +CLASS="CAPTION" +><P +>xine engine architecture</P +></DIV +></P +></DIV +><P +> Media streams usually consist of audio and video data multiplexed + into one bitstream in the so-called system-layer (e.g. AVI, Quicktime or MPEG). + A demuxer plugin is used to parse the system layer and extract audio and video + packages. The demuxer uses an input plugin to read the data and stores it + in pre-allocated buffers from the global buffer pool. + The buffers are then added to the audio or video stream fifo. + </P +><P +> From the other end of these fifos the audio and video decoder threads + consume the buffers and hand them over to the current audio or video + decoder plugin for decompression. These plugins then send the decoded + data to the output layer. The buffer holding the encoded + data is no longer needed and thus released to the global buffer pool. + </P +><P +> In the output layer, the video frames and audio samples pass through a + post plugin tree, which can apply effects or other operations to the data. + When reaching the output loops, frames and samples are enqueued to be + displayed, when the presentation time has arrived. + </P +><P +> A set of extra information travels with the data. Starting at the input and + demuxer level, where this information is generated, the data is attached to + the buffers as they wait in the fifo. The decoder loops copy the data to + a storage of their own. From there, every frame and audio buffer leaving + the stream layer is tagged with the data the decoder loop storage currently + holds. + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN461" +></A +>Plugin system</H2 +><P +> The plugin system enables some of xine's most valuable features: + <P +></P +><UL +><LI +><P +> drop-in extensiability + </P +></LI +><LI +><P +> support parallel installation of multiple (incompatible) libxine versions + </P +></LI +><LI +><P +> support for multiple plugin directories + (<TT +CLASS="FILENAME" +>$prefix/lib/xine/plugins</TT +>, + <TT +CLASS="FILENAME" +>$HOME/.xine/plugins</TT +>, ...) + </P +></LI +><LI +><P +> support for recursive plugin directories + (plugins are found even in subdirectories of the plugin directories) + </P +></LI +><LI +><P +> version management + (On start, xine finds all plugins in its plugin (sub)directories and + chooses an appropriate version (usually the newest) for each plugin.) + </P +></LI +><LI +><P +> simplification + (Plugins don't have to follow any special naming convention, + and any plugin may contain an arbitrary subset of input, demuxer, + decoder or output plugins.) + </P +></LI +></UL +> + </P +><P +> Essentally, plugins are just shared objects, ie dynamic libraries. In + contrast to normal dynamic libraries, they are stored outside of the + system's library PATHs and libxine does its own bookkeeping, which + enables most advanced features mentioned above. + </P +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN480" +></A +>Plugin location and filesystem layout</H3 +><P +> The primary goal for this new plugin mechanism was the need to support + simultaneous installation of several (most likely incompatible) + libxine versions without them overwriting each other's + plugins. Therefore, we have this simple layout: + </P +><P +> Plugins are installed below XINE_PLUGINDIR + (<TT +CLASS="FILENAME" +>/usr/local/lib/xine/plugins</TT +> by default). + Note that plugins are never directly installed into XINE_PLUGINDIR. + Instead, a separate subdirectory is created for each "plugin + provider". A plugin provider is equivalent with the exact version of + one source package. Typical examples include "xine-lib-0.9.11" or + "xine-vcdnav-1.0". Every source package is free to install an + arbitrary number of plugins in its own, private directory. If a + package installs several plugins, they may optionally be organized + further into subdirectories. + </P +><P +> So you will finally end up with something like this: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="SCREEN" +> /usr/local/lib/xine/plugins + xine-lib-0.9.11 + demux_mpeg_block.so + decode_mpeg.so + video_out_xv.so + ... + xine-vcdnav-0.9.11 + input_vcdnav.so + xine-lib-1.2 + input + file.so + stdin_fifo.so + vcd.so + demuxers + fli.so + avi.so + ... + decoders + ffmpeg.so + mpeg.so (may contain mpeg 1/2 audio and video decoders) + pcm.so + ... + output + video_xv.so + audio_oss.so + ... + xine-lib-3.0 + avi.so (avi demuxer) + mpeg.so (contains mpeg demuxers and audio/video decoders) + video_out_xv.so (Xv video out) + ...</PRE +></TD +></TR +></TABLE +> + </P +><P +> As you can see, every package is free to organize plugins at will + below its own plugin provider directory. + Additionally, administrators may choose to put plugins directly into + XINE_PLUGINDIR, or in a "local" subdirectory. + Users may wish to put additional plugins in ~/.xine/plugins/. + Again, there may be subdirectories to help organize the plugins. + </P +><P +> The default value for XINE_PLUGINDIR can be obtained using the + <B +CLASS="COMMAND" +>xine-config --plugindir</B +> command. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN490" +></A +>Plugin Content: What's inside the .so?</H3 +><P +> Each plugin library (.so file) contains an arbitrary number of (virtual) + plugins. Typically, it will contain exactly one plugin. However, it + may be useful to put a set of related plugins in one library, so they + can share common code. + </P +><P +> First of all, what is a virtual plugin? + A virtual plugin is essentially a structure that is defined by the + xine engine. This structure typically contains lots of function + pointers to the actual API functions. + For each plugin API, there are several API versions, and each API + version may specify a new, incompatible structure. Therefore, it is + essential that only those plugins are loaded that support current + libxine's API, so the .so file needs a plugin list that + provides libxine with the version information, even before it tries to + load any of the plugins. + </P +><P +> This plugin list is held in an array named <TT +CLASS="VARNAME" +>xine_plugin_info</TT +>": + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> plugin_info_t xine_plugin_info[] = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_DEMUX, 20, "flac", XINE_VERSION_CODE, NULL, demux_flac_init_class }, + { PLUGIN_AUDIO_DECODER, 13, "flacdec", XINE_VERSION_CODE, &dec_info_audio, init_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } + };</PRE +></TD +></TR +></TABLE +> + </P +><P +> The structure of xine_plugin_info may <SPAN +CLASS="emphasis" +><I +CLASS="EMPHASIS" +>never</I +></SPAN +> be changed. + If it ever needs to be changed, it must be renamed to avoid + erraneous loading of incompatible plugins. + </P +><P +> <TT +CLASS="VARNAME" +>xine_plugin_info</TT +> can contain any number of plugins + and must be terminated with a <SPAN +CLASS="TYPE" +>PLUGIN_NONE</SPAN +> entry. Available plugin + types are: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> #define PLUGIN_NONE 0 + #define PLUGIN_INPUT 1 + #define PLUGIN_DEMUX 2 + #define PLUGIN_AUDIO_DECODER 3 + #define PLUGIN_VIDEO_DECODER 4 + #define PLUGIN_SPU_DECODER 5 + #define PLUGIN_AUDIO_OUT 6 + #define PLUGIN_VIDEO_OUT 7 + #define PLUGIN_POST 8</PRE +></TD +></TR +></TABLE +> + </P +><P +> The plugin version number is generated from xine-lib's version number + like this: MAJOR * 10000 + MINOR * 100 + SUBMINOR. + This is not required, but it's an easy way to ensure that the version + increases for every release. + </P +><P +> Every entry in <TT +CLASS="VARNAME" +>xine_plugin_info</TT +> has an initialization + function for the plugin class context. + This function returns a pointer to freshly allocated (typically + via <TT +CLASS="FUNCTION" +>malloc()</TT +>) structure containing mainly function + pointers; these are the "methods" of the plugin class. + </P +><P +> The "plugin class" is not what we call to do the job yet (like decoding + a video or something), it must be instantiated. One reason for having the + class is to hold any global settings that must be accessed by every + instance. Remember that xine library is multistream capable: multible + videos can be decoded at the same time, thus several instances of the + same plugin are possible. + </P +><P +> If you think this is pretty much an object-oriented aproach, + then you're right. + </P +><P +> A fictitious file input plugin that supports input plugin API 12 and + 13, found in xine-lib 2.13.7 would then define this plugin list: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> #include <xine/plugin.h> + ... + plugin_t *init_api12(void) { + input_plugin_t *this; + + this = malloc(sizeof(input_plugin_t)); + ... + return (plugin_t *)this; + } + /* same thing, with different initialization for API 13 */ + + const plugin_info_t xine_plugin_info[] = { + { PLUGIN_INPUT, 12, "file", 21307, init_api12 }, + { PLUGIN_INPUT, 13, "file", 21307, init_api13 }, + { PLUGIN_NONE, 0, "", 0, NULL } + }</PRE +></TD +></TR +></TABLE +> + This input plugin supports two APIs, other plugins might provide a + mixture of demuxer and decoder plugins that belong together somehow + (ie. share common code). + </P +><P +> You'll find exact definitions of public functions and plugin structs + in the appropriate header files for each plugin type: + <TT +CLASS="FILENAME" +>input/input_plugin.h</TT +> for input plugins, + <TT +CLASS="FILENAME" +>demuxers/demux.h</TT +> for demuxer plugins, + <TT +CLASS="FILENAME" +>xine-engine/video_decoder.h</TT +> for video decoder plugins, + <TT +CLASS="FILENAME" +>xine-engine/audio_decoder.h</TT +> for audio decoder plugins, + <TT +CLASS="FILENAME" +>xine-engine/post.h</TT +> for post plugins, + <TT +CLASS="FILENAME" +>xine-engine/video_out.h</TT +> for video out plugins, + <TT +CLASS="FILENAME" +>xine-engine/audio_out.h</TT +> for audio out plugins. + Additional information will also be given in the dedicated sections below. + </P +><P +> Many plugins will need some additional "private" data fields. + These should be simply added at the end of the plugin structure. + For example a demuxer plugin called "foo" with two private + fields "xine" and "count" may have a plugin structure declared in + the following way: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> typedef struct { + /* public fields "inherited" from demux.h */ + demux_plugin_t demux_plugin; + + xine_t *xine; + int count; + } demux_foo_t;</PRE +></TD +></TR +></TABLE +> + </P +><P +> The plugin would then access public members via the + <TT +CLASS="VARNAME" +>demux_plugin</TT +> field and private fields directly. + </P +><P +> Summary: Plugins consist of two C-style classes, each representing a different context. + <P +></P +><UL +><LI +><P +> The first is the so called "plugin class" context. This is a singleton context, + which means it will exist either not at all or at most once per xine context. + This plugin class context is a C-style class which is subclassing the related + class from the xine plugin headers. This contains functions, which are + independent of the actual instance of the plugin. Most prominently, it contains + a factory method to instantiate the next context. + </P +></LI +><LI +><P +> The second context is the instance context. This is another C-style class, which + is constructed and disposed withing the plugin class context. This one does + the actual work and subclasses the related plugin struct from the xine plugin + headers. It is instantiated for every separate running instance of the plugin + </P +></LI +></UL +> + </P +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN529" +></A +>What is this metronom thingy?</H2 +><P +> Metronom serves two purposes: + <P +></P +><UL +><LI +><P +> Generate vpts (virtual presentation time stamps) from pts (presentation time stamps) + for a/v output and synchronization. + </P +></LI +><LI +><P +> Provide a master clock (system clock reference, scr), possibly provided + by external scr plugins (this can be used if some hardware decoder or network + server dictates the time). + </P +></LI +></UL +> + </P +><P +> pts/vpts values are given in 1/90000 sec units. pts values in mpeg streams + may wrap (that is, return to zero or any other value without further notice), + can be missing on some frames or (for broken streams) may "dance" around + the correct values. Metronom therefore has some heuristics built-in to generate + clean vpts values which can then be used in the output layers to schedule audio/video + output. + </P +><P +> The heuristics used in metronom have always been a field of research. Current metronom's + implementation <SPAN +CLASS="emphasis" +><I +CLASS="EMPHASIS" +>tries</I +></SPAN +> to stick to pts values as reported from demuxers, + that is, vpts may be obtained by a simple operation of vpts = pts + <TT +CLASS="VARNAME" +>vpts_offset</TT +>, + where <TT +CLASS="VARNAME" +>vpts_offset</TT +> takes into account any wraps. Whenever pts is zero, + metronom will estimate vpts based on previous values. If a difference is found between the + estimated and calculated vpts values by above formula, it will be smoothed by using a + "drift correction". + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN542" +></A +>How does xine synchronize audio and video?</H2 +><P +> Every image frame or audio buffer leaving decoder is tagged by metronom with + a vpts information. This will tell video_out and audio_out threads when that + data should be presented. Usually there isn't a significative delay associated + with video driver, so we expect it to get on screen at the time it's + delivered for drawing. Unfortunately the same isn't true for audio: all sound + systems implement some amount of buffering (or fifo), any data being send to it + <SPAN +CLASS="emphasis" +><I +CLASS="EMPHASIS" +>now</I +></SPAN +> will only get played some time in future. audio_out thread + must take this into account for making perfect A-V sync by asking the sound latency + to audio driver. + </P +><P +> Some audio drivers can't tell the current delay introduced in playback. This is + especially true for most sound servers like ESD or aRts and explain why in such + cases the sync is far from perfect. + </P +><P +> Another problem xine must handle is the sound card clock drift. vpts are + compared to the system clock (or even to a different clock provided by a scr plugin) + for presentation but sound card is sampling audio by it's own clocking + mechanism, so a small drift may occur. As the playback goes on this + error will accumulate possibly resulting in audio gaps or audio drops. To avoid that + annoying effect, two countermeasures are available (switchable with xine config + option <TT +CLASS="PARAMETER" +><I +>audio.av_sync_method</I +></TT +>): + <P +></P +><UL +><LI +><P +> The small sound card errors are feedbacked to metronom. The details + are given by <TT +CLASS="FILENAME" +>audio_out.c</TT +> comments: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="90%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> /* By adding gap errors (difference between reported and expected + * sound card clock) into metronom's vpts_offset we can use its + * smoothing algorithms to correct sound card clock drifts. + * obs: previously this error was added to xine scr. + * + * audio buf ---> metronom --> audio fifo --> (buf->vpts - hw_vpts) + * (vpts_offset + error) gap + * <---------- control --------------| + * + * Unfortunately audio fifo adds a large delay to our closed loop. + * + * These are designed to avoid updating the metronom too fast. + * - it will only be updated 1 time per second (so it has a chance of + * distributing the error for several frames). + * - it will only be updated 2 times for the whole audio fifo size + * length (so the control will wait to see the feedback effect) + * - each update will be of gap/SYNC_GAP_RATE. + * + * Sound card clock correction can only provide smooth playback for + * errors < 1% nominal rate. For bigger errors (bad streams) audio + * buffers may be dropped or gaps filled with silence. + */</PRE +></TD +></TR +></TABLE +> + </P +></LI +><LI +><P +> The audio is stretched or squeezed a slight bit by resampling, thus compensating + the drift: The next comment in <TT +CLASS="FILENAME" +>audio_out.c</TT +> explains: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="90%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> /* Alternative for metronom feedback: fix sound card clock drift + * by resampling all audio data, so that the sound card keeps in + * sync with the system clock. This may help, if one uses a DXR3/H+ + * decoder board. Those have their own clock (which serves as xine's + * master clock) and can only operate at fixed frame rates (if you + * want smooth playback). Resampling then avoids A/V sync problems, + * gaps filled with 0-frames and jerky video playback due to different + * clock speeds of the sound card and DXR3/H+. + */</PRE +></TD +></TR +></TABLE +> + </P +></LI +></UL +> + </P +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="OSD" +></A +>Overlays and OSD</H2 +><P +> The roots of xine overlay capabilities are DVD subpictures and subtitles support + (also known as 'spu'). The DVD subtitles are encoded in a RLE (Run Length Encoding - the + most simple compressing technique) format, with a palette of colors and transparency + levels. You probably thought that subtitles were just simple text saved into DVDs, right? + Wrong, they are bitmaps. + </P +><P +> In order to optimize to the most common case, xine's internal format for screen overlays + is a similar representation to the 'spu' data. This brings not only performance + benefit (since blending functions may skip large image areas due to RLE) but also + compatibility: it's possible to reencode any xine overlay to the original spu format + for displaying with mpeg hardware decoders like DXR3. + </P +><P +> Displaying subtitles requires the ability to sync them to the video stream. This + is done using the same kind of pts/vpts stuff of a-v sync code. DVD subtitles, + for example, may request: show this spu at pts1 and hide it at pts2. This brings the + concept of the 'video overlay manager', that is a event-driven module for managing + overlay's showing and hiding. + </P +><P +> The drawback of using internal RLE format is the difficulty in manipulating it + as graphic. To overcome that we created the 'OSD renderer', where OSD stands + for On Screen Display just like in TV sets. The osd renderer is a module + providing simple graphic primitives (lines, rectagles, draw text etc) over + a "virtual" bitmap area. Everytime we want to show that bitmap it will + be RLE encoded and sent to the overlay manager for displaying. + </P +><DIV +CLASS="MEDIAOBJECT" +><P +><IMG +SRC="overlays.png"><DIV +CLASS="CAPTION" +><P +>overlays architecture</P +></DIV +></P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN571" +></A +>Overlay Manager</H3 +><P +> The overlay manager interface is available to any xine plugin. It's a bit unlikely + to be used directly, anyway here's a code snippet for enqueueing an overlay for + displaying: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> video_overlay_event_t event; + + event.object.handle = this->video_overlay->get_handle(this->video_overlay,0); + + memset(this->event.object.overlay, 0, sizeof(*this->event.object.overlay)); + + /* set position and size for this overlay */ + event.object.overlay->x = 0; + event.object.overlay->y = 0; + event.object.overlay->width = 100; + event.object.overlay->height = 100; + + /* clipping region is mostly used by dvd menus for highlighting buttons */ + event.object.overlay->clip_top = 0; + event.object.overlay->clip_bottom = image_height; + event.object.overlay->clip_left = 0; + event.object.overlay->clip_right = image_width; + + /* the hard part: provide a RLE image */ + event.object.overlay->rle = your_rle; + event.object.overlay->data_size = your_size; + event.object.overlay->num_rle = your_rle_count; + + /* palette must contain YUV values for each color index */ + memcpy(event.object.overlay->clip_color, color, sizeof(color)); + + /* this table contains transparency levels for each color index. + 0 = completely transparent, 15 - completely opaque */ + memcpy(event.object.overlay->clip_trans, trans, sizeof(trans)); + + /* set the event type and time for displaying */ + event.event_type = EVENT_SHOW_SPU; + event.vpts = 0; /* zero is a special vpts value, it means 'now' */ + video_overlay->add_event(video_overlay, &event);</PRE +></TD +></TR +></TABLE +> + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN575" +></A +>OSD Renderer</H3 +><P +> OSD is a general API for rendering stuff over playing video. It's available both + to xine plugins and to frontends. + </P +><P +> The first thing you need is to allocate a OSD object for drawing from the + renderer. The code below allocates a 300x200 area. This size can't be changed + during the lifetime of a OSD object, but it's possible to place it anywhere + over the image. + </P +><TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> osd_object_t osd; + + osd = this->osd_renderer->new_object(osd_renderer, 300, 200);</PRE +></TD +></TR +></TABLE +><P +> Now we may want to set font and color for text rendering. Although we will + refer to fonts over this document, in fact the OSD can be any kind of bitmap. Font + files are searched and loaded during initialization from + <TT +CLASS="FILENAME" +>$prefix/share/xine/fonts/</TT +> and <TT +CLASS="FILENAME" +>~/.xine/fonts</TT +>. + There's a sample utility to convert truetype fonts at + <TT +CLASS="FILENAME" +>xine-lib/misc/xine-fontconv.c</TT +>. Palette may be manipulated directly, + however most of the time it's convenient to use pre-defined text palettes. + </P +><TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> /* set sans serif 24 font */ + osd_renderer->set_font(osd, "sans", 24); + + /* copy pre-defined colors for white, black border, transparent background to + starting at the index used by the first text palette */ + osd_renderer->set_text_palette(osd, TEXTPALETTE_WHITE_BLACK_TRANSPARENT, OSD_TEXT1); + + /* copy pre-defined colors for white, no border, translucid background to + starting at the index used by the second text palette */ + osd_renderer->set_text_palette(osd, TEXTPALETTE_WHITE_NONE_TRANSLUCID, OSD_TEXT2);</PRE +></TD +></TR +></TABLE +><P +> Now render the text and show it: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> osd_renderer->render_text(osd, 0, 0, "white text, black border", OSD_TEXT1); + osd_renderer->render_text(osd, 0, 30, "white text, no border", OSD_TEXT2); + + osd_renderer->show(osd, 0); /* 0 stands for 'now' */</PRE +></TD +></TR +></TABLE +> + </P +><P +> There's a 1:1 mapping between OSD objects and overlays, therefore the + second time you send an OSD object for displaying it will actually substitute + the first image. By using set_position() function we can move overlay + over the video. + </P +><TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> for( i=0; i < 100; i+=10 ) { + osd_renderer->set_position(osd, i, i ); + osd_renderer->show(osd, 0); + sleep(1); + } + osd_renderer->hide(osd, 0);</PRE +></TD +></TR +></TABLE +><P +> For additional functions please check osd.h or the public header. + </P +><DIV +CLASS="SECT3" +><HR><H4 +CLASS="SECT3" +><A +NAME="AEN590" +></A +>OSD palette notes</H4 +><P +> The palette functions demand some additional explanation, skip this if you + just want to write text fast without worring with details! :) + </P +><P +> We have a 256-entry palette, each one defining yuv and transparency levels. + Although xine fonts are bitmaps and may use any index they want, we have + defined a small convention: + </P +><TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> /* + Palette entries as used by osd fonts: + + 0: not used by font, always transparent + 1: font background, usually transparent, may be used to implement + translucid boxes where the font will be printed. + 2-5: transition between background and border (usually only alpha + value changes). + 6: font border. if the font is to be displayed without border this + will probably be adjusted to font background or near. + 7-9: transition between border and foreground + 10: font color (foreground) + */</PRE +></TD +></TR +></TABLE +><P +> The so called 'transitions' are used to implement font anti-aliasing. That + convention requires that any font file must use only the colors from 1 to 10. + When we use the set_text_palette() function we are just copying 11 palette + entries to the specified base index. + </P +><P +> That base index is the same we pass to render_text() function to use the + text palette. With this scheme is possible to have several diferent text + colors at the same time and also draw fonts over custom background. + </P +><TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> /* obtains size the text will occupy */ + renderer->get_text_size(osd, text, &width, &height); + + /* draws a box using font background color (translucid) */ + renderer->filled_rect(osd, x1, y1, x1+width, y1+height, OSD_TEXT2 + 1); + + /* render text */ + renderer->render_text(osd, x1, y1, text, OSD_TEXT2);</PRE +></TD +></TR +></TABLE +></DIV +><DIV +CLASS="SECT3" +><HR><H4 +CLASS="SECT3" +><A +NAME="AEN598" +></A +>OSD text and palette FAQ</H4 +><P +> Q: What is the format of the color palette entries? + </P +><P +> A: It's the same as used by overlay blending code (YUV). + </P +><P +> Q: What is the relation between a text palette and a palette + I set with xine_osd_set_palette? + </P +><P +> A: xine_osd_set_palette will set the entire 256 color palette + to be used when we blend the osd image. + "text palette" is a sequence of 11 colors from palette to be + used to render text. that is, by calling osd_render_text() + with color_base=100 will render text using colors 100-110. + </P +><P +> Q: Can I render text with colors in my own palette? + </P +><P +> A: Sure. Just pass the color_base to osd_render_text() + </P +><P +> Q: Has a text palette change effects on already drawed text? + </P +><P +> A: osd_set_text_palette() will overwrite some colors on palette + with pre-defined ones. So yes, it will change the color + on already drawed text (if you do it before calling osd_show, + of course). + If you don't want to change the colors of drawed text just + use different color_base values. + </P +><P +> Q: What about the shadows of osd-objects? Can I turn them off + or are they hardcoded? + </P +><P +> A: osd objects have no shadows by itself, but fonts use 11 + colors to produce an anti-aliased effect. + if you set a "text palette" with entries 0-9 being transparent + and 10 being foreground you will get rid of any borders or + anti-aliasing. + </P +></DIV +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN610" +></A +>MRLs</H2 +><P +> This section defines a draft for a syntactic specification of MRLs as + used by xine-lib. The language of MRLs is designed to be a true subset + of the language of URIs as given in RFC2396. A type 2 grammar for the + language of MRLs is given in EBNF below. + </P +><P +> Semantically, MRLs consist of two distinct parts that are evaluated by + different components of the xine architecture. The first part, + derivable from the symbol <input_source> in the given grammar, is + completely handed to the input plugins, with input plugins signaling + if they can handle the MRL. + </P +><P +> The second part, derivable from <stream_setup> and delimited from the + first by a crosshatch ('#') contains parameters that modify the + initialization and playback behaviour of the stream to which the MRL + is passed. The possible parameters are mentioned in the manpage to + xine-ui. + </P +><P +> The following definition should be regarded as a guideline only. + Of course any given input plugin only understands a subset of all + possible MRLs. On the other hand, invalid MRLs according to this + definition might be understood for convenience reasons. + Some user awareness is required at this point. + </P +><P +> EBNF grammar for MRLs: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> <mrl> ::= <input_source>[#<stream_setup>] + <input_source> ::= (<absolute_mrl>|<relative_mrl>) + <absolute_mrl> ::= <input>:(<net_path>|<abs_path>)[?<query>] + <relative_mrl> ::= (<abs_path>|<rel_path>) + <net_path> ::= //<authority>[<abs_path>] + <abs_path> ::= /<path_segments> + <rel_path> ::= <rel_segment>[<abs_path>] + <rel_segment> ::= <rel_char>{<rel_char>} + <rel_char> ::= (<unreserved>|<escaped>|;|@|&|=|+|$|,) + <input> ::= <alpha>{(<alpha>|<digit>|+|-|.)} + <authority> ::= (<server>|<reg_name>) + <server> ::= [[<userinfo>@]<host>[:<port>]] + <userinfo> ::= {(<unreserved>|<escaped>|;|:|&|=|+|$|,)} + <host> ::= (<hostname>|<ipv4_address>) + <hostname> ::= {<domainlabel>.}<toplabel>[.] + <domainlabel> ::= (<alphanum>|<alphanum>{(<alphanum>|-)}<alphanum>) + <toplabel> ::= (<alpha>|<alpha>{(<alphanum>|-)}<alphanum>) + <ipv4_address> ::= <digit>{<digit>}.<digit>{<digit>}.<digit>{<digit>}.<digit>{<digit>} + <port> ::= {<digit>} + <reg_name> ::= <reg_char>{<reg_char>} + <reg_char> ::= (<unreserved>|<escaped>|;|:|@|&|=|+|$|,) + <path_segments> ::= <segment>{/<segment>} + <segment> ::= {<path_char>}{;<param>} + <param> ::= {<path_char>} + <path_char> ::= (<unreserved>|<escaped>|:|@|&|=|+|$|,) + <query> ::= {<mrl_char>} + <stream_setup> ::= <stream_option>;{<stream_option>} + <stream_option> ::= (<configoption>|<engine_option>|novideo|noaudio|nospu) + <configoption> ::= <configentry>:<configvalue> + <configentry> ::= <unreserved>{<unreserved>} + <configvalue> ::= <conf_char>{<conf_char>} + <engine_option> ::= <unreserved>{<unreserved>}:<stream_char>{<stream_char>} + <stream_char> ::= (<unreserved>|<escaped>|:|@|&|=|+|$|,) + <mrl_char> ::= (<reserved>|<unreserved>|<escaped>) + <reserved> ::= (;|/|?|:|@|&|=|+|$|,) + <unreserved> ::= (<alphanum>|<mark>) + <mark> ::= (-|_|.|!|~|*|'|(|)) + <escaped> ::= %<hex><hex> + <hex> ::= (<digit>|A|B|C|D|E|F|a|b|c|d|e|f) + <alphanum> ::= (<alpha>|<digit>) + <alpha> ::= (<lowalpha>|<upalpha>) + <lowalpha> ::= (a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z) + <upalpha> ::= (A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z) + <digit> ::= (0|1|2|3|4|5|6|7|8|9)</PRE +></TD +></TR +></TABLE +> + </P +></DIV +></DIV +><DIV +CLASS="CHAPTER" +><HR><H1 +><A +NAME="STREAM" +></A +>Chapter 5. xine's stream layer</H1 +><DIV +CLASS="SECT1" +><H2 +CLASS="SECT1" +><A +NAME="AEN620" +></A +>Input layer</H2 +><P +> Many media players expect streams to be stored within files on + some local medium. In actual fact, media may be streamed over a + network (e.g. via HTTP or RTP), encoded onto a specialized medium + (e.g. DVD), etc. To allow you to access all this media, xine supports + the concept of an "input plugin". The tasks performed by an + input plugin are: + <P +></P +><UL +><LI +><P +> Validation of Media Resource Locators (MRLs). + </P +></LI +><LI +><P +> MRL specific session management (e.g. opening and closing local files). + </P +></LI +><LI +><P +> Reading blocks/specific numbers of bytes from the input device. + </P +></LI +></UL +> + </P +><P +> In addition to these tasks, the input plugin may keep track of some + input device-specific state information (e.g. a DVD plugin may keep + track of navigational state data such as current title/chapter). + </P +><P +> There are two classes of input device which xine recognizes. + Byte-oriented devices can, upon request, return an arbitary + non-zero number of bytes from a stream. Examples of such devices + are files or network streams. Block-oriented devices, however, have + a prefered block or "frame"-size. An example of such a device is + a DVD where data is stored in logical blocks of 2048 bytes. One may + pass the hint to xine that the plugin is block-oriented by setting the + INPUT_CAP_BLOCK capability. Note that this is only a hint and + xine does not guarantee that all requests to the plugin will + be purely block based. + </P +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN632" +></A +>Writing a xine input plugin</H3 +><P +> An input plugin provides API functions which allow the engine to + access the data source the plugin encapsulates. The input plugin API + is declared in <TT +CLASS="FILENAME" +>input/input_plugin.h</TT +>. + </P +><P +> An input plugin exports a public function of the form: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void *input_init_plugin(xine_t *xine, void *data);</PRE +></TD +></TR +></TABLE +> + This function initializes an input plugin class object with the + following functions: + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_description(input_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns a plaintext, one-line string describing the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_identifier(input_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns a shorter identifier describing the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> xine_mrl_t **get_dir(input_class_t *this_gen, const char *filename, int *nFiles);</PRE +></TD +></TR +></TABLE +> + Retrieves a directory listing from the plugin. This function is optional. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char **get_autoplay_list(input_class_t *this_gen, int *num_files);</PRE +></TD +></TR +></TABLE +> + Retrieves the autoplay playlist from the plugin. This function is optional. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int eject_media(input_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + Ejects the medium. This function is optional. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose(input_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function frees the memory used by the input plugin class object. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> input_plugin_t *get_instance(input_class_t *class_gen, xine_stream_t *stream, const char *mrl);</PRE +></TD +></TR +></TABLE +> + The plugin should try, if it can handle the specified MRL and return an + instance of itself if so. If not, NULL should be returned. + Note that input plugins are not guaranteed to be queried + in anay particular order and the first input plugin to claim an MRL + gets control so try not to duplicate MRLs already found within xine. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int open(input_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + You should do any device-specific initialisation within this function. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> uint32_t get_capabilities(input_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + Returns a bit mask describing the input device's capabilities. + You may logically OR the <TT +CLASS="VARNAME" +>INPUT_CAP_*</TT +> constants together to get + a suitable bit-mask (via the '|' operator). + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> off_t read(input_plugin_t *this_gen, char *buf, off_t nlen);</PRE +></TD +></TR +></TABLE +> + Reads a specified number of bytes into a buffer and returns the number of bytes actually copied. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> buf_element_t *read_block(input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t len);</PRE +></TD +></TR +></TABLE +> + Should the input plugin set the block-oriented hint and if the + demuxer supports it, this function will be called to read a block directly + into a xine buffer from the buffer pool. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> off_t seek(input_plugin_t *this_gen, off_t offset, int origin);</PRE +></TD +></TR +></TABLE +> + This function is called by xine when it is required that subsequent + reads come from another part of the stream. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> off_t get_current_pos(input_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + Returns the current position within a finite length stream. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> off_t get_length(input_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + Similarly this function returns the length of the stream. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> uint32_t get_blocksize(input_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + Returns the device's prefered block-size if applicable. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_mrl(input_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + Returns the current MRL. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int get_optional_data(input_plugin_t *this_gen, void *data, int data_type);</PRE +></TD +></TR +></TABLE +> + This function allows the input to advertise extra information that is + not available through other API functions. See <TT +CLASS="VARNAME" +>INPUT_OPTIONAL_*</TT +> defines. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose(input_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function closes all resources and frees the input_plugin_t object. + </P +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN676" +></A +>Demuxer layer</H2 +><P +> This section is designed to familiarize a programmer with general demuxer + concepts and how they apply to the xine multimedia library. + </P +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN679" +></A +>Introduction to demuxer theory</H3 +><P +> xine's demuxer layer is responsible for taking apart multimedia files or + streams so that the engine can decode them and present them to the user. + "Demuxer" is short for demultiplexor, which is the opposite of + multiplexing. This refers to the process of combining 2 or more things + into one. Multimedia streams usually, at a minimum, multiplex an audio + stream and a video stream together into one stream. Sometimes, there are + multiple audio streams (e.g., for multiple language tracks). Sometimes, + there is a subtitle data stream multiplexed into the multimedia stream. + </P +><P +> There are many different multimedia formats in existence and there are + varying strategies for demuxing different types of multimedia files. + Formats in the MPEG family, for example, are designed to allow easy + playback from almost any place within the file. Many formats cannot deal + with this circumstance and at least need to be demuxed from the beginning + of the stream and played through to the end. Some formats, such as MPEG and + AVI, have marker information before every chunk in the stream. Other + formats, such as Apple Quicktime, are required to have a master index that + contains all information for taking apart a file. Many game-oriented + multimedia formats are designed strictly for playing from start to finish + without any regard to random seeking within the file. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN683" +></A +>Input considerations</H3 +><P +> A xine demuxer interacts with xine's input layer in order to receive + data. The underlying input plugin might be a file, a network stream, or + a block-oriented disc storage device like a DVD. A file input offers the + most flexibility in being able to read either blocks of data or individual + bytes, and being able to seek freely. Other input plugins may not allow the + demuxer to seek (such as stdin or certain network streams). Some input + plugins only allow the demuxer to read blocks of data and not individual + bytes (such as the CD-DA input plugin). The demuxer needs to check the + capabilities of the underlying input plugin before attempting to seek + around. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN686" +></A +>Seeking Policy</H3 +><P +> If possible, it is desirable that a demuxer can seek randomly through + the stream. This is easier for some file formats and essentially impossible + for other formats. xine's seeking API function allows a seek target to be + specified in terms of stream offset from 0, or time in milliseconds from 0. + Offset-based seeking is useful for seek bars in multimedia applications. + Time-based seeking is useful for specifying, e.g., a 1-minute jump forward + or backward in a stream. + </P +><P +> If a multimedia stream has video, there generally needs to be a way to + identify keyframes in the stream in order to facilitate seeking. Many + game-oriented formats fall over in this area as they carry no keyframe + information aside from the implicit assumption that the first frame is a + keyframe. + </P +><P +> In a stream with video, a seek operation should always jump to a keyframe. + xine Policy: When the seek target is between 2 keyframes, jump to the + earlier keyframe. E.g., if there are keyframes at stream offsets 10000 and + 20000, and the user requests a seek to offset 18000, choose the keyframe + at offset 10000. + </P +><P +> Note that there can be difficulties when the audio and video streams are + not tightly interleaved. In many formats, the audio frames are several + time units ahead of the video frames for the purpose of pre-buffering. + This is a typical scenario in the middle of a stream: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> audio frame @ time 10 + video frame @ time 8 + audio frame @ time 11 + video frame @ time 9 + audio frame @ time 12 + keyframe @ time 10 + audio frame @ time 13</PRE +></TD +></TR +></TABLE +> + If the demuxer seeks to the keyframe @ time 10, the next audio chunk will + have a timestamp of 13, which is well ahead of where the video is. While + the xine engine will eventually recover, it will make playback choppy for + a few seconds after the seek. One strategy for dealing with this situation + is to seek back to the nearest keyframe before the requested seek and then + seek back to find the audio frame with the nearest timestamp before the + keyframe. In this example, that would mean seeking back to [af@time 10]. + Then, demux the chunks in order, but skip the video frames until the next + keyframe is encountered. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN693" +></A +>Writing a xine demuxer</H3 +><P +> A demuxer plugin provides API functions which allow the engine to + initialize demuxing, dispatch data chunks to the engine, seek within the + stream, get the stream length, among other functions. The demuxer API + is declared in <TT +CLASS="FILENAME" +>demuxers/demux.h</TT +>. + </P +><P +> Writing a new xine demuxer is largely a process of using other demuxers as + references and understanding how they interact with the engine. This + section will give a brief overview of each API function. + </P +><P +> A demuxer plugin exports a public function of the form: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void *demux_wc3movie_init_plugin(xine_t *xine, void *data);</PRE +></TD +></TR +></TABLE +> + This function initializes a demuxer plugin class object with 6 + demuxer-specific functions. These functions mainly provide information + that a frontend can use to build user-friendly features. These functions + include: + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_description(demux_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns a plaintext, one-line string describing the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_identifier(demux_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns a shorter identifier describing the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_extensions(demux_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns a string with the file extensions that this demuxer + is known to use. For example, Microsoft .WAV files use "wav". If there are + multiple known extensions, separate each extension with a space. For + example, Apple Quicktime has the extensions "mov qt mp4". + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_mimetypes(demux_class_t *this_gen)</PRE +></TD +></TR +></TABLE +> + This function returns a string with the MIME types that this demuxer is + known to use. Multiple MIME type specifications should be separated with a + semicolon (;). For example, Apple Quicktime uses several MIME types: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> return "video/quicktime: mov,qt: Quicktime animation;" + "video/x-quicktime: mov,qt: Quicktime animation;" + "application/x-quicktimeplayer: qtl: Quicktime list;";</PRE +></TD +></TR +></TABLE +> + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void class_dispose(demux_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function frees the memory used by the demuxer plugin class object. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> demux_plugin_t *open_plugin(demux_class_t *class_gen, xine_stream_t *stream, input_plugin_t *input_gen);</PRE +></TD +></TR +></TABLE +> + This function is invoked by the xine engine to determine if the demuxer is + able to handle a particular multimedia stream. The engine can specify if + the demuxer is supposed to check the stream by content (validate the actual + stream data and see if it is of the expected type), by extension (check the + name of the MRL and see if the file extension is correct), or explicitly + (the engine is passing on a user request to force this demuxer to be used). + </P +><P +> NOTE: In the course of checking the stream by content, care must be taken + not to consume bytes out of a non-seekable stream. If the stream is + non-seekable, use the input plugin's preview buffer facility to get a cache + of the first few bytes. If the stream is seekable, reset the stream before + operating on the data (you do not know where some other demuxer left the + stream positioned). + </P +><P +> If the demuxer can handle the stream, it creates a new demux_plugin_t + structure and initializes the main demuxer functions which are called by + the engine to do the tough demuxing duty. These functions include: + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void demux_send_headers(demux_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function generally reads the headers of the stream, does whatever it + has to do to figure out what audio and video codecs are used in the file, + and asks the xine engine to initialize the correct decoders with the + proper parameters (like width and height for video, sample rate and + channels for audio). + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int demux_send_chunk(demux_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function reads data from the stream and sends it to the appropriate + decoder. This is where the bulk of the demuxing work is performed. Despite + the name, the function is actually free to send as much data as it wants + to, or as much as it can. A good policy is to send an entire chunk of + compressed audio or video data and then return. The chunk is likely large + enough that it will have to be broken up into multiple xine buffers. If + a chunk of audio is 20000 bytes large, and the engine is returning + 4096-byte buffers, send 4 full buffers and 1 partial buffer to the audio + decoder and then return. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int demux_seek(demux_plugin_t *this_gen, off_t start_pos, int start_time);</PRE +></TD +></TR +></TABLE +> + This function is called by the engine to request stream repositioning. + This function should be implemented if possible. See the section on + "Seeking Policy" for more information. A seek operation should reposition + the demuxer's internal accounting variables to be ready to start + dispatching chunks from the new position when the xine engine calls + demux_send_chunk() again. If seeking is not feasible, the function quietly + returns and the demuxer's position is unaffected. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void demux_dispose(demux_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function frees the demux_plugin_t object. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int demux_get_status(demux_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns the current internal status of the demuxer. There + are 2 states: DEMUX_OK, for when the demuxer is demuxing or ready to demux, + and DEMUX_FINISHED, for when the demuxer has reached the end of the stream + or has encountered some sort of error. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int demux_get_stream_length(demux_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns the length (time duration) of the stream in + milliseconds. If the length of the stream cannot be determined, return 0. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> uint32_t demux_get_capabilities(demux_plugin_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns an array of bit flags indicating special features of + the demuxer. See <TT +CLASS="VARNAME" +>DEMUX_CAP_*</TT +> defines. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int demux_get_optional_data(demux_plugin_t *this_gen, void *data, int data_type);</PRE +></TD +></TR +></TABLE +> + This function allows the demuxer to advertise extra information that is + not available through other API functions. See <TT +CLASS="VARNAME" +>DEMUX_OPTIONAL_*</TT +> defines. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN733" +></A +>Buffer types</H3 +><P +> Demuxer must send data to decoders using two fifos names <TT +CLASS="VARNAME" +>video_fifo</TT +> + and <TT +CLASS="VARNAME" +>audio_fifo</TT +>. Both are available at <TT +CLASS="VARNAME" +>stream</TT +> + level. The following code fragment shows how it's done. + </P +><TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> buf_element_t *buf; + + buf = stream->video_fifo->buffer_pool_alloc(stream->video_fifo); + buf->type = BUF_CONTROL_START; + stream->video_fifo->put(stream->video_fifo, buf);</PRE +></TD +></TR +></TABLE +><P +> Buffers must have set the <TT +CLASS="VARNAME" +>type</TT +> field as shown. All buffer types are + defined in <TT +CLASS="FILENAME" +>xine-engine/buffer.h</TT +>. + </P +><P +> The control buffer types are very important and must be sent by all kinds of demuxers. + They tell decoders to start/stop their operations and inform metronom about + discontinuities, either relative or absolute. There is also a reset buffer + type that must be sent when demuxers are seeking as a "warm restart" indication to + the decoders. + </P +><P +> To help finding out buffer types for known codecs, functions from <TT +CLASS="FILENAME" +>buffer_types.c</TT +> + may be used to convert "FOURCC" codes or audio format tags (as used in AVI files) to the xine + byffer type: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> buf->type = fourcc_to_buf_video((void*)this->avi->bih.biCompression);</PRE +></TD +></TR +></TABLE +> + </P +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H2 +CLASS="SECT1" +><A +NAME="AEN747" +></A +>Decoder layer</H2 +><P +> This section is designed to familiarize a programmer with basic audio + and video decoding concepts and how they apply to the xine decoder API. + </P +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN750" +></A +>Audio and video decoders</H3 +><P +> Audio and video data requires an enormous amount of storage. Thus, the + raw data is encoded using a variety of compression techniques which + drastically reduces the amount of space required to transmit and store the + data. Before playback, the compressed data needs to be decoded. + </P +><P +> The process of decoding data is rather straightforward in a computer + science sense: An array of encoded data is fed into a decoder and the + decoder outputs an array of decoded data which is ready to be presented + to the user (either displayed on the screen or played through the + speakers). + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN754" +></A +>Video output formats</H3 +><P +> Raw video data comes in a variety of formats, most commonly in RGB and + YUV. xine's output layer currently only accepts data in YV12 format (a.k.a. + YUV 4:2:0 planar) or YUY2 format (a.k.a. YUV 4:2:2 packed). If the output + format is a RGB space, the data must be converted to an acceptable YUV + format before being dispatched to the video output unit. xine has a number + of support functions to facilitate converting RGB to YUV. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN757" +></A +>Audio output formats</H3 +><P +> Raw audio data equates to uncompressed PCM audio. xine's audio output + modules expect 8-bit PCM data to be unsigned and 16-bit PCM data to be + signed and in little endian format. When there is more than one channel, + the channel data is interleaved. For example, stereo data is interleaved + as left sample, right sample: LRLRLRLR. If there are 4 or 6 channels, the + same interleaving applies: 123456123456. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN760" +></A +>Writing a xine decoder</H3 +><P +> Writing a new xine decoder for an audio or video format entails + accumulating a buffer of encoded data, performing the necessary operations + for decoding and then passing it on the appropriate output module. The + best reference for understanding the decoder API is the various decoding + modules available. In particular, xine has example video and audio + decoders named <TT +CLASS="FILENAME" +>src/libxinevdec/foovideo.c</TT +> and + <TT +CLASS="FILENAME" +>src/libxineadec/fooaudio.c</TT +>, respectively. + </P +><P +> This section will give a brief overview of each API function. + The decoder API is declared in <TT +CLASS="FILENAME" +>src/xine-engine/video_decoder.h</TT +> + and <TT +CLASS="FILENAME" +>src/xine-engine/audio_decoder.h</TT +>. + </P +><P +> A decoder plugin must, like every plugin, export a public array of + plugin_info_t types. The array usually has 2 entries: The first contains + the plugin information regarding the decoder and the second entry is + a terminating NULL entry. However, there may be more entries. + Each entry contains 6 fields: + <P +></P +><UL +><LI +><P +> <TT +CLASS="VARNAME" +>plugin type</TT +>: Either PLUGIN_VIDEO_DECODER or PLUGIN_AUDIO_DECODER. + </P +></LI +><LI +><P +> <TT +CLASS="VARNAME" +>API</TT +>: The plugin API revision that this plugin adheres to. + </P +></LI +><LI +><P +> <TT +CLASS="VARNAME" +>name</TT +>: A character string that identifies the plugin. + </P +></LI +><LI +><P +> <TT +CLASS="VARNAME" +>version</TT +>: #define'd as XINE_VERSION_CODE. + </P +></LI +><LI +><P +> <TT +CLASS="VARNAME" +>supported types</TT +>: A structure that defines the buffer types that this plugin can handle. + </P +></LI +><LI +><P +> <TT +CLASS="VARNAME" +>init function</TT +>: The function that the xine engine calls in order to initialize this decoder plugin. + </P +></LI +></UL +> + The supported types field is a decoder_info_t structure. This struct + combines a list of buffer types that the plugin can handle, along with + a relative default priority. The priority allows xine to have multiple + plugins that can handle one data type and the plugin with the highest + priority takes precedence. The code defines the default priority, which + can be overriden by the user. + The list of buffer types is an array of uint32_t types from the list of + buffer types defined in <TT +CLASS="FILENAME" +>src/xine-engine/buffer.h</TT +>. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void *init_plugin(xine_t *xine, void *data);</PRE +></TD +></TR +></TABLE +> + This function allocates a plugin class and initializes a set of functions + for the xine engine to invoke. These functions include: + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_identifier(video_decoder_class_t *this);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_identifier(audio_decoder_class_t *this);</PRE +></TD +></TR +></TABLE +> + This function returns a brief character string identifying the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_description(video_decoder_class_t *this);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_description(audio_decoder_class_t *this);</PRE +></TD +></TR +></TABLE +> + This function returns a slightly longer description of the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose_class(video_decoder_class_t *this);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose_class(audio_decoder_class_t *this);</PRE +></TD +></TR +></TABLE +> + This function frees the resources allocated by the plugin class. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> video_decoder_t *open_plugin(video_decoder_class_t *class_gen, xine_stream_t *stream);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> audio_decoder_t *open_plugin(audio_decoder_class_t *class_gen, xine_stream_t *stream);</PRE +></TD +></TR +></TABLE +> + This function initializes the decoder plugin's private state. It also + initializes and returns either an audio_decoder_t or a video_decoder_t for + the engine. The decoder_t contains a number of functions that the plugin + invokes to handle data decoding. These functions include: + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void decode_data(video_decoder_t *this_gen, buf_element_t *buf);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void decode_data(audio_decoder_t *this_gen, buf_element_t *buf);</PRE +></TD +></TR +></TABLE +> + This function performs the bulk of the decoding work. The xine engine + delivers buffers (xine_buffer_t data types) to this function and it is up + to this function to assemble the parts of the buffer, decode the data, and + send the decoded data to the proper output unit. + </P +><P +> A buffer has a <TT +CLASS="VARNAME" +>decoder_flags</TT +> field which can have + a number of flags set. The first buffer that a decoder receives ought + to have the BUF_FLAG_HEADER flag set. This indicates that the buffer + content contains the essential setup information for decoding + (width, height, etc. for video; sample rate, channels, etc. for audio). + </P +><P +> If the BUF_FLAG_HEADER flag is not set, the content of the buffer should + be accumulated in a private buffer until a buffer with a + BUF_FLAG_FRAME_END flag is set. This indicates that the entire chunk has + been transmitted to the decoder and is ready to be decoded. Fetch either + an empty video frame or audio buffer from the appropriate output unit. Perform + the appropriate decoding operations and set the pts for the output buffer + (and the duration, a.k.a. video_step, for video). Dispatch the decoded + data to the output and reset the internal buffer accumulation accounting. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void flush(video_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void flush(audio_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function is called when either the xine engine flushes the stream, e.g., + after a seek operation or when decoding runs too slow and frames arrive in + the output loops fast enough. Decoders should release everything they have + already decoded, drop the rest and wait for new input. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void reset(video_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void reset(audio_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function is called when the xine engine resets the stream. + Decoders should get ready to receive data that has nothing to do + with the one it worked on up to now. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void discontinuity(video_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void discontinuity(audio_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function is called when the xine engine encounters a pts + discontinuity. Decoders should forget all timestamping information + they might have accumulated from the stream to not confuse metronom. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose(video_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose(audio_decoder_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function frees the resources used by the decoder plugin. + </P +></DIV +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN821" +></A +>SPU decoder</H3 +><P +> A lot written above also applies for subpicture unit (SPU) decoders. The + SPU decoder API is declared in <TT +CLASS="FILENAME" +>src/xine-engine/spu_decoder.h</TT +>. + Details on the data, SPU decoders are expected to output, see the section on + <A +HREF="#OSD" +>overlays and OSD</A +>. + </P +><P +> However, there are some differences to consider. At first, unlike audio and + video, subtitles do not form a continuous stream. The decoder will therefore + only be called once in a while. The metronom call for timestamping, + which for audio and video is done by the engine, has to be done manually for SPU: + <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> vpts = metronom->got_spu_packet(metronom, buf->pts);</PRE +></TD +></TR +></TABLE +> + </P +><P +> There are also two functions in the SPU decoder API, which have not been discussed above: + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int get_interact_info(spu_decoder_t *this_gen, void *data);</PRE +></TD +></TR +></TABLE +> + Since SPUs are sometimes (on DVDs for example) used for user interaction like menu + highlights, this function can be called to get <TT +CLASS="VARNAME" +>data</TT +> filled with + the current interaction information. The caller and the decoder have to agree on + what this is exactly. With DVDs, you can get a copy of the current NAV packet here. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void set_button(spu_decoder_t *this_gen, int32_t button, int32_t mode);</PRE +></TD +></TR +></TABLE +> + Also for interaction, you can ask the decoder here to change the + current highlighting. + </P +></DIV +></DIV +></DIV +><DIV +CLASS="CHAPTER" +><HR><H1 +><A +NAME="OUTPUT" +></A +>Chapter 6. xine's output layer</H1 +><DIV +CLASS="SECT1" +><H2 +CLASS="SECT1" +><A +NAME="AEN836" +></A +>Video output</H2 +><P +> In order to allow for device-dependant acceleration features, xine + calls upon the video output plugin for more than just displaying + images. The tasks performed by the video plugins are: + <P +></P +><UL +><LI +><P +> Allocation of <SPAN +CLASS="TYPE" +>vo_frame_t</SPAN +> structures and their + subsequent destruction. + </P +></LI +><LI +><P +> Allocation of memory for use by one frame (this is to allow + for the ability of some video output plugins to map frames directly + into video-card memory hence removing the need for the frame to + be copied across the PCI/AGP bus at display time). + </P +></LI +><LI +><P +> Most important, the ability to render/copy a given + frame to the output device. + </P +></LI +><LI +><P +> Optionally the copying of the frame from a file dependant + colour-space and depth into the frame structure. This is to allow for + on-the fly colour-space conversion and scaling if required (e.g. the XShm + ouput plugin uses this mechanism). + </P +></LI +></UL +> + </P +><P +> Although these extra responsibilities add great complexity to your + plugin it should be noted that they allow plugins to take full advantage + of any special hardware-acceleration without sacrificing flexibility. + </P +><DIV +CLASS="SECT2" +><HR><H3 +CLASS="SECT2" +><A +NAME="AEN850" +></A +>Writing a xine video out plugin</H3 +><P +> The video out plugin API is declared in <TT +CLASS="FILENAME" +>src/xine-engine/video_out.h</TT +> + The plugin info of video out plugins contains the visual type, priority, + and the init_class function of the plugin. + </P +><P +> The <TT +CLASS="VARNAME" +>visual_type</TT +> field is used by xine to + determine if the GUI used by the client is supported by the plugin + (e.g. X11 output plugins require the GUI to be running under the + X Windowing system) and also to determine the type of information passed to the + <TT +CLASS="FUNCTION" +>open_plugin()</TT +> function as its <TT +CLASS="VARNAME" +>visual</TT +> parameter. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_description(video_driver_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns a plaintext, one-line string describing the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> char *get_identifier(video_driver_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function returns a shorter identifier describing the plugin. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose(video_driver_class_t *this_gen);</PRE +></TD +></TR +></TABLE +> + This function frees the memory used by the video out plugin class object. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> vo_driver_t *get_instance(video_driver_class_t *class_gen, const void *visual);</PRE +></TD +></TR +></TABLE +> + Returns an instance of the plugin. + The <TT +CLASS="VARNAME" +>visual</TT +> is a pointer to a visual-dependant + structure/variable. For example, if you had previously claimed your + plugin was of the VISUAL_TYPE_X11 type, this would be a pointer + to a <SPAN +CLASS="TYPE" +>x11_visual_t</SPAN +>, which amongst other things hold the + <SPAN +CLASS="TYPE" +>Display</SPAN +> variable associated with the + X-server xine should display to. See plugin source-code for other + VISUAL_TYPE_* constants and associated structures. Note that this + field is provided by the client application and so if you wish to add another visual + type you will either need to extend an existing client or write a new + one. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> uint32_t get_capabilities(vo_driver_t *this_gen);</PRE +></TD +></TR +></TABLE +> + Returns a bit mask describing the output plugin's capabilities. + You may logically OR the <TT +CLASS="VARNAME" +>VO_CAP_*</TT +> constants together to get + a suitable bit-mask (via the '|' operator). + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int get_property(vo_driver_t *self, int property); + int set_property(vo_driver_t *self, int property, int value); + void get_property_min_max(vo_driver_t *self, int property, int *min, int *max);</PRE +></TD +></TR +></TABLE +> + Handle the getting, setting of properties and define their bounds. + Valid property IDs can be found in the <TT +CLASS="FILENAME" +>video_out.h</TT +> + header file. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int gui_data_exchange(vo_driver_t *self, int data_type, void *data);</PRE +></TD +></TR +></TABLE +> + Accepts various forms of data from the UI (e.g. the mouse has moved or the + window has been hidden). Look at existing plugins for examples of data + exchanges from various UIs. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> vo_frame_t *alloc_frame(vo_driver_t *self);</PRE +></TD +></TR +></TABLE +> + Returns a pointer to a xine video frame. + Typically the video plugin will add private fields to the end of the + <SPAN +CLASS="TYPE" +>vo_frame_t</SPAN +> structure which are used for internal purposes by the plugin. + </P +><P +> The function pointers within the frame structure provide a mechanism for the + driver to retain full control of how the frames are managed and rendered to. If + the VO_CAP_COPIES_IMAGE flag was set in the plugins capabilities then the + copy field is required and will be called sequentially for each 16-pixel high + strip in the image. The plugin may then decide, based on the frame's format, how + this is copied into the frame. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void update_frame_format(vo_driver_t *self, vo_frame_t *img, uint32_t width, uint32_t height, double ratio, int format, int flags);</PRE +></TD +></TR +></TABLE +> + This function will be called each time the colour-depth/space or size of a frame changes. + Typically this function would allocate sufficient memory for the frame, assign the pointers + to the individual planes of the frame to the <TT +CLASS="VARNAME" +>base</TT +> field of the + frame and perform any driver-specific changes. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void display_frame(vo_driver_t *self, vo_frame_t *vo_img);</PRE +></TD +></TR +></TABLE +> + Renders a given frame to the output device. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void overlay_begin(vo_driver_t *self, vo_frame_t *vo_img, int changed); + void overlay_blend(vo_driver_t *self, vo_frame_t *vo_img, vo_overlay_t *overlay); + void overlay_end(vo_driver_t *self, vo_frame_t *vo_img);</PRE +></TD +></TR +></TABLE +> + These are used to blend overlays on frames. <TT +CLASS="FUNCTION" +>overlay_begin()</TT +> is called, + when the overlay appears for the first time, <TT +CLASS="FUNCTION" +>overlay_blend()</TT +> is then + called for every subsequent frame and <TT +CLASS="FUNCTION" +>overlay_end()</TT +> is called, when + the overlay should disappear again. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> int redraw_needed(vo_driver_t *self);</PRE +></TD +></TR +></TABLE +> + Queries the driver, if the current frame needs to be drawn again. + </P +><P +> <TABLE +BORDER="0" +BGCOLOR="#E0E0E0" +WIDTH="100%" +><TR +><TD +><PRE +CLASS="PROGRAMLISTING" +> void dispose(vo_driver_t *self);</PRE +></TD +></TR +></TABLE +> + Releases all resources and frees the plugin. + </P +></DIV +></DIV +></DIV +></DIV +></BODY +></HTML +>
\ No newline at end of file |