1 /*****************************************************************************
2 * filter_chain.c : Handle chains of filter_t objects.
3 *****************************************************************************
4 * Copyright (C) 2008 VLC authors and VideoLAN
5 * Copyright (C) 2008-2014 RĂ©mi Denis-Courmont
7 * Author: Antoine Cellerier <dionoea at videolan dot org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
28 #include <vlc_filter.h>
29 #include <vlc_modules.h>
34 typedef struct chained_filter_t
36 /* Public part of the filter structure */
38 /* Private filter chain data (shhhh!) */
39 struct chained_filter_t *prev, *next;
44 /* Only use this with filter objects from _this_ C module */
45 static inline chained_filter_t *chained (filter_t *filter)
47 return (chained_filter_t *)filter;
53 filter_owner_t callbacks; /**< Inner callbacks */
54 filter_owner_t owner; /**< Owner (downstream) callbacks */
56 chained_filter_t *first, *last; /**< List of filters */
58 es_format_t fmt_in; /**< Chain input format (constant) */
59 es_format_t fmt_out; /**< Chain current output format */
60 unsigned length; /**< Number of filters */
61 bool b_allow_fmt_out_change; /**< Can the output format be changed? */
62 char psz_capability[1]; /**< Module capability for all chained filters */
68 static void FilterDeletePictures( picture_t * );
70 static filter_chain_t *filter_chain_NewInner( const filter_owner_t *callbacks,
71 const char *cap, bool fmt_out_change, const filter_owner_t *owner )
73 assert( callbacks != NULL && callbacks->sys != NULL );
74 assert( cap != NULL );
76 filter_chain_t *chain = malloc( sizeof(*chain) + strlen( cap ) );
77 if( unlikely(chain == NULL) )
80 chain->callbacks = *callbacks;
82 chain->owner = *owner;
85 es_format_Init( &chain->fmt_in, UNKNOWN_ES, 0 );
86 es_format_Init( &chain->fmt_out, UNKNOWN_ES, 0 );
88 chain->b_allow_fmt_out_change = fmt_out_change;
89 strcpy( chain->psz_capability, cap );
94 #undef filter_chain_New
96 * Filter chain initialisation
98 filter_chain_t *filter_chain_New( vlc_object_t *obj, const char *cap,
101 filter_owner_t callbacks = {
105 return filter_chain_NewInner( &callbacks, cap, fmt_out_change, NULL );
108 /** Chained filter picture allocator function */
109 static picture_t *filter_chain_VideoBufferNew( filter_t *filter )
111 if( chained(filter)->next != NULL )
113 picture_t *pic = picture_NewFromFormat( &filter->fmt_out.video );
115 msg_Err( filter, "Failed to allocate picture" );
120 filter_chain_t *chain = filter->owner.sys;
123 filter->owner.sys = chain->owner.sys;
124 picture_t *pic = chain->owner.video.buffer_new( filter );
125 filter->owner.sys = chain;
130 #undef filter_chain_NewVideo
131 filter_chain_t *filter_chain_NewVideo( vlc_object_t *obj, bool allow_change,
132 const filter_owner_t *restrict owner )
134 filter_owner_t callbacks = {
137 .buffer_new = filter_chain_VideoBufferNew,
141 return filter_chain_NewInner( &callbacks, "video filter2", allow_change,
146 * Filter chain destruction
148 void filter_chain_Delete( filter_chain_t *p_chain )
150 while( p_chain->first != NULL )
151 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
153 es_format_Clean( &p_chain->fmt_in );
154 es_format_Clean( &p_chain->fmt_out );
159 * Filter chain reinitialisation
161 void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
162 const es_format_t *p_fmt_out )
164 while( p_chain->first != NULL )
165 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
169 es_format_Clean( &p_chain->fmt_in );
170 es_format_Copy( &p_chain->fmt_in, p_fmt_in );
174 es_format_Clean( &p_chain->fmt_out );
175 es_format_Copy( &p_chain->fmt_out, p_fmt_out );
179 filter_t *filter_chain_AppendFilter( filter_chain_t *chain, const char *name,
181 const es_format_t *fmt_in,
182 const es_format_t *fmt_out )
184 vlc_object_t *parent = chain->callbacks.sys;
185 chained_filter_t *chained =
186 vlc_custom_create( parent, sizeof(*chained), "filter" );
187 if( unlikely(chained == NULL) )
190 filter_t *filter = &chained->filter;
194 if( chain->last != NULL )
195 fmt_in = &chain->last->filter.fmt_out;
197 fmt_in = &chain->fmt_in;
200 if( fmt_out == NULL )
201 fmt_out = &chain->fmt_out;
203 es_format_Copy( &filter->fmt_in, fmt_in );
204 es_format_Copy( &filter->fmt_out, fmt_out );
205 filter->b_allow_fmt_out_change = chain->b_allow_fmt_out_change;
208 filter->owner = chain->callbacks;
209 filter->owner.sys = chain;
211 filter->p_module = module_need( filter, chain->psz_capability, name,
213 if( filter->p_module == NULL )
216 if( filter->b_allow_fmt_out_change )
218 es_format_Clean( &chain->fmt_out );
219 es_format_Copy( &chain->fmt_out, &filter->fmt_out );
222 if( chain->last == NULL )
224 assert( chain->first == NULL );
225 chain->first = chained;
228 chain->last->next = chained;
229 chained->prev = chain->last;
230 chain->last = chained;
231 chained->next = NULL;
234 vlc_mouse_t *mouse = malloc( sizeof(*mouse) );
235 if( likely(mouse != NULL) )
236 vlc_mouse_Init( mouse );
237 chained->mouse = mouse;
238 chained->pending = NULL;
240 msg_Dbg( parent, "Filter '%s' (%p) appended to chain",
241 (name != NULL) ? name : module_get_name(filter->p_module, false),
247 msg_Err( parent, "Failed to create %s '%s'", chain->psz_capability,
250 msg_Err( parent, "Failed to create %s", chain->psz_capability );
251 es_format_Clean( &filter->fmt_out );
252 es_format_Clean( &filter->fmt_in );
253 vlc_object_release( filter );
257 void filter_chain_DeleteFilter( filter_chain_t *chain, filter_t *filter )
259 vlc_object_t *obj = chain->callbacks.sys;
260 chained_filter_t *chained = (chained_filter_t *)filter;
262 /* Remove it from the chain */
263 if( chained->prev != NULL )
264 chained->prev->next = chained->next;
267 assert( chained == chain->first );
268 chain->first = chained->next;
271 if( chained->next != NULL )
272 chained->next->prev = chained->prev;
275 assert( chained == chain->last );
276 chain->last = chained->prev;
279 assert( chain->length > 0 );
282 module_unneed( filter, filter->p_module );
284 msg_Dbg( obj, "Filter %p removed from chain", filter );
285 FilterDeletePictures( chained->pending );
287 free( chained->mouse );
288 es_format_Clean( &filter->fmt_out );
289 es_format_Clean( &filter->fmt_in );
290 vlc_object_release( filter );
291 /* FIXME: check fmt_in/fmt_out consitency */
295 int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
297 vlc_object_t *obj = chain->callbacks.sys;
301 while( str != NULL && str[0] != '\0' )
306 char *next = config_ChainCreate( &name, &cfg, str );
312 filter_t *filter = filter_chain_AppendFilter( chain, name, cfg,
316 msg_Err( obj, "Failed to append '%s' to chain", name );
330 while( ret > 0 ) /* Unwind */
332 filter_chain_DeleteFilter( chain, &chain->last->filter );
339 int filter_chain_ForEach( filter_chain_t *chain,
340 int (*cb)( filter_t *, void * ), void *opaque )
342 for( chained_filter_t *f = chain->first; f != NULL; f = f->next )
344 int ret = cb( &f->filter, opaque );
351 int filter_chain_GetLength( filter_chain_t *p_chain )
353 return p_chain->length;
356 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
359 if( p_chain->b_allow_fmt_out_change )
360 return &p_chain->fmt_out;
362 if( p_chain->last != NULL )
363 return &p_chain->last->filter.fmt_out;
365 /* Unless filter_chain_Reset has been called we are doomed */
366 return &p_chain->fmt_out;
369 static picture_t *FilterChainVideoFilter( chained_filter_t *f, picture_t *p_pic )
371 for( ; f != NULL; f = f->next )
373 filter_t *p_filter = &f->filter;
374 p_pic = p_filter->pf_video_filter( p_filter, p_pic );
379 msg_Warn( p_filter, "dropping pictures" );
380 FilterDeletePictures( f->pending );
382 f->pending = p_pic->p_next;
383 p_pic->p_next = NULL;
388 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
392 p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
396 for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
401 b->pending = p_pic->p_next;
402 p_pic->p_next = NULL;
404 p_pic = FilterChainVideoFilter( b->next, p_pic );
411 void filter_chain_VideoFlush( filter_chain_t *p_chain )
413 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
415 filter_t *p_filter = &f->filter;
417 FilterDeletePictures( f->pending );
420 filter_FlushPictures( p_filter );
425 block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block )
427 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
429 filter_t *p_filter = &f->filter;
431 p_block = p_filter->pf_audio_filter( p_filter, p_block );
438 void filter_chain_SubSource( filter_chain_t *p_chain, spu_t *spu,
439 mtime_t display_date )
441 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
443 filter_t *p_filter = &f->filter;
444 subpicture_t *p_subpic = p_filter->pf_sub_source( p_filter, display_date );
446 spu_PutSubpicture( spu, p_subpic );
450 subpicture_t *filter_chain_SubFilter( filter_chain_t *p_chain, subpicture_t *p_subpic )
452 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
454 filter_t *p_filter = &f->filter;
456 p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
464 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
466 vlc_mouse_t current = *p_src;
468 for( chained_filter_t *f = p_chain->last; f != NULL; f = f->prev )
470 filter_t *p_filter = &f->filter;
471 vlc_mouse_t *p_mouse = f->mouse;
473 if( p_filter->pf_video_mouse && p_mouse )
475 vlc_mouse_t old = *p_mouse;
476 vlc_mouse_t filtered;
479 if( p_filter->pf_video_mouse( p_filter, &filtered, &old, ¤t ) )
489 int filter_chain_MouseEvent( filter_chain_t *p_chain,
490 const vlc_mouse_t *p_mouse,
491 const video_format_t *p_fmt )
493 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
495 filter_t *p_filter = &f->filter;
497 if( p_filter->pf_sub_mouse )
499 vlc_mouse_t old = *f->mouse;
500 *f->mouse = *p_mouse;
501 if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
510 static void FilterDeletePictures( picture_t *picture )
514 picture_t *next = picture->p_next;
515 picture_Release( picture );