summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/hackersguide/hackersguide.sgml735
1 files changed, 582 insertions, 153 deletions
diff --git a/doc/hackersguide/hackersguide.sgml b/doc/hackersguide/hackersguide.sgml
index 90e45e561..f27ae3da6 100644
--- a/doc/hackersguide/hackersguide.sgml
+++ b/doc/hackersguide/hackersguide.sgml
@@ -567,17 +567,104 @@
renderer->render_text(osd, x1, y1, text, OSD_TEXT2 );
</programlisting>
</sect3>
+ <sect3>
+ <title>OSD text and palette FAQ</title>
+ <para>
+ Q: What is the format of the color palette entries?
+ </para>
+ <para>
+ A: It's the same as used by overlay blending code (YUV).
+ </para>
+ <para>
+ Q: What is the relation between a text palette and a palette
+ I set with xine_osd_set_palette?
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ Q: Can I render text with colors in my own palette?
+ </para>
+ <para>
+ A: Sure. just pass the color_base to osd_render_text()
+ </para>
+ <para>
+ Q: Has a text palette change effects on already drawed text?
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ Q: What about the shadows of osd-objects? Can I turn them off
+ or are they hardcoded?
+ </para>
+ <para>
+ 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.
+ </para>
+ </sect3>
</sect2>
</sect1>
<sect1>
<title>Plugin architecture</title>
<para>
- xine plugins are built as shared libraries that export at least one
- public function named <function>init_type_plugin</function>, where
- <function>type</function> reflects the function of the plugin (video,
- audio, etc). This function returns a pointer to freshly allocated (typically
- via <function>malloc()</function>) structure containing mainly
- function pointers; these are the "methods" of the plugin.
+ xine plugins are built as shared libraries that export a plugin info
+ record named <varname>xine_plugin_info</varname>. This is used by plugin
+ loader to identify the "virtual" plugin types inside the shared library.
+ </para>
+ <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, &amp;dec_info_audio, init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+ </programlisting>
+ <para>
+ <varname>xine_plugin_info</varname> can contain any number of plugins
+ and must be terminated with a <type>PLUGIN_NONE</type>. Available plugin
+ types are:
+ </para>
+ <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
+ </programlisting>
+ <para>
+ Every entry in <varname>xine_plugin_info</varname> has a class initialization
+ function. This function returns a pointer to freshly allocated (typically
+ via <function>malloc()</function>) structure containing mainly function
+ pointers; these are the "methods" of the plugin class.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
If you think this is pretty much an object-oriented aproach,
then you're right.
</para>
@@ -601,8 +688,8 @@
<programlisting>
typedef struct {
/* public fields "inherited" from demux.h */
-
demux_plugin_t demux_plugin;
+
xine_t *xine;
int count;
} demux_foo_t;
@@ -622,43 +709,22 @@
Use an existing demuxer plugin, e.g. demux_mpeg_block
as an example.
</para>
- <para>
- Demuxers need to start/stop their own thread for performance reasons
- (input plugins may block and if the demuxer runs in a seperate
- thread other xine modules can work during blocking time).
- </para>
- <para>
- Since xine 0.9.9 some important responsibilities were assigned to demuxers, like
- providing precise information about pts discontinuities. Besides that it's up
- to demuxer to tag buffer packets with the specified codec to use and provide control
- buffers with start and stop information for the rest of the engine.
- </para>
<sect2>
<title>Demuxer API</title>
<para>
You need to implement all the functions given in demux.h:
<programlisting>
-struct demux_plugin_s
-{
- /*
- * plugin interface version, lower versions _may_ be supported
- */
- int interface_version;
+struct demux_plugin_s {
/*
- * ask demuxer to open the given stream (input-plugin)
- * using the content-detection method specified in "stage"
- *
- * return values:
- * DEMUX_CAN_HANDLE on success
- * DEMUX_CANNOT_HANDLE on failure
+ * send headers, followed by BUF_CONTROL_HEADERS_DONE down the
+ * fifos, then return. do not start demux thread (yet)
*/
- int (*open) (demux_plugin_t *this, input_plugin_t *ip,
- int stage);
+ void (*send_headers) (demux_plugin_t *this);
/*
- * start demux thread
+ * ask demux to seek
*
* for seekable streams, a start position can be specified
*
@@ -667,82 +733,89 @@ struct demux_plugin_s
*
* if both parameters are !=0 start_pos will be used
* for non-seekable streams both values will be ignored
+ *
+ * returns the demux status (like get_status, but immediately after
+ * starting the demuxer)
*/
- void (*start) (demux_plugin_t *this, fifo_buffer_t *video_fifo,
- fifo_buffer_t *audio_fifo,
- off_t start_pos, int start_time);
-
+ int (*seek) (demux_plugin_t *this,
+ off_t start_pos, int start_time);
+
/*
- * ask running demux thread to seek
- *
- * for seekable streams, a start position can be specified
+ * send a chunk of data down to decoder fifos
*
- * start_pos : position in input source
- * start_time : position measured in seconds from stream start
+ * the meaning of "chunk" is specific to every demux, usually
+ * it involves parsing one unit of data from stream.
*
- * if both parameters are !=0 start_pos will be used
- * for non-seekable streams both values will be ignored
+ * this function will be called from demux loop and should return
+ * the demux current status
*/
- void (*seek) (demux_plugin_t *this,
- off_t start_pos, int start_time);
-
+ int (*send_chunk) (demux_plugin_t *this);
+
/*
- * stop & kill demux thread, free resources associated with current
- * input stream
+ * free resources
*/
- void (*stop) (demux_plugin_t *this) ;
+ void (*dispose) (demux_plugin_t *this) ;
/*
- * close demuxer, free all resources
+ * returns DEMUX_OK or DEMUX_FINISHED
*/
- void (*close) (demux_plugin_t *this) ;
+ int (*get_status) (demux_plugin_t *this) ;
/*
- * returns DEMUX_OK or DEMUX_FINISHED
+ * gets stream length in miliseconds (might be estimated)
+ * may return 0 for non-seekable streams
*/
- int (*get_status) (demux_plugin_t *this) ;
+ int (*get_stream_length) (demux_plugin_t *this);
/*
- * return human readable identifier for this plugin
+ * get audio/video frames
+ *
+ * experimental, function pointers can be NULL for now.
*/
- char* (*get_identifier) (void);
-
- /*
- * return MIME types supported for this plugin
+ int (*get_video_frame) (demux_plugin_t *this,
+ int timestamp, /* msec */
+ int *width, int *height,
+ int *ratio_code,
+ int *duration, /* msec */
+ int *format,
+ uint8_t *img) ;
+
+ /* called by video_out for every frame it receives */
+ void (*got_video_frame_cb) (demux_plugin_t *this,
+ vo_frame_t *frame);
+
+ /*
+ * return capabilities of demuxed stream
*/
- char* (*get_mimetypes) (void);
+ uint32_t (*get_capabilities) (demux_plugin_t *this);
+
/*
- * estimate stream length in seconds
- * may return 0 for non-seekable streams
+ * request optional data from input plugin.
+ */
+ int (*get_optional_data) (demux_plugin_t *this, void *data, int data_type);
+
+ /*
+ * "backwards" link to plugin class
*/
- int (*get_stream_length) (demux_plugin_t *this);
+ demux_class_t *demux_class;
} ;
</programlisting>
</para>
- <para>
- Demuxer plugins export only one function:
- <programlisting>
- demux_plugin_t *init_demux_plugin (int iface_version, xine_t *xine);
- </programlisting>
- this is called on startup when the demuxer plugin is loaded.
- The funtion should malloc() a demux_plugin_t* pointer,
- fill in the function pointers and return the demux_plugin_t * pointer.
- </para>
</sect2>
<sect2>
<title>Buffer types</title>
<para>
Demuxer must send data to decoders using two fifo of buffers <varname>fifo_video</varname>
- and <varname>audio_fifo</varname>. Both are passed on demuxer start() method. The following
- code fragment shows how it's done.
+ and <varname>audio_fifo</varname>. Both are available at <varname>stream</varname>
+ struct. The following code fragment shows how it's done.
</para>
<programlisting>
buf_element_t *buf;
@@ -924,48 +997,37 @@ struct demux_plugin_s
be purely block based.
</para>
<para>
- The input plugin should export at least the following function:
- </para>
- <programlisting>
- input_plugin_t *init_input_plugin (int iface, xine_t *xine)
- </programlisting>
- <para>
- The <varname>iface</varname> parameter is the input plugin interface
- xine is requesting (as of writing this is '5'). The
- <varname>xine</varname> parameter is a pointer to the global
- <type>xine_t</type> for this session.
- </para>
- <para>
- The function should return a pointer to an allocated
- <type>input_plugin_t</type> (defined in
- <filename>xine/input_plugin.h</filename>) which has the following
+ The plugin struct <type>input_plugin_t</type> (defined in
+ <filename>xine/input_plugin.h</filename>) has the following
definition:
</para>
<programlisting>
-struct input_plugin_s
-{
+struct input_plugin_s {
/*
- * plugin interface version, lower versions _may_ be supported
- */
- int interface_version;
-
- /*
- * return capabilities of input source
+ * return capabilities of the current playable entity. See
+ * get_current_pos below for a description of a "playable entity"
+ * Capabilities a created by "OR"ing a mask of constants listed
+ * below which start "INPUT_CAP".
+ *
+ * depending on the values set, some of the functions below
+ * will or will not get called or should (not) be able to
+ * do certain tasks.
+ *
+ * for example if INPUT_CAP_SEEKABLE is set,
+ * the seek() function is expected to work fully at any time.
+ * however, if the flag is not set, the seek() function should
+ * make a best-effort attempt to seek, e.g. at least
+ * relative forward seeking should work.
*/
-
uint32_t (*get_capabilities) (input_plugin_t *this);
/*
- * open input MRL - return 1 if succ
- */
- int (*open) (input_plugin_t *this, char *mrl);
-
- /*
* read nlen bytes, return number of bytes read
*/
off_t (*read) (input_plugin_t *this, char *buf, off_t nlen);
+
/*
* read one block, return newly allocated block (or NULL on failure)
* for blocked input sources len must be == blocksize
@@ -973,6 +1035,7 @@ struct input_plugin_s
*/
buf_element_t *(*read_block)(input_plugin_t *this, fifo_buffer_t *fifo, off_t len);
+
/*
* seek position, return new position
*
@@ -980,86 +1043,76 @@ struct input_plugin_s
*/
off_t (*seek) (input_plugin_t *this, off_t offset, int origin);
+
/*
* get current position in stream.
*
*/
off_t (*get_current_pos) (input_plugin_t *this);
+
/*
- * return length of input (-1 => unlimited, e.g. stream)
+ * return number of bytes in the next playable entity or -1 if the
+ * input is unlimited, as would be the case in a network stream.
+ *
+ * A "playable entity" tends to be the entities listed in a playback
+ * list or the units on which playback control generally works on.
+ * It might be the number of bytes in a VCD "segment" or "track" (if
+ * the track has no "entry" subdivisions), or the number of bytes in
+ * a PS (Program Segment or "Chapter") of a DVD. If there are no
+ * subdivisions of the input medium and it is considered one
+ * indivisible entity, it would be the byte count of that entity;
+ * for example, the length in bytes of an MPEG file.
+
+ * This length information is used, for example when in setting the
+ * absolute or relative play position or possibly calculating the
+ * bit rate.
*/
off_t (*get_length) (input_plugin_t *this);
- /*
- * return block size of input source (if supported, 0 otherwise)
- */
- uint32_t (*get_blocksize) (input_plugin_t *this);
/*
- * ls function
- * return value: NULL => filename is a file, **char=> filename is a dir
- */
- mrl_t** (*get_dir) (input_plugin_t *this, char *filename, int *nFiles);
-
- /*
- * eject/load the media (if it's possible)
+ * return block size in bytes of next complete playable entity (if
+ * supported, 0 otherwise). See the description above under
+ * get_length for a description of a "complete playable entity".
+ *
+ * this block size is only used for mpeg streams stored on
+ * a block oriented storage media, e.g. DVDs and VCDs, to speed
+ * up the demuxing process. only set this (and the INPUT_CAP_BLOCK
+ * flag) if this is the case for your input plugin.
*
- * returns 0 for temporary failures
+ * make this function simply return 0 if unsure.
*/
- int (*eject_media) (input_plugin_t *this);
+
+ uint32_t (*get_blocksize) (input_plugin_t *this);
+
/*
* return current MRL
*/
char * (*get_mrl) (input_plugin_t *this);
- /*
- * stop input source
- */
- void (*stop) (input_plugin_t *this);
/*
- * close input source
+ * request optional data from input plugin.
*/
- void (*close) (input_plugin_t *this);
+ int (*get_optional_data) (input_plugin_t *this, void *data, int data_type);
- /*
- * return human readable (verbose = 1 line) description for this plugin
- */
- char* (*get_description) (input_plugin_t *this);
/*
- * return short, human readable identifier for this plugin
- * this is used for GUI buttons, The identifier must have max. 4 characters
- * characters (max. 5 including terminating \0)
+ * close stream, free instance resources
*/
- char* (*get_identifier) (input_plugin_t *this);
+ void (*dispose) (input_plugin_t *this);
/*
- * generate autoplay list
- * return value: list of MRLs
+ * "backward" link to input plugin class struct
*/
- char** (*get_autoplay_list) (input_plugin_t *this, int *nFiles);
- /*
- * request optional data from input plugin.
- */
- int (*get_optional_data) (input_plugin_t *this, void *data, int data_type);
+ input_class_t *input_class;
- /*
- * check if it is possible/valid to directly branch to this MRL
- * optional: may be NULL
- */
-
- int (*is_branch_possible) (input_plugin_t *this, char *next_mrl);
};
</programlisting>
<para>
- Typically the <function>init_input_plugin</function> function will
- initialise all these parameters.
- </para>
- <para>
The <varname>get_capabilities</varname> parameter points to a function
which returns a bit mask describing the input device's capabilities.
You may logically OR the following constants together to get
@@ -1323,8 +1376,21 @@ struct vo_driver_s {
/* display a given frame */
void (*display_frame) (vo_driver_t *this, vo_frame_t *vo_img);
- /* overlay functions */
+ /* overlay_begin and overlay_end are used by drivers suporting
+ * persistent overlays. they can be optimized to update only when
+ * overlay image has changed.
+ *
+ * sequence of operation (pseudo-code):
+ * overlay_begin(this,img,true_if_something_changed_since_last_blend );
+ * while(visible_overlays)
+ * overlay_blend(this,img,overlay[i]);
+ * overlay_end(this,img);
+ *
+ * any function pointer from this group may be set to NULL.
+ */
+ void (*overlay_begin) (vo_driver_t *this, vo_frame_t *vo_img, int changed);
void (*overlay_blend) (vo_driver_t *this, vo_frame_t *vo_img, vo_overlay_t *overlay);
+ void (*overlay_end) (vo_driver_t *this, vo_frame_t *vo_img);
/*
* these can be used by the gui directly:
@@ -1346,16 +1412,21 @@ struct vo_driver_s {
int (*gui_data_exchange) (vo_driver_t *this, int data_type,
void *data);
- void (*exit) (vo_driver_t *this);
-
/* check if a redraw is needed (due to resize)
* this is only used for still frames, normal video playback
* must call that inside display_frame() function.
*/
int (*redraw_needed) (vo_driver_t *this);
+ /*
+ * free all resources, close driver
+ */
+
+ void (*dispose) (vo_driver_t *this);
+
+ void *node; /* needed by plugin_loader */
};
-</programlisting>
+ </programlisting>
<para>
The <varname>get_info</varname> field is simply a pointer to the
<function>get_video_out_plugin_info()</function> function described
@@ -1506,6 +1577,365 @@ struct vo_frame_s {
<sect1>
<title>Writing a new frontend to xine</title>
<para></para>
+ <sect2>
+ <title>Source code of a simple X11 frontend</title>
+<programlisting>
+/*
+** Copyright (C) 2003 Daniel Caujolle-Bert &lt;segfault@club-internet.fr&gt;
+**
+** 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.
+**
+*/
+#include &lt;stdio.h&gt;
+#include &lt;string.h&gt;
+#include &lt;math.h&gt;
+
+#include &lt;X11/X.h&gt;
+#include &lt;X11/Xlib.h&gt;
+#include &lt;X11/Xutil.h&gt;
+#include &lt;X11/keysym.h&gt;
+#include &lt;X11/Xatom.h&gt;
+#include &lt;X11/Xutil.h&gt;
+#include &lt;X11/extensions/XShm.h&gt;
+
+#include &lt;xine.h&gt;
+#include &lt;xine/xineutils.h&gt;
+
+#ifndef XShmGetEventBase
+extern int XShmGetEventBase(Display *);
+#endif
+
+#define MWM_HINTS_DECORATIONS (1L &lt;&lt; 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 completion_event;
+static int xpos, ypos, width, height, fullscreen;
+static double pixel_aspect;
+
+static int running = 0;
+
+
+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;
+}
+
+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-&gt;type) {
+ case XINE_EVENT_UI_PLAYBACK_FINISHED:
+ running = 0;
+ break;
+
+ case XINE_EVENT_PROGRESS:
+ {
+ xine_progress_data_t *pevent = (xine_progress_data_t *) event-&gt;data;
+
+ printf("%s [%d%%]\n", pevent-&gt;description, pevent-&gt;percent);
+ }
+ break;
+
+ }
+}
+
+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;
+ int i;
+ Atom XA_NO_BORDER;
+ MWMHints mwmhints;
+
+ if(argc &lt;= 1) {
+ printf("specify an mrl\n");
+ return 1;
+ }
+
+ for(i = 1; i &lt; argc; i++) {
+ if(!strcmp(argv[i], "-vo")) {
+ vo_driver = argv[++i];
+ }
+ else if(!strcmp(argv[i], "-ao")) {
+ ao_driver = argv[++i];
+ }
+ else
+ mrl = argv[i];
+ }
+
+ mrl = argv[1];
+ printf("mrl: '%s'\n", mrl);
+
+ if(!XInitThreads()) {
+ printf("XInitThreads() failed\n");
+ return 1;
+ }
+
+ xine = xine_new();
+ sprintf(configfile, "%s%s", xine_get_homedir(), "/.xine/config2");
+ xine_config_load(xine, configfile);
+ xine_init(xine);
+
+ display = XOpenDisplay(NULL);
+ screen = XDefaultScreen(display);
+ xpos = 0;
+ ypos = 0;
+ width = 320;
+ height = 200;
+
+ 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 *) &amp;mwmhints,
+ PROP_MWM_HINTS_ELEMENTS);
+
+ if(XShmQueryExtension(display) == True)
+ completion_event = XShmGetEventBase(display) + ShmCompletion;
+ else
+ completion_event = -1;
+
+ 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);
+
+ 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;
+
+ if(fabs(pixel_aspect - 1.0) &lt; 0.01)
+ pixel_aspect = 1.0;
+
+ vo_port = xine_open_video_driver(xine, vo_driver, XINE_VISUAL_TYPE_X11, (void *) &amp;vis);
+
+ ao_port = xine_open_audio_driver(xine , ao_driver, NULL);
+
+ stream = xine_stream_new(xine, ao_port, vo_port);
+ event_queue = xine_event_new_queue(stream);
+ xine_event_create_listener_thread(event_queue, event_listener, NULL);
+
+ xine_gui_send_vo_data(stream, XINE_GUI_SEND_DRAWABLE_CHANGED, (void *) window[fullscreen]);
+ xine_gui_send_vo_data(stream, XINE_GUI_SEND_VIDEOWIN_VISIBLE, (void *) 1);
+
+ 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, &amp;xevent);
+
+ switch(xevent.type) {
+
+ case KeyPress:
+ {
+ XKeyEvent kevent;
+ KeySym ksym;
+ char kbuf[256];
+ int len;
+
+ kevent = xevent.xkey;
+
+ XLockDisplay(display);
+ len = XLookupString(&amp;kevent, kbuf, sizeof(kbuf), &amp;ksym, NULL);
+ XUnlockDisplay(display);
+
+ switch (ksym) {
+
+ case XK_q:
+ case XK_Q:
+ running = 0;
+ break;
+
+ case XK_f:
+ case XK_F:
+ {
+ 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, &amp;xpos, &amp;ypos, &amp;tmp_win);
+ XUnlockDisplay(display);
+
+ xine_gui_send_vo_data(stream, XINE_GUI_SEND_DRAWABLE_CHANGED,
+ (void*) window[fullscreen]);
+ }
+ break;
+
+ case XK_Up:
+ xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME,
+ (xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) + 1));
+ break;
+
+ case XK_Down:
+ xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME,
+ (xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) - 1));
+ break;
+
+ case XK_plus:
+ xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL,
+ (xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) + 1));
+ break;
+
+ case XK_minus:
+ xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL,
+ (xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) - 1));
+ break;
+
+ case XK_space:
+ 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:
+ if(xevent.xexpose.count != 0)
+ break;
+ xine_gui_send_vo_data(stream, XINE_GUI_SEND_EXPOSE_EVENT, &amp;xevent);
+ break;
+
+ case ConfigureNotify:
+ {
+ XConfigureEvent *cev = (XConfigureEvent *) &amp;xevent;
+ Window tmp_win;
+
+ width = cev-&gt;width;
+ height = cev-&gt;height;
+
+ if((cev-&gt;x == 0) &amp;&amp; (cev-&gt;y == 0)) {
+ XLockDisplay(display);
+ XTranslateCoordinates(display, cev-&gt;window,
+ DefaultRootWindow(cev-&gt;display),
+ 0, 0, &amp;xpos, &amp;ypos, &amp;tmp_win);
+ XUnlockDisplay(display);
+ }
+ else {
+ xpos = cev-&gt;x;
+ ypos = cev-&gt;y;
+ }
+ }
+ break;
+
+ }
+
+ if(xevent.type == completion_event)
+ xine_gui_send_vo_data(stream, XINE_GUI_SEND_COMPLETION_EVENT, &amp;xevent);
+ }
+
+ 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 1;
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -Wall -O2 `xine-config --cflags` `xine-config --libs` -lX11 -lm -o xinimin xinimin.c"
+ * End:
+ */
+</programlisting>
+ </sect2>
</sect1>
</chapter>
@@ -1584,4 +2014,3 @@ struct vo_frame_s {
</book>
-