Forward port of branches/0.8.1-jpsaman-thedj revision 12070. The OSD menu subsystem...
authorJean-Paul Saman <jpsaman@videolan.org>
Wed, 10 Aug 2005 22:08:50 +0000 (22:08 +0000)
committerJean-Paul Saman <jpsaman@videolan.org>
Wed, 10 Aug 2005 22:08:50 +0000 (22:08 +0000)
To stream use a commandline similar to this:

./vlc dvdsimple:///dev/dvd --sub-filter=osdmenu --osdmenu-file=share/osdmenu/default.cfg --extraintf rc --sout '#transcode{sfilter=osdmenu}:std{mux=ts,access=udp,url=127.0.0.1:1234}' -vvvv

For local playback a commandline like this is needed:

./vlc dvdsimple:///dev/dvd --sub-filter=osdmenu --osdmenu-file=share/osdmenu/default.cfg -vvvv

Have fun with the basic functionality - jpsaman.

19 files changed:
Makefile.am
configure.ac
include/stream_output.h
include/vlc_common.h
include/vlc_objects.h
include/vlc_osd.h [new file with mode: 0644]
include/vlc_symbols.h
modules/LIST
modules/codec/dvbsub.c
modules/control/rc.c
modules/stream_out/transcode.c
modules/video_filter/Modules.am
modules/video_filter/osdmenu.c [new file with mode: 0644]
src/Makefile.am
src/misc/modules.c
src/misc/objects.c
src/osd/osd.c [new file with mode: 0644]
src/osd/osd_parser.c [new file with mode: 0644]
src/osd/osd_widgets.c [new file with mode: 0644]

index 43f3a97..1d92fc9 100644 (file)
@@ -112,6 +112,7 @@ HEADERS_include = \
        include/vlc_messages.h \
        include/vlc_meta.h \
        include/vlc_objects.h \
+       include/vlc_osd.h \
        include/vlc_playlist.h \
        include/vlc_spu.h \
        include/vlc_stream.h \
@@ -382,6 +383,9 @@ SOURCES_libvlc_common = \
        src/stream_output/announce.c \
        src/stream_output/sap.c \
        src/stream_output/acl.c \
+       src/osd/osd.c \
+       src/osd/osd_widgets.c \
+       src/osd/osd_parser.c \
        src/misc/charset.c \
        src/misc/httpd.c \
        src/misc/tls.c \
@@ -813,6 +817,49 @@ package-win32-base:
          cp $$i $(top_builddir)/vlc-${VERSION}/skins/ || true ; \
        done
 
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/dvd"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/dvd/selected"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/dvd/unselect"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/dvd/selection"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/dvd/volume"
+       for i in $(srcdir)/share/osdmenu/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/ || true ; \
+         unix2dos $(srcdir)/vlc-${VERSION}/osdmenu/`basename $$i` ; \
+       done
+       for i in $(srcdir)/share/osdmenu/dvd/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/dvd || true ; \
+       done
+       for i in $(srcdir)/share/osdmenu/dvd/unselect/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/dvd/unselect || true ; \
+       done
+       for i in $(srcdir)/share/osdmenu/dvd/selected/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/dvd/selected/ || true ; \
+       done
+       for i in $(srcdir)/share/osdmenu/dvd/selection/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/dvd/selection/ || true ; \
+       done
+       for i in $(srcdir)/share/osdmenu/dvd/volume/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/dvd/volume/ || true ; \
+       done
+
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/default"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/default/selected"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/default/selection"
+       mkdir -p "$(srcdir)/vlc-${VERSION}/osdmenu/default/volume"
+       for i in $(srcdir)/share/osdmenu/default/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/default || true ; \
+       done
+       for i in $(srcdir)/share/osdmenu/default/selected/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/default/selected/ || true ; \
+       done
+       for i in $(srcdir)/share/osdmenu/default/selection/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/default/selection/ || true ; \
+       done
+       for i in $(srcdir)/share/osdmenu/default/volume/*.*; do \
+         cp $$i $(srcdir)/vlc-${VERSION}/osdmenu/default/volume/ || true ; \
+       done
+
        mkdir -p "$(top_builddir)/vlc-${VERSION}/http/admin"
        mkdir -p "$(top_builddir)/vlc-${VERSION}/http/vlm"
        cp $(srcdir)/share/http/*.html $(top_builddir)/vlc-${VERSION}/http/ ;
index 05a31d6..7de9d8f 100644 (file)
@@ -2689,6 +2689,7 @@ AC_CHECK_HEADERS(png.h, [
   AC_CHECK_LIB(png, png_set_rows, [
     VLC_ADD_LDFLAGS([png],[-lpng -lz])
     VLC_ADD_PLUGINS([png])
+    VLC_ADD_PLUGINS([osdmenu])
     AC_DEFINE(HAVE_LIBPNG, [], [Define if you have the PNG library: libpng])],
     [],[-lz])
   LDFLAGS="${LDFLAGS_save}"
index 4b769c0..26a76b7 100644 (file)
@@ -1,12 +1,13 @@
 /*****************************************************************************
  * stream_output.h : stream output module
  *****************************************************************************
- * Copyright (C) 2002 the VideoLAN team
+ * Copyright (C) 2002-2005 the VideoLAN team
  * $Id$
  *
  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  *          Laurent Aimar <fenrir@via.ecp.fr>
  *          Eric Petit <titer@videolan.org>
+ *          Jean-Paul Saman <jpsaman #_at_# m2x.nl>
  *
  * 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
@@ -206,6 +207,9 @@ struct sout_stream_t
     sout_cfg_t        *p_cfg;
     char              *psz_next;
 
+    /* Subpicture unit */
+    spu_t             *p_spu;
+    
     /* add, remove a stream */
     sout_stream_id_t *(*pf_add)( sout_stream_t *, es_format_t * );
     int               (*pf_del)( sout_stream_t *, sout_stream_id_t * );
index 689df9d..7460b95 100644 (file)
@@ -405,6 +405,13 @@ typedef struct vod_media_t vod_media_t;
 typedef struct opengl_t     opengl_t;
 typedef struct opengl_sys_t opengl_sys_t;
 
+/* osdmenu */
+typedef struct osd_menu_t   osd_menu_t;
+typedef struct osd_state_t  osd_state_t;
+typedef struct osd_event_t  osd_event_t;
+typedef struct osd_button_t osd_button_t;
+typedef struct osd_menu_state_t osd_menu_state_t;
+
 /* VLM */
 typedef struct vlm_t         vlm_t;
 typedef struct vlm_message_t vlm_message_t;
index 8911773..a020a5e 100644 (file)
@@ -59,6 +59,7 @@
 #define VLC_OBJECT_TLS        (-25)
 #define VLC_OBJECT_SD         (-26)
 #define VLC_OBJECT_XML        (-27)
+#define VLC_OBJECT_OSDMENU    (-28)
 
 #define VLC_OBJECT_GENERIC  (-666)
 
diff --git a/include/vlc_osd.h b/include/vlc_osd.h
new file mode 100644 (file)
index 0000000..12a7a7d
--- /dev/null
@@ -0,0 +1,406 @@
+/*****************************************************************************\r
+ * osd.h - OSD menu definitions and function prototypes\r
+ *****************************************************************************\r
+ * Copyright (C) 2004-2005 M2X\r
+ * $Id: osd.h 9451 2004-12-01 01:07:08Z jpsaman $\r
+ *\r
+ * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.\r
+ *****************************************************************************/\r
+\r
+/**\r
+ * \file\r
+ * The OSD menu core creates the OSD menu structure in memory. It parses a \r
+ * configuration file that defines all elements that are part of the menu. The \r
+ * core also handles all actions and menu structure updates on behalf of video \r
+ * subpicture filters.\r
+ *\r
+ * The file modules/video_filters/osdmenu.c implements a subpicture filter that\r
+ * specifies the final information on positioning of the current state image. \r
+ * A subpicture filter is called each time a video picture has to be rendered, it\r
+ * also gives a start and end date to the subpicture. The subpicture can be streamed\r
+ * if used inside a transcoding command. For example:\r
+ *\r
+ *  vlc dvdsimple:///dev/dvd --extraintf rc\r
+ *  --sout='#transcode{osdenc=dvbsub,osdcoded=YUVP,sfilter=osdmenu} \r
+ *  --osdmenu-file share/osdmenu/dvd.cfg\r
+ *\r
+ * Each OSD menu element, called "action", defines a hotkey action. Each action \r
+ * can have several states (unselect, select, pressed). Each state has an image \r
+ * that represents the state visually. The commands "menu right", "menu left", \r
+ * "menu up" and "menu down" are used to navigate through the OSD menu structure.\r
+ * The commands "menu on" or "menu show" and "menu off" or "menu hide" respectively\r
+ * show and hide the OSD menu subpictures.\r
+ *\r
+ * There is one special element called "range". A range is an arbritary range\r
+ * of state images that can be browsed using "menu up" and "menu down" commands\r
+ * on the rc interface. \r
+ *  \r
+ * The OSD menu configuration file uses a very simple syntax and basic parser. \r
+ * A configuration file has the ".cfg". An example is "share/osdmenu/dvd256.cfg".\r
+ */\r
+  \r
+#ifndef _VLC_OSD_H\r
+#define _VLC_OSD_H 1\r
+\r
+#include "vlc_video.h"\r
+\r
+# ifdef __cplusplus\r
+extern "C" {\r
+# endif\r
+\r
+/**\r
+ * The OSD Menu configuration file format.\r
+ *\r
+ * The configuration file syntax is very basic and so is its parser. See the\r
+ * BNF formal representation below:\r
+ *\r
+ * The keywords FILENAME and PATHNAME represent the filename and pathname specification\r
+ * that is valid for the Operating System VLC is compiled for. \r
+ *\r
+ * The hotkey actions that are supported by VLC are documented in the file src/libvlc. The\r
+ * file include/vlc_keys.h defines some hotkey internals.\r
+ *\r
+ * CONFIG_FILE = FILENAME '.cfg'\r
+ * WS = [ ' ' | '\t' ]+\r
+ * OSDMEN_PATH = PATHNAME\r
+ * DIR = 'dir' WS OSDMENU_PATH '\n'\r
+ * STATE = [ 'unselect' | 'select' | 'pressed' ]\r
+ * HOTKEY_ACTION = 'key-' [ 'a' .. 'z', 'A' .. 'Z', '-' ]+\r
+ * \r
+ * ACTION_TYPE        = 'type' 'volume' '\n'\r
+ * ACTION_BLOCK_START = 'action' WS HOTKEY_ACTION WS '('POS','POS')' '\n'\r
+ * ACTION_BLOCK_END   = 'end' '\n'\r
+ * ACTION_STATE       = STATE WS FILENAME '\n'\r
+ * ACTION_RANGE_START = 'range' WS HOTKEY_ACTION WS DEFAULT_INDEX '\n'\r
+ * ACTION_RANGE_END   = 'end' '\n'\r
+ * ACTION_RANGE_STATE = FILENAME '\n'\r
+ *\r
+ * ACTION_BLOCK_RANGE = ACTION_RANGE_START [WS ACTION_RANGE_STATE]+ WS ACTION_RANGE_END\r
+ * ACTION_BLOCK = ACTION_BLOCK_START [WS ACTION_TYPE*] [ [WS ACTION_STATE]+3 | [WS ACTION_BLOCK_RANGE]+1 ] ACTION_BLOCK_END\r
+ * CONFIG_FILE_CONTENTS = DIR [ACTION_BLOCK]+\r
+ *\r
+ */ \r
+\r
+/**\r
+ * OSD menu button states\r
+ *\r
+ * Every button has three states, either it is unselected, selected or pressed.\r
+ * An OSD menu skin can associate images with each state.\r
+ *\r
+ *  OSD_BUTTON_UNSELECT 0\r
+ *  OSD_BUTTON_SELECT   1\r
+ *  OSD_BUTTON_PRESSED  2\r
+ */\r
+#define OSD_BUTTON_UNSELECT 0\r
+#define OSD_BUTTON_SELECT   1\r
+#define OSD_BUTTON_PRESSED  2\r
+\r
+/**\r
+ * OSD State object\r
+ *\r
+ * The OSD state object holds the state and associated images for a particular state\r
+ * on the screen. The picture is displayed when this state is the active state.\r
+ */\r
+struct osd_state_t\r
+{\r
+    osd_state_t *p_next;    /*< pointer to next state */\r
+    osd_state_t *p_prev;    /*< pointer to previous state */\r
+    picture_t   *p_pic;     /*< picture of state */\r
+    \r
+    char        *psz_state; /*< state name */\r
+    int          i_state;   /*< state index */ \r
+};\r
+\r
+/** \r
+ * OSD Button object\r
+ *\r
+ * An OSD Button has different states. Each state has an image for display. \r
+ */\r
+struct osd_button_t\r
+{\r
+    osd_button_t *p_next;   /*< pointer to next button */\r
+    osd_button_t *p_prev;   /*< pointer to previous button */\r
+    osd_button_t *p_up;     /*< pointer to up button */\r
+    osd_button_t *p_down;   /*< pointer to down button */\r
+    \r
+    osd_state_t *p_current_state; /*< pointer to current state image */\r
+    osd_state_t *p_states; /*< doubly linked list of states */\r
+    picture_t   *p_feedback; /*< feedback picture */\r
+        \r
+    char    *psz_name;     /*< name of button */\r
+    \r
+    /* These member should probably be a struct hotkey */\r
+    char    *psz_action;      /*< hotkey action name on button*/\r
+    char    *psz_action_down; /*< hotkey action name on range buttons for command "menu down" */\r
+    /* end of hotkey specifics */\r
+    \r
+    int     i_x;            /*< x-position of button visible state image */\r
+    int     i_y;            /*< y-position of button visible state image */ \r
+    \r
+    /* range style button */    \r
+    vlc_bool_t   b_range;    /*< button should be interpreted as range */\r
+    int          i_ranges;   /*< number of states */\r
+};\r
+\r
+/** \r
+ * OSD Menu State object\r
+ *\r
+ * Represents the current state as displayed. \r
+ */\r
+/* Represent the menu state */\r
+struct osd_menu_state_t\r
+{\r
+    int     i_x;        /*< x position of spu region */\r
+    int     i_y;        /*< y position of spu region */\r
+    int     i_width;    /*< width of spu region */\r
+    int     i_height;   /*< height of spu region */    \r
+    \r
+    picture_t    *p_pic;  /*< pointer to picture to display */\r
+    osd_button_t *p_visible; /*< shortcut to visible button */\r
+    \r
+    vlc_bool_t b_menu_visible; /*< menu currently visible? */\r
+    vlc_bool_t b_update;       /*< update OSD Menu when VLC_TRUE */\r
+    \r
+    /* quick hack to volume state. */\r
+    osd_button_t *p_volume; /*< pointer to volume range object. */\r
+};\r
+\r
+/**\r
+ * OSD Menu object\r
+ *\r
+ * The main OSD Menu object, which holds a linked list to all buttons\r
+ * and images that defines the menu. The p_state variable represents the\r
+ * current state of the OSD Menu.\r
+ */\r
+struct osd_menu_t\r
+{\r
+    VLC_COMMON_MEMBERS\r
+    \r
+    int     i_x;        /*< x-position of OSD Menu on the video screen */ \r
+    int     i_y;        /*< y-position of OSD Menu on the video screen */ \r
+    int     i_width;    /*< width of OSD Menu on the video screen */ \r
+    int     i_height;   /*< height of OSD Menu on the video screen */ \r
+    \r
+    char             *psz_path;  /*< directory where OSD menu images are stored */\r
+    osd_button_t     *p_button;  /*< doubly linked list of buttons */\r
+    osd_menu_state_t *p_state;   /*< current state of OSD menu */\r
+        \r
+    /* quick link in the linked list. */\r
+    osd_button_t  *p_last_button; /*< pointer to last button in the list */\r
+};\r
+\r
+/**\r
+ * Initialize an osd_menu_t object\r
+ *\r
+ * This functions has to be called before any call to other osd_menu_t* functions.\r
+ * It creates the osd_menu object and holds a pointer to it during its lifetime.\r
+ */\r
+VLC_EXPORT( osd_menu_t *, __osd_MenuCreate, ( vlc_object_t *, const char * ) );\r
+\r
+/**\r
+ * Delete the osd_menu_t object\r
+ *\r
+ * This functions has to be called to release the associated module and memory\r
+ * for the osdmenu. After return of this function the pointer to osd_menu_t* is invalid.\r
+ */\r
+VLC_EXPORT( void, __osd_MenuDelete, ( vlc_object_t *, osd_menu_t * ) );\r
+\r
+/**\r
+ * Change state on an osd_button_t.\r
+ *\r
+ * This function selects the specified state and returns a pointer to it. The \r
+ * following states are currently supported: \r
+ * \see OSD_BUTTON_UNSELECT\r
+ * \see OSD_BUTTON_SELECT   \r
+ * \see OSD_BUTTON_PRESSED  \r
+ */\r
+VLC_EXPORT( osd_state_t *, __osd_StateChange, ( osd_state_t *, const int ) );\r
+\r
+#define osd_MenuCreate(object,file) __osd_MenuCreate( VLC_OBJECT(object), file )\r
+#define osd_MenuDelete(object,osd)  __osd_MenuDelete( VLC_OBJECT(object), osd )\r
+#define osd_StateChange(object,value) __osd_StateChange( object, value )\r
+\r
+/**\r
+ * Show the OSD menu.\r
+ *\r
+ * Show the OSD menu on the video output or mux it into the stream. \r
+ * Every change to the OSD menu will now be visible in the output. An output \r
+ * can be a video output window or a stream (\see stream output)\r
+ */\r
+VLC_EXPORT( void, __osd_MenuShow, ( vlc_object_t * ) ); \r
+\r
+/**\r
+ * Hide the OSD menu.\r
+ *\r
+ * Stop showing the OSD menu on the video output or mux it into the stream.\r
+ */\r
+VLC_EXPORT( void, __osd_MenuHide, ( vlc_object_t * ) );\r
+\r
+/**\r
+ * Activate the action of this OSD menu item.\r
+ *\r
+ * The rc interface command "menu select" triggers the sending of an hotkey action\r
+ * to the hotkey interface. The hotkey that belongs to the current highlighted \r
+ * OSD menu item will be used.\r
+ */\r
+VLC_EXPORT( void, __osd_MenuActivate,   ( vlc_object_t * ) );\r
+\r
+#define osd_MenuShow(object) __osd_MenuShow( VLC_OBJECT(object) )\r
+#define osd_MenuHide(object) __osd_MenuHide( VLC_OBJECT(object) )\r
+#define osd_MenuActivate(object)   __osd_MenuActivate( VLC_OBJECT(object) )\r
+\r
+/**\r
+ * Next OSD menu item\r
+ *\r
+ * Select the next OSD menu item to be highlighted.\r
+ * Note: The actual position on screen of the menu item is determined by the the\r
+ * OSD menu configuration file.\r
+ */\r
+VLC_EXPORT( void, __osd_MenuNext, ( vlc_object_t * ) ); \r
+\r
+/**\r
+ * Previous OSD menu item\r
+ *\r
+ * Select the previous OSD menu item to be highlighted.\r
+ * Note: The actual position on screen of the menu item is determined by the the\r
+ * OSD menu configuration file.\r
+ */\r
+VLC_EXPORT( void, __osd_MenuPrev, ( vlc_object_t * ) );\r
+\r
+/**\r
+ * OSD menu item above\r
+ *\r
+ * Select the OSD menu item above the current item to be highlighted. \r
+ * Note: The actual position on screen of the menu item is determined by the the\r
+ * OSD menu configuration file.\r
+ */\r
+VLC_EXPORT( void, __osd_MenuUp,   ( vlc_object_t * ) );\r
+\r
+/**\r
+ * OSD menu item below\r
+ *\r
+ * Select the next OSD menu item below the current item to be highlighted.\r
+ * Note: The actual position on screen of the menu item is determined by the the\r
+ * OSD menu configuration file.\r
+ */\r
+VLC_EXPORT( void, __osd_MenuDown, ( vlc_object_t * ) );\r
+\r
+#define osd_MenuNext(object) __osd_MenuNext( VLC_OBJECT(object) )\r
+#define osd_MenuPrev(object) __osd_MenuPrev( VLC_OBJECT(object) )\r
+#define osd_MenuUp(object)   __osd_MenuUp( VLC_OBJECT(object) )\r
+#define osd_MenuDown(object) __osd_MenuDown( VLC_OBJECT(object) )\r
+\r
+/**\r
+ * Turn Volume Up\r
+ *\r
+ * Use the OSD menu to turn the audio volume up.\r
+ */\r
+VLC_EXPORT( void, __osd_VolumeUp, ( vlc_object_t * ) );\r
+\r
+/**\r
+ * Turn Volume Down\r
+ *\r
+ * Use the OSD menu to turn the audio volume down.\r
+ */\r
+VLC_EXPORT( void, __osd_VolumeDown, ( vlc_object_t * ) );\r
+\r
+#define osd_VolumeUp(object)   __osd_VolumeUp( VLC_OBJECT(object) )\r
+#define osd_VolumeDown(object) __osd_VolumeDown( VLC_OBJECT(object) )\r
+\r
+/**\r
+ * Retrieve a non modifyable pointer to the OSD Menu state\r
+ *\r
+ */\r
+static inline const osd_menu_state_t *osd_GetMenuState( osd_menu_t *p_osd )\r
+{\r
+    return( p_osd->p_state );\r
+}\r
+\r
+/**\r
+ * Get the last key press received by the OSD Menu\r
+ *\r
+ * Returns 0 when no key has been pressed or the value of the key pressed.\r
+ */\r
+static inline vlc_bool_t osd_GetKeyPressed( osd_menu_t *p_osd )\r
+{\r
+    return( p_osd->p_state->b_update );\r
+}       \r
+\r
+/**\r
+ * Set the key pressed to a value.\r
+ *\r
+ * Assign a new key value to the last key pressed on the OSD Menu.\r
+ */\r
+static inline void osd_SetKeyPressed( vlc_object_t *p_this, int i_value )\r
+{\r
+    vlc_value_t val;\r
+    \r
+    val.i_int = i_value;    \r
+    var_Set( p_this, "key-pressed", val );            \r
+}\r
+\r
+/**\r
+ * Update the OSD Menu visibility flag.\r
+ *\r
+ * VLC_TRUE means OSD Menu should be shown. VLC_FALSE means OSD Menu should not be shown.\r
+ */\r
+static inline void osd_SetMenuVisible( osd_menu_t *p_osd, vlc_bool_t b_value )\r
+{\r
+    vlc_value_t val;\r
+    \r
+    val.b_bool = p_osd->p_state->b_menu_visible = b_value; \r
+    var_Set( p_osd, "osd-menu-visible", val );\r
+}\r
+\r
+/**\r
+ * Update the OSD Menu update flag\r
+ *\r
+ * If the OSD Menu should be updated then set the update flag to VLC_TRUE, else to VLC_FALSE.\r
+ */\r
+static inline void osd_SetMenuUpdate( osd_menu_t *p_osd, vlc_bool_t b_value )\r
+{\r
+    vlc_value_t val;\r
+    \r
+    val.b_bool = p_osd->p_state->b_update = b_value;\r
+    var_Set( p_osd, "osd-menu-update", val );\r
+} \r
+\r
+/**\r
+ * Default feedback images\r
+ *\r
+ * Functions that provide the default OSD feedback images on hotkey commands. These feedback\r
+ * images are also part of the osd_button_t object. The types are declared in the include file\r
+ * include/osd.h\r
+ * @see osd.h \r
+ */\r
+VLC_EXPORT( picture_t *, osd_Slider, ( int i_width, int i_height, int i_position, short i_type ) );\r
+VLC_EXPORT( picture_t *, osd_Icon,   ( int i_width, int i_height, short i_type ) );\r
+\r
+/**\r
+ * Loading and parse the OSD Configuration file\r
+ *\r
+ * These functions load/unload the OSD menu configuration file and create/destroy the\r
+ * themable OSD menu structure on the OSD object.\r
+ */\r
+VLC_EXPORT( int,  osd_ConfigLoader, ( vlc_object_t *, const char *, osd_menu_t ** ) );\r
+VLC_EXPORT( void, osd_ConfigUnload, ( vlc_object_t *, osd_menu_t ** ) );\r
+\r
+# ifdef __cplusplus\r
+}\r
+# endif\r
+\r
+#endif /* _VLC_OSD_H */\r
index 6fd43ee..b8365e4 100644 (file)
@@ -391,6 +391,22 @@ struct module_symbols_t
     char * (*__vlc_fix_readdir_charset_inner) (vlc_object_t *, const char *);
     int (*vlc_scandir_inner) (const char *name, struct dirent ***namelist, int (*filter) ( const struct dirent * ), int (*compar) ( const struct dirent **, const struct dirent ** ));
     int (*vlc_alphasort_inner) (const struct dirent **a, const struct dirent **b);
