]> git.sesse.net Git - vlc/commitdiff
New filter chain handling API. This should make it possible to factorize
authorAntoine Cellerier <dionoea@videolan.org>
Tue, 3 Jun 2008 20:44:06 +0000 (22:44 +0200)
committerAntoine Cellerier <dionoea@videolan.org>
Wed, 4 Jun 2008 11:14:12 +0000 (13:14 +0200)
a lot of code everywhere a filter_t chain is used ("video filter2":
vout core, transcode, chain.c video filter; "sub filter": spu_Init;
"audio filter2": transcode; the other types of filter_t objects are
never chained)

Btw, why aren't "audio filter2" filters used anywhere in the aout core?

include/vlc_filter.h
src/Makefile.am
src/libvlccore.sym
src/misc/filter_chain.c [new file with mode: 0644]

index a1afd34104f50b5df974554b69466e36512d0242..dff75dc3307d95099f9737b65673efde75059db4 100644 (file)
@@ -1,10 +1,11 @@
 /*****************************************************************************
- * vlc_filter.h: filter related structures
+ * vlc_filter.h: filter related structures and functions
  *****************************************************************************
- * Copyright (C) 1999-2003 the VideoLAN team
+ * Copyright (C) 1999-2008 the VideoLAN team
  * $Id$
  *
  * Authors: Gildas Bazin <gbazin@videolan.org>
+ *          Antoine Cellerier <dionoea at videolan dot 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
@@ -55,6 +56,7 @@ struct filter_t
 
     /* Output format of filter */
     es_format_t         fmt_out;
+    bool                b_allow_fmt_out_change;
 
     /* Filter configuration */
     config_chain_t *    p_cfg;
@@ -81,8 +83,8 @@ struct filter_t
     /* Video output callbacks */
     picture_t     * ( * pf_vout_buffer_new) ( filter_t * );
     void            ( * pf_vout_buffer_del) ( filter_t *, picture_t * );
-    void            ( * pf_picture_link)    ( picture_t * );
-    void            ( * pf_picture_unlink)  ( picture_t * );
+    /* void            ( * pf_picture_link)    ( picture_t * );
+    void            ( * pf_picture_unlink)  ( picture_t * ); */
 
     /* SPU output callbacks */
     subpicture_t *  ( * pf_sub_buffer_new) ( filter_t * );
@@ -125,4 +127,30 @@ struct filter_t
         return p_outpic;                                                \
     }
 
+/**
+ * Filter chain management API
+ */
+
+typedef struct filter_chain_t filter_chain_t;
+
+VLC_EXPORT( filter_chain_t *, __filter_chain_New, ( vlc_object_t *, const char *, bool, int (*)( filter_t *, void * ), void (*)( filter_t * ), void *  ) );
+#define filter_chain_New( a, b, c, d, e, f ) __filter_chain_New( VLC_OBJECT( a ), b, c, d, e, f )
+VLC_EXPORT( void, filter_chain_Delete, ( filter_chain_t * ) );
+VLC_EXPORT( void, filter_chain_Reset, ( filter_chain_t *, const es_format_t *, const es_format_t * ) );
+
+VLC_EXPORT( filter_t *, filter_chain_AppendFilter, ( filter_chain_t *, const char *, config_chain_t *, const es_format_t *, const es_format_t * ) );
+VLC_EXPORT( int, filter_chain_AppendFromString, ( filter_chain_t *, const char * ) );
+VLC_EXPORT( int, filter_chain_DeleteFilter, ( filter_chain_t *, filter_t * ) );
+
+VLC_EXPORT( filter_t *, filter_chain_GetFilter, ( filter_chain_t *, int, const char * ) );
+VLC_EXPORT( int, filter_chain_GetLength, ( filter_chain_t * ) );
+VLC_EXPORT( const es_format_t *, filter_chain_GetFmtOut, ( filter_chain_t * ) );
+
+/**
+ * Apply the filter chain
+ */
+VLC_EXPORT( picture_t *, filter_chain_VideoFilter, ( filter_chain_t *, picture_t * ) );
+VLC_EXPORT( block_t *, filter_chain_AudioFilter, ( filter_chain_t *, block_t * ) );
+VLC_EXPORT( void, filter_chain_SubFilter, ( filter_chain_t *, mtime_t ) );
+
 #endif /* _VLC_FILTER_H */
