]> git.sesse.net Git - vlc/commitdiff
Merge branch 'dynamicoverlay'
authorJean-Paul Saman <jean-paul.saman@m2x.nl>
Wed, 9 Apr 2008 10:45:24 +0000 (12:45 +0200)
committerJean-Paul Saman <jean-paul.saman@m2x.nl>
Wed, 9 Apr 2008 10:45:24 +0000 (12:45 +0200)
16 files changed:
THANKS
configure.ac
include/vlc_vout.h
modules/video_filter/Modules.am
modules/video_filter/blendbench.c [new file with mode: 0644]
modules/video_filter/dynamicoverlay/Modules.am [new file with mode: 0644]
modules/video_filter/dynamicoverlay/dynamicoverlay.c [new file with mode: 0644]
modules/video_filter/dynamicoverlay/dynamicoverlay.h [new file with mode: 0644]
modules/video_filter/dynamicoverlay/dynamicoverlay_buffer.c [new file with mode: 0644]
modules/video_filter/dynamicoverlay/dynamicoverlay_commands.c [new file with mode: 0644]
modules/video_filter/dynamicoverlay/dynamicoverlay_list.c [new file with mode: 0644]
modules/video_filter/dynamicoverlay/dynamicoverlay_queue.c [new file with mode: 0644]
modules/video_filter/osdmenu.c
src/video_output/vout_subpictures.c
test/dynamicoverlay/Makefile [new file with mode: 0644]
test/dynamicoverlay/overlay-test.c [new file with mode: 0644]

diff --git a/THANKS b/THANKS
index 0ccd7abeed96604ab74be424eeb74eba3978e4d9..60cf28afa1ddac2c2484b98e5528d7a39d5841fe 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -208,6 +208,7 @@ Sebastian Jenny <jenny - sebastian &t gmail - com > - AAC decoding channel order
 Sebastien Chaumat <Sebastien.Chaumat at ens-lyon.fr> - YOPY port tests
 Sidney Doria <ssdoria qt gmail.com> - Brazilian Portuguese localisation
 Simon Damkjær Andersen <simondamkjaer at gmail.com> - playmode icons and the entire Fullscreen Panel design for the OSX GUI (v0.8.6)
+Soren Bog <avacore at videolan dot org> - dynamicoverlays
 Stefán Freyr Stefánsson <stefan.freyr -at- gmail.com> - Qt4 speed slider
 Steve Lhomme <steve dot lhomme at free dot fr> - MSVC fixes and Matroska enhancements
 Steve Brown <sbrown at cortland.com> - fix for optional PES size bug
index e5847113e175bd588bc818118b0236ab1cd40a54..1160a61d8ab7e655b2a63820efcb208702ab2b73 100644 (file)
@@ -1200,7 +1200,7 @@ dnl
 VLC_ADD_PLUGINS([dummy logger memcpy])
 VLC_ADD_PLUGINS([mpgv mpga m4v m4a h264 vc1 demux_cdg cdg ps pva avi asf mp4 rawdv rawvid nsv real aiff mjpeg demuxdump flacsys tta])
 VLC_ADD_PLUGINS([cvdsub svcdsub spudec subsdec subsusf t140 dvbsub cc mpeg_audio lpcm a52 dts cinepak flac])
-VLC_ADD_PLUGINS([deinterlace invert adjust transform wave ripple psychedelic gradient motionblur rv32 rotate noise grain extract sharpen seamcarving croppadd])
+VLC_ADD_PLUGINS([deinterlace invert adjust transform wave ripple psychedelic gradient motionblur rv32 rotate noise grain extract sharpen seamcarving croppadd dynamicoverlay blendbench])
 VLC_ADD_PLUGINS([converter_fixed mono])
 VLC_ADD_PLUGINS([trivial_resampler ugly_resampler])
 VLC_ADD_PLUGINS([trivial_channel_mixer trivial_mixer])
