]> git.sesse.net Git - vlc/commitdiff
Refactor dynamic overlays from Google Summer of Code project.
authorJean-Paul Saman <jean-paul.saman@m2x.nl>
Sat, 1 Mar 2008 13:27:20 +0000 (14:27 +0100)
committerJean-Paul Saman <jean-paul.saman@m2x.nl>
Wed, 9 Apr 2008 08:54:29 +0000 (10:54 +0200)
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.h [new file with mode: 0644]
modules/video_filter/dynamicoverlay/dynamicoverlay_queue.c [new file with mode: 0644]

diff --git a/modules/video_filter/dynamicoverlay/Modules.am b/modules/video_filter/dynamicoverlay/Modules.am
new file mode 100644 (file)
index 0000000..8cd12b3
--- /dev/null
@@ -0,0 +1,2 @@
+SOURCES_dynamicoverlay = dynamicoverlay.c dynamicoverlay_buffer.c dynamicoverlay_queue.c
+noinst_HEADERS = dynamicoverlay.h dynamicoverlay_commands.h
diff --git a/modules/video_filter/dynamicoverlay/dynamicoverlay.c b/modules/video_filter/dynamicoverlay/dynamicoverlay.c
new file mode 100644 (file)
index 0000000..399401c
--- /dev/null
@@ -0,0 +1,706 @@
+/*****************************************************************************
+ * 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 <fcntl.h>
+#include <vlc/vlc.h>
+#include <vlc_sout.h>
+#include <vlc_vout.h>
+
+#include <vlc_filter.h>
+#include <vlc_osd.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
+};
+
+/*****************************************************************************
+ * overlay_t: Overlay descriptor
+ *****************************************************************************/
+
+struct overlay_t
+{
+    int i_x, i_y;
+    int i_alpha;
+    vlc_bool_t b_active;
+
+    video_format_t format;
+    union {
+        picture_t *p_pic;
+        char *p_text;
+    } data;
+};
+typedef struct overlay_t overlay_t;
+
+static 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 );
+    p_ovl->data.p_text = NULL;
+
+    return p_ovl;
+}
+
+static int OverlayDestroy( overlay_t *p_ovl )
+{
+    if( p_ovl->data.p_text != NULL )
+        free( p_ovl->data.p_text );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * list_t: Command queue
+ *****************************************************************************/
+
+struct list_t
+{
+    overlay_t **pp_head, **pp_tail;
+};
+typedef struct list_t list_t;
+
+static 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;
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static 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];
+}
+
+static 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;
+}
+
+/*****************************************************************************
+ * 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;
+
+    vlc_bool_t b_updated, b_atomic;
+    queue_t atomic, pending, processed;
+    list_t overlays;
+};
+
+/*****************************************************************************
+ * Command functions
+ *****************************************************************************/
+
+#define SKIP \
+    { \
+        const char *psz_temp = psz_command; \
+        while( isspace( *psz_temp ) ) { \
+            ++psz_temp; \
+        } \
+        if( psz_temp == psz_command ) { \
+            return VLC_EGENERIC; \
+        } \
+        psz_command = psz_temp; \
+    }
+#define INT( name ) \
+    SKIP \
+    { \
+        char *psz_temp; \
+        p_myparams->name = strtol( psz_command, &psz_temp, 10 ); \
+        if( psz_temp == psz_command ) \
+        { \
+            return VLC_EGENERIC; \
+        } \
+        psz_command = psz_temp; \
+    }
+#define CHARS( name, count ) \
+    SKIP \
+    { \
+        if( psz_end - psz_command < count ) \
+        { \
+            return VLC_EGENERIC; \
+        } \
+        memcpy( p_myparams->name, psz_command, count ); \
+        psz_command += count; \
+    }
+#define COMMAND( name, param, ret, atomic, code ) \
+static int Command##name##Parse( const char *psz_command, \
+                                 const char *psz_end, \
+                                 commandparams_t *p_params ) \
+{ \
+    struct commandparams##name##_t *p_myparams = &p_params->name; \
+    param \
+    return VLC_SUCCESS; \
+}
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+#undef SKIP
+#undef INT
+#undef CHARS
+
+#define COMMAND( name, param, ret, atomic, code ) \
+static int Command##name##Exec( filter_t *p_filter, \
+                                const commandparams_t *p_gparams, \
+                                commandresults_t *p_gresults, \
+                                filter_sys_t *p_sys ) \
+{ \
+    const struct commandparams##name##_t *p_params = &p_gparams->name; \
+    struct commandresults##name##_t *p_results = &p_gresults->name; \
+    code \
+}
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+
+#define INT( name ) \
+    { \
+        int ret = BufferPrintf( p_output, " %d", p_myresults->name ); \
+        if( ret != VLC_SUCCESS ) { \
+            return ret; \
+        } \
+    }
+#define CHARS( name, count ) \
+    { \
+        int ret = BufferAdd( p_output, p_myresults->name, count ); \
+        if( ret != VLC_SUCCESS ) { \
+            return ret; \
+        } \
+    }
+#define COMMAND( name, param, ret, atomic, code ) \
+static int Command##name##Unparse( const commandresults_t *p_results, \
+                           buffer_t *p_output ) \
+{ \
+    const struct commandresults##name##_t *p_myresults = &p_results->name; \
+    ret \
+    return VLC_SUCCESS; \
+}
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+#undef INT
+#undef CHARS
+
+static commanddesc_t p_commands[] =
+{
+#define COMMAND( name, param, ret, atomic, code ) \
+{ #name, atomic, Command##name##Parse, Command##name##Exec, Command##name##Unparse },
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+};
+
+#define NUMCOMMANDS (sizeof(p_commands)/sizeof(commanddesc_t))
+
+/*****************************************************************************
+ * 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 );
+
+    msg_Dbg( p_filter, "%d commands are available:", NUMCOMMANDS );
+    for( size_t i_index = 0; i_index < NUMCOMMANDS; ++i_index )
+        msg_Dbg( p_filter, "    %s", p_commands[i_index].psz_command );
+
+    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 );
+
+    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 ) ) )
+    {
+        *p_end = '\0';
+        p_cmd = p_sys->input.p_begin;
+
+        commanddesc_t *p_lower = p_commands;
+        commanddesc_t *p_upper = p_commands + NUMCOMMANDS;
+        size_t i_index = 0;
+        while( 1 )
+        {
+            if( ( p_cmd[i_index] == '\0' ) ||
+                isspace( p_cmd[i_index] ) )
+            {
+                break;
+            }
+            commanddesc_t *p_cur = p_lower;
+            while( ( p_cur < p_upper ) &&
+                   ( p_cur->psz_command[i_index] != p_cmd[i_index] ) )
+            {
+                ++p_cur;
+            }
+            p_lower = p_cur;
+            while( ( p_cur < p_upper ) &&
+                   ( p_cur->psz_command[i_index] == p_cmd[i_index] ) )
+            {
+                ++p_cur;
+            }
+            p_upper = p_cur;
+            ++i_index;
+        }
+        if( p_lower >= p_upper )
+        {
+            /* No matching command */
+            p_cmd[i_index] = '\0';
+            msg_Err( p_filter, "Got invalid command: %s", p_cmd );
+            BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
+        }
+        else if ( p_lower + 1 < p_upper )
+        {
+            /* Command is not a unique prefix of a command */
+            p_cmd[i_index] = '\0';
+            msg_Err( p_filter, "Got ambiguous command: %s", p_cmd );
+            msg_Err( p_filter, "Possible completions are:" );
+            for( ; p_lower < p_upper; ++p_lower )
+            {
+                msg_Err( p_filter, "    %s", p_lower->psz_command );
+            }
+            BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
+        }
+        else
+        {
+            /* Command is valid */
+            command_t *p_command = malloc( sizeof( command_t ) );
+            if( !p_command )
+                return NULL;
+
+            p_command->p_command = p_lower;
+            p_command->p_command->pf_parser( p_cmd + i_index, p_end,
+                                             &p_command->params );
+
+            if( ( p_command->p_command->b_atomic == VLC_TRUE ) &&
+                ( p_sys->b_atomic == VLC_TRUE ) )
+                QueueEnqueue( &p_sys->atomic, p_command );
+            else
+                QueueEnqueue( &p_sys->pending, p_command );
+
+            p_cmd[i_index] = '\0';
+            msg_Dbg( p_filter, "Got valid command: %s", p_cmd );
+        }
+
+        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, p_sys );
+        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_unparser( &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",
+                 &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( p_filter,
+                                                  &p_overlay->format );
+            if( !*pp_region )
+            {
+                msg_Err( p_filter, "cannot allocate subpicture region" );
+                continue;
+            }
+            (*pp_region)->psz_text = strdup( p_overlay->data.p_text );
+        }
+        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( 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(psz_var); VLC_UNUSED(oldval); VLC_UNUSED(newval);
+    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..c2f1f5f
--- /dev/null
@@ -0,0 +1,128 @@
+/*****************************************************************************
+ * 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
+
+/*****************************************************************************
+ * 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 );
+
+/*****************************************************************************
+ * Command structures
+ *****************************************************************************/
+
+ #define INT( name ) int name;
+#define CHARS( name, count ) char name[count];
+#define COMMAND( name, param, ret, atomic, code ) \
+struct commandparams##name##_t \
+{ \
+    param \
+};
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+#undef INT
+#undef CHARS
+
+union commandparams_t
+{
+#define COMMAND( name, param, ret, atomic, code ) struct commandparams##name##_t name;
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+};
+typedef union commandparams_t commandparams_t;
+
+#define INT( name ) int name;
+#define CHARS( name, count ) char name[count];
+#define COMMAND( name, param, ret, atomic, code ) \
+struct commandresults##name##_t \
+{ \
+    ret \
+};
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+#undef INT
+#undef CHARS
+
+union commandresults_t {
+#define COMMAND( name, param, ret, atomic, code ) struct commandresults##name##_t name;
+#include "dynamicoverlay_commands.h"
+#undef COMMAND
+};
+typedef union commandresults_t commandresults_t;
+
+typedef struct commanddesc_t
+{
+    const char *psz_command;
+    vlc_bool_t b_atomic;
+    int ( *pf_parser ) ( const char *psz_command, const char *psz_end,
+                         commandparams_t *p_params );
+    int ( *pf_execute ) ( filter_t *p_filter, const commandparams_t *p_params,
+                          commandresults_t *p_results,
+                          struct filter_sys_t *p_sys );
+    int ( *pf_unparser ) ( const commandresults_t *p_results,
+                           buffer_t *p_output );
+} commanddesc_t;
+
+typedef struct command_t
+{
+    struct commanddesc_t *p_command;
+    int i_status;
+    commandparams_t params;
+    commandresults_t results;
+
+    struct command_t *p_next;
+} command_t;
+
+/*****************************************************************************
+ * 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 );
+
+#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..891072f
--- /dev/null
@@ -0,0 +1,142 @@
+/*****************************************************************************
+ * dynamicoverlay_commands.def : 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 "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;
+}
+
+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.h b/modules/video_filter/dynamicoverlay/dynamicoverlay_commands.h
new file mode 100644 (file)
index 0000000..5dced8b
--- /dev/null
@@ -0,0 +1,242 @@
+/*****************************************************************************
+ * dynamicoverlay_commands.def : 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.
+ *****************************************************************************/
+
+#include <sys/shm.h>
+
+/* Commands must be sorted alphabetically.
+   I haven't found out how to implement quick sort in cpp */
+COMMAND( DataSharedMem, INT( i_id ) INT( i_width ) INT( i_height )
+         CHARS( p_fourcc, 4 ) INT( i_shmid ), , VLC_TRUE, {
+    overlay_t *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;
+    }
+
+    struct shmid_ds shminfo;
+    if( shmctl( p_params->i_shmid, IPC_STAT, &shminfo ) == -1 ) {
+        msg_Err( p_filter, "Unable to access shared memory" );
+        return VLC_EGENERIC;
+    }
+    size_t i_size = shminfo.shm_segsz;
+
+    if( strncmp( p_params->p_fourcc, "TEXT", 4 ) == 0 ) {
+        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( 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 );
+
+        char *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 {
+        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, VLC_FOURCC( p_params->p_fourcc[0],
+                                                     p_params->p_fourcc[1],
+                                                     p_params->p_fourcc[2],
+                                                     p_params->p_fourcc[3] ),
+                         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;
+        }
+
+        size_t i_neededsize = 0;
+        for( size_t i_plane = 0; i_plane < 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;
+        }
+
+        char *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;
+        }
+
+        char *p_in = p_data;
+        for( size_t i_plane = 0; i_plane < p_ovl->data.p_pic->i_planes;
+             ++i_plane ) {
+            char *p_out = p_ovl->data.p_pic->p[i_plane].p_pixels;
+            for( size_t i_line = 0;
+                 i_line < 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;
+} )
+COMMAND( DeleteImage, INT( i_id ), , VLC_TRUE, {
+    p_sys->b_updated = VLC_TRUE;
+
+    return ListRemove( &p_sys->overlays, p_params->i_id );
+} )
+COMMAND( EndAtomic, , , VLC_FALSE, {
+    QueueTransfer( &p_sys->pending, &p_sys->atomic );
+    p_sys->b_atomic = VLC_FALSE;
+    return VLC_SUCCESS;
+} )
+COMMAND( GenImage, , INT( i_newid ), VLC_FALSE, {
+    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_newid = i_idx;
+    return VLC_SUCCESS;
+} )
+COMMAND( GetAlpha, INT( i_id ), INT( i_alpha ), VLC_FALSE, {
+    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;
+} )
+COMMAND( GetPosition, INT( i_id ), INT( i_x ) INT( i_y ), VLC_FALSE, {
+    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;
+} )
+COMMAND( GetVisibility, INT( i_id ), INT( i_vis ), VLC_FALSE, {
+    overlay_t *p_ovl = ListGet( &p_sys->overlays, p_params->i_id );
+    if( p_ovl == NULL ) {
+        return VLC_EGENERIC;
+    }
+    p_results->i_vis = ( p_ovl->b_active == VLC_TRUE ) ? 1 : 0;
+
+    return VLC_SUCCESS;
+} )
+COMMAND( SetAlpha, INT( i_id ) INT( i_alpha ), , VLC_TRUE, {
+    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;
+} )
+COMMAND( SetPosition, INT( i_id ) INT( i_x ) INT( i_y ), , VLC_TRUE, {
+    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;
+} )
+COMMAND( SetVisibility, INT( i_id ) INT( i_vis ), , VLC_TRUE, {
+    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->i_vis == 0 ) ? VLC_FALSE : VLC_TRUE;
+
+    p_sys->b_updated = VLC_TRUE;
+
+    return VLC_SUCCESS;
+} )
+COMMAND( StartAtomic, , , VLC_FALSE, {
+    p_sys->b_atomic = VLC_TRUE;
+    return VLC_SUCCESS;
+} )
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..4fb546a
--- /dev/null
@@ -0,0 +1,114 @@
+/*****************************************************************************
+ * 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 "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;
+}