index 089541234622cba2016a08248bef0415e13061c1..d91a861742ab1de9f9cf56b8f25fd12cc9c5e738 100644 (file)
@@ -362,6 +362,7 @@ SOURCES_libvlc_common = \
        misc/xml.c \
        misc/devices.c \
        extras/libc.c \
+       misc/filter_chain.c \
        $(NULL)
 
 SOURCES_libvlc_sout = \
index eb996393f9794735fb0e5345c8944ef4220f366b..f0b8828979e85fc4a377e06f75e088798e9d3bdc 100644 (file)
@@ -93,6 +93,18 @@ encode_URI_component
 EndMD5
 EnsureUTF8
 filename_sanitize
+filter_chain_AppendFilter
+filter_chain_AppendFromString
+filter_chain_AudioFilter
+filter_chain_Delete
+filter_chain_DeleteFilter
+filter_chain_GetFilter
+filter_chain_GetFmtOut
+filter_chain_GetLength
+__filter_chain_New
+filter_chain_Reset
+filter_chain_SubFilter
+filter_chain_VideoFilter
 FromLocale
 FromLocaleDup
 GetFallbackEncoding
diff --git a/src/misc/filter_chain.c b/src/misc/filter_chain.c
new file mode 100644 (file)
index 0000000..ec99cf1
--- /dev/null
@@ -0,0 +1,320 @@
+/*****************************************************************************
+ * filter_chain.c : Handle chains of filter_t objects.
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Author: Antoine Cellerier <dionoea at videolan dot 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 <vlc_filter.h>
+#include <vlc_arrays.h>
+
+struct filter_chain_t
+{
+    vlc_object_t *p_this; /* Parent object */
+
+    vlc_array_t filters; /* List of filters */
+
+    char *psz_capability; /* Capability of all the filters in the chain */
+    es_format_t fmt_in; /* Input format (read only) */
+    es_format_t fmt_out; /* Output format (writable depending on ... */
+    bool b_allow_fmt_out_change; /* allow changing fmt_out if true */
+
+    int (* pf_buffer_allocation_init)( filter_t *, void *p_data ); /* Callback called once filter allocation has succeeded to initialize the filter's buffer allocation callbacks. This function is responsible for setting p_owner if needed. */
+    void (* pf_buffer_allocation_clear)( filter_t * ); /* Callback called on filter removal from chain to clean up buffer allocation callbacks data (ie p_owner) */
+    void *p_buffer_allocation_data; /* Data for pf_buffer_allocation_init */
+};
+
+/**
+ * Filter chain initialisation
+ */
+filter_chain_t *__filter_chain_New( vlc_object_t *p_this,
+                                    const char *psz_capability,
+                                    bool b_allow_fmt_out_change,
+                      int (*pf_buffer_allocation_init)( filter_t *, void * ),
+                      void (*pf_buffer_allocation_clear)( filter_t * ),
+                      void *p_buffer_allocation_data )
+{
+    filter_chain_t *p_chain = (filter_chain_t *)
+        malloc( sizeof( filter_chain_t ) );
+    if( !p_chain ) return NULL;
+    p_chain->p_this = p_this;
+    vlc_array_init( &p_chain->filters );
+    p_chain->psz_capability = strdup( psz_capability );
+    if( !p_chain->psz_capability )
+    {
+        free( p_chain );
+        return NULL;
+    }
+    es_format_Init( &p_chain->fmt_in, UNKNOWN_ES, 0 );
+    es_format_Init( &p_chain->fmt_out, UNKNOWN_ES, 0 );
+    p_chain->b_allow_fmt_out_change = b_allow_fmt_out_change;
+
+    p_chain->pf_buffer_allocation_init = pf_buffer_allocation_init;
+    p_chain->pf_buffer_allocation_clear = pf_buffer_allocation_clear;
+    p_chain->p_buffer_allocation_data = p_buffer_allocation_data;
+
+    return p_chain;
+}
+
+/**
+ * Filter chain destruction
+ */
+void filter_chain_Delete( filter_chain_t *p_chain )
+{
+    while( p_chain->filters.i_count )
+        filter_chain_DeleteFilter( p_chain,
+                                   (filter_t*)p_chain->filters.pp_elems[0] );
+    vlc_array_clear( &p_chain->filters );
+    free( p_chain->psz_capability );
+    es_format_Clean( &p_chain->fmt_in );
+    es_format_Clean( &p_chain->fmt_out );
+    free( p_chain );
+}
+/**
+ * Filter chain reinitialisation
+ */
+void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
+                         const es_format_t *p_fmt_out )
+{
+    while( p_chain->filters.i_count )
+        filter_chain_DeleteFilter( p_chain,
+                                   (filter_t*)p_chain->filters.pp_elems[0] );
+    es_format_Clean( &p_chain->fmt_in );
+    es_format_Clean( &p_chain->fmt_out );
+    es_format_Copy( &p_chain->fmt_in, p_fmt_in );
+    es_format_Copy( &p_chain->fmt_out, p_fmt_out );
+}
+
+
+/**
+ * Modifying the filter chain
+ */
+filter_t *filter_chain_AppendFilter( filter_chain_t *p_chain,
+                                     const char *psz_name,
+                                     config_chain_t *p_cfg,
+                                     const es_format_t *p_fmt_in,
+                                     const es_format_t *p_fmt_out )
+{
+    filter_t *p_filter =
+        vlc_object_create( p_chain->p_this, VLC_OBJECT_FILTER );
+    if( !p_filter ) return NULL;
+    vlc_object_attach( p_filter, p_chain->p_this );
+
+    if( !p_fmt_in )
+    {
+        if( p_chain->filters.i_count )
+            p_fmt_in = &((filter_t*)p_chain->filters.pp_elems[p_chain->filters.i_count-1])->fmt_out;
+        else
+            p_fmt_in = &p_chain->fmt_in;
+    }
+
+    if( !p_fmt_out )
+    {
+        p_fmt_out = &p_chain->fmt_out;
+    }
+
+    es_format_Copy( &p_filter->fmt_in, p_fmt_in );
+    es_format_Copy( &p_filter->fmt_out, p_fmt_out );
+    p_filter->p_cfg = p_cfg;
+    p_filter->b_allow_fmt_out_change = p_chain->b_allow_fmt_out_change;
+
+    p_filter->p_module = module_Need( p_filter, p_chain->psz_capability,
+                                      psz_name, psz_name ? true : false );
+
+    if( !p_filter->p_module )
+        goto error;
+
+    if( p_filter->b_allow_fmt_out_change )
+    {
+        es_format_Clean( &p_chain->fmt_out );
+        es_format_Copy( &p_chain->fmt_out, &p_filter->fmt_out );
+    }
+
+    if( p_chain->pf_buffer_allocation_init( p_filter,
+            p_chain->p_buffer_allocation_data ) != VLC_SUCCESS )
+        goto error;
+
+    vlc_array_append( &p_chain->filters, p_filter );
+
+    msg_Dbg( p_chain->p_this, "Filter '%s' (%p) appended to chain",
+             psz_name, p_filter );
+
+    return p_filter;
+
+    error:
+        msg_Err( p_chain->p_this, "Failed to create video filter '%s'",
+                 psz_name );
+        if( p_filter->p_module ) module_Unneed( p_filter,
+                                                p_filter->p_module );
+        es_format_Clean( &p_filter->fmt_in );
+        es_format_Clean( &p_filter->fmt_out );
+        vlc_object_detach( p_filter );
+        vlc_object_release( p_filter );
+        return NULL;
+}
+
+int filter_chain_AppendFromString( filter_chain_t *p_chain,
+                                         const char *psz_string )
+{
+    config_chain_t *p_cfg = NULL;
+    char *psz_name = NULL;
+
+    if( !psz_string || !*psz_string ) return 0;
+
+    psz_string = config_ChainCreate( &psz_name, &p_cfg, psz_string );
+
+    filter_t *p_filter = filter_chain_AppendFilter( p_chain, psz_name, p_cfg,
+                                                    NULL, NULL );
+    if( !p_filter )
+    {
+        msg_Err( p_chain->p_this, "Failed while trying to append '%s' "
+                 "to filter chain", psz_name );
+        free( psz_name );
+        free( p_cfg );
+        return -1;
+    }
+    free( psz_name );
+
+    int ret = filter_chain_AppendFromString( p_chain, psz_string );
+    if( ret < 0 )
+    {
+        filter_chain_DeleteFilter( p_chain, p_filter );
+        return ret;
+    }
+    return 1 + ret;
+}
+
+int filter_chain_DeleteFilter( filter_chain_t *p_chain, filter_t *p_filter )
+{
+    int i;
+    /* Find the filter in the chain */
+    for( i = 0; i < p_chain->filters.i_count; i++ )
+        if( (filter_t*)p_chain->filters.pp_elems[i] == p_filter )
+            break;
+
+    /* Oops, filter wasn't found */
+    if( i == p_chain->filters.i_count )
+    {
+        msg_Err( p_chain->p_this, "Couldn't find filter '%s' (%p) when trying "
+                 "to remove it from chain", p_filter->psz_object_name,
+                 p_filter );
+        return VLC_EGENERIC;
+    }
+
+    /* Remove it from the chain */
+    vlc_array_remove( &p_chain->filters, i );
+    msg_Dbg( p_chain->p_this, "Filter '%s' (%p) removed from chain",
+             p_filter->psz_object_name, p_filter );
+
+    /* Destroy the filter object */
+    if( p_chain->pf_buffer_allocation_clear )
+        p_chain->pf_buffer_allocation_clear( p_filter );
+    vlc_object_detach( p_filter );
+    if( p_filter->p_module )
+        module_Unneed( p_filter, p_filter->p_module );
+    vlc_object_release( p_filter );
+
+    /* FIXME: check fmt_in/fmt_out consitency */
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * Reading from the filter chain
+ */
+filter_t *filter_chain_GetFilter( filter_chain_t *p_chain, int i_position,
+                                  const char *psz_name )
+{
+    int i;
+    filter_t **pp_filters = (filter_t **)p_chain->filters.pp_elems;
+
+    if( i_position < 0 )
+        return NULL;
+
+    if( !psz_name )
+    {
+        if( i_position >= p_chain->filters.i_count )
+            return NULL;
+        return pp_filters[i_position];
+    }
+
+    for( i = 0; i < p_chain->filters.i_count; i++ )
+    {
+        if( !strcmp( psz_name, pp_filters[i]->psz_object_name ) )
+            i_position--;
+        if( i_position < 0 )
+            return pp_filters[i];
+   }
+   return NULL;
+}
+
+int filter_chain_GetLength( filter_chain_t *p_chain )
+{
+    return p_chain->filters.i_count;
+}
+
+const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
+{
+    return &p_chain->fmt_out;
+}
+
+/**
+ * Apply the filter chain
+ */
+picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
+{
+    int i;
+    filter_t **pp_filter = (filter_t **)p_chain->filters.pp_elems;
+    for( i = 0; i < p_chain->filters.i_count; i++ )
+    {
+        filter_t *p_filter = pp_filter[i];
+        p_pic = p_filter->pf_video_filter( p_filter, p_pic );
+        if( !p_pic )
+            return NULL;
+    }
+    return p_pic;
+}
+
+block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block )
+{
+    int i;
+    filter_t **pp_filter = (filter_t **)p_chain->filters.pp_elems;
+    for( i = 0; i < p_chain->filters.i_count; i++ )
+    {
+        filter_t *p_filter = pp_filter[i];
+        p_block = p_filter->pf_audio_filter( p_filter, p_block );
+        if( !p_block )
+            return NULL;
+    }
+    return p_block;
+}
+
+void filter_chain_SubFilter( filter_chain_t *p_chain,
+                             mtime_t display_date )
+{
+    int i;
+    filter_t **pp_filter = (filter_t **)p_chain->filters.pp_elems;
+    for( i = 0; i < p_chain->filters.i_count; i++ )
+    {
+        filter_t *p_filter = pp_filter[i];
+        subpicture_t *p_subpic = p_filter->pf_sub_filter( p_filter, display_date );
+        if( p_subpic )
+            spu_DisplaySubpicture( (spu_t*)p_chain->p_this, p_subpic );
+    }
+}