+    osd_state_t * (*__osd_StateChange_inner) (osd_state_t *, const int);
+    picture_t * (*osd_Slider_inner) (int i_width, int i_height, int i_position, short i_type);
+    void (*osd_ConfigUnload_inner) (vlc_object_t *, osd_menu_t **);
+    void (*__osd_MenuShow_inner) (vlc_object_t *);
+    picture_t * (*osd_Icon_inner) (int i_width, int i_height, short i_type);
+    void (*__osd_VolumeDown_inner) (vlc_object_t *);
+    void (*__osd_MenuNext_inner) (vlc_object_t *);
+    void (*__osd_MenuDelete_inner) (vlc_object_t *, osd_menu_t *);
+    void (*__osd_MenuHide_inner) (vlc_object_t *);
+    int (*osd_ConfigLoader_inner) (vlc_object_t *, const char *, osd_menu_t **);
+    void (*__osd_MenuUp_inner) (vlc_object_t *);
+    void (*__osd_MenuDown_inner) (vlc_object_t *);
+    osd_menu_t * (*__osd_MenuCreate_inner) (vlc_object_t *, const char *);
+    void (*__osd_MenuPrev_inner) (vlc_object_t *);
+    void (*__osd_VolumeUp_inner) (vlc_object_t *);
+    void (*__osd_MenuActivate_inner) (vlc_object_t *);
 };
 # if defined (__PLUGIN__)
 #  define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner
@@ -766,6 +782,22 @@ struct module_symbols_t
 #  define __vlc_fix_readdir_charset (p_symbols)->__vlc_fix_readdir_charset_inner
 #  define vlc_scandir (p_symbols)->vlc_scandir_inner
 #  define vlc_alphasort (p_symbols)->vlc_alphasort_inner
+#  define __osd_StateChange (p_symbols)->__osd_StateChange_inner
+#  define osd_Slider (p_symbols)->osd_Slider_inner
+#  define osd_ConfigUnload (p_symbols)->osd_ConfigUnload_inner
+#  define __osd_MenuShow (p_symbols)->__osd_MenuShow_inner
+#  define osd_Icon (p_symbols)->osd_Icon_inner
+#  define __osd_VolumeDown (p_symbols)->__osd_VolumeDown_inner
+#  define __osd_MenuNext (p_symbols)->__osd_MenuNext_inner
+#  define __osd_MenuDelete (p_symbols)->__osd_MenuDelete_inner
+#  define __osd_MenuHide (p_symbols)->__osd_MenuHide_inner
+#  define osd_ConfigLoader (p_symbols)->osd_ConfigLoader_inner
+#  define __osd_MenuUp (p_symbols)->__osd_MenuUp_inner
+#  define __osd_MenuDown (p_symbols)->__osd_MenuDown_inner
+#  define __osd_MenuCreate (p_symbols)->__osd_MenuCreate_inner
+#  define __osd_MenuPrev (p_symbols)->__osd_MenuPrev_inner
+#  define __osd_VolumeUp (p_symbols)->__osd_VolumeUp_inner
+#  define __osd_MenuActivate (p_symbols)->__osd_MenuActivate_inner
 # elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__)
 /******************************************************************
  * STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access.
@@ -1144,6 +1176,22 @@ struct module_symbols_t
     ((p_symbols)->__vlc_fix_readdir_charset_inner) = __vlc_fix_readdir_charset; \
     ((p_symbols)->vlc_scandir_inner) = vlc_scandir; \
     ((p_symbols)->vlc_alphasort_inner) = vlc_alphasort; \
+    ((p_symbols)->__osd_StateChange_inner) = __osd_StateChange; \
+    ((p_symbols)->osd_Slider_inner) = osd_Slider; \
+    ((p_symbols)->osd_ConfigUnload_inner) = osd_ConfigUnload; \
+    ((p_symbols)->__osd_MenuShow_inner) = __osd_MenuShow; \
+    ((p_symbols)->osd_Icon_inner) = osd_Icon; \
+    ((p_symbols)->__osd_VolumeDown_inner) = __osd_VolumeDown; \
+    ((p_symbols)->__osd_MenuNext_inner) = __osd_MenuNext; \
+    ((p_symbols)->__osd_MenuDelete_inner) = __osd_MenuDelete; \
+    ((p_symbols)->__osd_MenuHide_inner) = __osd_MenuHide; \
+    ((p_symbols)->osd_ConfigLoader_inner) = osd_ConfigLoader; \
+    ((p_symbols)->__osd_MenuUp_inner) = __osd_MenuUp; \
+    ((p_symbols)->__osd_MenuDown_inner) = __osd_MenuDown; \
+    ((p_symbols)->__osd_MenuCreate_inner) = __osd_MenuCreate; \
+    ((p_symbols)->__osd_MenuPrev_inner) = __osd_MenuPrev; \
+    ((p_symbols)->__osd_VolumeUp_inner) = __osd_VolumeUp; \
+    ((p_symbols)->__osd_MenuActivate_inner) = __osd_MenuActivate; \
     (p_symbols)->net_ConvertIPv4_deprecated = NULL; \
     (p_symbols)->vlc_fix_readdir_charset_deprecated = NULL; \
 
index 34ed3a5..4656d14 100644 (file)
@@ -342,6 +342,8 @@ $Id$
  
  * opie: interface for Opie using QT/Embedded library.
 
+ * osdmenu: video_filter for displaying and streaming a On Screen Display menu
+
  * oss: audio output module using the OSS /dev/dsp interface.
  
  * packetizer_copy: Simple copy packetizer
index b9088cf..49e7edc 100644 (file)
@@ -1557,16 +1557,247 @@ static int OpenEncoder( vlc_object_t *p_this )
     return VLC_SUCCESS;
 }
 
+/* FIXME: this routine is a hack to convert VLC_FOURCC('Y','U','V','A') 
+ *        into VLC_FOURCC('Y','U','V','P')
+ */
+static subpicture_t *YuvaYuvp( encoder_t *p_enc, subpicture_t *p_subpic )
+{
+    subpicture_region_t *p_region = NULL;
+     
+    for( p_region = p_subpic->p_region; p_region; p_region = p_region->p_next )
+    {
+        video_format_t *p_fmt = &p_region->fmt;
+        int i = 0, j = 0, n = 0, p = 0;
+        int i_max_entries = 256;
+
+#ifdef RANDOM_DITHERING
+        int i_seed = 0xdeadbeef; /* random seed */
+#else
+        int *pi_delta;
+#endif
+        int i_pixels = p_region->picture.p[0].i_visible_lines
+                        * p_region->picture.p[0].i_pitch;
+        int i_iterator = p_region->picture.p[0].i_visible_lines * 3 / 4
+                            * p_region->picture.p[0].i_pitch
+                        + p_region->picture.p[0].i_pitch * 1 / 3;
+        int i_tolerance = 0;
+    
+        p_fmt->i_chroma = VLC_FOURCC('Y','U','V','P');
+        p_fmt->p_palette = (video_palette_t *) malloc( sizeof( video_palette_t ) );
+        p_fmt->p_palette->i_entries = 0;
+    
+        /* Find best iterator using Euclide’s algorithm */
+        for( ; i_iterator > 1 ; i_iterator-- )
+        {
+            int a = i_pixels;
+            int b = i_iterator;
+            int c;
+    
+            while( b )
+            {
+                c = a % b;
+                a = b;
+                b = c;
+            }
+    
+            if( a == 1 )
+            {
+                break;
+            }
+        }
+    
+        /* Count colors, build best palette */
+        for( i_tolerance = 0; i_tolerance < 128; i_tolerance++ )
+        {
+            vlc_bool_t b_success = VLC_TRUE;
+            p_fmt->p_palette->i_entries = 0;
+    
+            for( i = 0; i < i_pixels ; )
+            {
+                uint8_t y, u, v, a;
+                y = p_region->picture.p[0].p_pixels[i];
+                u = p_region->picture.p[1].p_pixels[i];
+                v = p_region->picture.p[2].p_pixels[i];
+                a = p_region->picture.p[3].p_pixels[i];
+                for( j = 0; j < p_fmt->p_palette->i_entries; j++ )
+                {
+                    if( abs((int)p_fmt->p_palette->palette[j][0] - (int)y) <= i_tolerance &&
+                        abs((int)p_fmt->p_palette->palette[j][1] - (int)u) <= i_tolerance &&
+                        abs((int)p_fmt->p_palette->palette[j][2] - (int)v) <= i_tolerance &&
+                        abs((int)p_fmt->p_palette->palette[j][3] - (int)a) <= i_tolerance / 2 )
+                    {
+                        break;
+                    }
+                }
+                if( j == p_fmt->p_palette->i_entries )
+                {
+                    p_fmt->p_palette->palette[j][0] = y;
+                    p_fmt->p_palette->palette[j][1] = u;
+                    p_fmt->p_palette->palette[j][2] = v;
+                    p_fmt->p_palette->palette[j][3] = a;
+                    p_fmt->p_palette->i_entries++;
+                }
+                if( p_fmt->p_palette->i_entries >= i_max_entries )
+                {
+                    b_success = VLC_FALSE;
+                    break;
+                }
+                i += i_iterator;
+                if( i > i_pixels )
+                {
+                    i -= i_pixels;
+                }
+            }
+    
+            if( b_success )
+            {
+                break;
+            }
+        }
+    
+#if DEBUG_DVBSUB
+        msg_Dbg( p_enc, "best palette has %d colors", p_fmt->p_palette->i_entries );
+#endif
+
+#ifndef RANDOM_DITHERING
+        pi_delta = malloc( ( p_region->picture.p[0].i_pitch + 1 )
+                            * sizeof(int) * 4  );
+        for( i = 0; i < (p_region->picture.p[0].i_pitch + 1) * 4 ; i++ )
+        {
+            pi_delta[ i ] = 0;
+        }
+#endif
+
+        /* Fill image with our new colours */
+        for( p = 0; p < p_region->picture.p[0].i_visible_lines ; p++ )
+        {
+            int i_ydelta = 0, i_udelta = 0, i_vdelta = 0, i_adelta = 0;
+    
+            for( n = 0; n < p_region->picture.p[0].i_pitch ; n++ )
+            {
+                int i_offset = p * p_region->picture.p[0].i_pitch + n;
+                int y, u, v, a;
+                int i_mindist, i_best;
+    
+                y = (int)p_region->picture.p[0].p_pixels[i_offset];
+                u = (int)p_region->picture.p[1].p_pixels[i_offset];
+                v = (int)p_region->picture.p[2].p_pixels[i_offset];
+                a = (int)p_region->picture.p[3].p_pixels[i_offset];
+    
+                /* Add dithering compensation */
+#ifdef RANDOM_DITHERING
+                y += ((i_seed & 0xff) - 0x80) * i_tolerance / 0x80;
+                u += (((i_seed >> 8) & 0xff) - 0x80) * i_tolerance / 0x80;
+                v += (((i_seed >> 16) & 0xff) - 0x80) * i_tolerance / 0x80;
+                a += (((i_seed >> 24) & 0xff) - 0x80) * i_tolerance / 0x80;
+#else
+                y += i_ydelta + pi_delta[ n * 4 ];
+                u += i_udelta + pi_delta[ n * 4 + 1 ];
+                v += i_vdelta + pi_delta[ n * 4 + 2 ];
+                a += i_adelta + pi_delta[ n * 4 + 3 ];
+#endif
+
+                /* Find best colour in palette */
+                for( i_mindist = 99999999, i_best = 0, j = 0; j < p_fmt->p_palette->i_entries; j++ )
+                {
+                    int i_dist = 0;
+    
+                    i_dist += abs((int)p_fmt->p_palette->palette[j][0] - y);
+                    i_dist += abs((int)p_fmt->p_palette->palette[j][1] - u);
+                    i_dist += abs((int)p_fmt->p_palette->palette[j][2] - v);
+                    i_dist += 2 * abs((int)p_fmt->p_palette->palette[j][3] - a);
+    
+                    if( i_dist < i_mindist )
+                    {
+                        i_mindist = i_dist;
+                        i_best = j;
+                    }
+                }
+    
+                /* Set pixel to best color */
+                p_region->picture.p[0].p_pixels[i_offset] = i_best;
+    
+                /* Update dithering state */
+#ifdef RANDOM_DITHERING
+                i_seed = (i_seed * 0x1283837) ^ 0x789479 ^ (i_seed >> 13);
+#else
+                i_ydelta = y - (int)p_fmt->p_palette->palette[i_best][0];
+                i_udelta = u - (int)p_fmt->p_palette->palette[i_best][1];
+                i_vdelta = v - (int)p_fmt->p_palette->palette[i_best][2];
+                i_adelta = a - (int)p_fmt->p_palette->palette[i_best][3];
+                pi_delta[ n * 4 ] = i_ydelta * 3 / 8;
+                pi_delta[ n * 4 + 1 ] = i_udelta * 3 / 8;
+                pi_delta[ n * 4 + 2 ] = i_vdelta * 3 / 8;
+                pi_delta[ n * 4 + 3 ] = i_adelta * 3 / 8;
+                i_ydelta = i_ydelta * 5 / 8;
+                i_udelta = i_udelta * 5 / 8;
+                i_vdelta = i_vdelta * 5 / 8;
+                i_adelta = i_adelta * 5 / 8;
+#endif
+            }
+        }
+#ifndef RANDOM_DITHERING
+        free( pi_delta );
+#endif
+
+        /* pad palette */
+        for( i = p_fmt->p_palette->i_entries; i < i_max_entries; i++ )
+        {
+            p_fmt->p_palette->palette[i][0] = 0;
+            p_fmt->p_palette->palette[i][1] = 0;
+            p_fmt->p_palette->palette[i][2] = 0;
+            p_fmt->p_palette->palette[i][3] = 0;
+        }
+        p_fmt->p_palette->i_entries = i_max_entries;
+#if DEBUG_DVBSUB
+        msg_Dbg( p_enc, "best palette has %d colors", p_fmt->p_palette->i_entries );
+#endif
+    }
+    return p_subpic;
+} /* End of hack */
+
 /****************************************************************************
  * Encode: the whole thing
  ****************************************************************************/
 static block_t *Encode( encoder_t *p_enc, subpicture_t *p_subpic )
 {
+    subpicture_t *p_temp = NULL;
+    subpicture_region_t *p_region = NULL;
     bs_t bits, *s = &bits;
     block_t *p_block;
 
-    if( !p_subpic || !p_subpic->p_region ) return 0;
-
+    if( !p_subpic || !p_subpic->p_region ) return NULL;
+    
+    /* FIXME: this is a hack to convert VLC_FOURCC('Y','U','V','A') into
+     *  VLC_FOURCC('Y','U','V','P')
+     */
+    p_region = p_subpic->p_region;
+    if( p_region->fmt.i_chroma == VLC_FOURCC('Y','U','V','A') )
+    {
+        p_temp = YuvaYuvp( p_enc, p_subpic );
+        if( !p_temp )
+        {
+            msg_Dbg( p_enc, "no picture in subpicture" );
+            return NULL;
+        }
+        p_region = p_subpic->p_region;
+    }
+    /* Sanity check */
+    if( !p_region ) return NULL;
+    if( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') &&
+        p_region->fmt.i_chroma != VLC_FOURCC('Y','U','V','P') ) return NULL;
+    
+    if( p_region->fmt.p_palette &&
+        ( p_region->fmt.p_palette->i_entries != 4 ) &&
+        ( p_region->fmt.p_palette->i_entries != 16 ) &&
+        ( p_region->fmt.p_palette->i_entries != 256 ) )
+    {
+        msg_Err( p_enc, "subpicture palette (%d) not handled",
+                    p_region->fmt.p_palette->i_entries );
+        return NULL;
+    }
+    /* End of hack */
+    
 #if DEBUG_DVBSUB
     msg_Dbg( p_enc, "encoding subpicture" );
 #endif
index ccbdc9d..f41160b 100644 (file)
@@ -5,6 +5,7 @@
  * $Id$
  *
  * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
+ *         Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
  *
  * 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
@@ -36,6 +37,7 @@
 #include <vlc/intf.h>
 #include <vlc/aout.h>
 #include <vlc/vout.h>
+#include <vlc_osd.h>
 
 #ifdef HAVE_UNISTD_H
 #    include <unistd.h>
@@ -58,6 +60,7 @@
 #endif
 
 #define MAX_LINE_LENGTH 256
+#define STATUS_CHANGE "status change: "
 
 /*****************************************************************************
  * Local prototypes
@@ -66,6 +69,8 @@ static int  Activate     ( vlc_object_t * );
 static void Deactivate   ( vlc_object_t * );
 static void Run          ( intf_thread_t * );
 
+static void Help         ( intf_thread_t *, vlc_bool_t );
+
 static vlc_bool_t ReadCommand( intf_thread_t *, char *, int * );
 
 static playlist_item_t *parse_MRL( intf_thread_t *, char * );
@@ -86,6 +91,18 @@ static int  VolumeMove   ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
 static int  AudioConfig  ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
+static int  Menu         ( vlc_object_t *, char const *,
+                           vlc_value_t, vlc_value_t, void * );
+
+/* Status Callbacks */
+static int TimeOffsetChanged( vlc_object_t *, char const *,
+                              vlc_value_t, vlc_value_t , void * );
+static int VolumeChanged    ( vlc_object_t *, char const *,
+                              vlc_value_t, vlc_value_t, void * );
+static int StateChanged     ( vlc_object_t *, char const *,
+                              vlc_value_t, vlc_value_t, void * );
+static int RateChanged      ( vlc_object_t *, char const *,
+                              vlc_value_t, vlc_value_t, void * );
 
 struct intf_sys_t
 {
@@ -93,6 +110,10 @@ struct intf_sys_t
     int i_socket;
     char *psz_unix_path;
 
+    /* status changes */
+    vlc_mutex_t       status_lock;
+    playlist_status_t i_last_state;
+    
 #ifdef WIN32
     HANDLE hConsoleIn;
     vlc_bool_t b_quiet;
@@ -108,10 +129,13 @@ void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
 {
     va_list args;
     va_start( args, psz_fmt );
-    if( p_intf->p_sys->i_socket == -1 ) vprintf( psz_fmt, args );
+    if( p_intf->p_sys->i_socket == -1 )
+        vprintf( psz_fmt, args );
     else
-    { net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, psz_fmt, args );
-      net_Printf( VLC_OBJECT(p_intf), p_intf->p_sys->i_socket, NULL, "\r" ); }
+    {
+        net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, psz_fmt, args );
+        net_Printf( VLC_OBJECT(p_intf), p_intf->p_sys->i_socket, NULL, "\r" );
+    }
     va_end( args );
 }
 
@@ -170,7 +194,7 @@ static int Activate( vlc_object_t *p_this )
     intf_thread_t *p_intf = (intf_thread_t*)p_this;
     playlist_t *p_playlist;
     char *psz_host, *psz_unix_path;
-    int *pi_socket = NULL;
+    int  *pi_socket = NULL;
 
 #if defined(HAVE_ISATTY) && !defined(WIN32)
     /* Check that stdin is a TTY */
@@ -273,7 +297,9 @@ static int Activate( vlc_object_t *p_this )
     p_intf->p_sys->pi_socket_listen = pi_socket;
     p_intf->p_sys->i_socket = -1;
     p_intf->p_sys->psz_unix_path = psz_unix_path;
-
+    vlc_mutex_init( p_intf, &p_intf->p_sys->status_lock );
+    p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
+    
     /* Non-buffered stdout */
     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
 
@@ -318,6 +344,7 @@ static void Deactivate( vlc_object_t *p_this )
 #endif
         free( p_intf->p_sys->psz_unix_path );
     }
+    vlc_mutex_destroy( &p_intf->p_sys->status_lock );    
     free( p_intf->p_sys );
 }
 
