Dshow: relicense to LGPL
[vlc.git] / src / misc / filter_chain.c
blob08bbcb60f8a5ded5c6afb70401f9f5557230b36d
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 static const struct filter_video_callbacks filter_chain_video_cbs =
133 .buffer_new = filter_chain_VideoBufferNew,
136 #undef filter_chain_NewVideo
137 filter_chain_t *filter_chain_NewVideo( vlc_object_t *obj, bool allow_change,
138 const filter_owner_t *restrict owner )
140 filter_owner_t callbacks = {
141 .video = &filter_chain_video_cbs,
142 .sys = obj,
145 return filter_chain_NewInner( &callbacks, "video filter",
146 "video converter", allow_change, owner, VIDEO_ES );
150 * Filter chain destruction
152 void filter_chain_Delete( filter_chain_t *p_chain )
154 while( p_chain->first != NULL )
155 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
157 es_format_Clean( &p_chain->fmt_in );
158 es_format_Clean( &p_chain->fmt_out );
160 free( p_chain );
163 * Filter chain reinitialisation
165 void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
166 const es_format_t *p_fmt_out )
168 while( p_chain->first != NULL )
169 filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
171 if( p_fmt_in )
173 es_format_Clean( &p_chain->fmt_in );
174 es_format_Copy( &p_chain->fmt_in, p_fmt_in );
176 if( p_fmt_out )
178 es_format_Clean( &p_chain->fmt_out );
179 es_format_Copy( &p_chain->fmt_out, p_fmt_out );
183 static filter_t *filter_chain_AppendInner( filter_chain_t *chain,
184 const char *name, const char *capability, config_chain_t *cfg,
185 const es_format_t *fmt_in, const es_format_t *fmt_out )
187 vlc_object_t *parent = chain->callbacks.sys;
188 chained_filter_t *chained =
189 vlc_custom_create( parent, sizeof(*chained), "filter" );
190 if( unlikely(chained == NULL) )
191 return NULL;
193 filter_t *filter = &chained->filter;
195 if( fmt_in == NULL )
197 if( chain->last != NULL )
198 fmt_in = &chain->last->filter.fmt_out;
199 else
200 fmt_in = &chain->fmt_in;
203 if( fmt_out == NULL )
204 fmt_out = &chain->fmt_out;
206 es_format_Copy( &filter->fmt_in, fmt_in );
207 es_format_Copy( &filter->fmt_out, fmt_out );
208 filter->b_allow_fmt_out_change = chain->b_allow_fmt_out_change;
209 filter->p_cfg = cfg;
210 filter->psz_name = name;
212 filter->owner = chain->callbacks;
213 filter->owner.sys = chain;
215 assert( capability != NULL );
216 if( name != NULL && filter->b_allow_fmt_out_change )
218 /* Append the "chain" video filter to the current list.
219 * This filter will be used if the requested filter fails to load.
220 * It will then try to add a video converter before. */
221 char name_chained[strlen(name) + sizeof(",chain")];
222 sprintf( name_chained, "%s,chain", name );
223 filter->p_module = module_need( filter, capability, name_chained, true );
225 else
226 filter->p_module = module_need( filter, capability, name, name != NULL );
228 if( filter->p_module == NULL )
229 goto error;
231 if( filter->b_allow_fmt_out_change )
233 es_format_Clean( &chain->fmt_out );
234 es_format_Copy( &chain->fmt_out, &filter->fmt_out );
237 if( chain->last == NULL )
239 assert( chain->first == NULL );
240 chain->first = chained;
242 else
243 chain->last->next = chained;
244 chained->prev = chain->last;
245 chain->last = chained;
246 chained->next = NULL;
248 vlc_mouse_t *mouse = malloc( sizeof(*mouse) );
249 if( likely(mouse != NULL) )
250 vlc_mouse_Init( mouse );
251 chained->mouse = mouse;
252 chained->pending = NULL;
254 msg_Dbg( parent, "Filter '%s' (%p) appended to chain",
255 (name != NULL) ? name : module_get_name(filter->p_module, false),
256 (void *)filter );
257 return filter;
259 error:
260 if( name != NULL )
261 msg_Err( parent, "Failed to create %s '%s'", capability, name );
262 else
263 msg_Err( parent, "Failed to create %s", capability );
264 es_format_Clean( &filter->fmt_out );
265 es_format_Clean( &filter->fmt_in );
266 vlc_object_release( filter );
267 return NULL;
270 filter_t *filter_chain_AppendFilter( filter_chain_t *chain,
271 const char *name, config_chain_t *cfg,
272 const es_format_t *fmt_in, const es_format_t *fmt_out )
274 return filter_chain_AppendInner( chain, name, chain->filter_cap, cfg,
275 fmt_in, fmt_out );
278 int filter_chain_AppendConverter( filter_chain_t *chain,
279 const es_format_t *fmt_in, const es_format_t *fmt_out )
281 return filter_chain_AppendInner( chain, NULL, chain->conv_cap, NULL,
282 fmt_in, fmt_out ) != NULL ? 0 : -1;
285 void filter_chain_DeleteFilter( filter_chain_t *chain, filter_t *filter )
287 vlc_object_t *obj = chain->callbacks.sys;
288 chained_filter_t *chained = (chained_filter_t *)filter;
290 /* Remove it from the chain */
291 if( chained->prev != NULL )
292 chained->prev->next = chained->next;
293 else
295 assert( chained == chain->first );
296 chain->first = chained->next;
299 if( chained->next != NULL )
300 chained->next->prev = chained->prev;
301 else
303 assert( chained == chain->last );
304 chain->last = chained->prev;
307 module_unneed( filter, filter->p_module );
309 msg_Dbg( obj, "Filter %p removed from chain", (void *)filter );
310 FilterDeletePictures( chained->pending );
312 free( chained->mouse );
313 es_format_Clean( &filter->fmt_out );
314 es_format_Clean( &filter->fmt_in );
316 vlc_object_release( filter );
317 /* FIXME: check fmt_in/fmt_out consitency */
321 int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
323 vlc_object_t *obj = chain->callbacks.sys;
324 char *buf = NULL;
325 int ret = 0;
327 while( str != NULL && str[0] != '\0' )
329 config_chain_t *cfg;
330 char *name;
332 char *next = config_ChainCreate( &name, &cfg, str );
334 str = next;
335 free( buf );
336 buf = next;
338 filter_t *filter = filter_chain_AppendFilter( chain, name, cfg,
339 NULL, NULL );
340 if( cfg )
341 config_ChainDestroy( cfg );
343 if( filter == NULL )
345 msg_Err( obj, "Failed to append '%s' to chain", name );
346 free( name );
347 goto error;
350 free( name );
351 ret++;
354 free( buf );
355 return ret;
357 error:
358 while( ret > 0 ) /* Unwind */
360 filter_chain_DeleteFilter( chain, &chain->last->filter );
361 ret--;
363 free( buf );
364 return VLC_EGENERIC;
367 int filter_chain_ForEach( filter_chain_t *chain,
368 int (*cb)( filter_t *, void * ), void *opaque )
370 for( chained_filter_t *f = chain->first; f != NULL; f = f->next )
372 int ret = cb( &f->filter, opaque );
373 if( ret )
374 return ret;
376 return VLC_SUCCESS;
379 bool filter_chain_IsEmpty(const filter_chain_t *chain)
381 return chain->first == NULL;
384 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
387 if( p_chain->b_allow_fmt_out_change )
388 return &p_chain->fmt_out;
390 if( p_chain->last != NULL )
391 return &p_chain->last->filter.fmt_out;
393 /* Unless filter_chain_Reset has been called we are doomed */
394 return &p_chain->fmt_out;
397 static picture_t *FilterChainVideoFilter( chained_filter_t *f, picture_t *p_pic )
399 for( ; f != NULL; f = f->next )
401 filter_t *p_filter = &f->filter;
402 p_pic = p_filter->pf_video_filter( p_filter, p_pic );
403 if( !p_pic )
404 break;
405 if( f->pending )
407 msg_Warn( p_filter, "dropping pictures" );
408 FilterDeletePictures( f->pending );
410 f->pending = p_pic->p_next;
411 p_pic->p_next = NULL;
413 return p_pic;
416 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
418 if( p_pic )
420 p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
421 if( p_pic )
422 return p_pic;
424 for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
426 p_pic = b->pending;
427 if( !p_pic )
428 continue;
429 b->pending = p_pic->p_next;
430 p_pic->p_next = NULL;
432 p_pic = FilterChainVideoFilter( b->next, p_pic );
433 if( p_pic )
434 return p_pic;
436 return NULL;
439 void filter_chain_VideoFlush( filter_chain_t *p_chain )
441 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
443 filter_t *p_filter = &f->filter;
445 FilterDeletePictures( f->pending );
446 f->pending = NULL;
448 filter_Flush( p_filter );
452 void filter_chain_SubSource( filter_chain_t *p_chain, spu_t *spu,
453 vlc_tick_t display_date )
455 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
457 filter_t *p_filter = &f->filter;
458 subpicture_t *p_subpic = p_filter->pf_sub_source( p_filter, display_date );
459 if( p_subpic )
460 spu_PutSubpicture( spu, p_subpic );
464 subpicture_t *filter_chain_SubFilter( filter_chain_t *p_chain, subpicture_t *p_subpic )
466 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
468 filter_t *p_filter = &f->filter;
470 p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
472 if( !p_subpic )
473 break;
475 return p_subpic;
478 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
480 vlc_mouse_t current = *p_src;
482 for( chained_filter_t *f = p_chain->last; f != NULL; f = f->prev )
484 filter_t *p_filter = &f->filter;
485 vlc_mouse_t *p_mouse = f->mouse;
487 if( p_filter->pf_video_mouse && p_mouse )
489 vlc_mouse_t old = *p_mouse;
490 vlc_mouse_t filtered;
492 *p_mouse = current;
493 if( p_filter->pf_video_mouse( p_filter, &filtered, &old, &current ) )
494 return VLC_EGENERIC;
495 current = filtered;
499 *p_dst = current;
500 return VLC_SUCCESS;
503 int filter_chain_MouseEvent( filter_chain_t *p_chain,
504 const vlc_mouse_t *p_mouse,
505 const video_format_t *p_fmt )
507 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
509 filter_t *p_filter = &f->filter;
511 if( p_filter->pf_sub_mouse )
513 vlc_mouse_t old = *f->mouse;
514 *f->mouse = *p_mouse;
515 if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
516 return VLC_EGENERIC;
520 return VLC_SUCCESS;
523 /* Helpers */
524 static void FilterDeletePictures( picture_t *picture )
526 while( picture )
528 picture_t *next = picture->p_next;
529 picture_Release( picture );
530 picture = next;