asx: remove useless test
[vlc.git] / src / misc / filter_chain.c
blob552ed9e5872f5e171545dfb4f2f18c8e7c51ecd1
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 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <vlc_filter.h>
29 #include <vlc_modules.h>
30 #include <vlc_mouse.h>
31 #include <vlc_spu.h>
32 #include <libvlc.h>
33 #include <assert.h>
35 typedef struct chained_filter_t
37 /* Public part of the filter structure */
38 filter_t filter;
39 /* Private filter chain data (shhhh!) */
40 struct chained_filter_t *prev, *next;
41 vlc_mouse_t *mouse;
42 picture_t *pending;
43 } chained_filter_t;
45 /* Only use this with filter objects from _this_ C module */
46 static inline chained_filter_t *chained(filter_t *filter)
48 return container_of(filter, chained_filter_t, filter);
51 /* */
52 struct filter_chain_t
54 filter_owner_t callbacks; /**< Inner callbacks */
55 filter_owner_t owner; /**< Owner (downstream) callbacks */
57 chained_filter_t *first, *last; /**< List of filters */
59 es_format_t fmt_in; /**< Chain input format (constant) */
60 es_format_t fmt_out; /**< Chain current output format */
61 bool b_allow_fmt_out_change; /**< Can the output format be changed? */
62 const char *filter_cap; /**< Filter modules capability */
63 const char *conv_cap; /**< Converter modules capability */
66 /**
67 * Local prototypes
69 static void FilterDeletePictures( picture_t * );
71 static filter_chain_t *filter_chain_NewInner( const filter_owner_t *callbacks,
72 const char *cap, const char *conv_cap, bool fmt_out_change,
73 const filter_owner_t *owner, enum es_format_category_e cat )
75 assert( callbacks != NULL && callbacks->sys != NULL );
76 assert( cap != NULL );
78 filter_chain_t *chain = malloc( sizeof (*chain) );
79 if( unlikely(chain == NULL) )
80 return NULL;
82 chain->callbacks = *callbacks;
83 if( owner != NULL )
84 chain->owner = *owner;
85 chain->first = NULL;
86 chain->last = NULL;
87 es_format_Init( &chain->fmt_in, cat, 0 );
88 es_format_Init( &chain->fmt_out, cat, 0 );
89 chain->b_allow_fmt_out_change = fmt_out_change;
90 chain->filter_cap = cap;
91 chain->conv_cap = conv_cap;
92 return chain;
95 #undef filter_chain_New
96 /**
97 * Filter chain initialisation
99 filter_chain_t *filter_chain_New( vlc_object_t *obj, const char *cap,
100 enum es_format_category_e cat )
102 filter_owner_t callbacks = {
103 .sys = obj,
106 return filter_chain_NewInner( &callbacks, cap, NULL, false, NULL, cat );
109 /** Chained filter picture allocator function */
110 static picture_t *filter_chain_VideoBufferNew( filter_t *filter )
112 if( chained(filter)->next != NULL )
114 picture_t *pic = picture_NewFromFormat( &filter->fmt_out.video );
115 if( pic == NULL )
116 msg_Err( filter, "Failed to allocate picture" );
117 return pic;
119 else
121 filter_chain_t *chain = filter->owner.sys;
123 /* XXX ugly */
124 filter->owner.sys = chain->owner.sys;
125 picture_t *pic = chain->owner.video.buffer_new( filter );
126 filter->owner.sys = chain;
127 return pic;
131 #undef filter_chain_NewVideo
132 filter_chain_t *filter_chain_NewVideo( vlc_object_t *obj, bool allow_change,
133 const filter_owner_t *restrict owner )
135 filter_owner_t callbacks = {
136 .sys = obj,
137 .video = {
138 .buffer_new = filter_chain_VideoBufferNew,
142 return filter_chain_NewInner( &callbacks, "video filter",
143 "video converter", allow_change, owner, VIDEO_ES );
147 * Filter chain destruction
149 void filter_chain_Delete( filter_chain_t *p_chain )
151 while( p_chain->first != NULL )
152 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
154 es_format_Clean( &p_chain->fmt_in );
155 es_format_Clean( &p_chain->fmt_out );
157 free( p_chain );
160 * Filter chain reinitialisation
162 void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
163 const es_format_t *p_fmt_out )
165 while( p_chain->first != NULL )
166 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
168 if( p_fmt_in )
170 es_format_Clean( &p_chain->fmt_in );
171 es_format_Copy( &p_chain->fmt_in, p_fmt_in );
173 if( p_fmt_out )
175 es_format_Clean( &p_chain->fmt_out );
176 es_format_Copy( &p_chain->fmt_out, p_fmt_out );
180 static filter_t *filter_chain_AppendInner( filter_chain_t *chain,
181 const char *name, const char *capability, config_chain_t *cfg,
182 const es_format_t *fmt_in, 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) )
188 return NULL;
190 filter_t *filter = &chained->filter;
192 if( fmt_in == NULL )
194 if( chain->last != NULL )
195 fmt_in = &chain->last->filter.fmt_out;
196 else
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;
206 filter->p_cfg = cfg;
207 filter->psz_name = name;
209 filter->owner = chain->callbacks;
210 filter->owner.sys = chain;
212 assert( capability != NULL );
213 if( name != NULL && filter->b_allow_fmt_out_change )
215 /* Append the "chain" video filter to the current list.
216 * This filter will be used if the requested filter fails to load.
217 * It will then try to add a video converter before. */
218 char name_chained[strlen(name) + sizeof(",chain")];
219 sprintf( name_chained, "%s,chain", name );
220 filter->p_module = module_need( filter, capability, name_chained, true );
222 else
223 filter->p_module = module_need( filter, capability, name, name != NULL );
225 if( filter->p_module == NULL )
226 goto error;
228 if( filter->b_allow_fmt_out_change )
230 es_format_Clean( &chain->fmt_out );
231 es_format_Copy( &chain->fmt_out, &filter->fmt_out );
234 if( chain->last == NULL )
236 assert( chain->first == NULL );
237 chain->first = chained;
239 else
240 chain->last->next = chained;
241 chained->prev = chain->last;
242 chain->last = chained;
243 chained->next = NULL;
245 vlc_mouse_t *mouse = malloc( sizeof(*mouse) );
246 if( likely(mouse != NULL) )
247 vlc_mouse_Init( mouse );
248 chained->mouse = mouse;
249 chained->pending = NULL;
251 msg_Dbg( parent, "Filter '%s' (%p) appended to chain",
252 (name != NULL) ? name : module_get_name(filter->p_module, false),
253 (void *)filter );
254 return filter;
256 error:
257 if( name != NULL )
258 msg_Err( parent, "Failed to create %s '%s'", capability, name );
259 else
260 msg_Err( parent, "Failed to create %s", capability );
261 es_format_Clean( &filter->fmt_out );
262 es_format_Clean( &filter->fmt_in );
263 vlc_object_release( filter );
264 return NULL;
267 filter_t *filter_chain_AppendFilter( filter_chain_t *chain,
268 const char *name, config_chain_t *cfg,
269 const es_format_t *fmt_in, const es_format_t *fmt_out )
271 return filter_chain_AppendInner( chain, name, chain->filter_cap, cfg,
272 fmt_in, fmt_out );
275 int filter_chain_AppendConverter( filter_chain_t *chain,
276 const es_format_t *fmt_in, const es_format_t *fmt_out )
278 return filter_chain_AppendInner( chain, NULL, chain->conv_cap, NULL,
279 fmt_in, fmt_out ) != NULL ? 0 : -1;
282 void filter_chain_DeleteFilter( filter_chain_t *chain, filter_t *filter )
284 vlc_object_t *obj = chain->callbacks.sys;
285 chained_filter_t *chained = (chained_filter_t *)filter;
287 /* Remove it from the chain */
288 if( chained->prev != NULL )
289 chained->prev->next = chained->next;
290 else
292 assert( chained == chain->first );
293 chain->first = chained->next;
296 if( chained->next != NULL )
297 chained->next->prev = chained->prev;
298 else
300 assert( chained == chain->last );
301 chain->last = chained->prev;
304 module_unneed( filter, filter->p_module );
306 msg_Dbg( obj, "Filter %p removed from chain", (void *)filter );
307 FilterDeletePictures( chained->pending );
309 free( chained->mouse );
310 es_format_Clean( &filter->fmt_out );
311 es_format_Clean( &filter->fmt_in );
313 vlc_object_release( filter );
314 /* FIXME: check fmt_in/fmt_out consitency */
318 int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
320 vlc_object_t *obj = chain->callbacks.sys;
321 char *buf = NULL;
322 int ret = 0;
324 while( str != NULL && str[0] != '\0' )
326 config_chain_t *cfg;
327 char *name;
329 char *next = config_ChainCreate( &name, &cfg, str );
331 str = next;
332 free( buf );
333 buf = next;
335 filter_t *filter = filter_chain_AppendFilter( chain, name, cfg,
336 NULL, NULL );
337 if( cfg )
338 config_ChainDestroy( cfg );
340 if( filter == NULL )
342 msg_Err( obj, "Failed to append '%s' to chain", name );
343 free( name );
344 goto error;
347 free( name );
348 ret++;
351 free( buf );
352 return ret;
354 error:
355 while( ret > 0 ) /* Unwind */
357 filter_chain_DeleteFilter( chain, &chain->last->filter );
358 ret--;
360 free( buf );
361 return VLC_EGENERIC;
364 int filter_chain_ForEach( filter_chain_t *chain,
365 int (*cb)( filter_t *, void * ), void *opaque )
367 for( chained_filter_t *f = chain->first; f != NULL; f = f->next )
369 int ret = cb( &f->filter, opaque );
370 if( ret )
371 return ret;
373 return VLC_SUCCESS;
376 bool filter_chain_IsEmpty(const filter_chain_t *chain)
378 return chain->first == NULL;
381 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
384 if( p_chain->b_allow_fmt_out_change )
385 return &p_chain->fmt_out;
387 if( p_chain->last != NULL )
388 return &p_chain->last->filter.fmt_out;
390 /* Unless filter_chain_Reset has been called we are doomed */
391 return &p_chain->fmt_out;
394 static picture_t *FilterChainVideoFilter( chained_filter_t *f, picture_t *p_pic )
396 for( ; f != NULL; f = f->next )
398 filter_t *p_filter = &f->filter;
399 p_pic = p_filter->pf_video_filter( p_filter, p_pic );
400 if( !p_pic )
401 break;
402 if( f->pending )
404 msg_Warn( p_filter, "dropping pictures" );
405 FilterDeletePictures( f->pending );
407 f->pending = p_pic->p_next;
408 p_pic->p_next = NULL;
410 return p_pic;
413 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
415 if( p_pic )
417 p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
418 if( p_pic )
419 return p_pic;
421 for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
423 p_pic = b->pending;
424 if( !p_pic )
425 continue;
426 b->pending = p_pic->p_next;
427 p_pic->p_next = NULL;
429 p_pic = FilterChainVideoFilter( b->next, p_pic );
430 if( p_pic )
431 return p_pic;
433 return NULL;
436 void filter_chain_VideoFlush( filter_chain_t *p_chain )
438 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
440 filter_t *p_filter = &f->filter;
442 FilterDeletePictures( f->pending );
443 f->pending = NULL;
445 filter_Flush( p_filter );
449 void filter_chain_SubSource( filter_chain_t *p_chain, spu_t *spu,
450 mtime_t display_date )
452 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
454 filter_t *p_filter = &f->filter;
455 subpicture_t *p_subpic = p_filter->pf_sub_source( p_filter, display_date );
456 if( p_subpic )
457 spu_PutSubpicture( spu, p_subpic );
461 subpicture_t *filter_chain_SubFilter( filter_chain_t *p_chain, subpicture_t *p_subpic )
463 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
465 filter_t *p_filter = &f->filter;
467 p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
469 if( !p_subpic )
470 break;
472 return p_subpic;
475 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
477 vlc_mouse_t current = *p_src;
479 for( chained_filter_t *f = p_chain->last; f != NULL; f = f->prev )
481 filter_t *p_filter = &f->filter;
482 vlc_mouse_t *p_mouse = f->mouse;
484 if( p_filter->pf_video_mouse && p_mouse )
486 vlc_mouse_t old = *p_mouse;
487 vlc_mouse_t filtered;
489 *p_mouse = current;
490 if( p_filter->pf_video_mouse( p_filter, &filtered, &old, &current ) )
491 return VLC_EGENERIC;
492 current = filtered;
496 *p_dst = current;
497 return VLC_SUCCESS;
500 int filter_chain_MouseEvent( filter_chain_t *p_chain,
501 const vlc_mouse_t *p_mouse,
502 const video_format_t *p_fmt )
504 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
506 filter_t *p_filter = &f->filter;
508 if( p_filter->pf_sub_mouse )
510 vlc_mouse_t old = *f->mouse;
511 *f->mouse = *p_mouse;
512 if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
513 return VLC_EGENERIC;
517 return VLC_SUCCESS;
520 /* Helpers */
521 static void FilterDeletePictures( picture_t *picture )
523 while( picture )
525 picture_t *next = picture->p_next;
526 picture_Release( picture );
527 picture = next;