@@ -437,8 +464,12 @@ static void Run( intf_thread_t *p_intf )
     var_AddCallback( p_intf, "logo-position", Other, NULL );
     var_Create( p_intf, "logo-transparency", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
     var_AddCallback( p_intf, "logo-transparency", Other, NULL );
-    
-    
+
+    /* OSD menu commands */
+    var_Create( p_intf, "menu", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
+    var_AddCallback( p_intf, "menu", Menu, NULL ); 
+
+    /* DVD commands */
     var_Create( p_intf, "pause", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
     var_AddCallback( p_intf, "pause", Input, NULL );
     var_Create( p_intf, "seek", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
@@ -460,7 +491,14 @@ static void Run( intf_thread_t *p_intf )
     var_AddCallback( p_intf, "fastforward", Input, NULL );
     var_Create( p_intf, "rewind", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
     var_AddCallback( p_intf, "rewind", Input, NULL );
-
+    var_Create( p_intf, "faster", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
+    var_AddCallback( p_intf, "faster", Input, NULL );
+    var_Create( p_intf, "slower", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
+    var_AddCallback( p_intf, "slower", Input, NULL );
+    var_Create( p_intf, "normal", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
+    var_AddCallback( p_intf, "normal", Input, NULL );
+
+    /* audio commands */
     var_Create( p_intf, "volume", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
     var_AddCallback( p_intf, "volume", Volume, NULL );
     var_Create( p_intf, "volup", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
@@ -472,6 +510,10 @@ static void Run( intf_thread_t *p_intf )
     var_Create( p_intf, "achan", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
     var_AddCallback( p_intf, "achan", AudioConfig, NULL );
 
+    /* status callbacks */
+    /* Listen to audio volume updates */
+    var_AddCallback( p_intf->p_vlc, "audio-volume", VolumeChanged, p_intf );
+    
 #ifdef WIN32
     /* Get the file descriptor of the console input */
     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
@@ -514,11 +556,63 @@ static void Run( intf_thread_t *p_intf )
                                                            FIND_PARENT );
                 }
             }
+            /* New input has been registered */
+            if( p_input )
+            {
+                if( !p_input->b_dead || !p_input->b_die )
+                {
+                    msg_rc( STATUS_CHANGE "( New input: %s )\r\n", p_input->input.p_item->psz_uri );
+                    msg_rc( STATUS_CHANGE "( audio volume: %d )\r\n", config_GetInt( p_intf, "volume" ));
+                }
+                var_AddCallback( p_input, "state", StateChanged, p_intf );
+                var_AddCallback( p_input, "rate-faster", RateChanged, p_intf );
+                var_AddCallback( p_input, "rate-slower", RateChanged, p_intf );
+                var_AddCallback( p_input, "rate", RateChanged, p_intf );
+                var_AddCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
+            }
         }
         else if( p_input->b_dead )
         {
+            var_DelCallback( p_input, "state", StateChanged, p_intf );
+            var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
+            var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
+            var_DelCallback( p_input, "rate", RateChanged, p_intf );
+            var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
             vlc_object_release( p_input );
             p_input = NULL;
+
+            if( p_playlist )
+            {
+                vlc_mutex_lock( &p_playlist->object_lock );
+                p_intf->p_sys->i_last_state = (int) PLAYLIST_STOPPED;
+                msg_rc( STATUS_CHANGE "( stop state: 0 )\r\n" );
+                vlc_mutex_unlock( &p_playlist->object_lock );
+            }
+        }
+        
+        if( (p_input != NULL) && !p_input->b_dead && !p_input->b_die &&
+            (p_playlist != NULL) )
+        {
+            vlc_mutex_lock( &p_playlist->object_lock );
+            if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
+                (p_playlist->status.i_status == PLAYLIST_STOPPED) )
+            {
+                p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
+                msg_rc( STATUS_CHANGE "( stop state: 0 )\r\n" );
+            }
+            else if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
+                (p_playlist->status.i_status == PLAYLIST_RUNNING) )
+            {
+                p_intf->p_sys->i_last_state = p_playlist->status.i_status;
+                msg_rc( STATUS_CHANGE "( play state: 1 )\r\n" );
+            }
+            else if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
+                (p_playlist->status.i_status == PLAYLIST_PAUSED) )
+            {
+                p_intf->p_sys->i_last_state = p_playlist->status.i_status;
+                msg_rc( STATUS_CHANGE "( pause state: 2 )\r\n" );
+            }
+            vlc_mutex_unlock( &p_playlist->object_lock );
         }
 
         if( p_input && b_showpos )
@@ -632,6 +726,32 @@ static void Run( intf_thread_t *p_intf )
             else
             {
                 msg_rc( "1\n" );
+
+                /* FIXME: This is a hack */
+                /* Replay the current state of the system. */
+                msg_rc( STATUS_CHANGE "( New input: %s )\r\n", p_input->input.p_item->psz_uri );
+                msg_rc( STATUS_CHANGE "( audio volume: %d )\r\n", config_GetInt( p_intf, "volume" ));
+
+                if( p_playlist )
+                {
+                    vlc_mutex_lock( &p_playlist->object_lock );
+                    switch( p_playlist->status.i_status )
+                    {
+                        case PLAYLIST_STOPPED:
+                            msg_rc( STATUS_CHANGE "( stop state: 0 )\r\n" );
+                            break;
+                        case PLAYLIST_RUNNING:
+                            msg_rc( STATUS_CHANGE "( play state: %d )\r\n", var_GetInteger( p_input, "state" ) );
+                            break;
+                        case PLAYLIST_PAUSED:
+                            msg_rc( STATUS_CHANGE "( pause state: 2 )\r\n" );
+                            break;
+                        default:
+                            msg_rc( STATUS_CHANGE "( state unknown )\r\n" );
+                            break;
+                    }
+                    vlc_mutex_unlock( &p_playlist->object_lock );
+                } /* End of current playlist status */                
             }
         }
         else if( !strcmp( psz_cmd, "get_time" ) )
@@ -677,82 +797,8 @@ static void Run( intf_thread_t *p_intf )
             if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "H", 1 ) )
                  b_longhelp = VLC_TRUE;
             else b_longhelp = VLC_FALSE;
-            
-            msg_rc(_("+----[ Remote control commands ]\n"));
-            msg_rc(  "| \n");
-            msg_rc(_("| add XYZ  . . . . . . . . . . add XYZ to playlist\n"));
-            msg_rc(_("| playlist . . .  show items currently in playlist\n"));
-            msg_rc(_("| play . . . . . . . . . . . . . . . . play stream\n"));
-            msg_rc(_("| stop . . . . . . . . . . . . . . . . stop stream\n"));
-            msg_rc(_("| next . . . . . . . . . . . .  next playlist item\n"));
-            msg_rc(_("| prev . . . . . . . . . .  previous playlist item\n"));
-            msg_rc(_("| goto . . . . . . . . . . . .  goto item at index\n"));
-            msg_rc(_("| title [X]  . . . . set/get title in current item\n"));
-            msg_rc(_("| title_n  . . . . . .  next title in current item\n"));
-            msg_rc(_("| title_p  . . . .  previous title in current item\n"));
-            msg_rc(_("| chapter [X]  . . set/get chapter in current item\n"));
-            msg_rc(_("| chapter_n  . . . .  next chapter in current item\n"));
-            msg_rc(_("| chapter_p  . .  previous chapter in current item\n"));
-            msg_rc(  "| \n");
-            msg_rc(_("| seek X . seek in seconds, for instance `seek 12'\n"));
-            msg_rc(_("| pause  . . . . . . . . . . . . . .  toggle pause\n"));
-            msg_rc(_("| fastforward  . . . . . .  .  set to maximum rate\n"));
-            msg_rc(_("| rewind  . . . . . . . . . .  set to minimum rate\n"));
-            msg_rc(_("| f  . . . . . . . . . . . . . . toggle fullscreen\n"));
-            msg_rc(_("| info . . .  information about the current stream\n"));
-            msg_rc(  "| \n");
-            msg_rc(_("| volume [X] . . . . . . . .  set/get audio volume\n"));
-            msg_rc(_("| volup [X]  . . . . .  raise audio volume X steps\n"));
-            msg_rc(_("| voldown [X]  . . . .  lower audio volume X steps\n"));
-            msg_rc(_("| adev [X] . . . . . . . . .  set/get audio device\n"));
-            msg_rc(_("| achan [X]. . . . . . . .  set/get audio channels\n"));
-            msg_rc(  "| \n");
-            
-            if (b_longhelp)
-            {
-                msg_rc(_("| marq-marquee STRING  . . overlay STRING in video\n"));
-                msg_rc(_("| marq-x X . . . . . . . . . . . .offset from left\n"));
-                msg_rc(_("| marq-y Y . . . . . . . . . . . . offset from top\n"));
-                msg_rc(_("| marq-position #. . .  .relative position control\n"));
-                msg_rc(_("| marq-color # . . . . . . . . . . font color, RGB\n"));
-                msg_rc(_("| marq-opacity # . . . . . . . . . . . . . opacity\n"));
-                msg_rc(_("| marq-timeout T. . . . . . . . . . timeout, in ms\n"));
-                msg_rc(_("| marq-size # . . . . . . . . font size, in pixels\n"));
-                msg_rc(  "| \n");
-                msg_rc(_("| time-format STRING . . . overlay STRING in video\n"));
-                msg_rc(_("| time-x X . . . . . . . . . . . .offset from left\n"));
-                msg_rc(_("| time-y Y . . . . . . . . . . . . offset from top\n"));
-                msg_rc(_("| time-position #. . . . . . . . relative position\n"));
-                msg_rc(_("| time-color # . . . . . . . . . . font color, RGB\n"));
-                msg_rc(_("| time-opacity # . . . . . . . . . . . . . opacity\n"));
-                msg_rc(_("| time-size # . . . . . . . . font size, in pixels\n"));
-                msg_rc(  "| \n");
-                msg_rc(_("| logo-file STRING . . . the overlay file path/name\n"));
-                msg_rc(_("| logo-x X . . . . . . . . . . . .offset from left\n"));
-                msg_rc(_("| logo-y Y . . . . . . . . . . . . offset from top\n"));
-                msg_rc(_("| logo-position #. . . . . . . . relative position\n"));
-                msg_rc(_("| logo-transparency #. . . . . . . . .transparency\n"));
-                msg_rc(  "| \n");
-                msg_rc(_("| mosaic-alpha # . . . . . . . . . . . . . . alpha\n"));
-                msg_rc(_("| mosaic-height #. . . . . . . . . . . . . .height\n"));
-                msg_rc(_("| mosaic-width # . . . . . . . . . . . . . . width\n"));
-                msg_rc(_("| mosaic-xoffset # . . . .top left corner position\n"));
-                msg_rc(_("| mosaic-yoffset # . . . .top left corner position\n"));
-                msg_rc(_("| mosaic-align 0..2,4..6,8..10. . .mosaic alignment\n"));
-                msg_rc(_("| mosaic-vborder # . . . . . . . . vertical border\n"));
-                msg_rc(_("| mosaic-hborder # . . . . . . . horizontal border\n"));
-                msg_rc(_("| mosaic-position {0=auto,1=fixed} . . . .position\n"));
-                msg_rc(_("| mosaic-rows #. . . . . . . . . . .number of rows\n"));
-                msg_rc(_("| mosaic-cols #. . . . . . . . . . .number of cols\n"));
-                msg_rc(_("| mosaic-keep-aspect-ratio {0,1} . . .aspect ratio\n"));
-                msg_rc(  "| \n");
-            }    
-            msg_rc(_("| help . . . . . . . . . . . . . this help message\n"));
-            msg_rc(_("| longhelp . . . . . . . . . a longer help message\n"));
-            msg_rc(_("| logout . . . . .  exit (if in socket connection)\n"));
-            msg_rc(_("| quit . . . . . . . . . . . . . . . . .  quit vlc\n"));
-            msg_rc(  "| \n");
-            msg_rc(_("+----[ end of help ]\n"));
+
+            Help( p_intf, b_longhelp );
         }
         else switch( psz_cmd[0] )
         {
@@ -790,8 +836,16 @@ static void Run( intf_thread_t *p_intf )
         i_size = 0; p_buffer[0] = 0;
     }
 
+    msg_rc( STATUS_CHANGE "( stop state: 0 )\r\n" );
+    msg_rc( STATUS_CHANGE "( quit )\r\n" );
+
     if( p_input )
     {
+        var_DelCallback( p_input, "state", StateChanged, p_intf );
+        var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
+        var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
+        var_DelCallback( p_input, "rate", RateChanged, p_intf );
+        var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
         vlc_object_release( p_input );
         p_input = NULL;
     }
@@ -801,8 +855,183 @@ static void Run( intf_thread_t *p_intf )
         vlc_object_release( p_playlist );
         p_playlist = NULL;
     }
+    
+    var_DelCallback( p_intf->p_vlc, "audio-volume", VolumeChanged, p_intf );
+}
+
+static void Help( intf_thread_t *p_intf, vlc_bool_t b_longhelp)
+{                
+    msg_rc(_("+----[ Remote control commands ]\n"));
+    msg_rc(  "| \n");
+    msg_rc(_("| add XYZ  . . . . . . . . . . add XYZ to playlist\n"));
+    msg_rc(_("| playlist . . .  show items currently in playlist\n"));
+    msg_rc(_("| play . . . . . . . . . . . . . . . . play stream\n"));
+    msg_rc(_("| stop . . . . . . . . . . . . . . . . stop stream\n"));
+    msg_rc(_("| next . . . . . . . . . . . .  next playlist item\n"));
+    msg_rc(_("| prev . . . . . . . . . .  previous playlist item\n"));
+    msg_rc(_("| goto . . . . . . . . . . . .  goto item at index\n"));
+    msg_rc(_("| title [X]  . . . . set/get title in current item\n"));
+    msg_rc(_("| title_n  . . . . . .  next title in current item\n"));
+    msg_rc(_("| title_p  . . . .  previous title in current item\n"));
+    msg_rc(_("| chapter [X]  . . set/get chapter in current item\n"));
+    msg_rc(_("| chapter_n  . . . .  next chapter in current item\n"));
+    msg_rc(_("| chapter_p  . .  previous chapter in current item\n"));
+    msg_rc(  "| \n");
+    msg_rc(_("| seek X . seek in seconds, for instance `seek 12'\n"));
+    msg_rc(_("| pause  . . . . . . . . . . . . . .  toggle pause\n"));
+    msg_rc(_("| fastforward  . . . . . .  .  set to maximum rate\n"));
+    msg_rc(_("| rewind  . . . . . . . . . .  set to minimum rate\n"));
+    msg_rc(_("| faster . . . . . . . .  faster playing of stream\n"));
+    msg_rc(_("| slower . . . . . . . .  slower playing of stream\n"));
+    msg_rc(_("| normal . . . . . . . .  normal playing of stream\n"));
+    msg_rc(_("| f  . . . . . . . . . . . . . . toggle fullscreen\n"));
+    msg_rc(_("| info . . .  information about the current stream\n"));
+    msg_rc(  "| \n");
+    msg_rc(_("| volume [X] . . . . . . . .  set/get audio volume\n"));
+    msg_rc(_("| volup [X]  . . . . .  raise audio volume X steps\n"));
+    msg_rc(_("| voldown [X]  . . . .  lower audio volume X steps\n"));
+    msg_rc(_("| adev [X] . . . . . . . . .  set/get audio device\n"));
+    msg_rc(_("| achan [X]. . . . . . . .  set/get audio channels\n"));
+    msg_rc(_("| menu [on|off|up|down|left|right|select] use menu\n"));
+    msg_rc(  "| \n");
+    
+    if (b_longhelp)
+    {
+        msg_rc(_("| marq-marquee STRING  . . overlay STRING in video\n"));
+        msg_rc(_("| marq-x X . . . . . . . . . . . .offset from left\n"));
+        msg_rc(_("| marq-y Y . . . . . . . . . . . . offset from top\n"));
+        msg_rc(_("| marq-position #. . .  .relative position control\n"));
+        msg_rc(_("| marq-color # . . . . . . . . . . font color, RGB\n"));
+        msg_rc(_("| marq-opacity # . . . . . . . . . . . . . opacity\n"));
+        msg_rc(_("| marq-timeout T. . . . . . . . . . timeout, in ms\n"));
+        msg_rc(_("| marq-size # . . . . . . . . font size, in pixels\n"));
+        msg_rc(  "| \n");
+        msg_rc(_("| time-format STRING . . . overlay STRING in video\n"));
+        msg_rc(_("| time-x X . . . . . . . . . . . .offset from left\n"));
+        msg_rc(_("| time-y Y . . . . . . . . . . . . offset from top\n"));
+        msg_rc(_("| time-position #. . . . . . . . relative position\n"));
+        msg_rc(_("| time-color # . . . . . . . . . . font color, RGB\n"));
+        msg_rc(_("| time-opacity # . . . . . . . . . . . . . opacity\n"));
+        msg_rc(_("| time-size # . . . . . . . . font size, in pixels\n"));
+        msg_rc(  "| \n");
+        msg_rc(_("| logo-file STRING . . . the overlay file path/name\n"));
+        msg_rc(_("| logo-x X . . . . . . . . . . . .offset from left\n"));
+        msg_rc(_("| logo-y Y . . . . . . . . . . . . offset from top\n"));
+        msg_rc(_("| logo-position #. . . . . . . . relative position\n"));
+        msg_rc(_("| logo-transparency #. . . . . . . . .transparency\n"));
+        msg_rc(  "| \n");
+        msg_rc(_("| mosaic-alpha # . . . . . . . . . . . . . . alpha\n"));
+        msg_rc(_("| mosaic-height #. . . . . . . . . . . . . .height\n"));
+        msg_rc(_("| mosaic-width # . . . . . . . . . . . . . . width\n"));
+        msg_rc(_("| mosaic-xoffset # . . . .top left corner position\n"));
+        msg_rc(_("| mosaic-yoffset # . . . .top left corner position\n"));
+        msg_rc(_("| mosaic-align 0..2,4..6,8..10. . .mosaic alignment\n"));
+        msg_rc(_("| mosaic-vborder # . . . . . . . . vertical border\n"));
+        msg_rc(_("| mosaic-hborder # . . . . . . . horizontal border\n"));
+        msg_rc(_("| mosaic-position {0=auto,1=fixed} . . . .position\n"));
+        msg_rc(_("| mosaic-rows #. . . . . . . . . . .number of rows\n"));
+        msg_rc(_("| mosaic-cols #. . . . . . . . . . .number of cols\n"));
+        msg_rc(_("| mosaic-keep-aspect-ratio {0,1} . . .aspect ratio\n"));
+        msg_rc(  "| \n");
+    }
+    msg_rc(_("| help . . . . . . . . . . . . . this help message\n"));
+    msg_rc(_("| longhelp . . . . . . . . . a longer help message\n"));
+    msg_rc(_("| logout . . . . .  exit (if in socket connection)\n"));
+    msg_rc(_("| quit . . . . . . . . . . . . . . . . .  quit vlc\n"));
+    msg_rc(  "| \n");
+    msg_rc(_("+----[ end of help ]\n"));
+}
+
+/********************************************************************
+ * Status callback routines
+ ********************************************************************/
+static int TimeOffsetChanged( vlc_object_t *p_this, char const *psz_cmd,
+    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)p_data;
+    input_thread_t *p_input = NULL;
+    
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
+    if( p_input )
+    {
+        msg_rc( STATUS_CHANGE "( time-offset: %d )\r\n", var_GetInteger( p_input, "time-offset" ) );
+        vlc_object_release( p_input );
+    }
+    vlc_mutex_unlock( &p_intf->p_sys->status_lock );
+    return VLC_SUCCESS;
 }
 
+static int VolumeChanged( vlc_object_t *p_this, char const *psz_cmd,
+    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)p_data;
+    
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    msg_rc( STATUS_CHANGE "( audio volume: %d )\r\n", newval.i_int );
+    vlc_mutex_unlock( &p_intf->p_sys->status_lock );
+    return VLC_SUCCESS;
+}
+
+static int StateChanged( vlc_object_t *p_this, char const *psz_cmd,
+    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)p_data;
+    playlist_t    *p_playlist = NULL;
+    input_thread_t *p_input = NULL;
+        
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
+    if( p_input )
+    {
+        p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_PARENT );
+        if( p_playlist )
+        {
+            char cmd[5] = "";
+            switch( p_playlist->status.i_status )
+            {
+            case PLAYLIST_STOPPED:
+                strncpy( &cmd[0], "stop", 4);
+                cmd[4] = '\0';
+                break;
+            case PLAYLIST_RUNNING:
+                strncpy( &cmd[0], "play", 4);
+                cmd[4] = '\0';
+                break;
+            case PLAYLIST_PAUSED:
+                strncpy( &cmd[0], "pause", 5);
+                cmd[5] = '\0';
+                break;
+            } /* var_GetInteger( p_input, "state" )  */
+            msg_rc( STATUS_CHANGE "( %s state: %d )\r\n", &cmd[0], newval.i_int );
+            vlc_object_release( p_playlist );
+        }
+        vlc_object_release( p_input );
+    }
+    vlc_mutex_unlock( &p_intf->p_sys->status_lock );
+    return VLC_SUCCESS;
+}
+
+static int RateChanged( vlc_object_t *p_this, char const *psz_cmd,
+    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)p_data;
+    input_thread_t *p_input = NULL;
+    
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
+    if( p_input )
+    {
+        msg_rc( STATUS_CHANGE "( new rate: %d )\r\n", var_GetInteger( p_input, "rate" ) );
+        vlc_object_release( p_input );
+    }
+    vlc_mutex_unlock( &p_intf->p_sys->status_lock );
+    return VLC_SUCCESS;
+}
+
+/********************************************************************
+ * Command routines
+ ********************************************************************/
 static int Input( vlc_object_t *p_this, char const *psz_cmd,
                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
@@ -813,6 +1042,15 @@ static int Input( vlc_object_t *p_this, char const *psz_cmd,
     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
     if( !p_input ) return VLC_ENOOBJ;
 
+    var_Get( p_input, "state", &val );
+    if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
+        ( strcmp( psz_cmd, "pause" ) != 0 ) )
+    {
+        msg_rc( _("press pause to continue\r\n") );
+        vlc_object_release( p_input );
+        return VLC_EGENERIC;
+    }
+    
     /* Parse commands that only require an input */
     if( !strcmp( psz_cmd, "pause" ) )
     {
@@ -840,15 +1078,39 @@ static int Input( vlc_object_t *p_this, char const *psz_cmd,
     }
     else if ( !strcmp( psz_cmd, "fastforward" ) )
     {
-        val.i_int = INPUT_RATE_MAX;
-
-        var_Set( p_input, "rate", val );
+        val.i_int = config_GetInt( p_intf, "key-jump+3sec" );
+        var_Set( p_intf->p_vlc, "key-pressed", val );
+        
         vlc_object_release( p_input );
         return VLC_SUCCESS;
     }
     else if ( !strcmp( psz_cmd, "rewind" ) )
     {
-        val.i_int = INPUT_RATE_MIN;
+        val.i_int = config_GetInt( p_intf, "key-jump-3sec" );
+        var_Set( p_intf->p_vlc, "key-pressed", val );
+
+        vlc_object_release( p_input );
+        return VLC_SUCCESS;
+    }
+    else if ( !strcmp( psz_cmd, "faster" ) )
+    {
+        val.b_bool = VLC_TRUE;
+
+        var_Set( p_input, "rate-faster", val );
+        vlc_object_release( p_input );
+        return VLC_SUCCESS;
+    }
+    else if ( !strcmp( psz_cmd, "slower" ) )
+    {
+        val.b_bool = VLC_TRUE;
+
+        var_Set( p_input, "rate-slower", val );
+        vlc_object_release( p_input );
+        return VLC_SUCCESS;
+    }
+    else if ( !strcmp( psz_cmd, "normal" ) )
+    {
+        val.i_int = INPUT_RATE_DEFAULT;
 
         var_Set( p_input, "rate", val );
         vlc_object_release( p_input );
@@ -953,6 +1215,19 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
         return VLC_ENOOBJ;
     }
 
+    if( p_playlist->p_input )
+    {
+        vlc_value_t val;
+        var_Get( p_playlist->p_input, "state", &val );
+        if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
+            ( strcmp( psz_cmd, "pause" ) != 0 ) )
+        {
+            msg_rc( _("press pause to continue\r\n") );
+            vlc_object_release( p_playlist );
+            return VLC_EGENERIC;
+        }
+    }    
+    
     /* Parse commands that require a playlist */
     if( !strcmp( psz_cmd, "prev" ) )
     {
@@ -964,7 +1239,21 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
     }
     else if( !strcmp( psz_cmd, "play" ) )
     {
-        playlist_Play( p_playlist );
+        if( p_playlist->p_input )
+        {
+            vlc_value_t val;
+            
+            var_Get( p_playlist->p_input, "rate", &val );
+            if( val.i_int != INPUT_RATE_DEFAULT )
+            {
+                val.i_int = INPUT_RATE_DEFAULT;
+                var_Set( p_playlist->p_input, "rate", val );
+            }
+            else
+            {
+                playlist_Play( p_playlist );
+            }
+        }
     }
     else if (!strcmp( psz_cmd, "goto" ) )
     {
@@ -1023,37 +1312,48 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
     intf_thread_t *p_intf = (intf_thread_t*)p_this;
-    vlc_object_t *p_pl;
-    vlc_value_t     val;
-    vlc_object_t *p_inp;
+    vlc_object_t  *p_playlist;
+    vlc_value_t    val;
+    vlc_object_t  *p_input;
 
-    p_pl = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
-                                           FIND_ANYWHERE );
-    if( !p_pl )
+    p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
+    if( !p_playlist )
     {
         return VLC_ENOOBJ;
     }
     
-    p_inp = vlc_object_find( p_this, VLC_OBJECT_INPUT,
-                                           FIND_ANYWHERE );
-    if( !p_inp )
+    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
+    if( !p_input )
     {
-        vlc_object_release( p_pl );
+        vlc_object_release( p_playlist );
         return VLC_ENOOBJ;
     }
 
+    if( p_input )
+    {
+        var_Get( p_input, "state", &val );
+        if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
+            ( strcmp( psz_cmd, "pause" ) != 0 ) )
+        {
+            msg_rc( _("press pause to continue\r\n") );
+            vlc_object_release( p_playlist );
+            vlc_object_release( p_input );
+            return VLC_EGENERIC;
+        }
+    }
+    
     /* Parse miscellaneous commands */
     if( !strcmp( psz_cmd, "marq-marquee" ) )
     {
         if( strlen( newval.psz_string ) > 0 )
         {
             val.psz_string = newval.psz_string;
-            var_Set( p_inp->p_libvlc, "marq-marquee", val );
+            var_Set( p_input->p_libvlc, "marq-marquee", val );
         }
         else 
         {
                 val.psz_string = "";
-                var_Set( p_inp->p_libvlc, "marq-marquee", val);
+                var_Set( p_input->p_libvlc, "marq-marquee", val);
         }
     }
     else if( !strcmp( psz_cmd, "marq-x" ) )
