+ filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
+ p_cfg, p_fmt_in,
+ p_fmt_out );
+ if( UpdateBufferFunctions( p_chain ) < 0 )
+ msg_Err( p_filter, "Woah! This doesn't look good." );
+ return p_filter;
+}
+
+int filter_chain_AppendFromString( filter_chain_t *p_chain,
+ const char *psz_string )
+{
+ const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_string );
+ if( i_ret < 0 )
+ return i_ret;
+
+ /* FIXME That one seems bad if a error is returned */
+ return UpdateBufferFunctions( p_chain );
+}
+
+int filter_chain_DeleteFilter( filter_chain_t *p_chain, filter_t *p_filter )
+{
+ const int i_ret = filter_chain_DeleteFilterInternal( p_chain, p_filter );
+ if( i_ret < 0 )
+ return i_ret;
+
+ /* FIXME That one seems bad if a error is returned */
+ return UpdateBufferFunctions( p_chain );
+}
+
+int filter_chain_GetLength( filter_chain_t *p_chain )
+{
+ return p_chain->length;
+}
+
+const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
+{
+
+ if( p_chain->b_allow_fmt_out_change )
+ return &p_chain->fmt_out;
+
+ if( p_chain->last != NULL )
+ return &p_chain->last->filter.fmt_out;
+
+ /* Unless filter_chain_Reset has been called we are doomed */
+ return &p_chain->fmt_out;
+}
+
+picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
+{
+ for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
+ {
+ filter_t *p_filter = &f->filter;
+
+ p_pic = p_filter->pf_video_filter( p_filter, p_pic );
+ if( !p_pic )
+ break;
+ }
+ return p_pic;
+}
+
+block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block )
+{
+ for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
+ {
+ filter_t *p_filter = &f->filter;
+
+ p_block = p_filter->pf_audio_filter( p_filter, p_block );
+ if( !p_block )
+ break;
+ }
+ return p_block;
+}
+
+void filter_chain_SubFilter( filter_chain_t *p_chain,
+ mtime_t display_date )
+{
+ for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
+ {
+ filter_t *p_filter = &f->filter;
+ subpicture_t *p_subpic = p_filter->pf_sub_filter( p_filter, display_date );
+ /* XXX I find that spu_t cast ugly */
+ if( p_subpic )
+ spu_DisplaySubpicture( (spu_t*)p_chain->p_this, p_subpic );
+ }
+}
+
+int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
+{
+ vlc_mouse_t current = *p_src;
+
+ for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
+ {
+ filter_t *p_filter = &f->filter;
+ vlc_mouse_t *p_mouse = f->mouse;
+
+ if( p_filter->pf_video_mouse && p_mouse )
+ {
+ vlc_mouse_t old = *p_mouse;
+ vlc_mouse_t filtered;
+
+ *p_mouse = current;
+ if( p_filter->pf_video_mouse( p_filter, &filtered, &old, ¤t ) )
+ return VLC_EGENERIC;
+ current = filtered;
+ }
+ }
+
+ *p_dst = current;
+ return VLC_SUCCESS;
+}
+
+int filter_chain_MouseEvent( filter_chain_t *p_chain,
+ const vlc_mouse_t *p_mouse,
+ const video_format_t *p_fmt )
+{
+ for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
+ {
+ filter_t *p_filter = &f->filter;
+
+ if( p_filter->pf_sub_mouse )
+ {
+ vlc_mouse_t old = *f->mouse;
+ *f->mouse = *p_mouse;
+ if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
+ return VLC_EGENERIC;
+ }
+ }
+
+ return VLC_SUCCESS;
+}
+
+/* Helpers */
+static filter_t *filter_chain_AppendFilterInternal( 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 )
+{
+ chained_filter_t *p_chained =
+ vlc_custom_create( p_chain->p_this, sizeof(*p_chained),
+ VLC_OBJECT_GENERIC, "filter" );
+ filter_t *p_filter = &p_chained->filter;
+ if( !p_filter )
+ return NULL;