@@ -6294,6 +6294,7 @@ AC_CONFIG_FILES([
   modules/video_chroma/Makefile
   modules/video_filter/Makefile
   modules/video_filter/atmo/Makefile
+  modules/video_filter/dynamicoverlay/Makefile
   modules/video_output/Makefile
   modules/video_output/msw/Makefile
   modules/video_output/qte/Makefile
index 30baf2517827185f3b73f55ae10d33f482ed1e96..601baa576e536d4e57b9d51d87683f8829630ee3 100644 (file)
@@ -211,10 +211,11 @@ struct subpicture_region_t
     int             i_x;                             /**< position of region */
     int             i_y;                             /**< position of region */
     int             i_align;                  /**< alignment within a region */
+    int             i_alpha;                               /**< transparency */
 
     char            *psz_text;       /**< text string comprising this region */
     char            *psz_html;       /**< HTML version of subtitle (NULL = use psz_text) */
-    text_style_t    *p_style;  /* a description of the text style formatting */
+    text_style_t    *p_style;        /**< a description of the text style formatting */
 
     subpicture_region_t *p_next;                /**< next region in the list */
     subpicture_region_t *p_cache;       /**< modified version of this region */
index fb3c14ce07303404b798e27ff07a9b9d5f69426a..0907700df0587d9d2dfc10e1cc7fbfa22145e4f9 100644 (file)
@@ -36,4 +36,5 @@ SOURCES_gaussianblur = gaussianblur.c
 SOURCES_grain = grain.c
 SOURCES_seamcarving = seamcarving.c
 SOURCES_croppadd = croppadd.c
+SOURCES_blendbench = blendbench.c
 noinst_HEADERS = filter_common.h filter_picture.h
diff --git a/modules/video_filter/blendbench.c b/modules/video_filter/blendbench.c
new file mode 100644 (file)
index 0000000..f21d6f4
--- /dev/null
@@ -0,0 +1,251 @@
+/*****************************************************************************
+ * blendbench.c : blending benchmark plugin for vlc
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Author: Søren Bøg <avacore@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_sout.h>
+#include <vlc_vout.h>
+
+#include "vlc_filter.h"
+#include "vlc_image.h"
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int Create( vlc_object_t * );
+static void Destroy( vlc_object_t * );
+
+static picture_t *Filter( filter_t *, picture_t * );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+#define LOOPS_TEXT N_("Number of time to blend")
+#define LOOPS_LONGTEXT N_("The number of time the blend will be performed")
+
+#define ALPHA_TEXT N_("Alpha of the blended image")
+#define ALPHA_LONGTEXT N_("Alpha with which the blend image is blended")
+
+#define BASE_IMAGE_TEXT N_("Image to be blended onto")
+#define BASE_IMAGE_LONGTEXT N_("The image which will be used to blend onto")
+
+#define BASE_CHROMA_TEXT N_("Chroma for the base image")
+#define BASE_CHROMA_LONGTEXT N_("Chroma which the base image will be loaded in")
+
+#define BLEND_IMAGE_TEXT N_("Image which will be blended.")
+#define BLEND_IMAGE_LONGTEXT N_("The image blended onto the base image")
+
+#define BLEND_CHROMA_TEXT N_("Chroma for the blend image")
+#define BLEND_CHROMA_LONGTEXT N_("Chroma which the blend image will be loaded" \
+                                 "in")
+
+#define CFG_PREFIX "blendbench-"
+
+vlc_module_begin();
+    set_description( _("Blending benchmark filter") );
+    set_shortname( _("blendbench" ));
+    set_category( CAT_VIDEO );
+    set_subcategory( SUBCAT_VIDEO_VFILTER );
+    set_capability( "video filter2", 0 );
+
+    set_section( N_("Benchmarking"), NULL );
+    add_integer( CFG_PREFIX "loops", 1000, NULL, LOOPS_TEXT,
+              LOOPS_LONGTEXT, VLC_FALSE );
+    add_integer_with_range( CFG_PREFIX "alpha", 128, 0, 255, NULL, ALPHA_TEXT,
+              ALPHA_LONGTEXT, VLC_FALSE );
+
+    set_section( N_("Base image"), NULL );
+    add_file( CFG_PREFIX "base-image", NULL, NULL, BASE_IMAGE_TEXT,
+              BASE_IMAGE_LONGTEXT, VLC_FALSE );
+    add_string( CFG_PREFIX "base-chroma", "I420", NULL, BASE_CHROMA_TEXT,
+              BASE_CHROMA_LONGTEXT, VLC_FALSE );
+
+    set_section( N_("Blend image"), NULL );
+    add_file( CFG_PREFIX "blend-image", NULL, NULL, BLEND_IMAGE_TEXT,
+              BLEND_IMAGE_LONGTEXT, VLC_FALSE );
+    add_string( CFG_PREFIX "blend-chroma", "YUVA", NULL, BLEND_CHROMA_TEXT,
+              BLEND_CHROMA_LONGTEXT, VLC_FALSE );
+
+    set_callbacks( Create, Destroy );
+vlc_module_end();
+
+static const char *ppsz_filter_options[] = {
+    "loops", "alpha", "base-image", "base-chroma", "blend-image",
+    "blend-chroma", NULL
+};
+
+/*****************************************************************************
+ * filter_sys_t: filter method descriptor
+ *****************************************************************************/
+struct filter_sys_t
+{
+    vlc_bool_t b_done;
+    int i_loops, i_alpha;
+
+    picture_t *p_base_image;
+    picture_t *p_blend_image;
+
+    vlc_fourcc_t i_base_chroma;
+    vlc_fourcc_t i_blend_chroma;
+};
+
+static int LoadImage( vlc_object_t *p_this, picture_t **pp_pic,
+                       vlc_fourcc_t i_chroma, char *psz_file, const char *psz_name )
+{
+    image_handler_t *p_image;
+    video_format_t fmt_in, fmt_out;
+
+    memset( &fmt_in, 0, sizeof(video_format_t) );
+    memset( &fmt_out, 0, sizeof(video_format_t) );
+
+    fmt_out.i_chroma = i_chroma;
+    p_image = image_HandlerCreate( p_this );
+    *pp_pic = image_ReadUrl( p_image, psz_file, &fmt_in, &fmt_out );
+    image_HandlerDelete( p_image );
+
+    if( *pp_pic == NULL ) {
+        msg_Err( p_this, "Unable to load %s image", psz_name );
+        return VLC_EGENERIC;
+    }
+
+    msg_Dbg( p_this, "%s image has dim %d x %d (Y plane)", psz_name,
+             (*pp_pic)->p[Y_PLANE].i_visible_pitch,
+             (*pp_pic)->p[Y_PLANE].i_visible_lines );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Create: allocates video thread output method
+ *****************************************************************************/
+static int Create( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    filter_sys_t *p_sys;
+    char *psz_temp;
+
+    /* Allocate structure */
+    p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
+    if( p_filter->p_sys == NULL )
+    {
+        msg_Err( p_filter, "out of memory" );
+        return VLC_ENOMEM;
+    }
+    p_sys = p_filter->p_sys;
+    p_sys->b_done = VLC_FALSE;
+
+    p_filter->pf_video_filter = Filter;
+
+    /* needed to get options passed in transcode using the
+     * adjust{name=value} syntax */
+    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
+                       p_filter->p_cfg );
+
+    p_sys->i_loops = var_CreateGetIntegerCommand( p_filter,
+                                                  CFG_PREFIX "loops" );
+    p_sys->i_alpha = var_CreateGetIntegerCommand( p_filter,
+                                                  CFG_PREFIX "alpha" );
+
+    psz_temp = var_CreateGetStringCommand( p_filter, CFG_PREFIX "base-chroma" );
+    p_sys->i_base_chroma = VLC_FOURCC( psz_temp[0], psz_temp[1],
+                                       psz_temp[2], psz_temp[3] );
+    LoadImage( p_this, &p_sys->p_base_image, p_sys->i_base_chroma,
+               var_CreateGetStringCommand( p_filter, CFG_PREFIX "base-image" ),
+               "Base" );
+
+    psz_temp = var_CreateGetStringCommand( p_filter,
+                                           CFG_PREFIX "blend-chroma" );
+    p_sys->i_blend_chroma = VLC_FOURCC( psz_temp[0], psz_temp[1],
+                                        psz_temp[2], psz_temp[3] );
+    LoadImage( p_this, &p_sys->p_blend_image, p_sys->i_blend_chroma,
+               var_CreateGetStringCommand( p_filter, CFG_PREFIX "blend-image" ),
+               "Blend" );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Destroy: destroy video thread output method
+ *****************************************************************************/
+static void Destroy( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    p_sys->p_base_image->pf_release( p_sys->p_base_image );
+    p_sys->p_blend_image->pf_release( p_sys->p_blend_image );
+}
+
+/*****************************************************************************
+ * Render: displays previously rendered output
+ *****************************************************************************/
+static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    if( p_sys->b_done )
+    {
+        return p_pic;
+    }
+
+    filter_t *p_blend;
+
+    p_blend = vlc_object_create( p_filter, VLC_OBJECT_FILTER );
+    vlc_object_attach( p_blend, p_filter );
+    p_blend->fmt_out.video = p_sys->p_base_image->format;
+    p_blend->fmt_in.video = p_sys->p_blend_image->format;
+    p_blend->p_module = module_Need( p_blend, "video blending", 0, 0 );
+
+    mtime_t time = mdate();
+    for( int i_iter = 0; i_iter < p_sys->i_loops; ++i_iter )
+    {
+        p_blend->pf_video_blend( p_blend, p_sys->p_base_image,
+                                 p_sys->p_base_image, p_sys->p_blend_image, 0,
+                                 0, p_sys->i_alpha );
+    }
+    time = mdate() - time;
+
+    msg_Info( p_filter, "Blended %d images in %f sec.", p_sys->i_loops,
+              time / 1000000.0f );
+    msg_Info( p_filter, "Speed is: %f images/second, %f pixels/second",
+              (float) p_sys->i_loops / time * 1000000,
+              (float) p_sys->i_loops / time * 1000000 *
+                  p_sys->p_blend_image->p[Y_PLANE].i_visible_pitch *
+                  p_sys->p_blend_image->p[Y_PLANE].i_visible_lines );
+
+    module_Unneed( p_blend, p_blend->p_module );
+
+    vlc_object_detach( p_blend );
+    vlc_object_release( p_blend );
+
+    p_sys->b_done = VLC_TRUE;
+    return p_pic;
+}
diff --git a/modules/video_filter/dynamicoverlay/Modules.am b/modules/video_filter/dynamicoverlay/Modules.am
new file mode 100644 (file)
index 0000000..5f645e4
--- /dev/null
@@ -0,0 +1,2 @@
+SOURCES_dynamicoverlay = dynamicoverlay_buffer.c dynamicoverlay_queue.c dynamicoverlay_list.c dynamicoverlay_commands.c  dynamicoverlay.c
+noinst_HEADERS = dynamicoverlay.h
diff --git a/modules/video_filter/dynamicoverlay/dynamicoverlay.c b/modules/video_filter/dynamicoverlay/dynamicoverlay.c
new file mode 100644 (file)
index 0000000..cee5c5c
--- /dev/null
@@ -0,0 +1,421 @@
+/*****************************************************************************
+ * dynamicoverlay.c : dynamic overlay plugin for vlc
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Author: Søren Bøg <avacore@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_sout.h>
+#include <vlc_vout.h>
+#include <vlc_filter.h>
+#include <vlc_osd.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "dynamicoverlay.h"
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int Create( vlc_object_t * );
+static void Destroy( vlc_object_t * );
+static subpicture_t *Filter( filter_t *, mtime_t );
+
+static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
+                           vlc_value_t oldval, vlc_value_t newval,
+                           void *p_data );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+#define INPUT_TEXT N_("Input FIFO")
+#define INPUT_LONGTEXT N_("FIFO which will be read for commands")
+
+#define OUTPUT_TEXT N_("Output FIFO")
+#define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")
+
+vlc_module_begin();
+    set_description( _("Dynamic video overlay") );
+    set_shortname( _("Overlay" ));
+    set_category( CAT_VIDEO );
+    set_subcategory( SUBCAT_VIDEO_VFILTER );
+    set_capability( "sub filter", 0 );
+
+    add_file( "overlay-input", NULL, NULL, INPUT_TEXT, INPUT_LONGTEXT,
+              VLC_FALSE );
+    add_file( "overlay-output", NULL, NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
+              VLC_FALSE );
+
+    add_shortcut( "overlay" );
+    set_callbacks( Create, Destroy );
+vlc_module_end();
+
+static const char *ppsz_filter_options[] = {
+    "input", "output", NULL
+};
+
+/*****************************************************************************
+ * Create: allocates adjust video thread output method
+ *****************************************************************************
+ * This function allocates and initializes a adjust vout method.
+ *****************************************************************************/
+static int Create( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    filter_sys_t *p_sys;
+
+    /* Allocate structure */
+    p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
+    if( p_filter->p_sys == NULL )
+    {
+        msg_Err( p_filter, "out of memory" );
+        return VLC_ENOMEM;
+    }
+    p_sys = p_filter->p_sys;
+
+    BufferInit( &p_sys->input );
+    BufferInit( &p_sys->output );
+    QueueInit( &p_sys->atomic );
+    QueueInit( &p_sys->pending );
+    QueueInit( &p_sys->processed );
+    ListInit( &p_sys->overlays );
+
+    p_sys->i_inputfd = -1;
+    p_sys->i_outputfd = -1;
+    p_sys->b_updated = VLC_TRUE;
+    p_sys->b_atomic = VLC_FALSE;
+
+    p_filter->pf_sub_filter = Filter;
+
+    config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
+                       p_filter->p_cfg );
+
+    p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
+                                                       "overlay-input" );
+    p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
+                                                        "overlay-output" );
+
+    var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
+    var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
+
+    RegisterCommand( p_filter );
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Destroy: destroy adjust video thread output method
+ *****************************************************************************
+ * Terminate an output method created by adjustCreateOutputMethod
+ *****************************************************************************/
+static void Destroy( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+
+    BufferDestroy( &p_filter->p_sys->input );
+    BufferDestroy( &p_filter->p_sys->output );
+    QueueDestroy( &p_filter->p_sys->atomic );
+    QueueDestroy( &p_filter->p_sys->pending );
+    QueueDestroy( &p_filter->p_sys->processed );
+    ListDestroy( &p_filter->p_sys->overlays );
+    UnregisterCommand( p_filter );
+
+    free( p_filter->p_sys->psz_inputfile );
+    free( p_filter->p_sys->psz_outputfile );
+    free( p_filter->p_sys );
+}
+
+/*****************************************************************************
+ * Render: displays previously rendered output
+ *****************************************************************************
+ * This function send the currently rendered image to adjust modified image,
+ * waits until it is displayed and switch the two rendering buffers, preparing
+ * next frame.
+ *****************************************************************************/
+static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    /* We might need to open these at any time. */
+    if( p_sys->i_inputfd == -1 )
+    {
+        p_sys->i_inputfd = open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
+        if( p_sys->i_inputfd == -1 )
+        {
+            msg_Warn( p_filter, "Failed to grab input file: %s (%s)",
+                      p_sys->psz_inputfile, strerror( errno ) );
+        }
+        else
+        {
+            msg_Info( p_filter, "Grabbed input file: %s",
+                      p_sys->psz_inputfile );
+        }
+    }
+
+    if( p_sys->i_outputfd == -1 )
+    {
+        p_sys->i_outputfd = open( p_sys->psz_outputfile,
+                                  O_WRONLY | O_NONBLOCK );
+        if( p_sys->i_outputfd == -1 )
+        {
+            if( errno != ENXIO )
+            {
+                msg_Warn( p_filter, "Failed to grab output file: %s (%s)",
+                          p_sys->psz_outputfile, strerror( errno ) );
+            }
+        }
+        else
+        {
+            msg_Info( p_filter, "Grabbed output file: %s",
+                      p_sys->psz_outputfile );
+        }
+    }
+
+    /* Read any waiting commands */
+    if( p_sys->i_inputfd != -1 )
+    {
+        char p_buffer[1024];
+        ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
+        if( i_len == -1 )
+        {
+            /* We hit an error */
+            if( errno != EAGAIN )
+            {
+                msg_Warn( p_filter, "Error on input file: %s",
+                          strerror( errno ) );
+                close( p_sys->i_inputfd );
+                p_sys->i_inputfd = -1;
+            }
+        }
+        else if( i_len == 0 )
+        {
+            /* We hit the end-of-file */
+        }
+        else
+        {
+            BufferAdd( &p_sys->input, p_buffer, i_len );
+        }
+    }
+
+    /* Parse any complete commands */
+    char *p_end, *p_cmd;
+    while( ( p_end = memchr( p_sys->input.p_begin, '\n',
+                             p_sys->input.i_length ) ) )
+    {
+        commanddesc_t *p_cur = NULL;
+        vlc_bool_t b_found = VLC_FALSE;
+        size_t i_index = 0;
+
+        *p_end = '\0';
+        p_cmd = BufferGetToken( &p_sys->input );
+
+        msg_Info( p_filter, "Search command: %s", p_cmd );
+        for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
+        {
+            p_cur = p_sys->pp_commands[i_index];
+            if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) )
+            {
+                p_cmd[strlen(p_cur->psz_command)] = '\0';
+                b_found = VLC_TRUE;
+                break;
+            }
+        }
+
+        if( !b_found )
+        {
+            /* No matching command */
+            msg_Err( p_filter, "Got invalid command: %s", p_cmd );
+            BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
+        }
+        else
+        {
+            msg_Info( p_filter, "Got valid command: %s", p_cmd );
+
+            command_t *p_cmddesc = malloc( sizeof( command_t ) );
+            if( !p_cmddesc )
+                return NULL;
+
+            p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
+            p_cmddesc->p_command = p_cur;
+            p_cmddesc->p_command->pf_parser( p_cmd, p_end,
+                                             &p_cmddesc->params );
+
+            if( ( p_cmddesc->p_command->b_atomic == VLC_TRUE ) &&
+                ( p_sys->b_atomic == VLC_TRUE ) )
+                QueueEnqueue( &p_sys->atomic, p_cmddesc );
+            else
+                QueueEnqueue( &p_sys->pending, p_cmddesc );
+        }
+
+        BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
+    }
+
+    /* Process any pending commands */
+    command_t *p_command = NULL;
+    while( (p_command = QueueDequeue( &p_sys->pending )) )
+    {
+        p_command->i_status =
+            p_command->p_command->pf_execute( p_filter, &p_command->params,
+                                              &p_command->results );
+        QueueEnqueue( &p_sys->processed, p_command );
+    }
+
+    /* Output any processed commands */
+    while( (p_command = QueueDequeue( &p_sys->processed )) )
+    {
+        if( p_command->i_status == VLC_SUCCESS )
+        {
+            const char *psz_success = "SUCCESS:";
+            const char *psz_nl = "\n";
+            BufferAdd( &p_sys->output, psz_success, 8 );
+            p_command->p_command->pf_unparse( &p_command->results,
+                                              &p_sys->output );
+            BufferAdd( &p_sys->output, psz_nl, 1 );
+        }
+        else
+        {
+            BufferPrintf( &p_sys->output, "FAILURE: %d\n",
+                          p_command->i_status );
+        }
+    }
+
+    /* Try emptying the output buffer */
+    if( p_sys->i_outputfd != -1 )
+    {
+        ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
+                              p_sys->output.i_length );
+        if( i_len == -1 )
+        {
+            /* We hit an error */
+            if( errno != EAGAIN )
+            {
+                msg_Warn( p_filter, "Error on output file: %s",
+                          strerror( errno ) );
+                close( p_sys->i_outputfd );
+                p_sys->i_outputfd = -1;
+            }
+        }
+        else
+        {
+            BufferDel( &p_sys->output, i_len );
+        }
+    }
+
+    if( p_sys->b_updated == VLC_FALSE )
+        return NULL;
+
+    subpicture_t *p_spu = NULL;
+    overlay_t *p_overlay = NULL;
+
+    p_spu = p_filter->pf_sub_buffer_new( p_filter );
+    if( !p_spu )
+    {
+        msg_Err( p_filter, "cannot allocate subpicture" );
+        return NULL;
+    }
+
+    p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
+    p_spu->i_x = 0;
+    p_spu->i_y = 0;
+    p_spu->b_absolute = VLC_TRUE;
+    p_spu->i_start = date;
+    p_spu->i_stop = 0;
+    p_spu->b_ephemer = VLC_TRUE;
+
+    subpicture_region_t **pp_region = &p_spu->p_region;
+    while( (p_overlay = ListWalk( &p_sys->overlays )) )
+    {
+        msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d",
+                 (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y,
+                 p_overlay->i_alpha );
+
+        if( p_overlay->format.i_chroma == VLC_FOURCC('T','E','X','T') )
+        {
+            *pp_region = p_spu->pf_create_region( VLC_OBJECT(p_filter),
+                                                  &p_overlay->format );
+            if( !*pp_region )
+                break;
+            (*pp_region)->psz_text = strdup( p_overlay->data.p_text );
+            (*pp_region)->p_style = malloc( sizeof(struct text_style_t) );
+            if( !(*pp_region)->p_style )
+            {
+                p_spu->pf_destroy_region( VLC_OBJECT(p_filter), (*pp_region) );
+                *pp_region = NULL;
+                break;
+            }
+            p_filter->p_libvlc->pf_memcpy( (*pp_region)->p_style,
+                                            &p_overlay->fontstyle,
+                                            sizeof(struct text_style_t) );
+        }
+        else
+        {
+            picture_t clone;
+            if( vout_AllocatePicture( p_filter, &clone,
+                                      p_overlay->format.i_chroma,
+                                      p_overlay->format.i_width,
+                                      p_overlay->format.i_height,
+                                      p_overlay->format.i_aspect ) )
+            {
+                msg_Err( p_filter, "cannot allocate picture" );
+                continue;
+            }
+            vout_CopyPicture( p_filter, &clone, p_overlay->data.p_pic );
+            *pp_region = p_spu->pf_make_region( VLC_OBJECT(p_filter),
+                                                &p_overlay->format,
+                                                &clone );
+            if( !*pp_region )
+            {
+                msg_Err( p_filter, "cannot allocate subpicture region" );
+                continue;
+            }
+        }
+        (*pp_region)->i_x = p_overlay->i_x;
+        (*pp_region)->i_y = p_overlay->i_y;
+        (*pp_region)->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
+        (*pp_region)->i_alpha = p_overlay->i_alpha;
+        pp_region = &(*pp_region)->p_next;
+    }
+
+    p_sys->b_updated = VLC_FALSE;
+    return p_spu;
+}
+
+static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
+                           vlc_value_t oldval, vlc_value_t newval,
+                           void *p_data )
+{
+    filter_sys_t *p_sys = (filter_sys_t *)p_data;
+    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
+
+    if( !strncmp( psz_var, "overlay-input", 13 ) )
+        p_sys->psz_inputfile = newval.psz_string;
+    else if( !strncmp( psz_var, "overlay-output", 14 ) )
+        p_sys->psz_outputfile = newval.psz_string;
+
+    return VLC_EGENERIC;
+}
diff --git a/modules/video_filter/dynamicoverlay/dynamicoverlay.h b/modules/video_filter/dynamicoverlay/dynamicoverlay.h
new file mode 100644 (file)
index 0000000..db8cac5
--- /dev/null
@@ -0,0 +1,170 @@
+/*****************************************************************************
+ * dynamicoverlay.h : dynamic overlay plugin for vlc
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Author: Jean-Paul Saman <jpsaman@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef DYNAMIC_OVERLAY_H
+#define DYNAMIC_OVERLAY_H   1
+
+#include <vlc/vlc.h>
+#include <vlc_filter.h>
+
+/*****************************************************************************
+ * buffer_t: Command and response buffer
+ *****************************************************************************/
+
+typedef struct buffer_t
+{
+    size_t i_size;                         /**< Size of the allocated memory */
+    size_t i_length;                          /**< Length of the stored data */
+
+    char *p_memory;                       /**< Start of the allocated memory */
+    char *p_begin;                             /**< Start of the stored data */
+} buffer_t;
+
+int BufferInit( buffer_t *p_buffer );
+int BufferDestroy( buffer_t *p_buffer );
+int BufferAdd( buffer_t *p_buffer, const char *p_data, size_t i_len );
+int BufferPrintf( buffer_t *p_buffer, const char *p_fmt, ... );
+int BufferDel( buffer_t *p_buffer, int i_len );
+char *BufferGetToken( buffer_t *p_buffer );
+
+/*****************************************************************************
+ * Command structures
+ *****************************************************************************/
+
+/** struct commandparams_t - command params structure */
+typedef struct commandparams_t
+{
+    int32_t i_id;       /*< overlay id */
+    int32_t i_shmid;    /*< shared memory identifier */
+
+    vlc_fourcc_t fourcc;/*< chroma */
+
+    int32_t i_x;        /*< x position of overlay */
+    int32_t i_y;        /*< y position of overlay */
+    int32_t i_width;    /*< width of overlay */
+    int32_t i_height;   /*< height of overlay */
+
+    int32_t i_alpha;    /*< alpha value of overlay */
+
+    struct text_style_t fontstyle; /*< text style */
+
+    vlc_bool_t b_visible; /*< visibility flag of overlay */
+} commandparams_t;
+
+typedef struct commanddesc_t
+{
+    const char *psz_command;
+    vlc_bool_t b_atomic;
+    int ( *pf_parser ) ( char *psz_command, char *psz_end,
+                         commandparams_t *p_params );
+    int ( *pf_execute ) ( filter_t *p_filter, const commandparams_t *p_params,
+                          commandparams_t *p_results );
+    int ( *pf_unparse ) ( const commandparams_t *p_results,
+                          buffer_t *p_output );
+} commanddesc_t;
+
+typedef struct command_t
+{
+    struct commanddesc_t *p_command;
+    int i_status;
+    commandparams_t params;
+    commandparams_t results;
+    struct command_t *p_next;
+} command_t;
+
+void RegisterCommand( filter_t *p_filter );
+void UnregisterCommand( filter_t *p_filter );
+
+/*****************************************************************************
+ * queue_t: Command queue
+ *****************************************************************************/
+
+typedef struct queue_t
+{
+    command_t *p_head;                  /**< Head (first entry) of the queue */
+    command_t *p_tail;                   /**< Tail (last entry) of the queue */
+} queue_t;
+
+int QueueInit( queue_t *p_queue );
+int QueueDestroy( queue_t *p_queue );
+int QueueEnqueue( queue_t *p_queue, command_t *p_cmd );
+command_t *QueueDequeue( queue_t *p_queue );
+int QueueTransfer( queue_t *p_sink, queue_t *p_source );
+
+/*****************************************************************************
+ * overlay_t: Overlay descriptor
+ *****************************************************************************/
+
+typedef struct overlay_t
+{
+    int i_x, i_y;
+    int i_alpha;
+    vlc_bool_t b_active;
+
+    video_format_t format;
+    struct text_style_t fontstyle;
+    union {
+        picture_t *p_pic;
+        char *p_text;
+    } data;
+} overlay_t;
+
+overlay_t *OverlayCreate( void );
+int OverlayDestroy( overlay_t *p_ovl );
+
+/*****************************************************************************
+ * list_t: Command queue
+ *****************************************************************************/
+
+typedef struct list_t
+{
+    overlay_t **pp_head, **pp_tail;
+} list_t;
+
+int ListInit( list_t *p_list );
+int ListDestroy( list_t *p_list );
+ssize_t ListAdd( list_t *p_list, overlay_t *p_new );
+int ListRemove( list_t *p_list, size_t i_idx );
+overlay_t *ListGet( list_t *p_list, size_t i_idx );
+overlay_t *ListWalk( list_t *p_list );
+
+/*****************************************************************************
+ * filter_sys_t: adjust filter method descriptor
+ *****************************************************************************/
+
+struct filter_sys_t
+{
+    buffer_t input, output;
+
+    int i_inputfd, i_outputfd;
+    char *psz_inputfile, *psz_outputfile;
+
+    commanddesc_t **pp_commands; /* array of commands */
+    size_t i_commands;
+
+    vlc_bool_t b_updated, b_atomic;
+    queue_t atomic, pending, processed;
+    list_t overlays;
+};
+
+#endif
diff --git a/modules/video_filter/dynamicoverlay/dynamicoverlay_buffer.c b/modules/video_filter/dynamicoverlay/dynamicoverlay_buffer.c
new file mode 100644 (file)
index 0000000..ee0e2db
--- /dev/null
@@ -0,0 +1,160 @@
+/*****************************************************************************
+ * dynamicoverlay_buffer.h : dynamic overlay buffer
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Author: Søren Bøg <avacore@videolan.org>
+ *         Jean-Paul Saman <jpsaman@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_osd.h>
+#include <vlc_filter.h>
+
+#include <ctype.h>
+
+#include "dynamicoverlay.h"
+
+/*****************************************************************************
+ * buffer_t: Command and response buffer
+ *****************************************************************************/
+
+int BufferInit( buffer_t *p_buffer )
+{
+    memset( p_buffer, 0, sizeof( buffer_t ) );
+    p_buffer->p_memory = NULL;
+    p_buffer->p_begin = NULL;
+
+    return VLC_SUCCESS;
+}
+
+int BufferDestroy( buffer_t *p_buffer )
+{
+    if( p_buffer->p_memory != NULL )
+    {
+        free( p_buffer->p_memory );
+    }
+    p_buffer->p_memory = NULL;
+    p_buffer->p_begin = NULL;
+
+    return VLC_SUCCESS;
+}
+
+char *BufferGetToken( buffer_t *p_buffer )
+{
+    char *p_char = p_buffer->p_begin;
+
+    while( isspace( p_char[0] ) || p_char[0] == '\0' )
+    {
+        if( p_char <= (p_buffer->p_begin + p_buffer->i_length) )
+            p_char++;
+        else
+            return NULL;
+    }
+    return p_char;
+}
+
+int BufferAdd( buffer_t *p_buffer, const char *p_data, size_t i_len )
+{
+    if( ( p_buffer->i_size - p_buffer->i_length -
+          ( p_buffer->p_begin - p_buffer->p_memory ) ) < i_len )
+    {
+        /* We'll have to do some rearranging to fit the new data. */
+        if( ( p_buffer->i_size - p_buffer->i_length ) >= i_len )
+        {
+            /* We have room in the current buffer, just need to move it */
+            memmove( p_buffer->p_memory, p_buffer->p_begin,
+                     p_buffer->i_length );
+            p_buffer->p_begin = p_buffer->p_memory;
+        }
+        else
+        {
+            // We need a bigger buffer
+            size_t i_newsize = 1024;
+            while( i_newsize < p_buffer->i_length + i_len )
+                i_newsize *= 2;
+            /* TODO: Should I handle wrapping here? */
+
+            /* I'm not using realloc here, as I can avoid a memcpy/memmove in
+               some (most?) cases, and reset the start of the buffer. */
+            char *p_newdata = malloc( i_newsize );
+            if( p_newdata == NULL )
+                return VLC_ENOMEM;
+            if( p_buffer->p_begin != NULL )
+            {
+                memcpy( p_newdata, p_buffer->p_begin, p_buffer->i_length );
+                free( p_buffer->p_memory );
+            }
+            p_buffer->p_memory = p_buffer->p_begin = p_newdata;
+            p_buffer->i_size = i_newsize;
+        }
+    }
+
+    /* Add the new data to the end of the current */
+    memcpy( p_buffer->p_begin + p_buffer->i_length, p_data, i_len );
+    p_buffer->i_length += i_len;
+    return VLC_SUCCESS;
+}
+
+int BufferPrintf( buffer_t *p_buffer, const char *p_fmt, ... )
+{
+    int i_len;
+    int status;
+    char *psz_data;
+
+    va_list va_list1, va_list2;
+    va_start( va_list1, p_fmt );
+    va_copy( va_list2, va_list1 );
+
+    i_len = vsnprintf( NULL, 0, p_fmt, va_list1 );
+    if( i_len < 0 )
+        return VLC_EGENERIC;
+    va_end( va_list1 );
+
+    psz_data = malloc( i_len + 1 );
+    if( psz_data == NULL ) {
+        return VLC_ENOMEM;
+    }
+    if( vsnprintf( psz_data, i_len + 1, p_fmt, va_list2 ) != i_len )
+    {
+        return VLC_EGENERIC;
+    }
+    va_end( va_list2 );
+    status = BufferAdd( p_buffer, psz_data, i_len );
+    free( psz_data );
+    return status;
+}
+
+int BufferDel( buffer_t *p_buffer, int i_len )
+{
+    p_buffer->i_length -= i_len;
+    if( p_buffer->i_length == 0 )
+    {
+        /* No data, we can reset the buffer now. */
+        p_buffer->p_begin = p_buffer->p_memory;
+    }
+    else
+    {
+        p_buffer->p_begin += i_len;
+    }
+    return VLC_SUCCESS;
+}
diff --git a/modules/video_filter/dynamicoverlay/dynamicoverlay_commands.c b/modules/video_filter/dynamicoverlay/dynamicoverlay_commands.c
new file mode 100644 (file)
index 0000000..14630fc
--- /dev/null
@@ -0,0 +1,932 @@
+/*****************************************************************************
+ * dynamicoverlay_commands.c : dynamic overlay plugin commands
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Author: Søren Bøg <avacore@videolan.org>
+ *         Jean-Paul Saman <jpsaman@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_arrays.h>
+#include <vlc_vout.h>
+#include <vlc_filter.h>
+#include <vlc_osd.h>
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/shm.h>
+
+#include "dynamicoverlay.h"
+
+
+/*****************************************************************************
+ * overlay_t: Overlay descriptor
+ *****************************************************************************/
+
+overlay_t *OverlayCreate( void )
+{
+    overlay_t *p_ovl = malloc( sizeof( overlay_t ) );
+    if( p_ovl == NULL )
+       return NULL;
+    memset( p_ovl, 0, sizeof( overlay_t ) );
+
+    p_ovl->i_x = p_ovl->i_y = 0;
+    p_ovl->i_alpha = 0xFF;
+    p_ovl->b_active = VLC_FALSE;
+    vout_InitFormat( &p_ovl->format, VLC_FOURCC( '\0','\0','\0','\0') , 0, 0,
+                     VOUT_ASPECT_FACTOR );
+    memcpy( &p_ovl->fontstyle, &default_text_style, sizeof(struct text_style_t) );
+    p_ovl->data.p_text = NULL;
+
+    return p_ovl;
+}
+
+int OverlayDestroy( overlay_t *p_ovl )
+{
+    if( p_ovl->data.p_text != NULL )
+        free( p_ovl->data.p_text );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Command parsers
+ *****************************************************************************/
+static int skip_space( char **psz_command )
+{
+    char *psz_temp = *psz_command;
+
+    while( isspace( *psz_temp ) )
+    {
+        ++psz_temp;
+    }
+    if( psz_temp == *psz_command )
+    {
+        return VLC_EGENERIC;
+    }
+    *psz_command = psz_temp;
+    return VLC_SUCCESS;
+}
+
+static int parse_digit( char **psz_command, int32_t *value )
+{
+    char *psz_temp;
+    *value = strtol( *psz_command, &psz_temp, 10 );
+    if( psz_temp == *psz_command )
+    {
+        return VLC_EGENERIC;
+    }
+    *psz_command = psz_temp;
+    return VLC_SUCCESS;
+}
+
+static int parse_char( char **psz_command, char **psz_end,
+                       int count, char *psz_value )
+{
+    if( *psz_end - *psz_command < count )
+    {
+        return VLC_EGENERIC;
+    }
+    memcpy( psz_value, *psz_command, count );
+    *psz_command += count;
+    return VLC_SUCCESS;
+}
+
+static int parser_DataSharedMem( char *psz_command,
+                                 char *psz_end,
+                                 commandparams_t *p_params )
+{
+    /* Parse: 0 128 128 RGBA 9404459 */
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_width ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_height ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isascii( *psz_command ) )
+    {
+        if( parse_char( &psz_command, &psz_end, 4, (char*)&p_params->fourcc )
+            == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_shmid ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int parser_Id( char *psz_command, char *psz_end,
+                      commandparams_t *p_params )
+{
+    (void)(psz_end);
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int parser_None( char *psz_command, char *psz_end,
+                        commandparams_t *p_params )
+{
+    (void)(psz_command);
+    (void)(psz_end);
+    (void)(p_params);
+    return VLC_SUCCESS;
+}
+
+static int parser_SetAlpha( char *psz_command, char *psz_end,
+                            commandparams_t *p_params )
+{
+    (void)(psz_end);
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC  )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_alpha ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int parser_SetPosition( char *psz_command, char *psz_end,
+                               commandparams_t *p_params )
+{
+    (void)(psz_end);
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_x ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_y ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int parser_SetTextAlpha( char *psz_command, char *psz_end,
+                                commandparams_t *p_params )
+{
+    (void)(psz_end);
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->fontstyle.i_font_alpha ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int parser_SetTextColor( char *psz_command, char *psz_end,
+                                commandparams_t *p_params )
+{
+    int r, g, b;
+    (void)(psz_end);
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &r ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &g ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &b ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    p_params->fontstyle.i_font_color = (r<<24) | (g<<16) | (b<<8);
+    return VLC_SUCCESS;
+}
+
+static int parser_SetTextSize( char *psz_command, char *psz_end,
+                               commandparams_t *p_params )
+{
+    (void)(psz_end);
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->fontstyle.i_font_size ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int parser_SetVisibility( char *psz_command, char *psz_end,
+                                 commandparams_t *p_params )
+{
+    (void)(psz_end);
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        if( parse_digit( &psz_command, &p_params->i_id ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+    }
+    skip_space( &psz_command );
+    if( isdigit( *psz_command ) )
+    {
+        int32_t i_vis = 0;
+        if( parse_digit( &psz_command, &i_vis ) == VLC_EGENERIC )
+            return VLC_EGENERIC;
+        p_params->b_visible = (i_vis == 1) ? VLC_TRUE : VLC_FALSE;
+    }
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Command unparser functions
+ *****************************************************************************/
+
+static int unparse_default( const commandparams_t *p_results,
+                            buffer_t *p_output )
+{
+    (void)(p_results);
+    VLC_UNUSED(p_output);
+    return VLC_SUCCESS;
+}
+
+static int unparse_GenImage( const commandparams_t *p_results,
+                             buffer_t *p_output )
+{
+    int ret = BufferPrintf( p_output, " %d", p_results->i_id );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    return VLC_SUCCESS;
+}
+
+static int unparse_GetAlpha( const commandparams_t *p_results,
+                             buffer_t *p_output )
+{
+    int ret = BufferPrintf( p_output, " %d", p_results->i_alpha );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    return VLC_SUCCESS;
+}
+
+static int unparse_GetPosition( const commandparams_t *p_results,
+                                buffer_t *p_output )
+{
+    int ret = BufferPrintf( p_output, " %d", p_results->i_x );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    ret = BufferPrintf( p_output, " %d", p_results->i_y );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    return VLC_SUCCESS;
+}
+
+static int unparse_GetTextAlpha( const commandparams_t *p_results,
+                                 buffer_t *p_output )
+{
+    int ret = BufferPrintf( p_output, " %d", p_results->fontstyle.i_font_alpha );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    return VLC_SUCCESS;
+}
+
+static int unparse_GetTextColor( const commandparams_t *p_results,
+                                 buffer_t *p_output )
+{
+    int ret = BufferPrintf( p_output, " %d", (p_results->fontstyle.i_font_color & 0xff0000)>>24 );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    ret = BufferPrintf( p_output, " %d", (p_results->fontstyle.i_font_color & 0x00ff00)>>16 );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    ret = BufferPrintf( p_output, " %d", (p_results->fontstyle.i_font_color & 0x0000ff)>>8 );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    return VLC_SUCCESS;
+}
+
+static int unparse_GetTextSize( const commandparams_t *p_results,
+                                buffer_t *p_output )
+{
+    int ret = BufferPrintf( p_output, " %d", p_results->fontstyle.i_font_size );
+    if( ret != VLC_SUCCESS )
+        return ret;
+
+    return VLC_SUCCESS;
+}
+
+static int unparse_GetVisibility( const commandparams_t *p_results,
+                             buffer_t *p_output )
+{
+    int ret = BufferPrintf( p_output, " %d", (p_results->b_visible ? 1 : 0) );
+    if( ret != VLC_SUCCESS ) {
+        return ret;
+    }
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Command functions
+ *****************************************************************************/
+static int exec_DataSharedMem( filter_t *p_filter,
+                               const commandparams_t *p_params,
+                               commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    struct shmid_ds shminfo;
+    overlay_t *p_ovl;
+    size_t i_size;
+
+    (void)(p_results);
+
+    p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+    {
+        msg_Err( p_filter, "Invalid overlay: %d", p_params->i_id );
+        return VLC_EGENERIC;
+    }
+
+    if( shmctl( p_params->i_shmid, IPC_STAT, &shminfo ) == -1 )
+    {
+        msg_Err( p_filter, "Unable to access shared memory" );
+        return VLC_EGENERIC;
+    }
+    i_size = shminfo.shm_segsz;
+
+    if( p_params->fourcc == VLC_FOURCC('T','E','X','T') )
+    {
+        char *p_data;
+
+        if( (p_params->i_height != 1) || (p_params->i_width < 1) )
+        {
+            msg_Err( p_filter,
+                     "Invalid width and/or height. when specifing text height "
+                     "must be 1 and width the number of bytes in the string, "
+                     "including the null terminator" );
+            return VLC_EGENERIC;
+        }
+
+        if( (size_t)p_params->i_width > i_size )
+        {
+            msg_Err( p_filter,
+                     "Insufficient data in shared memory. need %d, got %d",
+                     p_params->i_width, i_size );
+            return VLC_EGENERIC;
+        }
+
+        p_ovl->data.p_text = malloc( p_params->i_width );
+        if( p_ovl->data.p_text == NULL )
+        {
+            msg_Err( p_filter, "Unable to allocate string storage" );
+            return VLC_ENOMEM;
+        }
+
+        vout_InitFormat( &p_ovl->format, VLC_FOURCC('T','E','X','T'),
+                         0, 0, 0 );
+
+        p_data = shmat( p_params->i_shmid, NULL, SHM_RDONLY );
+        if( p_data == NULL )
+        {
+            msg_Err( p_filter, "Unable to attach to shared memory" );
+            free( p_ovl->data.p_text );
+            p_ovl->data.p_text = NULL;
+            return VLC_ENOMEM;
+        }
+
+        memcpy( p_ovl->data.p_text, p_data, p_params->i_width );
+
+        shmdt( p_data );
+    }
+    else
+    {
+        uint8_t *p_data, *p_in;
+        size_t i_neededsize = 0;
+
+        p_ovl->data.p_pic = malloc( sizeof( picture_t ) );
+        if( p_ovl->data.p_pic == NULL )
+        {
+            msg_Err( p_filter, "Unable to allocate picture structure" );
+            return VLC_ENOMEM;
+        }
+
+        vout_InitFormat( &p_ovl->format, p_params->fourcc,
+                         p_params->i_width, p_params->i_height,
+                         VOUT_ASPECT_FACTOR );
+        if( vout_AllocatePicture( p_filter, p_ovl->data.p_pic,
+                                  p_ovl->format.i_chroma, p_params->i_width,
+                                  p_params->i_height, p_ovl->format.i_aspect ) )
+        {
+            msg_Err( p_filter, "Unable to allocate picture" );
+            free( p_ovl->data.p_pic );
+            p_ovl->data.p_pic = NULL;
+            return VLC_ENOMEM;
+        }
+
+        for( size_t i_plane = 0; i_plane < (size_t)p_ovl->data.p_pic->i_planes;
+             ++i_plane )
+        {
+            i_neededsize += p_ovl->data.p_pic->p[i_plane].i_visible_lines *
+                            p_ovl->data.p_pic->p[i_plane].i_visible_pitch;
+        }
+
+        if( i_neededsize > i_size )
+        {
+            msg_Err( p_filter,
+                     "Insufficient data in shared memory. need %d, got %d",
+                     i_neededsize, i_size );
+            p_ovl->data.p_pic->pf_release( p_ovl->data.p_pic );
+            free( p_ovl->data.p_pic );
+            p_ovl->data.p_pic = NULL;
+            return VLC_EGENERIC;
+        }
+
+        p_data = shmat( p_params->i_shmid, NULL, SHM_RDONLY );
+        if( p_data == NULL )
+        {
+            msg_Err( p_filter, "Unable to attach to shared memory" );
+            p_ovl->data.p_pic->pf_release( p_ovl->data.p_pic );
+            free( p_ovl->data.p_pic );
+            p_ovl->data.p_pic = NULL;
+            return VLC_ENOMEM;
+        }
+
+        p_in = p_data;
+        for( size_t i_plane = 0; i_plane < (size_t)p_ovl->data.p_pic->i_planes;
+             ++i_plane )
+        {
+            uint8_t *p_out = p_ovl->data.p_pic->p[i_plane].p_pixels;
+            for( size_t i_line = 0;
+                 i_line < (size_t)p_ovl->data.p_pic->p[i_plane].i_visible_lines;
+                 ++i_line )
+            {
+                p_filter->p_libvlc->pf_memcpy( p_out, p_in,
+                                p_ovl->data.p_pic->p[i_plane].i_visible_pitch );
+                p_out += p_ovl->data.p_pic->p[i_plane].i_pitch;
+                p_in += p_ovl->data.p_pic->p[i_plane].i_visible_pitch;
+            }
+        }
+        shmdt( p_data );
+    }
+
+    p_sys->b_updated = p_ovl->b_active;
+
+    return VLC_SUCCESS;
+}
+
+static int exec_DeleteImage( filter_t *p_filter,
+                             const commandparams_t *p_params,
+                             commandparams_t *p_results )
+{
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    p_sys->b_updated = VLC_TRUE;
+
+    return ListRemove( &p_sys->overlays, p_params->i_id );
+}
+
+static int exec_EndAtomic( filter_t *p_filter,
+                           const commandparams_t *p_params,
+                           commandparams_t *p_results )
+{
+    (void)(p_params);
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    QueueTransfer( &p_sys->pending, &p_sys->atomic );
+    p_sys->b_atomic = VLC_FALSE;
+    return VLC_SUCCESS;
+}
+
+static int exec_GenImage( filter_t *p_filter,
+                          const commandparams_t *p_params,
+                          commandparams_t *p_results )
+{
+    (void)(p_params);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = OverlayCreate();
+    if( p_ovl == NULL )
+        return VLC_ENOMEM;
+
+    ssize_t i_idx = ListAdd( &p_sys->overlays, p_ovl );
+    if( i_idx < 0 )
+        return i_idx;
+
+    p_results->i_id = i_idx;
+    return VLC_SUCCESS;
+}
+
+static int exec_GetAlpha( filter_t *p_filter,
+                          const commandparams_t *p_params,
+                          commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_results->i_alpha = p_ovl->i_alpha;
+    return VLC_SUCCESS;
+}
+
+static int exec_GetPosition( filter_t *p_filter,
+                             const commandparams_t *p_params,
+                             commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_results->i_x = p_ovl->i_x;
+    p_results->i_y = p_ovl->i_y;
+    return VLC_SUCCESS;
+}
+
+static int exec_GetTextAlpha( filter_t *p_filter,
+                              const commandparams_t *p_params,
+                              commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_results->fontstyle.i_font_alpha = p_ovl->fontstyle.i_font_alpha;
+    return VLC_SUCCESS;
+}
+
+static int exec_GetTextColor( filter_t *p_filter,
+                              const commandparams_t *p_params,
+                              commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_results->fontstyle.i_font_color = p_ovl->fontstyle.i_font_color;
+    return VLC_SUCCESS;
+}
+
+static int exec_GetTextSize( filter_t *p_filter,
+                             const commandparams_t *p_params,
+                             commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_results->fontstyle.i_font_size = p_ovl->fontstyle.i_font_size;
+    return VLC_SUCCESS;
+}
+
+static int exec_GetVisibility( filter_t *p_filter,
+                               const commandparams_t *p_params,
+                               commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_results->b_visible = ( p_ovl->b_active == VLC_TRUE ) ? 1 : 0;
+    return VLC_SUCCESS;
+}
+
+static int exec_SetAlpha( filter_t *p_filter,
+                          const commandparams_t *p_params,
+                          commandparams_t *p_results )
+{
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_ovl->i_alpha = p_params->i_alpha;
+    p_sys->b_updated = p_ovl->b_active;
+    return VLC_SUCCESS;
+}
+
+static int exec_SetPosition( filter_t *p_filter,
+                             const commandparams_t *p_params,
+                             commandparams_t *p_results )
+{
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_ovl->i_x = p_params->i_x;
+    p_ovl->i_y = p_params->i_y;
+
+    p_sys->b_updated = p_ovl->b_active;
+    return VLC_SUCCESS;
+}
+
+static int exec_SetTextAlpha( filter_t *p_filter,
+                              const commandparams_t *p_params,
+                              commandparams_t *p_results )
+{
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_ovl->fontstyle.i_font_alpha = p_params->fontstyle.i_font_alpha;
+    p_sys->b_updated = p_ovl->b_active;
+    return VLC_SUCCESS;
+}
+
+static int exec_SetTextColor( filter_t *p_filter,
+                              const commandparams_t *p_params,
+                              commandparams_t *p_results )
+{
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_ovl->fontstyle.i_font_color = p_params->fontstyle.i_font_color;
+    p_sys->b_updated = p_ovl->b_active;
+    return VLC_SUCCESS;
+}
+
+static int exec_SetTextSize( filter_t *p_filter,
+                              const commandparams_t *p_params,
+                              commandparams_t *p_results )
+{
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_ovl->fontstyle.i_font_size = p_params->fontstyle.i_font_size;
+    p_sys->b_updated = p_ovl->b_active;
+    return VLC_SUCCESS;
+}
+
+static int exec_SetVisibility( filter_t *p_filter,
+                               const commandparams_t *p_params,
+                               commandparams_t *p_results )
+{
+    (void)(p_results);
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL )
+        return VLC_EGENERIC;
+
+    p_ovl->b_active = p_params->b_visible;// ? VLC_FALSE : VLC_TRUE;
+    p_sys->b_updated = VLC_TRUE;
+    return VLC_SUCCESS;
+}
+
+static int exec_StartAtomic( filter_t *p_filter,
+                             const commandparams_t *p_params,
+                             commandparams_t *p_results )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    (void)(p_params);
+    (void)(p_results);
+
+    p_sys->b_atomic = VLC_TRUE;
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Command functions
+ *****************************************************************************/
+static commanddesc_t p_commands[] =
+{
+    {   .psz_command = "DataSharedMem",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_DataSharedMem,
+        .pf_execute = exec_DataSharedMem,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "DeleteImage",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_Id,
+        .pf_execute = exec_DeleteImage,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "EndAtomic",
+        .b_atomic = VLC_FALSE,
+        .pf_parser = parser_None,
+        .pf_execute = exec_EndAtomic,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "GenImage",
+        .b_atomic = VLC_FALSE,
+        .pf_parser = parser_None,
+        .pf_execute = exec_GenImage,
+        .pf_unparse = unparse_GenImage,
+    },
+    {   .psz_command = "GetAlpha",
+        .b_atomic = VLC_FALSE,
+        .pf_parser = parser_Id,
+        .pf_execute = exec_GetAlpha,
+        .pf_unparse = unparse_GetAlpha,
+    },
+    {   .psz_command = "GetPosition",
+        .b_atomic = VLC_FALSE,
+        .pf_parser = parser_Id,
+        .pf_execute = exec_GetPosition,
+        .pf_unparse = unparse_GetPosition,
+    },
+    {   .psz_command = "GetTextAlpha",
+        .b_atomic = VLC_FALSE,
+        .pf_parser = parser_Id,
+        .pf_execute = exec_GetTextAlpha,
+        .pf_unparse = unparse_GetTextAlpha,
+    },
+    {   .psz_command = "GetTextColor",
+        .b_atomic = VLC_FALSE,
+        .pf_parser = parser_Id,
+        .pf_execute = exec_GetTextColor,
+        .pf_unparse = unparse_GetTextColor,
+    },
+    {   .psz_command = "GetTextSize",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_Id,
+        .pf_execute = exec_GetTextSize,
+        .pf_unparse = unparse_GetTextSize,
+    },
+    {   .psz_command = "GetVisibility",
+        .b_atomic = VLC_FALSE,
+        .pf_parser = parser_Id,
+        .pf_execute = exec_GetVisibility,
+        .pf_unparse = unparse_GetVisibility,
+    },
+    {   .psz_command = "SetAlpha",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_SetAlpha,
+        .pf_execute = exec_SetAlpha,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "SetPosition",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_SetPosition,
+        .pf_execute = exec_SetPosition,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "SetTextAlpha",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_SetTextAlpha,
+        .pf_execute = exec_SetTextAlpha,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "SetTextColor",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_SetTextColor,
+        .pf_execute = exec_SetTextColor,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "SetTextSize",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_SetTextSize,
+        .pf_execute = exec_SetTextSize,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "SetVisibility",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_SetVisibility,
+        .pf_execute = exec_SetVisibility,
+        .pf_unparse = unparse_default,
+    },
+    {   .psz_command = "StartAtomic",
+        .b_atomic = VLC_TRUE,
+        .pf_parser = parser_None,
+        .pf_execute = exec_StartAtomic,
+        .pf_unparse = unparse_default,
+    }
+};
+
+void RegisterCommand( filter_t *p_filter )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    size_t i_index = 0;
+
+    p_sys->i_commands = ARRAY_SIZE(p_commands);
+    p_sys->pp_commands = (commanddesc_t **) calloc( p_sys->i_commands, sizeof(commanddesc_t*) );
+    if( !p_sys->pp_commands ) return;
+    for( i_index = 0; i_index < p_sys->i_commands; i_index ++ )
+    {
+        p_sys->pp_commands[i_index] = (commanddesc_t *) malloc( sizeof(commanddesc_t) );
+        if( !p_sys->pp_commands[i_index] ) return;
+        p_sys->pp_commands[i_index]->psz_command = strdup( p_commands[i_index].psz_command );
+        p_sys->pp_commands[i_index]->b_atomic = p_commands[i_index].b_atomic;
+        p_sys->pp_commands[i_index]->pf_parser = p_commands[i_index].pf_parser;
+        p_sys->pp_commands[i_index]->pf_execute = p_commands[i_index].pf_execute;
+        p_sys->pp_commands[i_index]->pf_unparse = p_commands[i_index].pf_unparse;
+    }
+
+    msg_Dbg( p_filter, "%d commands are available", p_sys->i_commands );
+    for( size_t i_index = 0; i_index < p_sys->i_commands; i_index++ )
+        msg_Dbg( p_filter, "    %s", p_sys->pp_commands[i_index]->psz_command );
+}
+
+void UnregisterCommand( filter_t *p_filter )
+{
+    filter_sys_t *p_sys = (filter_sys_t*) p_filter->p_sys;
+    size_t i_index = 0;
+
+    for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
+    {
+        free( p_sys->pp_commands[i_index]->psz_command );
+        free( p_sys->pp_commands[i_index] );
+    }
+    free( p_sys->pp_commands );
+    p_sys->pp_commands = NULL;
+    p_sys->i_commands = 0;
+}
diff --git a/modules/video_filter/dynamicoverlay/dynamicoverlay_list.c b/modules/video_filter/dynamicoverlay/dynamicoverlay_list.c
new file mode 100644 (file)
index 0000000..06924fb
--- /dev/null
@@ -0,0 +1,143 @@
+/*****************************************************************************
+ * dynamicoverlay_list.h : dynamic overlay list
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Author: Søren Bøg <avacore@videolan.org>
+ *         Jean-Paul Saman <jpsaman@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_osd.h>
+
+#include <fcntl.h>
+#include "dynamicoverlay.h"
+
+/*****************************************************************************
+ * list_t: Command queue
+ *****************************************************************************/
+
+int ListInit( list_t *p_list )
+{
+    p_list->pp_head = malloc( 16 * sizeof( overlay_t * ) );
+    if( p_list->pp_head == NULL )
+        return VLC_ENOMEM;
+
+    p_list->pp_tail = p_list->pp_head + 16;
+    memset( p_list->pp_head, 0, 16 * sizeof( overlay_t * ) );
+
+    return VLC_SUCCESS;
+}
+
+int ListDestroy( list_t *p_list )
+{
+    for( overlay_t **pp_cur = p_list->pp_head;
+         pp_cur < p_list->pp_tail;
+         ++pp_cur )
+    {
+        if( *pp_cur != NULL )
+        {
+            OverlayDestroy( *pp_cur );
+            free( *pp_cur );
+        }
+    }
+    free( p_list->pp_head );
+
+    return VLC_SUCCESS;
+}
+
+ssize_t ListAdd( list_t *p_list, overlay_t *p_new )
+{
+    /* Find an available slot */
+    for( overlay_t **pp_cur = p_list->pp_head;
+         pp_cur < p_list->pp_tail;
+         ++pp_cur )
+    {
+        if( *pp_cur == NULL )
+        {
+            *pp_cur = p_new;
+            return pp_cur - p_list->pp_head;
+        }
+    }
+
+    /* Have to expand */
+    size_t i_size = p_list->pp_tail - p_list->pp_head;
+    size_t i_newsize = i_size * 2;
+    p_list->pp_head = realloc( p_list->pp_head,
+                               i_newsize * sizeof( overlay_t * ) );
+    if( p_list->pp_head == NULL )
+        return VLC_ENOMEM;
+
+    p_list->pp_tail = p_list->pp_head + i_newsize;
+    memset( p_list->pp_head + i_size, 0, i_size * sizeof( overlay_t * ) );
+    p_list->pp_head[i_size] = p_new;
+    return i_size;
+}
+
+int ListRemove( list_t *p_list, size_t i_idx )
+{
+    int ret;
+
+    if( ( i_idx >= (size_t)( p_list->pp_tail - p_list->pp_head ) ) ||
+        ( p_list->pp_head[i_idx] == NULL ) )
+    {
+        return VLC_EGENERIC;
+    }
+
+    ret = OverlayDestroy( p_list->pp_head[i_idx] );
+    free( p_list->pp_head[i_idx] );
+    p_list->pp_head[i_idx] = NULL;
+
+    return ret;
+}
+
+overlay_t *ListGet( list_t *p_list, size_t i_idx )
+{
+    if( ( i_idx >= (size_t)( p_list->pp_tail - p_list->pp_head ) ) ||
+        ( p_list->pp_head[i_idx] == NULL ) )
+    {
+        return NULL;
+    }
+    return p_list->pp_head[i_idx];
+}
+
+overlay_t *ListWalk( list_t *p_list )
+{
+    static overlay_t **pp_cur = NULL;
+
+    if( pp_cur == NULL )
+        pp_cur = p_list->pp_head;
+    else
+        pp_cur = pp_cur + 1;
+
+    for( ; pp_cur < p_list->pp_tail; ++pp_cur )
+    {
+        if( ( *pp_cur != NULL ) &&
+            ( (*pp_cur)->b_active == VLC_TRUE )&&
+            ( (*pp_cur)->format.i_chroma != VLC_FOURCC( '\0','\0','\0','\0') ) )
+        {
+            return *pp_cur;
+        }
+    }
+    pp_cur = NULL;
+    return NULL;
+}
diff --git a/modules/video_filter/dynamicoverlay/dynamicoverlay_queue.c b/modules/video_filter/dynamicoverlay/dynamicoverlay_queue.c
new file mode 100644 (file)
index 0000000..9fd757a
--- /dev/null
@@ -0,0 +1,115 @@
+/*****************************************************************************
+ * dynamicoverlay_commands.c : dynamic overlay plugin commands
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Author: Søren Bøg <avacore@videolan.org>
+ *         Jean-Paul Saman <jpsaman@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_osd.h>
+
+#include "dynamicoverlay.h"
+
+/*****************************************************************************
+ * queue_t: Command queue
+ *****************************************************************************/
+
+int QueueInit( queue_t *p_queue )
+{
+    memset( p_queue, 0, sizeof( queue_t ) );
+    p_queue->p_head = NULL;
+    p_queue->p_tail = NULL;
+
+    return VLC_SUCCESS;
+}
+
+int QueueDestroy( queue_t *p_queue )
+{
+    command_t *p_cur = p_queue->p_head, *p_temp;
+    while( p_cur != NULL )
+    {
+        p_temp = p_cur;
+        p_cur = p_cur->p_next;
+        free( p_temp );
+    }
+    p_queue->p_head = NULL;
+    p_queue->p_tail = NULL;
+
+    return VLC_SUCCESS;
+}
+
+int QueueEnqueue( queue_t *p_queue, command_t *p_cmd )
+{
+    if( p_queue->p_tail != NULL )
+    {
+        p_queue->p_tail->p_next = p_cmd;
+    }
+    if( p_queue->p_head == NULL )
+    {
+        p_queue->p_head = p_cmd;
+    }
+    p_queue->p_tail = p_cmd;
+    p_cmd->p_next = NULL;
+
+    return VLC_SUCCESS;
+}
+
+command_t *QueueDequeue( queue_t *p_queue )
+{
+    if( p_queue->p_head == NULL )
+    {
+        return NULL;
+    }
+    else
+    {
+        command_t *p_ret = p_queue->p_head;
+        if( p_queue->p_head == p_queue->p_tail )
+        {
+            p_queue->p_head = p_queue->p_tail = NULL;
+        }
+        else
+        {
+            p_queue->p_head = p_queue->p_head->p_next;
+        }
+        return p_ret;
+    }
+}
+
+int QueueTransfer( queue_t *p_sink, queue_t *p_source )
+{
+    if( p_source->p_head == NULL ) {
+        return VLC_SUCCESS;
+    }
+
+    if( p_sink->p_head == NULL ) {
+        p_sink->p_head = p_source->p_head;
+        p_sink->p_tail = p_source->p_tail;
+    } else {
+        p_sink->p_tail->p_next = p_source->p_head;
+        p_sink->p_tail = p_source->p_tail;
+    }
+    p_source->p_head = p_source->p_tail = NULL;
+
+    return VLC_SUCCESS;
+}
index c52644af1b1587f8b0f044a3089a81bffbcdd5a1..5ba5c178389e524db02d740230801ad07143a5fa 100644 (file)
@@ -435,6 +435,7 @@ static subpicture_region_t *create_picture_region( filter_t *p_filter, subpictur
     p_region->i_x = 0;
     p_region->i_y = 0;
     p_region->i_align = p_filter->p_sys->i_position;
+    p_region->i_alpha = p_filter->p_sys->i_alpha;
 #if 0
     msg_Dbg( p_filter, "SPU picture region position (%d,%d) (%d,%d) [%p]",
         p_region->i_x, p_region->i_y,
index 9dccc5324d81c405a4e91f76909d401cbac2eaa9..040345d6f5bb496d0f4469d20336a9c260c0ebff 100644 (file)
@@ -291,6 +291,7 @@ subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
     if( !p_region ) return NULL;
 
     memset( p_region, 0, sizeof(subpicture_region_t) );
+    p_region->i_alpha = 0xff;
     p_region->p_next = NULL;
     p_region->p_cache = NULL;
     p_region->fmt = *p_fmt;
@@ -336,6 +337,7 @@ subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this,
     (void)p_this;
     if( !p_region ) return NULL;
     memset( p_region, 0, sizeof(subpicture_region_t) );
+    p_region->i_alpha = 0xff;
     p_region->p_next = 0;
     p_region->p_cache = 0;
     p_region->fmt = *p_fmt;
@@ -901,6 +903,7 @@ void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
                 p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / 1000;
                 p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / 1000;
                 p_region->p_cache->i_align = p_region->i_align;
+                p_region->p_cache->i_alpha = p_region->i_alpha;
 
                 p_pic = p_spu->p_scale->pf_video_filter(
                                  p_spu->p_scale, &p_region->p_cache->picture );
@@ -1060,7 +1063,7 @@ void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
                 {
                     p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
                         p_pic_src, &p_region->picture, i_x_offset, i_y_offset,
-                        i_fade_alpha * p_subpic->i_alpha / 255 );
+                        i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025 );
                 }
                 else
                 {
diff --git a/test/dynamicoverlay/Makefile b/test/dynamicoverlay/Makefile
new file mode 100644 (file)
index 0000000..18876ca
--- /dev/null
@@ -0,0 +1,4 @@
+all: overlay-test
+
+overlay-test: overlay-test.c
+       gcc -g2 --std=c99 -D_XOPEN_SOURCE=500 overlay-test.c -lm -o overlay-test
diff --git a/test/dynamicoverlay/overlay-test.c b/test/dynamicoverlay/overlay-test.c
new file mode 100644 (file)
index 0000000..63774d5
--- /dev/null
@@ -0,0 +1,351 @@
+/*****************************************************************************
+ * overlay-test.c : test program for the dynamic overlay plugin
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Author: Søren Bøg <avacore@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <math.h>
+
+#include <sys/fcntl.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <unistd.h>
+
+/*****************************************************************************
+ * Images
+ *****************************************************************************/
+
+#define WIDTH 128
+#define HEIGHT 128
+
+#define TEXT "Hello world!"
+#define TEXTSIZE sizeof( TEXT )
+
+char *p_imageRGBA;
+char *p_text;
+
+void DataCreate( void ) {
+    char *p_data = p_imageRGBA;
+    for( size_t i = 0; i < HEIGHT; ++i ) {
+        for( size_t j = 0; j < HEIGHT; ++j ) {
+            *(p_data++) = i * 4 & 0xFF;
+            *(p_data++) = 0xFF;
+            *(p_data++) = 0x00;
+            *(p_data++) = j * 4 & 0xFF;
+        }
+    }
+
+    memcpy( p_text, TEXT, TEXTSIZE );
+}
+
+/*****************************************************************************
+ * I/O Helpers
+ *****************************************************************************/
+
+int IsFailure( char *psz_text ) {
+    return strncmp( psz_text, "SUCCESS:", 8 );
+}
+
+void CheckResult( FILE *p_res ) {
+    char psz_resp[9];
+
+    fscanf( p_res, "%8s", &psz_resp );
+    if( IsFailure( psz_resp ) ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+}
+
+void CheckedCommand( FILE *p_cmd, FILE *p_res, char *p_format, ... ) {
+    va_list ap;
+    va_start( ap, p_format );
+
+    vfprintf( p_cmd, p_format, ap );
+    fflush( p_cmd );
+    if( p_res != NULL ) {
+        CheckResult( p_res );
+    }
+    va_end( ap );
+}
+
+int GenImage( FILE *p_cmd, FILE *p_res ) {
+    int i_overlay;
+
+    printf( "Getting an overlay..." );
+    CheckedCommand( p_cmd, p_res, "GenImage\n" );
+    fscanf( p_res, "%d", &i_overlay );
+    printf( " done. Overlay is %d\n", i_overlay );
+
+    return i_overlay;
+}
+
+void DeleteImage( FILE *p_cmd, FILE *p_res, int i_overlay ) {
+    printf( "Removing image..." );
+    CheckedCommand( p_cmd, p_res, "DeleteImage %d\n", i_overlay );
+    printf( " done\n" );
+}
+
+void StartAtomic( FILE *p_cmd, FILE *p_res ) {
+    CheckedCommand( p_cmd, p_res, "StartAtomic\n" );
+}
+
+void EndAtomic( FILE *p_cmd, FILE *p_res ) {
+    CheckedCommand( p_cmd, p_res, "EndAtomic\n" );
+}
+
+void DataSharedMem( FILE *p_cmd, FILE *p_res, int i_overlay, int i_width,
+                    int i_height, char *psz_format, int i_shmid ) {
+
+    printf( "Sending data via shared memory..." );
+    CheckedCommand( p_cmd, p_res, "DataSharedMem %d %d %d %s %d\n", i_overlay,
+                    i_width, i_height, psz_format, i_shmid );
+    printf( " done\n" );
+}
+
+void SetAlpha( FILE *p_cmd, FILE *p_res, int i_overlay, int i_alpha ) {
+    CheckedCommand( p_cmd, p_res, "SetAlpha %d %d\n", i_overlay, i_alpha );
+}
+
+void SetPosition( FILE *p_cmd, FILE *p_res, int i_overlay, int i_x, int i_y ) {
+    CheckedCommand( p_cmd, p_res, "SetPosition %d %d %d\n", i_overlay, i_x,
+                    i_y );
+}
+
+void SetVisibility( FILE *p_cmd, FILE *p_res, int i_overlay, int i_visible ) {
+    CheckedCommand( p_cmd, p_res, "SetVisibility %d %d\n", i_overlay,
+                    i_visible );
+}
+
+void SetTextAlpha( FILE *p_cmd, FILE *p_res, int i_overlay, int i_alpha ) {
+    CheckedCommand( p_cmd, p_res, "SetTextAlpha %d %d\n", i_overlay, i_alpha );
+}
+
+void SetTextColor( FILE *p_cmd, FILE *p_res, int i_overlay, int i_red,
+                   int i_green, int i_blue ) {
+    CheckedCommand( p_cmd, p_res, "SetTextColor %d %d %d %d\n", i_overlay,
+                    i_red, i_green, i_blue );
+}
+
+void SetTextSize( FILE *p_cmd, FILE *p_res, int i_overlay, int i_size ) {
+    CheckedCommand( p_cmd, p_res, "SetTextSize %d %d\n", i_overlay, i_size );
+}
+
+int GetTextSize( FILE *p_cmd, FILE *p_res, int i_overlay ) {
+    int i_size;
+
+    CheckedCommand( p_cmd, p_res, "GetTextSize %d\n", i_overlay );
+    fscanf( p_res, "%d", &i_size );
+
+    return i_size;
+}
+
+/*****************************************************************************
+ * Test Routines
+ *****************************************************************************/
+
+void BasicTest( FILE *p_cmd, FILE *p_res, int i_overlay ) {
+    printf( "Activating overlay..." );
+    SetVisibility( p_cmd, p_res, i_overlay, 1 );
+    printf( " done\n" );
+
+    printf( "Sweeping alpha..." );
+    for( int i_alpha = 0xFF; i_alpha >= -0xFF ; i_alpha -= 8 ) {
+        SetAlpha( p_cmd, p_res, i_overlay, abs( i_alpha ) );
+        usleep( 20000 );
+    }
+    SetAlpha( p_cmd, p_res, i_overlay, 255 );
+    printf( " done\n" );
+
+    printf( "Circle motion..." );
+    for( float f_theta = 0; f_theta <= 2 * M_PI ; f_theta += M_PI / 64.0 ) {
+        SetPosition( p_cmd, p_res, i_overlay,
+                     (int)( - cos( f_theta ) * 100.0 + 100.0 ),
+                     (int)( - sin( f_theta ) * 100.0 + 100.0 ) );
+        usleep( 20000 );
+    }
+    SetPosition( p_cmd, p_res, i_overlay, 0, 100 );
+    printf( " done\n" );
+
+    printf( "Atomic motion..." );
+    StartAtomic( p_cmd, p_res );
+    SetPosition( p_cmd, NULL, i_overlay, 200, 50 );
+    sleep( 1 );
+    SetPosition( p_cmd, NULL, i_overlay, 0, 0 );
+    EndAtomic( p_cmd, p_res );
+    CheckResult( p_res );
+    CheckResult( p_res );
+    printf( " done\n" );
+
+    sleep( 5 );
+}
+
+void TextTest( FILE *p_cmd, FILE *p_res, int i_overlay ) {
+    printf( "Sweeping (text) alpha..." );
+    for( int i_alpha = 0xFF; i_alpha >= -0xFF ; i_alpha -= 8 ) {
+        SetTextAlpha( p_cmd, p_res, i_overlay, abs( i_alpha ) );
+        usleep( 20000 );
+    }
+    SetTextAlpha( p_cmd, p_res, i_overlay, 255 );
+    printf( " done\n" );
+
+    printf( "Sweeping colors..." );
+    for( int i_red = 0xFF; i_red >= 0x00 ; i_red -= 8 ) {
+        SetTextColor( p_cmd, p_res, i_overlay, i_red, 0xFF, 0xFF );
+        usleep( 20000 );
+    }
+    for( int i_green = 0xFF; i_green >= 0x00 ; i_green -= 8 ) {
+        SetTextColor( p_cmd, p_res, i_overlay, 0x00, i_green, 0xFF );
+        usleep( 20000 );
+    }
+    for( int i_blue = 0xFF; i_blue >= 0x00 ; i_blue -= 8 ) {
+        SetTextColor( p_cmd, p_res, i_overlay, 0x00, 0x00, i_blue );
+        usleep( 20000 );
+    }
+    SetTextColor( p_cmd, p_res, i_overlay, 0x00, 0x00, 0x00 );
+    printf( " done\n" );
+
+    printf( "Getting size..." );
+    int i_basesize = GetTextSize( p_cmd, p_res, i_overlay );
+    printf( " done. Size is %d\n", i_basesize );
+
+    printf( "Sweeping size..." );
+    for( float f_theta = 0; f_theta <= M_PI ; f_theta += M_PI / 128.0 ) {
+        SetTextSize( p_cmd, p_res, i_overlay,
+                     i_basesize * ( 1 + 3 * sin( f_theta ) ) );
+        usleep( 20000 );
+    }
+    SetTextSize( p_cmd, p_res, i_overlay, i_basesize );
+    printf( " done\n" );
+
+    sleep( 5 );
+}
+
+/*****************************************************************************
+ * main
+ *****************************************************************************/
+
+int main( int i_argc, char *ppsz_argv[] ) {
+    if( i_argc != 3 ) {
+        printf( "Incorrect number of parameters.\n"
+                "Usage is: %s command-fifo response-fifo\n", ppsz_argv[0] );
+        exit( -2 );
+    }
+
+    printf( "Creating shared memory for RGBA..." );
+    int i_shmRGBA = shmget( IPC_PRIVATE, WIDTH * HEIGHT * 4, S_IRWXU );
+    if( i_shmRGBA == -1 ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    printf( " done, ID is %d. Text...", i_shmRGBA );
+    int i_shmText = shmget( IPC_PRIVATE, TEXTSIZE, S_IRWXU );
+    if( i_shmText == -1 ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    printf( " done, ID is %d\n", i_shmText );
+
+    printf( "Attaching shared memory for RGBA..." );
+    p_imageRGBA = shmat( i_shmRGBA, NULL, 0 );
+    if( p_imageRGBA == (void*)-1 ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    printf( " done. Text..." );
+    p_text = shmat( i_shmText, NULL, 0 );
+    if( p_text == (void*)-1 ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    printf( " done\n" );
+
+    printf( "Queueing shared memory for destruction, RGBA..." );
+    if( shmctl( i_shmRGBA, IPC_RMID, 0 ) == -1 ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    printf( " done. Text..." );
+    if( shmctl( i_shmText, IPC_RMID, 0 ) == -1 ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    printf( " done\n" );
+
+    printf( "Generating data..." );
+    DataCreate();
+    printf( " done\n" );
+
+    printf( "Making FIFOs..." );
+    if( mkfifo( ppsz_argv[1], S_IRWXU ) ) {
+        if( errno != EEXIST ) {
+            printf( " failed\n" );
+            exit( -1 );
+        }
+        printf( " input already exists..." );
+    }
+    if( mkfifo( ppsz_argv[2], S_IRWXU ) ) {
+        if( errno != EEXIST ) {
+            printf( " failed\n" );
+            exit( -1 );
+        }
+        printf( " output already exists..." );
+    }
+    printf( " done\n" );
+
+    printf( "Please make sure vlc is running.\n"
+            "You should append parameters similar to the following:\n"
+            "--sub-filter overlay{input=%s,output=%s}\n",
+            ppsz_argv[1], ppsz_argv[2] );
+
+    printf( "Opening FIFOs..." );
+    FILE *p_cmd = fopen( ppsz_argv[1], "w" );
+    if( p_cmd == NULL ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    FILE *p_res = fopen( ppsz_argv[2], "r" );
+    if( p_res == NULL ) {
+        printf( " failed\n" );
+        exit( -1 );
+    }
+    printf( " done\n" );
+
+    int i_overlay_image = GenImage( p_cmd, p_res );
+    int i_overlay_text = GenImage( p_cmd, p_res );
+    DataSharedMem( p_cmd, p_res, i_overlay_image, WIDTH, HEIGHT, "RGBA",
+                   i_shmRGBA );
+    DataSharedMem( p_cmd, p_res, i_overlay_text, TEXTSIZE, 1, "TEXT",
+                   i_shmText );
+
+    BasicTest( p_cmd, p_res, i_overlay_image );
+    BasicTest( p_cmd, p_res, i_overlay_text );
+    TextTest( p_cmd, p_res, i_overlay_text );
+
+    DeleteImage( p_cmd, p_res, i_overlay_image );
+    DeleteImage( p_cmd, p_res, i_overlay_text );
+}