@@ -1061,7 +1361,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "marq-x", val );
+            var_Set( p_input->p_libvlc, "marq-x", val );
         }
     }
     else if( !strcmp( psz_cmd, "marq-y" ) )
@@ -1069,7 +1369,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "marq-y", val );
+            var_Set( p_input->p_libvlc, "marq-y", val );
         }
     }
     else if( !strcmp( psz_cmd, "marq-position" ) )
@@ -1077,7 +1377,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "marq-position", val );
+            var_Set( p_input->p_libvlc, "marq-position", val );
         }
     }
     else if( !strcmp( psz_cmd, "marq-color" ) )
@@ -1085,7 +1385,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = strtol( newval.psz_string, NULL, 0 );
-            var_Set( p_inp->p_libvlc, "marq-color", val );
+            var_Set( p_input->p_libvlc, "marq-color", val );
         }
     }
     else if( !strcmp( psz_cmd, "marq-opacity" ) )
@@ -1093,7 +1393,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = strtol( newval.psz_string, NULL, 0 );
-            var_Set( p_inp->p_libvlc, "marq-opacity", val );
+            var_Set( p_input->p_libvlc, "marq-opacity", val );
         }
     }
     else if( !strcmp( psz_cmd, "marq-size" ) )
@@ -1101,7 +1401,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "marq-size", val );
+            var_Set( p_input->p_libvlc, "marq-size", val );
         }
     }
     else if( !strcmp( psz_cmd, "marq-timeout" ) )
@@ -1109,7 +1409,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp, "marq-timeout", val );
+            var_Set( p_input, "marq-timeout", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-alpha" ) )
@@ -1117,7 +1417,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-alpha", val );
+            var_Set( p_input->p_libvlc, "mosaic-alpha", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-height" ) )
@@ -1125,7 +1425,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-height", val );
+            var_Set( p_input->p_libvlc, "mosaic-height", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-width" ) )
@@ -1133,7 +1433,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-width", val );
+            var_Set( p_input->p_libvlc, "mosaic-width", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-xoffset" ) )
@@ -1141,7 +1441,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-xoffset", val );
+            var_Set( p_input->p_libvlc, "mosaic-xoffset", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-yoffset" ) )
@@ -1149,7 +1449,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-yoffset", val );
+            var_Set( p_input->p_libvlc, "mosaic-yoffset", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-align" ) )
@@ -1157,7 +1457,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0 )
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-align", val );
+            var_Set( p_input->p_libvlc, "mosaic-align", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-vborder" ) )
@@ -1165,7 +1465,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-vborder", val );
+            var_Set( p_input->p_libvlc, "mosaic-vborder", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-hborder" ) )
@@ -1173,7 +1473,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-hborder", val );
+            var_Set( p_input->p_libvlc, "mosaic-hborder", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-position" ) )
@@ -1181,7 +1481,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-position", val );
+            var_Set( p_input->p_libvlc, "mosaic-position", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-rows" ) )
@@ -1189,7 +1489,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-rows", val );
+            var_Set( p_input->p_libvlc, "mosaic-rows", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-cols" ) )
@@ -1197,7 +1497,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-cols", val );
+            var_Set( p_input->p_libvlc, "mosaic-cols", val );
         }
     }
     else if( !strcmp( psz_cmd, "mosaic-keep-aspect-ratio" ) )
@@ -1205,7 +1505,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0)
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "mosaic-keep-aspect-ratio", val );
+            var_Set( p_input->p_libvlc, "mosaic-keep-aspect-ratio", val );
         }
     }
     else if( !strcmp( psz_cmd, "time-format" ) )
@@ -1213,12 +1513,12 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0 )
         {
             val.psz_string = newval.psz_string;
-            var_Set( p_inp->p_libvlc, "time-format", val );
+            var_Set( p_input->p_libvlc, "time-format", val );
         }
         else 
         {
-                val.psz_string = "";
-                var_Set( p_inp->p_libvlc, "time-format", val);
+            val.psz_string = "";
+            var_Set( p_input->p_libvlc, "time-format", val);
         }
     }
     else if( !strcmp( psz_cmd, "time-x" ) )
@@ -1226,7 +1526,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "time-x", val );
+            var_Set( p_input->p_libvlc, "time-x", val );
         }
     }
     else if( !strcmp( psz_cmd, "time-y" ) )
@@ -1234,7 +1534,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "time-y", val );
+            var_Set( p_input->p_libvlc, "time-y", val );
         }
     }
     else if( !strcmp( psz_cmd, "time-position" ) )
@@ -1242,7 +1542,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "time-position", val );
+            var_Set( p_input->p_libvlc, "time-position", val );
         }
     }
     else if( !strcmp( psz_cmd, "time-color" ) )
@@ -1250,7 +1550,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = strtol( newval.psz_string, NULL, 0 );
-            var_Set( p_inp->p_libvlc, "time-color", val );
+            var_Set( p_input->p_libvlc, "time-color", val );
         }
     }
     else if( !strcmp( psz_cmd, "time-opacity" ) )
@@ -1258,7 +1558,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = strtol( newval.psz_string, NULL, 0 );
-            var_Set( p_inp->p_libvlc, "time-opacity", val );
+            var_Set( p_input->p_libvlc, "time-opacity", val );
         }
     }
     else if( !strcmp( psz_cmd, "time-size" ) )
@@ -1266,7 +1566,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "time-size", val );
+            var_Set( p_input->p_libvlc, "time-size", val );
         }
     }
     else if( !strcmp( psz_cmd, "logo-file" ) )
@@ -1274,7 +1574,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0 )
         {
             val.psz_string = newval.psz_string;
-            var_Set( p_inp->p_libvlc, "logo-file", val );
+            var_Set( p_input->p_libvlc, "logo-file", val );
         }
     }
     else if( !strcmp( psz_cmd, "logo-x" ) )
@@ -1282,7 +1582,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "logo-x", val );
+            var_Set( p_input->p_libvlc, "logo-x", val );
         }
     }
     else if( !strcmp( psz_cmd, "logo-y" ) )
@@ -1290,7 +1590,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "logo-y", val );
+            var_Set( p_input->p_libvlc, "logo-y", val );
         }
     }
     else if( !strcmp( psz_cmd, "logo-position" ) )
@@ -1298,7 +1598,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = atoi( newval.psz_string );
-            var_Set( p_inp->p_libvlc, "logo-position", val );
+            var_Set( p_input->p_libvlc, "logo-position", val );
         }
     }
     else if( !strcmp( psz_cmd, "logo-transparency" ) )
@@ -1306,7 +1606,7 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         if( strlen( newval.psz_string ) > 0) 
         {
             val.i_int = strtol( newval.psz_string, NULL, 0 );
-            var_Set( p_inp->p_libvlc, "logo-transparency", val );
+            var_Set( p_input->p_libvlc, "logo-transparency", val );
         }
     }
 
@@ -1318,14 +1618,22 @@ static int Other( vlc_object_t *p_this, char const *psz_cmd,
         msg_rc( "unknown command!\n" );
     }
 
-    vlc_object_release( p_pl );
-    vlc_object_release( p_inp );
+    vlc_object_release( p_playlist );
+    vlc_object_release( p_input );
     return VLC_SUCCESS;
 }
 
 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
+    playlist_t *p_playlist;
+    
+    p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
+    if( p_playlist )
+    {
+        playlist_Stop( p_playlist );
+        vlc_object_release( p_playlist );
+    }    
     p_this->p_vlc->b_die = VLC_TRUE;
     return VLC_SUCCESS;
 }
@@ -1360,13 +1668,23 @@ static int Volume( vlc_object_t *p_this, char const *psz_cmd,
     {
         /* Set. */
         audio_volume_t i_volume = atoi( newval.psz_string );
-        if ( i_volume > AOUT_VOLUME_MAX )
+        if ( (i_volume > (audio_volume_t)AOUT_VOLUME_MAX) )
         {
             msg_rc( "Volume must be in the range %d-%d\n", AOUT_VOLUME_MIN,
                     AOUT_VOLUME_MAX );
             i_error = VLC_EBADVAR;
         }
-        else i_error = aout_VolumeSet( p_this, i_volume );
+        else
+        {
+            if( i_volume == AOUT_VOLUME_MIN )
+            {
+                vlc_value_t keyval;
+
+                keyval.i_int = config_GetInt( p_intf, "key-vol-mute" );
+                var_Set( p_intf->p_vlc, "key-pressed", keyval );
+            }        
+            i_error = aout_VolumeSet( p_this, i_volume );
+        }
     }
     else
     {
@@ -1378,7 +1696,7 @@ static int Volume( vlc_object_t *p_this, char const *psz_cmd,
         }
         else
         {
-            msg_rc( "Volume is %d\n", i_volume );
+            msg_rc( STATUS_CHANGE "( audio volume: %d )\r\n", i_volume );
             i_error = VLC_SUCCESS;
         }
     }
@@ -1403,14 +1721,16 @@ static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
     {
         if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
             i_error = VLC_EGENERIC;
+        osd_VolumeUp( p_this );
     }
     else
     {
         if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
             i_error = VLC_EGENERIC;
+        osd_VolumeDown( p_this );
     }
 
-    if ( !i_error ) msg_rc( "Volume is %d\n", i_volume );
+    if ( !i_error ) msg_rc( STATUS_CHANGE "( audio volume: %d )\r\n", i_volume );
     return i_error;
 }
 
@@ -1489,6 +1809,65 @@ static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
     return i_error;
 }
 
+/* OSD menu commands */
+static int Menu( vlc_object_t *p_this, char const *psz_cmd,
+    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    playlist_t    *p_playlist = NULL;
+    vlc_value_t val;
+    int i_error = VLC_EGENERIC;
+
+    if ( !*newval.psz_string )
+    {
+        msg_rc( "please provide one of the following paramaters\r\n[on|off|up|down|left|right|select]\r\n" );
+        return i_error;
+    }
+    
+    p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
+    if( !p_playlist )
+        return VLC_ENOOBJ;
+
+    if( p_playlist->p_input )
+    {
+        var_Get( p_playlist->p_input, "state", &val );
+        if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
+            ( strcmp( newval.psz_string, "select" ) != 0 ) )
+        {
+            msg_rc( _("press menu select to continue\r\n") );
+            vlc_object_release( p_playlist );
+            return VLC_EGENERIC;
+        }
+        vlc_object_release( p_playlist );
+    }
+    
+    val.psz_string = strdup( newval.psz_string );
+    if( !strcmp( val.psz_string, "on" ) || !strcmp( val.psz_string, "show" ))
+        osd_MenuShow( p_this );
+    else if( !strcmp( val.psz_string, "off" ) || !strcmp( val.psz_string, "hide" ) )
+        osd_MenuHide( p_this );
+    else if( !strcmp( val.psz_string, "up" ) )
+        osd_MenuUp( p_this );
+    else if( !strcmp( val.psz_string, "down" ) )
+        osd_MenuDown( p_this );
+    else if( !strcmp( val.psz_string, "left" ) )
+        osd_MenuPrev( p_this );
+    else if( !strcmp( val.psz_string, "right" ) )
+        osd_MenuNext( p_this );
+    else if( !strcmp( val.psz_string, "select" ) )
+        osd_MenuActivate( p_this );
+    else
+    {
+        msg_rc( "please provide one of the following paramaters\r\n[on|off|up|down|left|right|select]\r\n" );
+        if( val.psz_string ) free( val.psz_string );
+            return i_error;
+    }
+
+    i_error = VLC_SUCCESS;
+    if( val.psz_string ) free( val.psz_string );
+    return i_error;
+}
+
 #ifdef WIN32
 vlc_bool_t ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
 {
index 940ebf2..fcb4eaf 100644 (file)
@@ -6,6 +6,7 @@
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *          Gildas Bazin <gbazin@videolan.org>
+ *          Jean-Paul Saman <jpsaman #_at_# m2x dot nl> 
  *
  * 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
@@ -271,6 +272,11 @@ static void transcode_spu_close  ( sout_stream_t *, sout_stream_id_t * );
 static int  transcode_spu_process( sout_stream_t *, sout_stream_id_t *,
                                    block_t *, block_t ** );
 
+static int  transcode_osd_new    ( sout_stream_t *, sout_stream_id_t * );
+static void transcode_osd_close  ( sout_stream_t *, sout_stream_id_t * );
+static int  transcode_osd_process( sout_stream_t *, sout_stream_id_t *,
+                                   block_t *, block_t ** );
+                                   
 static int  EncoderThread( struct sout_stream_sys_t * p_sys );
 
 static int pi_channels_maps[6] =
@@ -338,6 +344,13 @@ struct sout_stream_sys_t
     sout_cfg_t      *p_spu_cfg;
     spu_t           *p_spu;
 
+    /* OSD Menu */
+    sout_stream_id_t *id_osd;   /* extension for streaming OSD menus */
+    vlc_fourcc_t    i_osdcodec; /* codec osd menu (0 if not transcode) */
+    char            *psz_osdenc;
+    sout_cfg_t      *p_osd_cfg;
+    vlc_bool_t      b_osd;      /* VLC_TRUE when osd es is registered */
+    
     /* Sync */
     vlc_bool_t      b_master_sync;
     mtime_t         i_master_drift;
@@ -413,6 +426,13 @@ static int Open( vlc_object_t *p_this )
 
     if( p_sys->i_acodec )
     {
+        if( (strncmp( (char *)&p_sys->i_acodec, "mp3", 3) == 0) &&
+                            (p_sys->i_channels > 2) )
+        {
+            msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2",
+                      p_sys->i_channels );
+            p_sys->i_channels = 2;
+        }                    
         msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s",
                  (char *)&p_sys->i_acodec, p_sys->i_sample_rate,
                  p_sys->i_channels, p_sys->i_abitrate / 1000 );
@@ -518,7 +538,7 @@ static int Open( vlc_object_t *p_this )
     }
 
     /* Subpictures transcoding parameters */
-    p_sys->p_spu = 0;
+    p_sys->p_spu = NULL;
     p_sys->psz_senc = NULL;
     p_sys->p_spu_cfg = NULL;
     p_sys->i_scodec = 0;
@@ -560,6 +580,36 @@ static int Open( vlc_object_t *p_this )
     }
     if( val.psz_string ) free( val.psz_string );
 
+    /* OSD menu transcoding parameters */
+    p_sys->psz_osdenc = NULL;
+    p_sys->p_osd_cfg  = NULL;
+    p_sys->i_osdcodec = 0;
+    p_sys->b_osd      = VLC_FALSE;
+    if( config_GetInt( p_stream, "osd" ) )
+    {
+/*        vlc_value_t val;*/
+        char *psz_next;
+       
+        psz_next = sout_CfgCreate( &p_sys->psz_osdenc,
+                                   &p_sys->p_osd_cfg, strdup( "dvbsub") );
+        if( psz_next ) free( psz_next );
+        
+        p_sys->i_osdcodec = VLC_FOURCC('Y','U','V','P' );        
+
+        msg_Dbg( p_stream, "codec osd=%4.4s", (char *)&p_sys->i_osdcodec );
+
+/*        val.psz_string = strdup("osdmenu");
+        if( !p_sys->p_spu )
+        {
+            p_sys->p_spu = spu_Create( p_stream );
+            var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
+            spu_Init( p_sys->p_spu );
+        }            
+        var_Set( p_sys->p_spu, "sub-filter", val );        
+        if( val.psz_string ) free( val.psz_string );*/
+    }
+
+    /* Audio settings */
     var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val );
     p_sys->b_master_sync = val.b_bool;
     if( p_sys->f_fps > 0 ) p_sys->b_master_sync = VLC_TRUE;
@@ -639,6 +689,20 @@ static void Close( vlc_object_t * p_this )
     if( p_sys->psz_senc ) free( p_sys->psz_senc );
 
     if( p_sys->p_spu ) spu_Destroy( p_sys->p_spu );
+    
+    while( p_sys->p_osd_cfg != NULL )
+    {
+        sout_cfg_t *p_next = p_sys->p_osd_cfg->p_next;
+
+        if( p_sys->p_osd_cfg->psz_name )
+            free( p_sys->p_osd_cfg->psz_name );
+        if( p_sys->p_osd_cfg->psz_value )
+            free( p_sys->p_osd_cfg->psz_value );
+        free( p_sys->p_osd_cfg );
+
+        p_sys->p_osd_cfg = p_next;
+    }
+    if( p_sys->psz_osdenc ) free( p_sys->psz_osdenc );    
 
     vlc_object_destroy( p_sys );
 }
@@ -666,7 +730,6 @@ struct sout_stream_id_t
     date_t          interpolated_pts;
 };
 
-
 static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
@@ -833,6 +896,19 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         if( !id->id ) goto error;
     }
 
+    if( config_GetInt( p_stream, "osd" ) )
+    {
+        /* Create a fake OSD menu elementary stream */
+        if( !p_sys->b_osd && (p_sys->i_osdcodec != 0 || p_sys->psz_osdenc) )
+        {
+            if( transcode_osd_new( p_stream, p_sys->id_osd ) )
+            {
+                msg_Err( p_stream, "cannot create osd chain" );
+                goto error;
+            }
+            p_sys->b_osd = VLC_TRUE;
+        }
+    }
     return id;
 
  error:
@@ -856,6 +932,9 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
 
+    if( config_GetInt( p_stream, "osd" ) && p_sys->b_osd )
+        transcode_osd_close( p_stream, p_sys->id_osd );
+
     if( id->b_transcode )
     {
         switch( id->p_decoder->fmt_in.i_cat )
@@ -895,10 +974,15 @@ static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
                  block_t *p_buffer )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
-    block_t *p_out;
+    block_t *p_out = NULL;
 
     if( !id->b_transcode && id->id )
     {
+        /* Transcode OSD menu pictures. */
+        if( p_sys->b_osd )
+        {
+            transcode_osd_process( p_stream, id, p_buffer, &p_out );                
+        }
         return p_sys->p_out->pf_send( p_sys->p_out, id->id, p_buffer );
     }
     else if( !id->b_transcode )
@@ -1025,7 +1109,7 @@ static int transcode_audio_new( sout_stream_t *p_stream,
     id->p_decoder->pf_decode_audio = 0;
     id->p_decoder->pf_aout_buffer_new = audio_new_buffer;
     id->p_decoder->pf_aout_buffer_del = audio_del_buffer;
-    //id->p_decoder->p_cfg = p_sys->p_video_cfg;
+    /* id->p_decoder->p_cfg = p_sys->p_audio_cfg; */
 
     id->p_decoder->p_module =
         module_Need( id->p_decoder, "decoder", "$codec", 0 );
@@ -1318,7 +1402,7 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
     for( i = 0; i < PICTURE_RING_SIZE; i++ )
         id->p_decoder->p_owner->pp_pics[i] = 0;
     id->p_decoder->p_owner->p_sys = p_sys;
-    //id->p_decoder->p_cfg = p_sys->p_video_cfg;
+    /* id->p_decoder->p_cfg = p_sys->p_video_cfg; */
 
     id->p_decoder->p_module =
         module_Need( id->p_decoder, "decoder", "$codec", 0 );
@@ -2114,7 +2198,7 @@ static int transcode_spu_new( sout_stream_t *p_stream, sout_stream_id_t *id )
     id->p_decoder->pf_spu_buffer_new = spu_new_buffer;
     id->p_decoder->pf_spu_buffer_del = spu_del_buffer;
     id->p_decoder->p_owner = (decoder_owner_sys_t *)p_stream;
-    //id->p_decoder->p_cfg = p_sys->p_spu_cfg;
+    /* id->p_decoder->p_cfg = p_sys->p_spu_cfg; */
 
     id->p_decoder->p_module =
         module_Need( id->p_decoder, "decoder", "$codec", 0 );
@@ -2217,3 +2301,186 @@ static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic )
     sout_stream_t *p_stream = (sout_stream_t *)p_dec->p_owner;
     spu_DestroySubpicture( p_stream->p_sys->p_spu, p_subpic );
 }
