playlist/item: playlist_Add: fix documentation
[vlc.git] / src / misc / filter_chain.c
blobae0316af73852746e4fa47a87a624daac28e3b24
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 (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 unsigned length; /**< Number of filters */
62 bool b_allow_fmt_out_change; /**< Can the output format be changed? */
63 const char *filter_cap; /**< Filter modules capability */
64 const char *conv_cap; /**< Converter modules capability */
67 /**
68 * Local prototypes
70 static void FilterDeletePictures( picture_t * );
72 static filter_chain_t *filter_chain_NewInner( const filter_owner_t *callbacks,
73 const char *cap, const char *conv_cap, bool fmt_out_change,
74 const filter_owner_t *owner )
76 assert( callbacks != NULL && callbacks->sys != NULL );
77 assert( cap != NULL );
79 filter_chain_t *chain = malloc( sizeof (*chain) );
80 if( unlikely(chain == NULL) )
81 return NULL;
83 chain->callbacks = *callbacks;
84 if( owner != NULL )
85 chain->owner = *owner;
86 chain->first = NULL;
87 chain->last = NULL;
88 es_format_Init( &chain->fmt_in, UNKNOWN_ES, 0 );
89 es_format_Init( &chain->fmt_out, UNKNOWN_ES, 0 );
90 chain->length = 0;
91 chain->b_allow_fmt_out_change = fmt_out_change;
92 chain->filter_cap = cap;
93 chain->conv_cap = conv_cap;
94 return chain;
97 #undef filter_chain_New
98 /**
99 * Filter chain initialisation
101 filter_chain_t *filter_chain_New( vlc_object_t *obj, const char *cap )
103 filter_owner_t callbacks = {
104 .sys = obj,
107 return filter_chain_NewInner( &callbacks, cap, NULL, false, NULL );
110 /** Chained filter picture allocator function */
111 static picture_t *filter_chain_VideoBufferNew( filter_t *filter )
113 if( chained(filter)->next != NULL )
115 picture_t *pic = picture_NewFromFormat( &filter->fmt_out.video );
116 if( pic == NULL )
117 msg_Err( filter, "Failed to allocate picture" );
118 return pic;
120 else
122 filter_chain_t *chain = filter->owner.sys;
124 /* XXX ugly */
125 filter->owner.sys = chain->owner.sys;
126 picture_t *pic = chain->owner.video.buffer_new( filter );
127 filter->owner.sys = chain;
128 return pic;
132 #undef filter_chain_NewVideo
133 filter_chain_t *filter_chain_NewVideo( vlc_object_t *obj, bool allow_change,
134 const filter_owner_t *restrict owner )
136 filter_owner_t callbacks = {
137 .sys = obj,
138 .video = {
139 .buffer_new = filter_chain_VideoBufferNew,
143 return filter_chain_NewInner( &callbacks, "video filter",
144 "video converter", allow_change, owner );
148 * Filter chain destruction
150 void filter_chain_Delete( filter_chain_t *p_chain )
152 while( p_chain->first != NULL )
153 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
155 es_format_Clean( &p_chain->fmt_in );
156 es_format_Clean( &p_chain->fmt_out );
158 free( p_chain );
161 * Filter chain reinitialisation
163 void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
164 const es_format_t *p_fmt_out )
166 while( p_chain->first != NULL )
167 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
169 if( p_fmt_in )
171 es_format_Clean( &p_chain->fmt_in );
172 es_format_Copy( &p_chain->fmt_in, p_fmt_in );
174 if( p_fmt_out )
176 es_format_Clean( &p_chain->fmt_out );
177 es_format_Copy( &p_chain->fmt_out, p_fmt_out );
181 static filter_t *filter_chain_AppendInner( filter_chain_t *chain,
182 const char *name, const char *capability, config_chain_t *cfg,
183 const es_format_t *fmt_in, const es_format_t *fmt_out )
185 vlc_object_t *parent = chain->callbacks.sys;
186 chained_filter_t *chained =
187 vlc_custom_create( parent, sizeof(*chained), "filter" );
188 if( unlikely(chained == NULL) )
189 return NULL;
191 filter_t *filter = &chained->filter;
193 if( fmt_in == NULL )
195 if( chain->last != NULL )
196 fmt_in = &chain->last->filter.fmt_out;
197 else
198 fmt_in = &chain->fmt_in;
201 if( fmt_out == NULL )
202 fmt_out = &chain->fmt_out;
204 es_format_Copy( &filter->fmt_in, fmt_in );
205 es_format_Copy( &filter->fmt_out, fmt_out );
206 filter->b_allow_fmt_out_change = chain->b_allow_fmt_out_change;
207 filter->p_cfg = cfg;
209 filter->owner = chain->callbacks;
210 filter->owner.sys = chain;
212 assert( capability != NULL );
214 filter->p_module = module_need( filter, capability, name, name != NULL );
215 if( filter->p_module == NULL )
216 goto error;
218 if( filter->b_allow_fmt_out_change )
220 es_format_Clean( &chain->fmt_out );
221 es_format_Copy( &chain->fmt_out, &filter->fmt_out );
224 if( chain->last == NULL )
226 assert( chain->first == NULL );
227 chain->first = chained;
229 else
230 chain->last->next = chained;
231 chained->prev = chain->last;
232 chain->last = chained;
233 chained->next = NULL;
234 chain->length++;
236 vlc_mouse_t *mouse = malloc( sizeof(*mouse) );
237 if( likely(mouse != NULL) )
238 vlc_mouse_Init( mouse );
239 chained->mouse = mouse;
240 chained->pending = NULL;
242 msg_Dbg( parent, "Filter '%s' (%p) appended to chain",
243 (name != NULL) ? name : module_get_name(filter->p_module, false),
244 (void *)filter );
245 return filter;
247 error:
248 if( name != NULL )
249 msg_Err( parent, "Failed to create %s '%s'", capability, name );
250 else
251 msg_Err( parent, "Failed to create %s", capability );
252 es_format_Clean( &filter->fmt_out );
253 es_format_Clean( &filter->fmt_in );
254 vlc_object_release( filter );
255 return NULL;
258 filter_t *filter_chain_AppendFilter( filter_chain_t *chain,
259 const char *name, config_chain_t *cfg,
260 const es_format_t *fmt_in, const es_format_t *fmt_out )
262 return filter_chain_AppendInner( chain, name, chain->filter_cap, cfg,
263 fmt_in, fmt_out );
266 int filter_chain_AppendConverter( filter_chain_t *chain,
267 const es_format_t *fmt_in, const es_format_t *fmt_out )
269 return filter_chain_AppendInner( chain, NULL, chain->conv_cap, NULL,
270 fmt_in, fmt_out ) != NULL ? 0 : -1;
273 void filter_chain_DeleteFilter( filter_chain_t *chain, filter_t *filter )
275 vlc_object_t *obj = chain->callbacks.sys;
276 chained_filter_t *chained = (chained_filter_t *)filter;
278 /* Remove it from the chain */
279 if( chained->prev != NULL )
280 chained->prev->next = chained->next;
281 else
283 assert( chained == chain->first );
284 chain->first = chained->next;
287 if( chained->next != NULL )
288 chained->next->prev = chained->prev;
289 else
291 assert( chained == chain->last );
292 chain->last = chained->prev;
295 assert( chain->length > 0 );
296 chain->length--;
298 module_unneed( filter, filter->p_module );
300 msg_Dbg( obj, "Filter %p removed from chain", (void *)filter );
301 FilterDeletePictures( chained->pending );
303 free( chained->mouse );
304 es_format_Clean( &filter->fmt_out );
305 es_format_Clean( &filter->fmt_in );
307 vlc_object_release( filter );
308 /* FIXME: check fmt_in/fmt_out consitency */
312 int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
314 vlc_object_t *obj = chain->callbacks.sys;
315 char *buf = NULL;
316 int ret = 0;
318 while( str != NULL && str[0] != '\0' )
320 config_chain_t *cfg;
321 char *name;
323 char *next = config_ChainCreate( &name, &cfg, str );
325 str = next;
326 free( buf );
327 buf = next;
329 filter_t *filter = filter_chain_AppendFilter( chain, name, cfg,
330 NULL, NULL );
331 if( cfg )
332 config_ChainDestroy( cfg );
334 if( filter == NULL )
336 msg_Err( obj, "Failed to append '%s' to chain", name );
337 free( name );
338 goto error;
341 free( name );
342 ret++;
345 free( buf );
346 return ret;
348 error:
349 while( ret > 0 ) /* Unwind */
351 filter_chain_DeleteFilter( chain, &chain->last->filter );
352 ret--;
354 free( buf );
355 return VLC_EGENERIC;
358 int filter_chain_ForEach( filter_chain_t *chain,
359 int (*cb)( filter_t *, void * ), void *opaque )
361 for( chained_filter_t *f = chain->first; f != NULL; f = f->next )
363 int ret = cb( &f->filter, opaque );
364 if( ret )
365 return ret;
367 return VLC_SUCCESS;
370 int filter_chain_GetLength( filter_chain_t *p_chain )
372 return p_chain->length;
375 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
378 if( p_chain->b_allow_fmt_out_change )
379 return &p_chain->fmt_out;
381 if( p_chain->last != NULL )
382 return &p_chain->last->filter.fmt_out;
384 /* Unless filter_chain_Reset has been called we are doomed */
385 return &p_chain->fmt_out;
388 static picture_t *FilterChainVideoFilter( chained_filter_t *f, picture_t *p_pic )
390 for( ; f != NULL; f = f->next )
392 filter_t *p_filter = &f->filter;
393 p_pic = p_filter->pf_video_filter( p_filter, p_pic );
394 if( !p_pic )
395 break;
396 if( f->pending )
398 msg_Warn( p_filter, "dropping pictures" );
399 FilterDeletePictures( f->pending );
401 f->pending = p_pic->p_next;
402 p_pic->p_next = NULL;
404 return p_pic;
407 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
409 if( p_pic )
411 p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
412 if( p_pic )
413 return p_pic;
415 for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
417 p_pic = b->pending;
418 if( !p_pic )
419 continue;
420 b->pending = p_pic->p_next;
421 p_pic->p_next = NULL;
423 p_pic = FilterChainVideoFilter( b->next, p_pic );
424 if( p_pic )
425 return p_pic;
427 return NULL;
430 void filter_chain_VideoFlush( filter_chain_t *p_chain )
432 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
434 filter_t *p_filter = &f->filter;
436 FilterDeletePictures( f->pending );
437 f->pending = NULL;
439 filter_Flush( p_filter );
443 void filter_chain_SubSource( filter_chain_t *p_chain, spu_t *spu,
444 mtime_t display_date )
446 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
448 filter_t *p_filter = &f->filter;
449 subpicture_t *p_subpic = p_filter->pf_sub_source( p_filter, display_date );
450 if( p_subpic )
451 spu_PutSubpicture( spu, p_subpic );
455 subpicture_t *filter_chain_SubFilter( filter_chain_t *p_chain, subpicture_t *p_subpic )
457 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
459 filter_t *p_filter = &f->filter;
461 p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
463 if( !p_subpic )
464 break;
466 return p_subpic;
469 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
471 vlc_mouse_t current = *p_src;
473 for( chained_filter_t *f = p_chain->last; f != NULL; f = f->prev )
475 filter_t *p_filter = &f->filter;
476 vlc_mouse_t *p_mouse = f->mouse;
478 if( p_filter->pf_video_mouse && p_mouse )
480 vlc_mouse_t old = *p_mouse;
481 vlc_mouse_t filtered;
483 *p_mouse = current;
484 if( p_filter->pf_video_mouse( p_filter, &filtered, &old, &current ) )
485 return VLC_EGENERIC;
486 current = filtered;
490 *p_dst = current;
491 return VLC_SUCCESS;
494 int filter_chain_MouseEvent( filter_chain_t *p_chain,
495 const vlc_mouse_t *p_mouse,
496 const video_format_t *p_fmt )
498 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
500 filter_t *p_filter = &f->filter;
502 if( p_filter->pf_sub_mouse )
504 vlc_mouse_t old = *f->mouse;
505 *f->mouse = *p_mouse;
506 if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
507 return VLC_EGENERIC;
511 return VLC_SUCCESS;
514 /* Helpers */
515 static void FilterDeletePictures( picture_t *picture )
517 while( picture )
519 picture_t *next = picture->p_next;
520 picture_Release( picture );
521 picture = next;