+
+/*
+ * OSD menu
+ */
+static int transcode_osd_new( sout_stream_t *p_stream, sout_stream_id_t *id )
+{
+    sout_stream_sys_t *p_sys = p_stream->p_sys;
+    es_format_t fmt;
+    
+    fmt.i_cat = SPU_ES;
+    fmt.i_id = 0xbd1f; /* pid ?? */
+    fmt.i_group = 3;   /* pmt entry ?? */
+    fmt.i_codec = VLC_FOURCC( 'Y', 'U', 'V', 'A' );
+    fmt.psz_language = strdup( "osd" );
+    
+    id = malloc( sizeof( sout_stream_id_t ) );
+    memset( id, 0, sizeof(sout_stream_id_t) );
+
+    id->id = NULL;
+    id->p_decoder = NULL;
+    id->p_encoder = NULL;
+
+    /* Create encoder object */
+    id->p_encoder = vlc_object_create( p_stream, VLC_OBJECT_ENCODER );
+    if( !id->p_encoder )
+    {
+        msg_Err( p_stream, "out of memory" );
+        goto error;
+    }
+    vlc_object_attach( id->p_encoder, p_stream );
+    id->p_encoder->p_module = NULL;
+
+    /* Create fake destination format */
+    es_format_Init( &id->p_encoder->fmt_out, fmt.i_cat, 0 );
+    id->p_encoder->fmt_out.i_id    = fmt.i_id;
+    id->p_encoder->fmt_out.i_group = fmt.i_group;
+    id->p_encoder->fmt_out.psz_language = strdup( fmt.psz_language );
+    
+    if( p_sys->i_osdcodec != 0 || p_sys->psz_osdenc )
+    {
+        msg_Dbg( p_stream, "creating osdmenu transcoding from fcc=`%4.4s' "
+                 "to fcc=`%4.4s'", (char*)&fmt.i_codec,
+                 (char*)&p_sys->i_osdcodec );
+
+        /* Complete destination format */
+        id->p_encoder->fmt_out.i_codec = p_sys->i_osdcodec;
+        
+        /*
+         * Open encoder
+         */
+        
+        /* Initialization of encoder format structures */
+        es_format_Init( &id->p_encoder->fmt_in, fmt.i_cat, fmt.i_codec );
+        id->p_encoder->fmt_in.psz_language = strdup( fmt.psz_language );
+
+        id->p_encoder->p_cfg = p_sys->p_osd_cfg;
+
+        id->p_encoder->p_module =
+            module_Need( id->p_encoder, "encoder", p_sys->psz_osdenc, VLC_TRUE );
+
+        if( !id->p_encoder->p_module )
+        {
+            msg_Err( p_stream, "cannot find encoder" );
+            goto error;
+        }
+        
+        /* open output stream */
+        id->id = p_sys->p_out->pf_add( p_sys->p_out, &id->p_encoder->fmt_out );
+        id->b_transcode = VLC_TRUE;
+
+        if( !id->id ) goto error;
+    }
+    else
+    {
+        msg_Dbg( p_stream, "not transcoding a stream (fcc=`%4.4s')",
+                 (char*)&fmt.i_codec );
+        id->id = p_sys->p_out->pf_add( p_sys->p_out, &fmt );
+        id->b_transcode = VLC_FALSE;
+
+        if( !id->id ) goto error;
+    }
+
+    p_sys->id_osd = id;
+    p_sys->b_osd = VLC_TRUE;
+
+    if( !p_sys->p_spu )
+    {
+        p_sys->p_spu = spu_Create( p_stream );
+        if( spu_Init( p_sys->p_spu ) != VLC_SUCCESS )
+            msg_Err( p_sys, "spu initialisation failed" );
+    }
+    
+    if( fmt.psz_language )
+        free( fmt.psz_language );
+        
+    return VLC_SUCCESS;
+    
+ error:
+    msg_Err( p_stream, "starting osd encoding thread failed" );
+    if( id->p_encoder->p_module )
+            module_Unneed( id->p_encoder, id->p_encoder->p_module );
+    if( id->p_encoder )
+    {
+        vlc_object_detach( id->p_encoder );
+        vlc_object_destroy( id->p_encoder );
+    }
+    if( fmt.psz_language ) free( fmt.psz_language );
+    if( id ) free( id );
+    p_sys->id_osd = NULL;
+    p_sys->b_osd = VLC_FALSE;
+    return VLC_EGENERIC;
+}
+    
+static void transcode_osd_close( sout_stream_t *p_stream, sout_stream_id_t *id)
+{    
+    sout_stream_sys_t *p_sys = p_stream->p_sys;
+    
+    /* Close encoder */
+    if( p_sys->b_osd && id )
+    {
+        if( id->p_encoder->p_module )
+            module_Unneed( id->p_encoder, id->p_encoder->p_module );
+        
+        if( id->id ) p_sys->p_out->pf_del( p_sys->p_out, id->id );
+    
+        if( id->p_encoder )
+        {
+            vlc_object_detach( id->p_encoder );
+            vlc_object_destroy( id->p_encoder );
+        }
+    }
+    p_sys->b_osd = VLC_FALSE;
+    if( id ) free( id );
+}
+
+static int transcode_osd_process( sout_stream_t *p_stream,
+                                  sout_stream_id_t *id,
+                                  block_t *in, block_t **out )
+{
+    sout_stream_sys_t *p_sys = p_stream->p_sys;
+    subpicture_t *p_subpic = NULL;
+    
+    /* Check if we have a subpicture to send */
+    if( p_sys->p_spu && in->i_dts > 0)
+    {
+        p_subpic = spu_SortSubpictures( p_sys->p_spu, in->i_dts );
+    }
+    else
+    {
+        msg_Warn( p_stream, "spu channel not initialized, doing it now" );
+        if( !p_sys->p_spu )
+        {
+            p_sys->p_spu = spu_Create( p_stream );
+            if( spu_Init( p_sys->p_spu ) != VLC_SUCCESS )
+                msg_Err( p_stream, "spu initialisation failed" );
+        }
+    }
+        
+    if( p_subpic )
+    {
+        block_t *p_block = NULL;
+        
+        if( p_sys->b_master_sync && p_sys->i_master_drift )
+        {
+            p_subpic->i_start -= p_sys->i_master_drift;
+            if( p_subpic->i_stop ) p_subpic->i_stop -= p_sys->i_master_drift;
+        }
+    
+        p_block = p_sys->id_osd->p_encoder->pf_encode_sub( p_sys->id_osd->p_encoder, p_subpic );
+        if( p_block )
+        {
+            p_block->i_dts = p_block->i_pts = in->i_dts;
+            block_ChainAppend( out, p_block );
+            if( *out )
+            {
+                if( p_sys->p_out->pf_send( p_sys->p_out, p_sys->id_osd->id, *out ) == VLC_SUCCESS )
+                    spu_DestroySubpicture( p_sys->p_spu, p_subpic );
+            }            
+            return VLC_SUCCESS;            
+        }
+    }
+    return VLC_EGENERIC;
+}
index 24a035f..d8ab571 100644 (file)
@@ -16,4 +16,5 @@ SOURCES_marq = marq.c
 SOURCES_rss = rss.c
 SOURCES_motiondetect = motiondetect.c
 SOURCES_rv32 = rv32.c
+SOURCES_osdmenu = osdmenu.c
 noinst_HEADERS += filter_common.h
diff --git a/modules/video_filter/osdmenu.c b/modules/video_filter/osdmenu.c
new file mode 100644 (file)
index 0000000..5039608
--- /dev/null
@@ -0,0 +1,398 @@
+/*****************************************************************************
+ * osdmenu.c: osd filter module
+ *****************************************************************************
+ * Copyright (C) 2004-2005 M2X
+ * $Id: osdmenu.c 11131 2005-05-23 11:04:07Z hartman $
+ *
+ * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
+ *
+ * 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 implid 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, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+
+#include <vlc_filter.h>
+#include <vlc_video.h>
+
+#include <osd.h>
+#include <vlc_osd.h>
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+/* FIXME: Future extension make the definition file in XML format. */
+#define OSD_FILE_TEXT N_("OSD menu configuration file")
+#define OSD_FILE_LONGTEXT N_( \
+    "An OSD menu configuration file that menu actions with button images" )
+
+#define OSD_PATH_TEXT N_("Path to OSD menu images")
+#define OSD_PATH_LONGTEXT N_( \
+    "Specify another path to the OSD menu images. This will override the path as defined in the " \
+    "OSD configuration file." )
+
+#define POSX_TEXT N_("X coordinate of the OSD menu")
+#define POSX_LONGTEXT N_("You can move the OSD menu by left-clicking on it." )
+
+#define POSY_TEXT N_("Y coordinate of the OSD menu")
+#define POSY_LONGTEXT N_("You can move the OSD menu by left-clicking on it." )
+
+#define POS_TEXT N_("OSD menu position")
+#define POS_LONGTEXT N_( \
+  "You can enforce the OSD menu position on the video " \
+  "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
+  "also use combinations of these values).")
+
+#define TIMEOUT_TEXT N_("Timeout of OSD menu")
+#define TIMEOUT_LONGTEXT N_( \
+    "OSD menu pictures get a default timeout of 15 seconds added to their remaining time." \
+    "This will ensure that they are at least the specified time visible.")
+    
+static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
+static char *ppsz_pos_descriptions[] =
+{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
+  N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
+
+/* subfilter functions */
+static int  CreateFilter ( vlc_object_t * );
+static void DestroyFilter( vlc_object_t * );
+static subpicture_t *Filter( filter_t *, mtime_t );
+static int OSDMenuUpdateEvent( vlc_object_t *, char const *,
+                    vlc_value_t, vlc_value_t, void * );                    
+static int OSDMenuVisibleEvent( vlc_object_t *, char const *,
+                    vlc_value_t, vlc_value_t, void * );
+
+#define OSD_CFG "osdmenu-"
+
+vlc_module_begin();
+    add_integer( OSD_CFG "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_FALSE );
+    add_integer( OSD_CFG "y", -1, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_FALSE );
+    add_integer( OSD_CFG "position", 8, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
+        change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
+    add_string( OSD_CFG "file", NULL, NULL, OSD_FILE_TEXT, OSD_FILE_LONGTEXT, VLC_FALSE );
+    add_string( OSD_CFG "file-path", NULL, NULL, OSD_PATH_TEXT, OSD_PATH_LONGTEXT, VLC_FALSE );
+    add_integer( OSD_CFG "timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT, VLC_FALSE );
+
+    set_capability( "sub filter", 100 );
+    set_description( N_("On Screen Display menu subfilter") );
+    set_shortname( N_("OSD menu") );
+    add_shortcut( "osdmenu" );
+    set_category( CAT_VIDEO );
+    set_subcategory( SUBCAT_VIDEO_SUBPIC );
+    set_callbacks( CreateFilter, DestroyFilter );
+vlc_module_end();
+
+/*****************************************************************************
+ * Sub filter code
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+struct filter_sys_t
+{
+    vlc_mutex_t  lock;
+
+    int          position;      /* relative positioning of SPU images */
+    mtime_t      i_last_date;   /* last mdate SPU object has been sent to SPU subsytem */
+    int          i_timeout;     /* duration SPU object is valid on the video output in seconds */
+    
+    vlc_bool_t   b_absolute;    /* do we use absolute positioning or relative? */
+    vlc_bool_t   b_update;      /* Update OSD Menu by sending SPU objects */
+    vlc_bool_t   b_visible;     /* OSD Menu is visible */
+    
+    char        *psz_file;      /* OSD Menu configuration file */
+    osd_menu_t  *p_menu;        /* pointer to OSD Menu object */
+};
+
+/*****************************************************************************
+ * CreateFilter: Create the filter and open the definition file
+ *****************************************************************************/
+static int CreateFilter ( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    vlc_value_t val;
+    int posx, posy;
+
+    p_filter->p_sys = (filter_sys_t *) malloc( sizeof( filter_sys_t ) );
+    if( !p_filter->p_sys )
+    {
+        msg_Err( p_filter, "out of memory" );
+        return VLC_ENOMEM;
+    }        
+    
+    /* Populating struct */
+    p_filter->p_sys->p_menu = NULL;
+    p_filter->p_sys->psz_file = NULL;
+    
+    vlc_mutex_init( p_filter, &p_filter->p_sys->lock );
+
+    p_filter->p_sys->psz_file = config_GetPsz( p_filter, OSD_CFG "file" );
+    if( p_filter->p_sys->psz_file == NULL || *p_filter->p_sys->psz_file == '\0' ) 
+    {
+        msg_Err( p_filter, "unable to get filename" );
+        goto error;
+    }
+
+    var_Create( p_this, OSD_CFG "position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    var_Get( p_this, OSD_CFG "position", &val );
+    p_filter->p_sys->position = val.i_int;
+    var_Create( p_this, OSD_CFG "x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    var_Get( p_this, OSD_CFG "x", &val );
+    posx = val.i_int;
+    var_Create( p_this, OSD_CFG "y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    var_Get( p_this, OSD_CFG "y", &val );
+    posy = val.i_int;
+    var_Create( p_this, OSD_CFG "timeout", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    var_Get( p_this, OSD_CFG "timeout", &val );
+    p_filter->p_sys->i_timeout = val.i_int; /* in seconds */
+
+    /* Load the osd menu subsystem */
+    p_filter->p_sys->p_menu = osd_MenuCreate( p_this, p_filter->p_sys->psz_file );
+    if( p_filter->p_sys->p_menu == NULL )
+        goto error;
+    
+    /* Check if menu position was overridden */
+    p_filter->p_sys->b_absolute = VLC_TRUE;
+    
+    if( posx < 0 || posy < 0)
+    {
+        p_filter->p_sys->b_absolute = VLC_FALSE;
+        p_filter->p_sys->p_menu->i_x = 0;
+        p_filter->p_sys->p_menu->i_y = 0;
+    }
+    else if( posx >= 0 || posy >= 0 )
+    {
+        p_filter->p_sys->p_menu->i_x = posx;
+        p_filter->p_sys->p_menu->i_y = posy;
+    }
+    else if( p_filter->p_sys->p_menu->i_x < 0 || p_filter->p_sys->p_menu->i_y < 0 )
+    {
+        p_filter->p_sys->b_absolute = VLC_FALSE;
+        p_filter->p_sys->p_menu->i_x = 0;
+        p_filter->p_sys->p_menu->i_y = 0;
+    }
+    
+    /* Set up p_filter */
+    p_filter->p_sys->i_last_date = mdate();
+    
+    /* Keep track of OSD Events */
+    p_filter->p_sys->b_update = VLC_FALSE;
+    p_filter->p_sys->b_visible = VLC_FALSE;
+    
+    var_AddCallback( p_filter->p_sys->p_menu, "osd-menu-update", OSDMenuUpdateEvent, p_filter );        
+    var_AddCallback( p_filter->p_sys->p_menu, "osd-menu-visible", OSDMenuVisibleEvent, p_filter );        
+
+    /* Attach subpicture filter callback */
+    p_filter->pf_sub_filter = Filter;
+    
+    es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
+    p_filter->fmt_out.i_priority = 0;
+    
+    msg_Dbg( p_filter, "successfully loaded osdmenu filter" );    
+    return VLC_SUCCESS;
+    
+error:
+    msg_Err( p_filter, "osdmenu filter discarded" );
+    vlc_mutex_destroy( &p_filter->p_sys->lock );
+    if( p_filter->p_sys->p_menu )
+    {
+        osd_MenuDelete( p_this, p_filter->p_sys->p_menu );
+        p_filter->p_sys->p_menu = NULL;
+    }
+    if( p_filter->p_sys->psz_file ) free( p_filter->p_sys->psz_file );
+    if( p_filter->p_sys ) free( p_filter->p_sys );
+    return VLC_EGENERIC;    
+}
+
+/*****************************************************************************
+ * DestroyFilter: Make a clean exit of this plugin
+ *****************************************************************************/
+static void DestroyFilter( vlc_object_t *p_this )
+{
+    filter_t     *p_filter = (filter_t*)p_this;
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    var_Destroy( p_this, OSD_CFG "file" );
+    var_Destroy( p_this, OSD_CFG "x" );
+    var_Destroy( p_this, OSD_CFG "y" );
+    var_Destroy( p_this, OSD_CFG "position" );
+    var_Destroy( p_this, OSD_CFG "timeout" );
+    
+    var_DelCallback( p_sys->p_menu, "osd-menu-update", OSDMenuUpdateEvent, p_filter );        
+    var_DelCallback( p_sys->p_menu, "osd-menu-visible", OSDMenuVisibleEvent, p_filter );        
+
+    osd_MenuDelete( p_filter, p_sys->p_menu );
+    
+    vlc_mutex_destroy( &p_filter->p_sys->lock );    
+    if( p_sys->psz_file) free( p_sys->psz_file );    
+    if( p_sys ) free( p_sys );   
+     
+    msg_Dbg( p_filter, "osdmenu filter destroyed" );    
+}
+
+/*****************************************************************************
+ * OSDMenuEvent: callback for OSD Menu events
+ *****************************************************************************/
+static int OSDMenuVisibleEvent( vlc_object_t *p_this, char const *psz_var,
+                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    filter_t *p_filter = (filter_t *) p_data;
+    
+    p_filter->p_sys->b_visible = VLC_TRUE;        
+    return VLC_SUCCESS;
+}
+
+static int OSDMenuUpdateEvent( vlc_object_t *p_this, char const *psz_var,
+                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    filter_t *p_filter = (filter_t *) p_data;
+    
+    p_filter->p_sys->b_update = VLC_TRUE;        
+    return VLC_SUCCESS;
+}
+
+#if 0
+/*****************************************************************************
+ * create_text_region : compose a text region SPU
+ *****************************************************************************/
+static subpicture_region_t *create_text_region( filter_t *p_filter, subpicture_t *p_spu, 
+    int i_width, int i_height, const char *psz_text )
+{
+    subpicture_region_t *p_region;
+    video_format_t       fmt;    
+    
+    /* Create new SPU region */
+    memset( &fmt, 0, sizeof(video_format_t) );
+    fmt.i_chroma = VLC_FOURCC( 'T','E','X','T' );
+    fmt.i_aspect = VOUT_ASPECT_FACTOR;
+    fmt.i_sar_num = fmt.i_sar_den = 1;
+    fmt.i_width = fmt.i_visible_width = i_width;
+    fmt.i_height = fmt.i_visible_height = i_height;
+    fmt.i_x_offset = fmt.i_y_offset = 0;
+    p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
+    if( !p_region )
+    {
+        msg_Err( p_filter, "cannot allocate another SPU region" );
+        return NULL;
+    }
+    p_region->psz_text = strdup( psz_text );
+    p_region->i_x = 0; 
+    p_region->i_y = 40;
+#if 1    
+    msg_Dbg( p_filter, "SPU text region position (%d,%d) (%d,%d) [%s]", 
+        p_region->i_x, p_region->i_y, 
+        p_region->fmt.i_width, p_region->fmt.i_height, p_region->psz_text );
+#endif
+    return p_region;                                
+}    
+#endif
+
+/*****************************************************************************
+ * create_picture_region : compose a text region SPU
+ *****************************************************************************/
+static subpicture_region_t *create_picture_region( filter_t *p_filter, subpicture_t *p_spu,
+    int i_width, int i_height, picture_t *p_pic )
+{
+    subpicture_region_t *p_region;
+    video_format_t       fmt;    
+
+        /* Create new SPU region */
+    memset( &fmt, 0, sizeof(video_format_t) );
+    fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
+    fmt.i_aspect = VOUT_ASPECT_FACTOR;
+    fmt.i_sar_num = fmt.i_sar_den = 1;
+    fmt.i_width = fmt.i_visible_width = i_width;
+    fmt.i_height = fmt.i_visible_height = i_height;
+    fmt.i_x_offset = fmt.i_y_offset = 0;
+    p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
+    if( !p_region )
+    {
+        msg_Err( p_filter, "cannot allocate SPU region" );
+        p_filter->pf_sub_buffer_del( p_filter, p_spu );
+        return NULL;
+    }
+    vout_CopyPicture( p_filter, &p_region->picture, p_pic );
+    p_region->i_x = 0;
+    p_region->i_y = 0;
+
+#if 0        
+    msg_Dbg( p_filter, "SPU picture region position (%d,%d) (%d,%d) [%p]", 
+        p_region->i_x, p_region->i_y, 
+        p_region->fmt.i_width, p_region->fmt.i_height, p_pic );
+#endif
+    return p_region;
+}
+
+/****************************************************************************
+ * Filter: the whole thing
+ ****************************************************************************
+ * This function outputs subpictures at regular time intervals.
+ ****************************************************************************/
+static subpicture_t *Filter( filter_t *p_filter, mtime_t i_date )
+{
+    filter_sys_t *p_sys = p_filter->p_sys;    
+    subpicture_t *p_spu;
+    subpicture_region_t *p_region;
+    
+    if( !p_filter->p_sys->b_update ) return NULL;
+
+    p_filter->p_sys->i_last_date = i_date;
+    p_filter->p_sys->b_update = VLC_FALSE; 
+    
+    /* Allocate the subpicture internal data. */
+    p_spu = p_filter->pf_sub_buffer_new( p_filter );
+    if( !p_spu ) return NULL;
+    
+    p_spu->b_absolute = p_sys->b_absolute;    
+    p_spu->i_start = p_sys->i_last_date = i_date; 
+    /* this never works why ? */
+    p_spu->i_stop = (p_sys->i_timeout == 0) ? 0 : i_date + (mtime_t)(p_sys->i_timeout * 1000000);
+    p_spu->b_ephemer = VLC_TRUE;
+    p_spu->b_fade = VLC_TRUE;    
+    p_spu->i_flags = p_sys->position;
+    p_filter->p_sys->b_update = VLC_FALSE; 
+    
+    /* Send an empty subpicture to clear the display
+     * when OSD menu should be hidden and menu picture is not allocated.
+     */
+    if( !p_filter->p_sys->p_menu->p_state->p_pic )
+        return p_spu;
+    if( p_filter->p_sys->b_visible == VLC_FALSE )
+        return p_spu;
+            
+    /* Create new spu regions */    
+    p_region = create_picture_region( p_filter, p_spu, 
+        p_filter->p_sys->p_menu->p_state->i_width, p_filter->p_sys->p_menu->p_state->i_height, 
+        p_filter->p_sys->p_menu->p_state->p_pic );
+    #if 0        
+    p_region->p_next = create_text_region( p_filter, p_spu, 
+        p_filter->p_sys->p_menu->p_state->i_width, p_filter->p_sys->p_menu->p_state->i_height,         
+        p_filter->p_sys->p_menu->p_state->p_visible->psz_action );
+    #endif  
+    
+    /* proper positioning of OSD menu image */
+    p_spu->i_x = p_filter->p_sys->p_menu->p_state->i_x;
+    p_spu->i_y = p_filter->p_sys->p_menu->p_state->i_y;
+    
+    p_spu->p_region = p_region;
+    return p_spu;
+}
index c614c86..e3f7223 100644 (file)
@@ -1,2 +1,2 @@
-DIST_SUBDIRS = interface playlist input audio_output video_output stream_output misc
+DIST_SUBDIRS = interface playlist input audio_output video_output stream_output misc osd
 EXTRA_DIST = misc/modules_builtin.h.in
index 443d885..b28b707 100644 (file)
 #include "vlc_vlm.h"
 
 #include "vlc_image.h"
+#include "vlc_osd.h"
 
 #if defined( _MSC_VER ) && defined( UNDER_CE )
 #    include "modules_builtin_evc.h"
index 23ba6c6..b03a1dc 100644 (file)
@@ -55,6 +55,7 @@
 #include "vlc_vod.h"
 #include "vlc_tls.h"
 #include "vlc_xml.h"
+#include "vlc_osd.h"
 
 /*****************************************************************************
  * Local prototypes
@@ -207,6 +208,10 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type )
             i_size = sizeof( announce_handler_t );
             psz_type = "announce handler";
             break;
+        case VLC_OBJECT_OSDMENU:
+            i_size = sizeof( osd_menu_t );
+            psz_type = "osd menu";
+            break;
         default:
             i_size = i_type > 0
                       ? i_type > (int)sizeof(vlc_object_t)
diff --git a/src/osd/osd.c b/src/osd/osd.c
new file mode 100644 (file)
index 0000000..659b58a
--- /dev/null
@@ -0,0 +1,620 @@
+/*****************************************************************************\r
+ * osd.c - The OSD Menu core code.\r
+ *****************************************************************************\r
+ * Copyright (C) 2005 M2X\r
+ * $Id: osd.c 9451 2004-12-01 01:07:08Z jpsaman $\r
+ *\r
+ * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.\r
+ *****************************************************************************/\r
+\r
+/*****************************************************************************\r
+ * Preamble\r
+ *****************************************************************************/\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include <vlc/vlc.h>\r
+#include <vlc_keys.h>\r
+#include <vlc_osd.h>\r
+\r
+#undef OSD_MENU_DEBUG\r
+\r
+/*****************************************************************************\r
+ * Local prototypes\r
+ *****************************************************************************/\r
+\r
+static void osd_UpdateState( osd_menu_state_t *, int, int, int, int, picture_t * );\r
+static inline osd_state_t *osd_VolumeStateChange( osd_state_t *, int );\r
+\r
+osd_menu_t *__osd_MenuCreate( vlc_object_t *p_this, const char *psz_file )\r
+{\r
+    osd_menu_t  *p_osd = NULL;\r
+    vlc_value_t lockval;\r
+    int         i_volume = 0;\r
+    int         i_steps = 0;\r
+\r
+    /* to be sure to avoid multiple creation */\r
+    var_Create( p_this->p_libvlc, "osd_mutex", VLC_VAR_MUTEX );\r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+    \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        vlc_value_t val;\r
+        \r
+        msg_Dbg( p_this, "creating osd menu object" );\r
+        if( ( p_osd = vlc_object_create( p_this, VLC_OBJECT_OSDMENU ) ) == NULL )\r
+        {\r
+            msg_Err( p_this, "out of memory" );\r
+            vlc_mutex_unlock( lockval.p_address );\r
+            return NULL;\r
+        }\r
+\r
+        /* Parse configuration file */\r
+        if( osd_ConfigLoader( p_this, psz_file, &p_osd ) )\r
+            goto error;\r
+        \r
+        /* Setup default button (first button) */\r
+        p_osd->p_state->p_visible = p_osd->p_button;\r
+        p_osd->p_state->p_visible->p_current_state =\r
+            osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );\r
+        p_osd->i_width  = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch;\r
+        p_osd->i_height = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines;\r
+\r
+        /* Update the volume state images to match the current volume */\r
+        i_volume = config_GetInt( p_this, "volume" );\r
+        i_steps = (i_volume / AOUT_VOLUME_STEP / 3); /* 3 is a magic number for 32 volume decrease steps */\r
+        p_osd->p_state->p_volume->p_current_state = osd_VolumeStateChange( p_osd->p_state->p_volume->p_states, i_steps );\r
+\r
+        /* Initialize OSD state */\r
+        osd_UpdateState( p_osd->p_state, p_osd->i_x, p_osd->i_y,\r
+                         p_osd->i_width, p_osd->i_height, NULL );\r
+        \r
+        vlc_object_yield( p_osd );\r
+        vlc_object_attach( p_osd, p_this->p_vlc );\r
+        \r
+        /* Signal when an update of OSD menu is needed */\r
+        var_Create( p_osd, "osd-menu-update", VLC_VAR_BOOL );\r
+        var_Create( p_osd, "osd-menu-visible", VLC_VAR_BOOL );\r
+\r
+        val.b_bool = VLC_FALSE;\r
+        var_Set( p_osd, "osd-menu-update", val );\r
+        var_Set( p_osd, "osd-menu-visible", val );        \r
+    }\r
+    vlc_mutex_unlock( lockval.p_address );\r
+    return p_osd;\r
+    \r
+error:\r
+    msg_Err( p_this, "creating osd menu object failed" );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+    vlc_object_destroy( p_osd );\r
+    return NULL;    \r
+}\r
+\r
+void __osd_MenuDelete( vlc_object_t *p_this, osd_menu_t *p_osd )\r
+{\r
+    vlc_value_t lockval;\r
+\r
+    if( !p_osd || !p_this ) return;\r
+    \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+\r
+    vlc_object_release( p_osd );    \r
+    if( p_osd->i_refcount > 0 )\r
+    {\r
+        vlc_mutex_unlock( lockval.p_address );        \r
+        return;\r
+    }    \r
+\r
+    var_Destroy( p_osd, "osd-menu-visible" );\r
+    var_Destroy( p_osd, "osd-menu-update" );\r
+    \r
+    osd_ConfigUnload( p_this, &p_osd );\r
+    vlc_object_detach( p_osd );\r
+    vlc_object_destroy( p_osd );\r
+    p_osd = NULL;\r
+    \r
+    vlc_mutex_unlock( lockval.p_address );        \r
+}\r
+\r
+osd_state_t *__osd_StateChange( osd_state_t *p_states, const int i_state )\r
+{\r
+    osd_state_t *p_current = p_states;\r
+    osd_state_t *p_temp = NULL;\r
+    int i = 0;\r
+\r
+    for( i=0; p_current != NULL; i++ )\r
+    {\r
+        if( p_current->i_state == i_state )\r
+            return p_current;\r
+        p_temp = p_current->p_next;\r
+        p_current = p_temp;\r
+    }\r
+    return p_states;\r
+}\r
+\r
+/* The volume can be modified in another interface while the OSD Menu \r
+ * has not been instantiated yet. This routines updates the "volume OSD menu item"\r
+ * to reflect the current state of the GUI.\r
+ */\r
+static inline osd_state_t *osd_VolumeStateChange( osd_state_t *p_current, int i_steps )\r
+{\r
+    osd_state_t *p_temp = NULL;\r
+    int i;\r
+    \r
+    for( i=0; (i < i_steps) && (p_current != NULL); i++ )\r
+    {    \r
+        p_temp = p_current->p_next;\r
+        if( !p_temp ) return p_current;\r
+        p_current = p_temp;\r
+    }\r
+    return (!p_temp) ? p_current : p_temp;\r
+}\r
+\r
+/* Update the state of the OSD Menu */\r
+static void osd_UpdateState( osd_menu_state_t *p_state, int i_x, int i_y,\r
+        int i_width, int i_height, picture_t *p_pic )\r
+{\r
+    p_state->i_x = i_x;\r
+    p_state->i_y = i_y;\r
+    p_state->i_width = i_width;\r
+    p_state->i_height = i_height;\r
+    p_state->p_pic = p_pic;\r
+}\r
+\r
+void __osd_MenuShow( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+    \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_MenuNext failed" );\r
+        return;\r
+    }        \r
+\r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+\r
+#if defined(OSD_MENU_DEBUG)\r
+    msg_Dbg( p_osd, "menu on" );\r
+#endif            \r
+    p_button = p_osd->p_state->p_visible;\r
+    if( p_button )\r
+    {                    \r
+        if( !p_button->b_range ) \r
+            p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );\r
+        p_osd->p_state->p_visible = p_osd->p_button;\r
+        \r
+        if( !p_osd->p_state->p_visible->b_range ) \r
+            p_osd->p_state->p_visible->p_current_state =\r
+                osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );\r
+        \r
+        osd_UpdateState( p_osd->p_state,\r
+                p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic );                \r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+    }\r
+    osd_SetMenuVisible( p_osd, VLC_TRUE );\r
+    \r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
+            \r
+void __osd_MenuHide( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    vlc_value_t lockval;\r
+    \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_MenuNext failed" );\r
+        return;\r
+    }\r
+        \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+\r
+#if defined(OSD_MENU_DEBUG)\r
+    msg_Dbg( p_osd, "menu off" );\r
+#endif\r
+    osd_UpdateState( p_osd->p_state,\r
+                p_osd->p_state->i_x, p_osd->p_state->i_y,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                NULL );\r
+    osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+\r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
+\r
+void __osd_MenuActivate( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+    \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_MenuNext failed" );\r
+        return;\r
+    }\r
+    \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+    \r
+#if defined(OSD_MENU_DEBUG)\r
+    msg_Dbg( p_osd, "select" );\r
+#endif\r
+    p_button = p_osd->p_state->p_visible;\r
+    /*\r
+     * Is there a menu item above or below? If so, then select it.\r
+     */\r
+    if( p_button && p_button->p_up)\r
+    {\r
+        vlc_object_release( (vlc_object_t*) p_osd );\r
+        vlc_mutex_unlock( lockval.p_address );\r
+        __osd_MenuUp( p_this );   /* "menu select" means go to menu item above. */\r
+        return;\r
+    }    \r
+    if( p_button && p_button->p_down)\r
+    {    \r
+        vlc_object_release( (vlc_object_t*) p_osd );\r
+        vlc_mutex_unlock( lockval.p_address );\r
+        __osd_MenuDown( p_this ); /* "menu select" means go to menu item below. */\r
+        return;\r
+    }\r
+\r
+    if( p_button && !p_button->b_range )\r
+    {\r
+        p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_PRESSED );\r
+        osd_UpdateState( p_osd->p_state,\r
+                p_button->i_x, p_button->i_y,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_button->p_current_state->p_pic );\r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+        osd_SetMenuVisible( p_osd, VLC_TRUE );\r
+        osd_SetKeyPressed( VLC_OBJECT(p_osd->p_vlc), config_GetInt( p_osd, p_button->psz_action ) );\r
+#if defined(OSD_MENU_DEBUG)\r
+        msg_Dbg( p_osd, "select (%d, %s)", config_GetInt( p_osd, p_button->psz_action ), p_button->psz_action );\r
+#endif\r
+    }\r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );            \r
+}           \r
+\r
+void __osd_MenuNext( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+    \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_MenuNext failed" );\r
+        return;\r
+    }\r
+            \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+    \r
+    p_button = p_osd->p_state->p_visible;\r
+    if( p_button )\r
+    {\r
+        if( !p_button->b_range ) \r
+            p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );\r
+        if( p_button->p_next )\r
+            p_osd->p_state->p_visible = p_button->p_next;\r
+        else\r
+            p_osd->p_state->p_visible = p_osd->p_button;\r
+            \r
+        if( !p_osd->p_state->p_visible->b_range ) \r
+            p_osd->p_state->p_visible->p_current_state =\r
+                osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );\r
+        \r
+        osd_UpdateState( p_osd->p_state, \r
+                p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic );\r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+    }\r
+#if defined(OSD_MENU_DEBUG)\r
+    msg_Dbg( p_osd, "direction right [button %s]", p_osd->p_state->p_visible->psz_action );            \r
+#endif\r
+    \r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
+\r
+void __osd_MenuPrev( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+    \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_MenuPrev failed" );\r
+        return;\r
+    }\r
+\r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+    \r
+    p_button = p_osd->p_state->p_visible;\r
+    if( p_button )\r
+    {                    \r
+        if( !p_button->b_range ) \r
+            p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );\r
+        if( p_button->p_prev )\r
+            p_osd->p_state->p_visible = p_button->p_prev;\r
+        else\r
+            p_osd->p_state->p_visible = p_osd->p_last_button;                \r
+        \r
+        if( !p_osd->p_state->p_visible->b_range ) \r
+            p_osd->p_state->p_visible->p_current_state =\r
+                osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );\r
+        \r
+        osd_UpdateState( p_osd->p_state, \r
+                p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic );\r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+    }\r
+#if defined(OSD_MENU_DEBUG)\r
+    msg_Dbg( p_osd, "direction left [button %s]", p_osd->p_state->p_visible->psz_action );            \r
+#endif\r
+            \r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
+\r
+void __osd_MenuUp( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+#if defined(OSD_MENU_DEBUG)    \r
+    vlc_value_t val;\r
+#endif\r
+        \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_MenuDown failed" );\r
+        return;\r
+    }\r
+            \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+\r
+    p_button = p_osd->p_state->p_visible;\r
+    if( p_button )\r
+    {\r
+        if( !p_button->b_range ) \r
+        {\r
+            p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_SELECT );\r
+            if( p_button->p_up )\r
+                p_osd->p_state->p_visible = p_button->p_up;                    \r
+        }\r
+        \r
+        if( p_button->b_range && p_osd->p_state->p_visible->b_range ) \r
+        {                    \r
+            osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state;\r
+            if( p_temp && p_temp->p_next )\r
+                p_osd->p_state->p_visible->p_current_state = p_temp->p_next;\r
+        }\r
+        else if( !p_osd->p_state->p_visible->b_range )\r
+        {    \r
+            p_osd->p_state->p_visible->p_current_state =\r
+                osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );\r
+        } \r
+        \r
+        osd_UpdateState( p_osd->p_state, \r
+                p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic );\r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+        /* If this is a range style action with associated images of only one state, \r
+            * then perform "menu select" on every menu navigation\r
+            */\r
+        if( p_button->b_range ) \r
+        {\r
+            osd_SetKeyPressed( VLC_OBJECT(p_osd->p_vlc), config_GetInt(p_osd, p_button->psz_action) );\r
+#if defined(OSD_MENU_DEBUG)\r
+            msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action );\r
+#endif\r
+        }\r
+    }\r
+#if defined(OSD_MENU_DEBUG)\r
+    msg_Dbg( p_osd, "direction up [button %s]", p_osd->p_state->p_visible->psz_action );            \r
+#endif            \r
+    \r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
+\r
+void __osd_MenuDown( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+#if defined(OSD_MENU_DEBUG)    \r
+    vlc_value_t val;\r
+#endif\r
+        \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_MenuDown failed" );\r
+        return;\r
+    }\r
+    \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+    \r
+    p_button = p_osd->p_state->p_visible;\r
+    if( p_button )\r
+    {\r
+        if( !p_button->b_range ) \r
+        {\r
+            p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_SELECT );\r
+            if( p_button->p_down )\r
+                p_osd->p_state->p_visible = p_button->p_down;\r
+        }\r
+        \r
+        if( p_button->b_range && p_osd->p_state->p_visible->b_range ) \r
+        {\r
+            osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state;\r
+            if( p_temp && p_temp->p_prev )\r
+                p_osd->p_state->p_visible->p_current_state = p_temp->p_prev;\r
+        }\r
+        else if( !p_osd->p_state->p_visible->b_range )\r
+        {\r
+            p_osd->p_state->p_visible->p_current_state =\r
+                osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );\r
+        }\r
+\r
+        osd_UpdateState( p_osd->p_state, \r
+                p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_osd->p_state->p_visible->p_current_state->p_pic );\r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+        /* If this is a range style action with associated images of only one state, \r
+         * then perform "menu select" on every menu navigation\r
+         */\r
+        if( p_button->b_range ) \r
+        {\r
+            osd_SetKeyPressed( VLC_OBJECT(p_osd->p_vlc), config_GetInt(p_osd, p_button->psz_action_down) );\r
+#if defined(OSD_MENU_DEBUG)\r
+            msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action_down );\r
+#endif\r
+        }\r
+    }\r
+#if defined(OSD_MENU_DEBUG)\r
+    msg_Dbg( p_osd, "direction down [button %s]", p_osd->p_state->p_visible->psz_action );            \r
+#endif            \r
+\r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
+\r
+/**\r
+ * Audio volume up\r
+ *\r
+ * The OSD Menu audio volume bar is updated to reflect the new audio volume. Call this function\r
+ * when the audio volume is updated outside the OSD menu command "menu up".\r
+ */\r
+void __osd_VolumeUp( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+    int i_volume = 0;\r
+    int i_steps = 0;\r
+    \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_VolumeUp failed" );\r
+        return;\r
+    }\r
+    \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+\r
+    /* Update the volume state images to match the current volume */\r
+    i_volume = config_GetInt( p_this, "volume" );\r
+    i_steps = (i_volume / AOUT_VOLUME_STEP / 3); /* 3 is a magic number for 32 volume decrease steps */\r
+    p_osd->p_state->p_volume->p_current_state = osd_VolumeStateChange( p_osd->p_state->p_volume->p_states, i_steps );\r
+    \r
+    p_button = p_osd->p_state->p_volume;\r
+    if( p_osd->p_state->p_volume ) \r
+        p_osd->p_state->p_visible = p_osd->p_state->p_volume;\r
+    if( p_button && p_button->b_range )\r
+    {\r
+        osd_state_t *p_temp = p_button->p_current_state;\r
+        if( p_temp->p_next )\r
+            p_button->p_current_state = p_temp->p_next;\r
+        \r
+        osd_UpdateState( p_osd->p_state, \r
+                p_button->i_x, p_button->i_y,\r
+                p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_button->p_current_state->p_pic );\r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+        osd_SetMenuVisible( p_osd, VLC_TRUE );\r
+    }\r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
+\r
+/**\r
+ * Audio volume down\r
+ *\r
+ * The OSD Menu audio volume bar is updated to reflect the new audio volume. Call this function\r
+ * when the audio volume is updated outside the OSD menu command "menu down".\r
+ */\r
+void __osd_VolumeDown( vlc_object_t *p_this )\r
+{\r
+    osd_menu_t *p_osd = NULL;\r
+    osd_button_t *p_button = NULL;\r
+    vlc_value_t lockval;\r
+    int i_volume = 0;\r
+    int i_steps = 0;\r
+        \r
+    if( ( p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ) ) == NULL )\r
+    {\r
+        msg_Err( p_this, "osd_VolumeDown failed" );\r
+        return;\r
+    }\r
+    \r
+    var_Get( p_this->p_libvlc, "osd_mutex", &lockval );\r
+    vlc_mutex_lock( lockval.p_address );\r
+\r
+    /* Update the volume state images to match the current volume */\r
+    i_volume = config_GetInt( p_this, "volume" );\r
+    i_steps = (i_volume / AOUT_VOLUME_STEP / 3); /* 3 is a magic number for 32 volume decrease steps */\r
+    p_osd->p_state->p_volume->p_current_state = osd_VolumeStateChange( p_osd->p_state->p_volume->p_states, i_steps );\r
+\r
+    p_button = p_osd->p_state->p_volume;\r
+    if( p_osd->p_state->p_volume ) \r
+        p_osd->p_state->p_visible = p_osd->p_state->p_volume;            \r
+    if( p_button && p_button->b_range )\r
+    {\r
+        osd_state_t *p_temp = p_button->p_current_state;\r
+        if( p_temp && p_temp->p_prev )\r
+            p_button->p_current_state = p_temp->p_prev;\r
+\r
+        osd_UpdateState( p_osd->p_state, \r
+                p_button->i_x, p_button->i_y,\r
+                p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,\r
+                p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,\r
+                p_button->p_current_state->p_pic );\r
+        osd_SetMenuUpdate( p_osd, VLC_TRUE );\r
+        osd_SetMenuVisible( p_osd, VLC_TRUE );\r
+    }\r
+    vlc_object_release( (vlc_object_t*) p_osd );\r
+    vlc_mutex_unlock( lockval.p_address );\r
+}\r
diff --git a/src/osd/osd_parser.c b/src/osd/osd_parser.c
new file mode 100644 (file)
index 0000000..020c3b2
--- /dev/null
@@ -0,0 +1,929 @@
+/*****************************************************************************\r
+ * osd_parser.c - The OSD Menu  parser core code.\r
+ *****************************************************************************\r
+ * Copyright (C) 2005 M2X\r
+ * $Id: osd_parser.c 9451 2004-12-01 01:07:08Z jpsaman $\r
+ *\r
+ * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.\r
+ *****************************************************************************/\r
+\r
+/*****************************************************************************\r
+ * Preamble\r
+ *****************************************************************************/\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include <vlc/vlc.h>\r
+#include <vlc_config.h>\r
+//#include <vlc_es.h>\r
+#include <vlc_video.h>\r
+\r
+#include <vlc_keys.h>\r
+#include <vlc_image.h>\r
+#include <vlc_osd.h>\r
+\r
+\r
+#undef OSD_MENU_DEBUG\r
+\r
+/*****************************************************************************\r
+ * Local prototypes\r
+ *****************************************************************************/\r
+static const char *ppsz_button_states[] = { "unselect", "select", "pressed" };\r
+\r
+/* OSD Menu structure support routines */\r
+static osd_menu_t   *osd_MenuNew( osd_menu_t *, const char *, int, int );\r
+static osd_button_t *osd_ButtonNew( const char *, int, int );\r
+static osd_state_t  *osd_StateNew( vlc_object_t *, const char *, const char * );\r
+\r
+static void osd_MenuFree  ( vlc_object_t *, osd_menu_t * );\r
+static void osd_ButtonFree( vlc_object_t *, osd_button_t * );\r
+static void osd_StatesFree( vlc_object_t *, osd_state_t * );\r
+\r
+static picture_t *osd_LoadImage( vlc_object_t *, const char *);\r
+\r
+#if 0\r
+/*****************************************************************************\r
+ * osd_YuvaYuvp\r
+ *****************************************************************************/\r
+static picture_t *osd_YuvaYuvp( vlc_object_t *p_this, picture_t *p_picture )\r
+{\r
+    video_format_t *p_fmt = NULL;\r
+    int i = 0, j = 0, n = 0, p = 0;\r
+    int i_max_entries = 256;\r
+\r
+#ifdef RANDOM_DITHERING\r
+    int i_seed = 0xdeadbeef; /* random seed */\r
+#else\r
+    int *pi_delta = NULL;\r
+#endif\r
+    int i_pixels = p_picture->p[0].i_visible_lines\r
+                    * p_picture->p[0].i_pitch;\r
+    int i_iterator = p_picture->p[0].i_visible_lines * 3 / 4\r
+                        * p_picture->p[0].i_pitch\r
+                    + p_picture->p[0].i_pitch * 1 / 3;\r
+    int i_tolerance = 0;\r
+\r
+    p_fmt = (video_format_t*) malloc( sizeof( video_format_t ) );\r
+    if( !p_fmt )\r
+    {\r
+        msg_Err( p_this, "couldn't allocate video_format_t ... aborting YUVA to YUVP conversion of picture" );\r
+        return p_picture;\r
+    }\r
+    p_fmt->i_chroma = VLC_FOURCC('Y','U','V','P');\r
+    p_fmt->p_palette = (video_palette_t *) malloc( sizeof( video_palette_t ) );\r
+    if( !p_fmt->p_palette )\r
+    {\r
+        msg_Err( p_this, "couldn't allocate video_palette_t ... aborting YUVA to YUVP conversion of picture" );\r
+        free( p_fmt );\r
+        return p_picture;        \r
+    }    \r
+    p_fmt->p_palette->i_entries = 0;\r
+\r
+    /* Find best iterator using Euclide’s algorithm */\r
+    for( ; i_iterator > 1 ; i_iterator-- )\r
+    {\r
+        int a = i_pixels;\r
+        int b = i_iterator;\r
+        int c;\r
+\r
+        while( b )\r
+        {\r
+            c = a % b;\r
+            a = b;\r
+            b = c;\r
+        }\r
+\r
+        if( a == 1 )\r
+        {\r
+            break;\r
+        }\r
+    }\r
+\r
+    /* Count colors, build best palette */\r
+    for( i_tolerance = 0; i_tolerance < 128; i_tolerance++ )\r
+    {\r
+        vlc_bool_t b_success = VLC_TRUE;\r
+        p_fmt->p_palette->i_entries = 0;\r
+\r
+        for( i = 0; i < i_pixels ; )\r
+        {\r
+            uint8_t y  = 0, u = 0, v = 0, a = 0;\r
+            y = p_picture->p[0].p_pixels[i];\r
+            u = p_picture->p[1].p_pixels[i];\r
+            v = p_picture->p[2].p_pixels[i];\r
+            a = p_picture->p[3].p_pixels[i];\r
+            for( j = 0; j < p_fmt->p_palette->i_entries; j++ )\r
+            {\r
+                if( abs((int)p_fmt->p_palette->palette[j][0] - (int)y) <= i_tolerance &&\r
+                    abs((int)p_fmt->p_palette->palette[j][1] - (int)u) <= i_tolerance &&\r
+                    abs((int)p_fmt->p_palette->palette[j][2] - (int)v) <= i_tolerance &&\r
+                    abs((int)p_fmt->p_palette->palette[j][3] - (int)a) <= i_tolerance / 2 )\r
+                {\r
+                    break;\r
+                }\r
+            }\r
+            if( j == p_fmt->p_palette->i_entries )\r
+            {\r
+                p_fmt->p_palette->palette[j][0] = y;\r
+                p_fmt->p_palette->palette[j][1] = u;\r
+                p_fmt->p_palette->palette[j][2] = v;\r
+                p_fmt->p_palette->palette[j][3] = a;\r
+                p_fmt->p_palette->i_entries++;\r
+            }\r
+            if( p_fmt->p_palette->i_entries >= i_max_entries )\r
+            {\r
+                b_success = VLC_FALSE;\r
+                break;\r
+            }\r
+            i += i_iterator;\r
+            if( i > i_pixels )\r
+            {\r
+                i -= i_pixels;\r
+            }\r
+        }\r
+\r
+        if( b_success )\r
+        {\r
+            break;\r
+        }\r
+    }\r
+\r
+#if OSD_MENU_DEBUG\r
+    msg_Dbg( p_this, "best palette has %d colors", p_fmt->p_palette->i_entries );\r
+#endif\r
+\r
+#ifndef RANDOM_DITHERING\r
+    pi_delta = malloc( ( p_picture->p[0].i_pitch + 1 ) * sizeof(int) * 4  );\r
+    if( !pi_delta )\r
+    {\r
+        msg_Err( p_this, "couldn't allocate video_palette_t ... aborting YUVA to YUVP conversion of picture" );\r
+        free( p_fmt->p_palette );\r
+        free( p_fmt );\r
+        return p_picture;        \r
+    }    \r
+    \r
+    for( i = 0; i < (p_picture->p[0].i_pitch + 1) * 4 ; i++ )\r
+    {\r
+        pi_delta[ i ] = 0;\r
+    }\r
+#endif\r
+\r
+    /* Fill image with our new colours */\r
+    for( p = 0; p < p_picture->p[0].i_visible_lines ; p++ )\r
+    {\r
+        int i_ydelta = 0, i_udelta = 0, i_vdelta = 0, i_adelta = 0;\r
+\r
+        for( n = 0; n < p_picture->p[0].i_pitch ; n++ )\r
+        {\r
+            int i_offset = p * p_picture->p[0].i_pitch + n;\r
+            int y, u, v, a;\r
+            int i_mindist, i_best;\r
+\r
+            y = (int)p_picture->p[0].p_pixels[i_offset];\r
+            u = (int)p_picture->p[1].p_pixels[i_offset];\r
+            v = (int)p_picture->p[2].p_pixels[i_offset];\r
+            a = (int)p_picture->p[3].p_pixels[i_offset];\r
+\r
+            /* Add dithering compensation */\r
+#ifdef RANDOM_DITHERING\r
+            y += ((i_seed & 0xff) - 0x80) * i_tolerance / 0x80;\r
+            u += (((i_seed >> 8) & 0xff) - 0x80) * i_tolerance / 0x80;\r
+            v += (((i_seed >> 16) & 0xff) - 0x80) * i_tolerance / 0x80;\r
+            a += (((i_seed >> 24) & 0xff) - 0x80) * i_tolerance / 0x80;\r
+#else\r
+            y += i_ydelta + pi_delta[ n * 4 ];\r
+            u += i_udelta + pi_delta[ n * 4 + 1 ];\r
+            v += i_vdelta + pi_delta[ n * 4 + 2 ];\r
+            a += i_adelta + pi_delta[ n * 4 + 3 ];\r
+#endif\r
+\r
+            /* Find best colour in palette */\r
+            for( i_mindist = 99999999, i_best = 0, j = 0; j < p_fmt->p_palette->i_entries; j++ )\r
+            {\r
+                int i_dist = 0;\r
+\r
+                i_dist += abs((int)p_fmt->p_palette->palette[j][0] - y);\r
+                i_dist += abs((int)p_fmt->p_palette->palette[j][1] - u);\r
+                i_dist += abs((int)p_fmt->p_palette->palette[j][2] - v);\r
+                i_dist += 2 * abs((int)p_fmt->p_palette->palette[j][3] - a);\r
+\r
+                if( i_dist < i_mindist )\r
+                {\r
+                    i_mindist = i_dist;\r
+                    i_best = j;\r
+                }\r
+            }\r
+\r
+            /* Set pixel to best color */\r
+            p_picture->p[0].p_pixels[i_offset] = i_best;\r
+\r
+            /* Update dithering state */\r
+#ifdef RANDOM_DITHERING\r
+            i_seed = (i_seed * 0x1283837) ^ 0x789479 ^ (i_seed >> 13);\r
+#else\r
+            i_ydelta = y - (int)p_fmt->p_palette->palette[i_best][0];\r
+            i_udelta = u - (int)p_fmt->p_palette->palette[i_best][1];\r
+            i_vdelta = v - (int)p_fmt->p_palette->palette[i_best][2];\r
+            i_adelta = a - (int)p_fmt->p_palette->palette[i_best][3];\r
+            pi_delta[ n * 4 ] = i_ydelta * 3 / 8;\r
+            pi_delta[ n * 4 + 1 ] = i_udelta * 3 / 8;\r
+            pi_delta[ n * 4 + 2 ] = i_vdelta * 3 / 8;\r
+            pi_delta[ n * 4 + 3 ] = i_adelta * 3 / 8;\r
+            i_ydelta = i_ydelta * 5 / 8;\r
+            i_udelta = i_udelta * 5 / 8;\r
+            i_vdelta = i_vdelta * 5 / 8;\r
+            i_adelta = i_adelta * 5 / 8;\r
+#endif\r
+        }\r
+    }\r
+\r
+#ifndef RANDOM_DITHERING\r
+    free( pi_delta );\r
+#endif\r
+\r
+    /* pad palette */\r
+    for( i = p_fmt->p_palette->i_entries; i < i_max_entries; i++ )\r
+    {\r
+        p_fmt->p_palette->palette[i][0] = 0;\r
+        p_fmt->p_palette->palette[i][1] = 0;\r
+        p_fmt->p_palette->palette[i][2] = 0;\r
+        p_fmt->p_palette->palette[i][3] = 0;\r
+    }\r
+    p_fmt->p_palette->i_entries = i_max_entries;\r
+#if OSD_MENU_DEBUG\r
+    msg_Dbg( p_this, "best palette has %d colors", p_fmt->p_palette->i_entries );\r
+#endif\r
+\r
+    p_picture->format.i_chroma = VLC_FOURCC('Y','U','V','P');\r
+    if( p_picture->format.p_palette )\r
+        free( p_picture->format.p_palette );\r
+    p_picture->format.p_palette = p_fmt->p_palette;\r
+    free( p_fmt );\r
+    return p_picture;\r
+}\r
+#endif\r
+/*****************************************************************************\r
+ * osd_LoadImage: loads the logo image into memory\r
+ *****************************************************************************/\r
+static picture_t *osd_LoadImage( vlc_object_t *p_this, const char *psz_filename )\r
+{\r
+    picture_t *p_pic = NULL;\r
+    image_handler_t *p_image;\r
+    video_format_t fmt_in = {0}, fmt_out = {0};\r
+\r
+    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');\r
+    p_image = image_HandlerCreate( p_this );\r
+    if( p_image )\r
+    {\r
+        p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );\r
+        image_HandlerDelete( p_image );\r
+#if 0        \r
+        p_pic = osd_YuvaYuvp( p_this, p_pic );\r
+#endif\r
+    }\r
+    else msg_Err( p_this, "unable to handle this chroma" );\r
+\r
+    return p_pic;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Create a new Menu structure\r
+ *****************************************************************************/\r
+static osd_menu_t *osd_MenuNew( osd_menu_t *p_menu, const char *psz_path, int i_x, int i_y )\r
+{\r
+    if( !p_menu ) return NULL;\r
+    \r
+    p_menu->p_state = (osd_menu_state_t *) malloc( sizeof( osd_menu_state_t ) );\r
+    if( !p_menu->p_state )\r
+        msg_Err( p_menu, "memory allocation for OSD Menu state failed." );\r
+\r
+    if( psz_path != NULL )\r
+        p_menu->psz_path = strdup( psz_path );\r
+    else\r
+        p_menu->psz_path = NULL;\r
+    p_menu->i_x = i_x;\r
+    p_menu->i_y = i_y;\r
+    \r
+    return p_menu; \r
+}\r
+\r
+/*****************************************************************************\r
+ * Free the menu\r
+ *****************************************************************************/\r
+static void osd_MenuFree( vlc_object_t *p_this, osd_menu_t *p_menu )\r
+{\r
+    msg_Dbg( p_this, "freeing menu" );\r
+    osd_ButtonFree( p_this, p_menu->p_button );\r
+    p_menu->p_button = NULL;\r
+    p_menu->p_last_button = NULL;\r
+    if( p_menu->psz_path ) free( p_menu->psz_path );\r
+    p_menu->psz_path = NULL;\r
+    if( p_menu->p_state ) free( p_menu->p_state );\r
+    p_menu->p_state = NULL;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Create a new button\r
+ *****************************************************************************/\r
+static osd_button_t *osd_ButtonNew( const char *psz_action, int i_x, int i_y )\r
+{\r
+    osd_button_t *p_button = NULL;\r
+    p_button = (osd_button_t*) malloc( sizeof(osd_button_t) );\r
+    if( !p_button )\r
+        return NULL;\r
+    \r
+    memset( p_button, 0, sizeof(osd_button_t) );\r
+    p_button->psz_action = strdup(psz_action);\r
+    p_button->psz_action_down = NULL;\r
+    p_button->p_feedback = NULL;\r
+    p_button->i_x = i_x;\r
+    p_button->i_y = i_y;\r
+    \r
+    return p_button;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Free a button\r
+ *****************************************************************************/ \r
+static void osd_ButtonFree( vlc_object_t *p_this, osd_button_t *p_button )\r
+{\r
+    osd_button_t *p_current = p_button;\r
+    osd_button_t *p_next = NULL;\r
+    osd_button_t *p_prev = NULL;\r
+    \r
+    /* First walk to the end. */\r
+    while( p_current->p_next )\r
+    {\r
+        p_next = p_current->p_next;\r
+        p_current = p_next;        \r
+    }\r
+    /* Then free end first and walk to the start. */\r
+    while( p_current->p_prev )\r
+    {\r
+        msg_Dbg( p_this, "+ freeing button %s [%p]", p_current->psz_action, p_current );\r
+        p_prev = p_current->p_prev;\r
+        p_current = p_prev;\r
+        if( p_current->p_next )\r
+        {\r
+            if( p_current->p_next->psz_name ) \r
+                free( p_current->p_next->psz_name );\r
+            if( p_current->p_next->psz_action )\r
+                free( p_current->p_next->psz_action );\r
+            if( p_current->p_next->psz_action_down )\r
+                free( p_current->p_next->psz_action_down );\r
+            if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
+                free( p_current->p_feedback->p_data_orig );\r
+            if( p_current->p_feedback )\r
+                free( p_current->p_feedback );\r
+            \r
+            p_current->p_next->psz_action_down = NULL;\r
+            p_current->p_next->psz_action = NULL;\r
+            p_current->p_next->psz_name = NULL;\r
+            p_current->p_feedback = NULL;\r
+                            \r
+            /* Free all states first */            \r
+            if( p_current->p_next->p_states )   \r
+                osd_StatesFree( p_this, p_current->p_next->p_states );\r
+            p_current->p_next->p_states = NULL;          \r
+            if( p_current->p_next) free( p_current->p_next );\r
+            p_current->p_next = NULL;  \r
+        }            \r
+        \r
+        if( p_current->p_up )\r
+        {\r
+            if( p_current->p_up->psz_name ) \r
+                free( p_current->p_up->psz_name );\r
+            if( p_current->p_up->psz_action )\r
+                free( p_current->p_up->psz_action );\r
+            if( p_current->p_up->psz_action_down )\r
+                free( p_current->p_up->psz_action_down );\r
+            if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
+                free( p_current->p_feedback->p_data_orig );\r
+            if( p_current->p_feedback )\r
+                free( p_current->p_feedback );\r
+            \r
+            p_current->p_up->psz_action_down = NULL;\r
+            p_current->p_up->psz_action = NULL;\r
+            p_current->p_up->psz_name = NULL;\r
+            p_current->p_feedback = NULL;\r
+            \r
+            /* Free all states first */            \r
+            if( p_current->p_up->p_states )   \r
+                osd_StatesFree( p_this, p_current->p_up->p_states );\r
+            p_current->p_up->p_states = NULL;          \r
+            if( p_current->p_up ) free( p_current->p_up );\r
+            p_current->p_up = NULL;          \r
+        }\r
+    }    \r
+    /* Free the last one. */\r
+    if( p_button ) \r
+    {\r
+        msg_Dbg( p_this, "+ freeing button %s [%p]", p_button->psz_action, p_button );    \r
+        if( p_button->psz_name ) free( p_button->psz_name );\r
+        if( p_button->psz_action ) free( p_button->psz_action );\r
+        if( p_button->psz_action_down ) free( p_button->psz_action_down );   \r
+        if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
+            free( p_current->p_feedback->p_data_orig );\r
+        if( p_current->p_feedback )\r
+            free( p_current->p_feedback );\r
+        \r
+        p_button->psz_name = NULL;\r
+        p_button->psz_action = NULL;\r
+        p_button->psz_action_down = NULL;\r
+        p_current->p_feedback = NULL;\r
+                \r
+        if( p_button->p_states )\r
+            osd_StatesFree( p_this, p_button->p_states );\r
+        p_button->p_states = NULL;          \r
+        free( p_button );\r
+        p_button = NULL;\r
+    }\r
+}\r
+\r
+/*****************************************************************************\r
+ * Create a new state image\r
+ *****************************************************************************/\r
+static osd_state_t *osd_StateNew( vlc_object_t *p_this, const char *psz_file, const char *psz_state )\r
+{\r
+    osd_state_t *p_state = NULL;\r
+    p_state = (osd_state_t*) malloc( sizeof(osd_state_t) );\r
+    if( !p_state )\r
+        return NULL;\r
+        \r
+    memset( p_state, 0, sizeof(osd_state_t) );    \r
+    p_state->p_pic = osd_LoadImage( p_this, psz_file );\r
+\r
+    if( psz_state )\r
+    {\r
+        p_state->psz_state = strdup( psz_state );\r
+        if( strncmp( ppsz_button_states[0], psz_state, strlen(ppsz_button_states[0]) ) == 0 )\r
+            p_state->i_state = OSD_BUTTON_UNSELECT;\r
+        else if( strncmp( ppsz_button_states[1], psz_state, strlen(ppsz_button_states[1]) ) == 0 )\r
+            p_state->i_state = OSD_BUTTON_SELECT;\r
+        else if( strncmp( ppsz_button_states[2], psz_state, strlen(ppsz_button_states[2]) ) == 0 )\r
+            p_state->i_state = OSD_BUTTON_PRESSED;\r
+    }\r
+    return p_state;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Free state images\r
+ *****************************************************************************/\r
+static void osd_StatesFree( vlc_object_t *p_this, osd_state_t *p_states )\r
+{\r
+    osd_state_t *p_state = p_states;\r
+    osd_state_t *p_next = NULL;\r
+    osd_state_t *p_prev = NULL;\r
+    \r
+    while( p_state->p_next )\r
+    {\r
+        p_next = p_state->p_next;\r
+        p_state = p_next;\r
+    }\r
+    /* Then free end first and walk to the start. */\r
+    while( p_state->p_prev )\r
+    {\r
+        msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_state );\r
+        p_prev = p_state->p_prev;\r
+        p_state = p_prev;\r
+        if( p_state->p_next )\r
+        {\r
+            if( p_state->p_next->p_pic && p_state->p_next->p_pic->p_data_orig )\r
+                free( p_state->p_next->p_pic->p_data_orig );\r
+            if( p_state->p_next->p_pic ) free( p_state->p_next->p_pic );\r
+            p_state->p_next->p_pic = NULL;      \r
+            if( p_state->p_next->psz_state ) free( p_state->p_next->psz_state );\r
+            p_state->p_next->psz_state = NULL;\r
+            free( p_state->p_next );\r
+            p_state->p_next = NULL;        \r
+        }\r
+    }\r
+    /* Free the last one. */\r
+    if( p_states )\r
+    {\r
+        msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_states );\r
+        if( p_states->p_pic && p_states->p_pic->p_data_orig )\r
+            free( p_states->p_pic->p_data_orig );\r
+        if( p_states->p_pic ) free( p_states->p_pic );\r
+        p_states->p_pic = NULL;\r
+        if( p_state->psz_state ) free( p_state->psz_state );\r
+        p_state->psz_state = NULL;\r
+        free( p_states );\r
+        p_states = NULL;\r
+    }\r
+}\r
+\r
+/*****************************************************************************\r
+ * osd_ConfigLoader: Load and parse osd text configurationfile\r
+ *****************************************************************************/\r
+int osd_ConfigLoader( vlc_object_t *p_this, const char *psz_file,\r
+    osd_menu_t **p_menu )\r
+{\r
+    osd_button_t   *p_current = NULL; /* button currently processed */\r
+    osd_button_t   *p_prev = NULL;    /* previous processed button */ \r
+\r
+#define MAX_FILE_PATH 256    \r
+    FILE       *fd = NULL;\r
+    int        result = 0;\r
+    \r
+    msg_Dbg( p_this, "opening osd definition file %s", psz_file );\r
+    fd = fopen( psz_file, "r" );\r
+    if( !fd )  \r
+    {\r
+        msg_Err( p_this, "failed opening osd definition file %s", psz_file );\r
+        return VLC_EGENERIC;\r
+    }\r
+    \r
+    /* Read first line */    \r
+    if( !feof( fd ) )\r
+    {\r
+        char action[25] = "";\r
+        char path[MAX_FILE_PATH] = "";\r
+        char *psz_path = NULL;\r
+        size_t i_len = 0;\r
+\r
+        /* override images path ? */\r
+        psz_path = config_GetPsz( p_this, "osdmenu-file-path" );\r
+        if( psz_path == NULL )\r
+        {                                    \r
+            result = fscanf(fd, "%24s %255s", &action[0], &path[0] );\r
+        }\r
+        else\r
+        {\r
+            /* psz_path is not null and therefor &path[0] cannot be NULL \r
+             * it might be null terminated.\r
+             */\r
+            strncpy( &path[0], psz_path, MAX_FILE_PATH );\r
+            free( psz_path );\r
+            psz_path = NULL;\r
+        }\r
+        /* NULL terminate before asking the length of path[] */\r
+        path[MAX_FILE_PATH-1] = '\0';\r
+        i_len = strlen(&path[0]);\r
+        if( i_len == MAX_FILE_PATH )\r
+            i_len--; /* truncate to prevent buffer overflow */\r
+#if defined(WIN32) || defined(UNDER_CE)\r
+        if( (i_len > 0) && path[i_len] != '\\' )\r
+            path[i_len] = '\\';\r
+#else        \r
+        if( (i_len > 0) && path[i_len] != '/' )\r
+            path[i_len] = '/';\r
+#endif\r
+        path[i_len+1] = '\0';\r
+        if( result == 0 || result == EOF )\r
+            goto error;                        \r
+        msg_Dbg( p_this, "%s=%s", &action[0], &path[0] );\r
+         \r
+        if( i_len == 0 )\r
+            *p_menu = osd_MenuNew( *p_menu, NULL, 0, 0 );\r
+        else\r
+            *p_menu = osd_MenuNew( *p_menu, &path[0], 0, 0 );\r
+    }\r
+    \r
+    if( !*p_menu )\r
+        goto error;\r
+        \r
+    /* read successive lines */\r
+    while( !feof( fd ) )\r
+    {   \r
+        osd_state_t   *p_state_current = NULL; /* button state currently processed */\r
+        osd_state_t   *p_state_prev = NULL;    /* previous state processed button */ \r
+        \r
+        char cmd[25] = "";      \r
+        char action[25] = "";\r
+        char state[25]  = "";\r
+        char file[256]  = "";\r
+        char path[512]  = "";        \r
+        int  i_x = 0;\r
+        int  i_y = 0;\r
+\r
+        result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );\r
+        if( result == 0 )\r
+            goto error;              \r
+        if( strncmp( &cmd[0], "action", 6 ) != 0 )\r
+            break;\r
+        msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );                    \r
+                \r
+        p_prev = p_current;\r
+        p_current = osd_ButtonNew( &action[0], i_x, i_y );   \r
+        if( !p_current )\r
+            goto error;\r
+        \r
+        if( p_prev )\r
+            p_prev->p_next = p_current;            \r
+        else\r
+            (*p_menu)->p_button = p_current;            \r
+        p_current->p_prev = p_prev;\r
+        \r
+        /* parse all states */\r
+        while( !feof( fd ) )\r
+        {\r
+            char type[25] = "";\r
+            \r
+            result = fscanf( fd, "\t%24s", &state[0] );   \r
+            if( result == 0 )\r
+                goto error;\r
+            \r
+            /* FIXME: We only parse one level deep now */    \r
+            if( strncmp( &state[0], "action", 6 ) == 0 )\r
+            {\r
+                osd_button_t   *p_up = NULL;\r
+                \r
+                result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );\r
+                if( result == 0 )\r
+                    goto error;\r
+                /* create new button */                \r
+                p_up = osd_ButtonNew( &action[0], i_x, i_y );                 \r
+                if( !p_up ) \r
+                    goto error;\r
+                /* Link to list */                    \r
+                p_up->p_down = p_current;\r
+                p_current->p_up = p_up;                 \r
+                msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );\r
+                /* Parse type state */\r
+                result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );   \r
+                if( result == 0 )\r
+                    goto error;\r
+                if( strncmp( &cmd[0], "type", 4 ) == 0 )\r
+                {  \r
+                    if( strncmp( &type[0], "volume", 6 ) == 0 )\r
+                    {\r
+                        (*p_menu)->p_state->p_volume = p_up;\r
+                        msg_Dbg( p_this, " + type=%s", &type[0] );\r
+                    }\r
+                }\r
+                /* Parse range state */\r
+                result = fscanf( fd, "\t%24s", &state[0] );   \r
+                if( result == 0 )\r
+                    goto error;\r
+                /* Parse the range state */        \r
+                if( strncmp( &state[0], "range", 5 ) == 0 )\r
+                {\r
+                    osd_state_t   *p_range_current = NULL; /* range state currently processed */\r
+                    osd_state_t   *p_range_prev = NULL;    /* previous state processed range */ \r
+                    int i_index = 0;\r
+                                                            \r
+                    p_up->b_range = VLC_TRUE;\r
+    \r
+                    result = fscanf( fd, "\t%24s", &action[0] );   \r
+                    if( result == 0 )\r
+                        goto error;\r
+                    \r
+                    result = fscanf( fd, "\t%d", &i_index );   \r
+                    if( result == 0 )\r
+                        goto error;\r
+                                                                                            \r
+                    msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );\r
+                    \r
+                    if( p_up->psz_action_down ) free( p_up->psz_action_down );\r
+                    p_up->psz_action_down = strdup( &action[0] );                     \r
+                    \r
+                    /* Parse range contstruction :\r
+                     * range <hotkey> \r
+                     *      <state1> <file1>\r
+                     *\r
+                     *      <stateN> <fileN>\r
+                     * end \r
+                     */                \r
+                    while( !feof( fd ) )\r
+                    {\r
+                        result = fscanf( fd, "\t%255s", &file[0] );   \r
+                        if( result == 0 )\r
+                            goto error;\r
+                        if( strncmp( &file[0], "end", 3 ) == 0 )\r
+                            break;\r
+                        \r
+                        p_range_prev = p_range_current;\r
+            \r
+                        if( (*p_menu)->psz_path )\r
+                        {\r
+                            size_t i_path_size = strlen( (*p_menu)->psz_path );\r
+                            size_t i_file_size = strlen( &file[0] );\r
+                            \r
+                            strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
+                            strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
+                            path[ i_path_size + i_file_size ] = '\0';\r
+                            \r
+                            p_range_current = osd_StateNew( p_this, &path[0], "pressed" );\r
+                        }\r
+                        else /* absolute paths are used. */\r
+                            p_range_current = osd_StateNew( p_this, &file[0], "pressed" );\r
+                            \r
+                        if( !p_range_current || !p_range_current->p_pic )\r
+                            goto error;\r
+                            \r
+                        /* increment the number of ranges for this button */                \r
+                        p_up->i_ranges++;\r
+                        \r
+                        if( p_range_prev )\r
+                            p_range_prev->p_next = p_range_current;\r
+                        else\r
+                            p_up->p_states = p_range_current;                                \r
+                        p_range_current->p_prev = p_range_prev;\r
+                        \r
+                        msg_Dbg( p_this, "  |- range=%d, file=%s%s", \r
+                                p_up->i_ranges, \r
+                                (*p_menu)->psz_path, &file[0] );                        \r
+                    }\r
+                    if( i_index > 0 )\r
+                    { \r
+                        osd_state_t *p_range = NULL;\r
+                        \r
+                        /* Find the default index for state range */                        \r
+                        p_range = p_up->p_states;\r
+                        while( (--i_index > 0) && p_range->p_next )\r
+                        {\r
+                            osd_state_t *p_temp = NULL;\r
+                            p_temp = p_range->p_next;\r
+                            p_range = p_temp;\r
+                        }\r
+                        p_up->p_current_state = p_range;\r
+                    }                    \r
+                    else p_up->p_current_state = p_up->p_states;\r
+                    \r
+                }   \r
+                result = fscanf( fd, "\t%24s", &state[0] );   \r
+                if( result == 0 )\r
+                    goto error;                    \r
+                if( strncmp( &state[0], "end", 3 ) != 0 )\r
+                    goto error;\r
+                    \r
+                /* Continue at the beginning of the while() */\r
+                continue;\r
+            }\r
+            \r
+            /* Parse the range state */        \r
+            if( strncmp( &state[0], "range", 5 ) == 0 )\r
+            {\r
+                osd_state_t   *p_range_current = NULL; /* range state currently processed */\r
+                osd_state_t   *p_range_prev = NULL;    /* previous state processed range */ \r
+                int i_index = 0;\r
+                                                        \r
+                p_current->b_range = VLC_TRUE;\r
+\r
+                result = fscanf( fd, "\t%24s", &action[0] );   \r
+                if( result == 0 )\r
+                    goto error;\r
+                \r
+                result = fscanf( fd, "\t%d", &i_index );   \r
+                if( result == 0 )\r
+                    goto error;\r
+                                                                                        \r
+                msg_Dbg( p_this, " + hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );                        \r
+                if( p_current->psz_action_down ) free( p_current->psz_action_down );\r
+                p_current->psz_action_down = strdup( &action[0] );                     \r
+                \r
+                /* Parse range contstruction :\r
+                 * range <hotkey> \r
+                 *      <state1> <file1>\r
+                 *\r
+                 *      <stateN> <fileN>\r
+                 * end \r
+                 */                \r
+                while( !feof( fd ) )\r
+                {\r
+                    result = fscanf( fd, "\t%255s", &file[0] );   \r
+                    if( result == 0 )\r
+                        goto error;\r
+                    if( strncmp( &file[0], "end", 3 ) == 0 )\r
+                        break;\r
+                    \r
+                    p_range_prev = p_range_current;\r
+        \r
+                    if( (*p_menu)->psz_path )\r
+                    {\r
+                        size_t i_path_size = strlen( (*p_menu)->psz_path );\r
+                        size_t i_file_size = strlen( &file[0] );\r
+                        \r
+                        strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
+                        strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
+                        path[ i_path_size + i_file_size ] = '\0';\r
+                        \r
+                        p_range_current = osd_StateNew( p_this, &path[0], "pressed" );\r
+                    }\r
+                    else /* absolute paths are used. */\r
+                        p_range_current = osd_StateNew( p_this, &file[0], "pressed" );\r
+                        \r
+                    if( !p_range_current || !p_range_current->p_pic )\r
+                        goto error;\r
+                        \r
+                    /* increment the number of ranges for this button */                \r
+                    p_current->i_ranges++;\r
+                    \r
+                    if( p_range_prev )\r
+                        p_range_prev->p_next = p_range_current;\r
+                    else\r
+                        p_current->p_states = p_range_current;                                \r
+                    p_range_current->p_prev = p_range_prev;\r
+                    \r
+                    msg_Dbg( p_this, "  |- range=%d, file=%s%s", \r
+                            p_current->i_ranges, \r
+                            (*p_menu)->psz_path, &file[0] );                        \r
+                }\r
+                if( i_index > 0 )\r
+                {             \r
+                    osd_state_t *p_range = NULL;\r
+                       \r
+                    /* Find the default index for state range */                        \r
+                    p_range = p_current->p_states;\r
+                    while( (--i_index > 0) && p_range->p_next )\r
+                    {\r
+                        osd_state_t *p_temp = NULL;\r
+                        p_temp = p_range->p_next;\r
+                        p_range = p_temp;\r
+                    }\r
+                    p_current->p_current_state = p_range;\r
+                }                    \r
+                else p_current->p_current_state = p_current->p_states;\r
+                /* Continue at the beginning of the while() */\r
+                continue;\r
+            }   \r
+            if( strncmp( &state[0], "end", 3 ) == 0 )\r
+                break;\r
+                \r
+            result = fscanf( fd, "\t%255s", &file[0] );   \r
+            if( result == 0 )\r
+                goto error;                                \r
+            \r
+            p_state_prev = p_state_current;\r
+\r
+            if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&\r
+                ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&\r
+                ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )\r
+            {\r
+                msg_Err( p_this, "invalid button state %s for button %s expected %d: unselect, select or pressed)",\r
+                                    &state[0], &action[0], strlen(&state[0]));\r
+                goto error;\r
+            }\r
+            \r
+            if( (*p_menu)->psz_path )\r
+            {\r
+                size_t i_path_size = strlen( (*p_menu)->psz_path );\r
+                size_t i_file_size = strlen( &file[0] );\r
+                \r
+                strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
+                strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
+                path[ i_path_size + i_file_size ] = '\0';\r
+                \r
+                p_state_current = osd_StateNew( p_this, &path[0], &state[0] );\r
+            }\r
+            else /* absolute paths are used. */\r
+                p_state_current = osd_StateNew( p_this, &file[0], &state[0] );\r
+                \r
+            if( !p_state_current || !p_state_current->p_pic )\r
+                goto error;\r
+                \r
+            if( p_state_prev )\r
+                p_state_prev->p_next = p_state_current;\r
+            else\r
+                p_current->p_states = p_state_current;                                \r
+            p_state_current->p_prev = p_state_prev;\r
+            \r
+            msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0], (*p_menu)->psz_path, &file[0] );\r
+        }\r
+        p_current->p_current_state = p_current->p_states;\r
+    }\r
+\r
+    /* Find the last button and store its pointer. \r
+     * The OSD menu behaves like a roundrobin list.\r
+     */        \r
+    p_current = (*p_menu)->p_button;\r
+    while( p_current && p_current->p_next )\r
+    {\r
+        osd_button_t *p_temp = NULL;\r
+        p_temp = p_current->p_next;\r
+        p_current = p_temp;\r
+    }\r
+    (*p_menu)->p_last_button = p_current;\r
+    fclose( fd );\r
+    return 0;\r
+    \r
+#undef MAX_FILE_PATH \r
+error:\r
+    msg_Err( p_this, "parsing file failed (returned %d)", result );\r
+    fclose( fd );\r
+    return 1;        \r
+}\r
+\r
+/*****************************************************************************\r
+ * osd_ConfigUnload: Load and parse osd text configurationfile\r
+ *****************************************************************************/\r
+void osd_ConfigUnload( vlc_object_t *p_this, osd_menu_t **p_osd)\r
+{\r
+    msg_Dbg( p_this, "unloading OSD menu structure" );\r
+    osd_MenuFree( p_this, *p_osd );\r
+}\r
diff --git a/src/osd/osd_widgets.c b/src/osd/osd_widgets.c
new file mode 100644 (file)
index 0000000..02da110
--- /dev/null
@@ -0,0 +1,289 @@
+/*****************************************************************************\r
+ * osd_widgets.c : OSD widgets manipulation functions\r
+ *****************************************************************************\r
+ * Copyright (C) 2005 M2X\r
+ * Copyright (C) 2004 VideoLAN (Centrale Réseaux) and its contributors\r
+ *\r
+ * $Id: osd_widgets.c 9274 2004-11-10 15:16:51Z gbazin $\r
+ *\r
+ * Author: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>\r
+ *\r
+ * Based on: src/video_output/video_widgets.c\r
+ *     from: Yoann Peronneau <yoann@videolan.org>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.\r
+ *****************************************************************************/\r
+\r
+/*****************************************************************************\r
+ * Preamble\r
+ *****************************************************************************/\r
+#include <stdlib.h>                                                /* free() */\r
+#include <vlc/vlc.h>\r
+#include <vlc/vout.h>\r
+#include <osd.h>\r
+\r
+#define STYLE_EMPTY 0\r
+#define STYLE_FILLED 1\r
+\r
+/*****************************************************************************\r
+ * Local prototypes\r
+ *****************************************************************************/\r
+static void DrawRect( picture_t *, int, int, int, int, short );\r
+static void DrawTriangle( picture_t *, int, int, int, int, short );\r
+static picture_t *osd_CreatePicture( int, int );\r
+\r
+/*****************************************************************************\r
+ * Draws a rectangle at the given position in the subpic.\r
+ * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY).\r
+ *****************************************************************************/\r
+static void DrawRect( picture_t *p_picture, int i_x1, int i_y1,\r
+                      int i_x2, int i_y2, short fill )\r
+{\r
+    int x, y;\r
+    uint8_t *p_a = p_picture->A_PIXELS;\r
+    int i_pitch = p_picture->Y_PITCH;\r
+\r
+    if( fill == STYLE_FILLED )\r
+    {\r
+        for( y = i_y1; y <= i_y2; y++ )\r
+        {\r
+            for( x = i_x1; x <= i_x2; x++ )\r
+            {\r
+                p_a[ x + i_pitch * y ] = 0xff;\r
+            }\r
+        }\r
+    }\r
+    else\r
+    {\r
+        for( y = i_y1; y <= i_y2; y++ )\r
+        {\r
+            p_a[ i_x1 + i_pitch * y ] = 0xff;\r
+            p_a[ i_x2 + i_pitch * y ] = 0xff;\r
+        }\r
+        for( x = i_x1; x <= i_x2; x++ )\r
+        {\r
+            p_a[ x + i_pitch * i_y1 ] = 0xff;\r
+            p_a[ x + i_pitch * i_y2 ] = 0xff;\r
+        }\r
+    }\r
+}\r
+\r
+/*****************************************************************************\r
+ * Draws a triangle at the given position in the subpic.\r
+ * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY).\r
+ *****************************************************************************/\r
+static void DrawTriangle( picture_t *p_picture, int i_x1, int i_y1,\r
+                          int i_x2, int i_y2, short fill )\r
+{\r
+    int x, y, i_mid, h;\r
+    uint8_t *p_a = p_picture->A_PIXELS;\r
+    int i_pitch = p_picture->Y_PITCH;\r
+\r
+    i_mid = i_y1 + ( ( i_y2 - i_y1 ) >> 1 );\r
+\r
+    if( i_x2 >= i_x1 )\r
+    {\r
+        if( fill == STYLE_FILLED )\r
+        {\r
+            for( y = i_y1; y <= i_mid; y++ )\r
+            {\r
+                h = y - i_y1;\r
+                for( x = i_x1; x <= i_x1 + h && x <= i_x2; x++ )\r
+                {\r
+                    p_a[ x + i_pitch * y ] = 0xff;\r
+                    p_a[ x + i_pitch * ( i_y2 - h ) ] = 0xff;\r
+                }\r
+            }\r
+        }\r
+        else\r
+        {\r
+            for( y = i_y1; y <= i_mid; y++ )\r
+            {\r
+                h = y - i_y1;\r
+                p_a[ i_x1 + i_pitch * y ] = 0xff;\r
+                p_a[ i_x1 + h + i_pitch * y ] = 0xff;\r
+                p_a[ i_x1 + i_pitch * ( i_y2 - h ) ] = 0xff;\r
+                p_a[ i_x1 + h + i_pitch * ( i_y2 - h ) ] = 0xff;\r
+            }\r
+        }\r
+    }\r
+    else\r
+    {\r
+        if( fill == STYLE_FILLED )\r
+        {\r
+            for( y = i_y1; y <= i_mid; y++ )\r
+            {\r
+                h = y - i_y1;\r
+                for( x = i_x1; x >= i_x1 - h && x >= i_x2; x-- )\r
+                {\r
+                    p_a[ x + i_pitch * y ] = 0xff;\r
+                    p_a[ x + i_pitch * ( i_y2 - h ) ] = 0xff;\r
+                }\r
+            }\r
+        }\r
+        else\r
+        {\r
+            for( y = i_y1; y <= i_mid; y++ )\r
+            {\r
+                h = y - i_y1;\r
+                p_a[ i_x1 + i_pitch * y ] = 0xff;\r
+                p_a[ i_x1 - h + i_pitch * y ] = 0xff;\r
+                p_a[ i_x1 + i_pitch * ( i_y2 - h ) ] = 0xff;\r
+                p_a[ i_x1 - h + i_pitch * ( i_y2 - h ) ] = 0xff;\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+/*****************************************************************************\r
+ * Create Picture: creates picture\r
+ *****************************************************************************/\r
+static picture_t *osd_CreatePicture( int i_width, int i_height )\r
+{\r
+    picture_t *p_picture = NULL;\r
+    uint8_t *p_y, *p_u, *p_v, *p_a;\r
+    int i_pitch;\r
+\r
+    p_picture = (picture_t*) malloc( sizeof(picture_t) );\r
+    if( p_picture == NULL )\r
+    {\r
+        return NULL;\r
+    }\r
+    /* Clear the memory */\r
+    memset( p_picture, 0, sizeof(picture_t) );\r
+\r
+    p_y = p_picture->Y_PIXELS;\r
+    p_u = p_picture->U_PIXELS;\r
+    p_v = p_picture->V_PIXELS;\r
+    p_a = p_picture->A_PIXELS;\r
+    i_pitch = p_picture->Y_PITCH;\r
+\r
+    /* Initialize the region pixels (only the alpha will be changed later) */\r
+    memset( p_y, 0xff, i_pitch * i_height );\r
+    memset( p_u, 0x80, i_pitch * i_height );\r
+    memset( p_v, 0x80, i_pitch * i_height );\r
+    memset( p_a, 0x00, i_pitch * i_height );\r
+\r
+    return p_picture;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Displays an OSD slider.\r
+ * Types are: OSD_HOR_SLIDER and OSD_VERT_SLIDER.\r
+ *****************************************************************************/\r
+picture_t *osd_Slider( int i_width, int i_height, int i_position, short i_type )\r
+{\r
+    picture_t *p_picture;\r
+    int i_x_margin, i_y_margin, i_x, i_y;\r
+\r
+    p_picture = osd_CreatePicture( i_width, i_height );\r
+    if( p_picture == NULL )\r
+        return NULL;\r
+\r
+    i_y_margin = i_height / 10;\r
+    i_x_margin = i_y_margin;\r
+    if( i_type == OSD_HOR_SLIDER )\r
+    {\r
+        i_width = i_width - 2 * i_x_margin;\r
+        i_height = i_height / 20;\r
+        i_x = i_x_margin;\r
+        i_y = i_height - i_y_margin - i_height;\r
+    }\r
+    else\r
+    {\r
+        i_width = i_width / 40;\r
+        i_height = i_height - 2 * i_y_margin;\r
+        i_x = i_width - i_x_margin - i_width;\r
+        i_y = i_y_margin;\r
+    }\r
+\r
+    if( i_type == OSD_HOR_SLIDER )\r
+    {\r
+        int i_x_pos = ( i_width - 2 ) * i_position / 100;\r
+        DrawRect( p_picture, i_x_pos - 1, 2, i_x_pos + 1,\r
+                  i_height - 3, STYLE_FILLED );\r
+        DrawRect( p_picture, 0, 0, i_width - 1, i_height - 1, STYLE_EMPTY );\r
+    }\r
+    else if( i_type == OSD_VERT_SLIDER )\r
+    {\r
+        int i_y_pos = i_height / 2;\r
+        DrawRect( p_picture, 2, i_height - ( i_height - 2 ) * i_position / 100,\r
+                  i_width - 3, i_height - 3, STYLE_FILLED );\r
+        DrawRect( p_picture, 1, i_y_pos, 1, i_y_pos, STYLE_FILLED );\r
+        DrawRect( p_picture, i_width - 2, i_y_pos,\r
+                  i_width - 2, i_y_pos, STYLE_FILLED );\r
+        DrawRect( p_picture, 0, 0, i_width - 1, i_height - 1, STYLE_EMPTY );\r
+    }\r
+\r
+    return p_picture;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Displays an OSD icon.\r
+ * Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON, OSD_SPEAKER_ICON, OSD_MUTE_ICON\r
+ *****************************************************************************/\r
+picture_t *osd_Icon( int i_width, int i_height, short i_type )\r
+{\r
+    picture_t *p_picture = NULL;\r
+    int i_x_margin, i_y_margin, i_x, i_y;\r
+\r
+    p_picture = osd_CreatePicture( i_width, i_height );\r
+    if( p_picture == NULL )\r
+        return NULL;\r
+\r
+    i_y_margin = i_height / 15;\r
+    i_x_margin = i_y_margin;\r
+    i_x = i_width - i_x_margin - i_width;\r
+    i_y = i_y_margin;\r
+\r
+    if( i_type == OSD_PAUSE_ICON )\r
+    {\r
+        int i_bar_width = i_width / 3;\r
+        DrawRect( p_picture, 0, 0, i_bar_width - 1, i_height -1, STYLE_FILLED );\r
+        DrawRect( p_picture, i_width - i_bar_width, 0,\r
+                  i_width - 1, i_height - 1, STYLE_FILLED );\r
+    }\r
+    else if( i_type == OSD_PLAY_ICON )\r
+    {\r
+        int i_mid = i_height >> 1;\r
+        int i_delta = ( i_width - i_mid ) >> 1;\r
+        int i_y2 = ( ( i_height - 1 ) >> 1 ) * 2;\r
+        DrawTriangle( p_picture, i_delta, 0, i_width - i_delta, i_y2,\r
+                      STYLE_FILLED );\r
+    }\r
+    else if( i_type == OSD_SPEAKER_ICON || i_type == OSD_MUTE_ICON )\r
+    {\r
+        int i_mid = i_height >> 1;\r
+        int i_delta = ( i_width - i_mid ) >> 1;\r
+        int i_y2 = ( ( i_height - 1 ) >> 1 ) * 2;\r
+        DrawRect( p_picture, i_delta, i_mid / 2, i_width - i_delta,\r
+                  i_height - 1 - i_mid / 2, STYLE_FILLED );\r
+        DrawTriangle( p_picture, i_width - i_delta, 0, i_delta, i_y2,\r
+                      STYLE_FILLED );\r
+        if( i_type == OSD_MUTE_ICON )\r
+        {\r
+            uint8_t *p_a = p_picture->A_PIXELS;\r
+            int i_pitch = p_picture->Y_PITCH;\r
+            int i;\r
+            for( i = 1; i < i_pitch; i++ )\r
+            {\r
+                int k = i + ( i_height - i - 1 ) * i_pitch;\r
+                p_a[ k ] = 0xff - p_a[ k ];\r
+            }\r
+        }\r
+    }\r
+    \r
+    return p_picture;\r
+}\r