codec: avcodec: remove leftoff cc code
[vlc.git] / src / misc / filter_chain.c
blob741a63c9ad058de5be86287be8c906faa73816c7
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;
208 filter->psz_name = name;
210 filter->owner = chain->callbacks;
211 filter->owner.sys = chain;
213 assert( capability != NULL );
214 if( name != NULL && filter->b_allow_fmt_out_change )
216 /* Append the "chain" video filter to the current list.
217 * This filter will be used if the requested filter fails to load.
218 * It will then try to add a video converter before. */
219 char name_chained[strlen(name) + sizeof(",chain")];
220 sprintf( name_chained, "%s,chain", name );
221 filter->p_module = module_need( filter, capability, name_chained, true );
223 else
224 filter->p_module = module_need( filter, capability, name, name != NULL );
226 if( filter->p_module == NULL )
227 goto error;
229 if( filter->b_allow_fmt_out_change )
231 es_format_Clean( &chain->fmt_out );
232 es_format_Copy( &chain->fmt_out, &filter->fmt_out );
235 if( chain->last == NULL )
237 assert( chain->first == NULL );
238 chain->first = chained;
240 else
241 chain->last->next = chained;
242 chained->prev = chain->last;
243 chain->last = chained;
244 chained->next = NULL;
245 chain->length++;
247 vlc_mouse_t *mouse = malloc( sizeof(*mouse) );
248 if( likely(mouse != NULL) )
249 vlc_mouse_Init( mouse );
250 chained->mouse = mouse;
251 chained->pending = NULL;
253 msg_Dbg( parent, "Filter '%s' (%p) appended to chain",
254 (name != NULL) ? name : module_get_name(filter->p_module, false),
255 (void *)filter );
256 return filter;
258 error:
259 if( name != NULL )
260 msg_Err( parent, "Failed to create %s '%s'", capability, name );
261 else
262 msg_Err( parent, "Failed to create %s", capability );
263 es_format_Clean( &filter->fmt_out );
264 es_format_Clean( &filter->fmt_in );
265 vlc_object_release( filter );
266 return NULL;
269 filter_t *filter_chain_AppendFilter( filter_chain_t *chain,
270 const char *name, config_chain_t *cfg,
271 const es_format_t *fmt_in, const es_format_t *fmt_out )
273 return filter_chain_AppendInner( chain, name, chain->filter_cap, cfg,
274 fmt_in, fmt_out );
277 int filter_chain_AppendConverter( filter_chain_t *chain,
278 const es_format_t *fmt_in, const es_format_t *fmt_out )
280 return filter_chain_AppendInner( chain, NULL, chain->conv_cap, NULL,
281 fmt_in, fmt_out ) != NULL ? 0 : -1;
284 void filter_chain_DeleteFilter( filter_chain_t *chain, filter_t *filter )
286 vlc_object_t *obj = chain->callbacks.sys;
287 chained_filter_t *chained = (chained_filter_t *)filter;
289 /* Remove it from the chain */
290 if( chained->prev != NULL )
291 chained->prev->next = chained->next;
292 else
294 assert( chained == chain->first );
295 chain->first = chained->next;
298 if( chained->next != NULL )
299 chained->next->prev = chained->prev;
300 else
302 assert( chained == chain->last );
303 chain->last = chained->prev;
306 assert( chain->length > 0 );
307 chain->length--;
309 module_unneed( filter, filter->p_module );
311 msg_Dbg( obj, "Filter %p removed from chain", (void *)filter );
312 FilterDeletePictures( chained->pending );
314 free( chained->mouse );
315 es_format_Clean( &filter->fmt_out );
316 es_format_Clean( &filter->fmt_in );
318 vlc_object_release( filter );
319 /* FIXME: check fmt_in/fmt_out consitency */
323 int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
325 vlc_object_t *obj = chain->callbacks.sys;
326 char *buf = NULL;
327 int ret = 0;
329 while( str != NULL && str[0] != '\0' )
331 config_chain_t *cfg;
332 char *name;
334 char *next = config_ChainCreate( &name, &cfg, str );
336 str = next;
337 free( buf );
338 buf = next;
340 filter_t *filter = filter_chain_AppendFilter( chain, name, cfg,
341 NULL, NULL );
342 if( cfg )
343 config_ChainDestroy( cfg );
345 if( filter == NULL )
347 msg_Err( obj, "Failed to append '%s' to chain", name );
348 free( name );
349 goto error;
352 free( name );
353 ret++;
356 free( buf );
357 return ret;
359 error:
360 while( ret > 0 ) /* Unwind */
362 filter_chain_DeleteFilter( chain, &chain->last->filter );
363 ret--;
365 free( buf );
366 return VLC_EGENERIC;
369 int filter_chain_ForEach( filter_chain_t *chain,
370 int (*cb)( filter_t *, void * ), void *opaque )
372 for( chained_filter_t *f = chain->first; f != NULL; f = f->next )
374 int ret = cb( &f->filter, opaque );
375 if( ret )
376 return ret;
378 return VLC_SUCCESS;
381 int filter_chain_GetLength( filter_chain_t *p_chain )
383 return p_chain->length;
386 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
389 if( p_chain->b_allow_fmt_out_change )
390 return &p_chain->fmt_out;
392 if( p_chain->last != NULL )
393 return &p_chain->last->filter.fmt_out;
395 /* Unless filter_chain_Reset has been called we are doomed */
396 return &p_chain->fmt_out;
399 static picture_t *FilterChainVideoFilter( chained_filter_t *f, picture_t *p_pic )
401 for( ; f != NULL; f = f->next )
403 filter_t *p_filter = &f->filter;
404 p_pic = p_filter->pf_video_filter( p_filter, p_pic );
405 if( !p_pic )
406 break;
407 if( f->pending )
409 msg_Warn( p_filter, "dropping pictures" );
410 FilterDeletePictures( f->pending );
412 f->pending = p_pic->p_next;
413 p_pic->p_next = NULL;
415 return p_pic;
418 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
420 if( p_pic )
422 p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
423 if( p_pic )
424 return p_pic;
426 for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
428 p_pic = b->pending;
429 if( !p_pic )
430 continue;
431 b->pending = p_pic->p_next;
432 p_pic->p_next = NULL;
434 p_pic = FilterChainVideoFilter( b->next, p_pic );
435 if( p_pic )
436 return p_pic;
438 return NULL;
441 void filter_chain_VideoFlush( filter_chain_t *p_chain )
443 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
445 filter_t *p_filter = &f->filter;
447 FilterDeletePictures( f->pending );
448 f->pending = NULL;
450 filter_Flush( p_filter );
454 void filter_chain_SubSource( filter_chain_t *p_chain, spu_t *spu,
455 mtime_t display_date )
457 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
459 filter_t *p_filter = &f->filter;
460 subpicture_t *p_subpic = p_filter->pf_sub_source( p_filter, display_date );
461 if( p_subpic )
462 spu_PutSubpicture( spu, p_subpic );
466 subpicture_t *filter_chain_SubFilter( filter_chain_t *p_chain, subpicture_t *p_subpic )
468 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
470 filter_t *p_filter = &f->filter;
472 p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
474 if( !p_subpic )
475 break;
477 return p_subpic;
480 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
482 vlc_mouse_t current = *p_src;
484 for( chained_filter_t *f = p_chain->last; f != NULL; f = f->prev )
486 filter_t *p_filter = &f->filter;
487 vlc_mouse_t *p_mouse = f->mouse;
489 if( p_filter->pf_video_mouse && p_mouse )
491 vlc_mouse_t old = *p_mouse;
492 vlc_mouse_t filtered;
494 *p_mouse = current;
495 if( p_filter->pf_video_mouse( p_filter, &filtered, &old, &current ) )
496 return VLC_EGENERIC;
497 current = filtered;
501 *p_dst = current;
502 return VLC_SUCCESS;
505 int filter_chain_MouseEvent( filter_chain_t *p_chain,
506 const vlc_mouse_t *p_mouse,
507 const video_format_t *p_fmt )
509 for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
511 filter_t *p_filter = &f->filter;
513 if( p_filter->pf_sub_mouse )
515 vlc_mouse_t old = *f->mouse;
516 *f->mouse = *p_mouse;
517 if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
518 return VLC_EGENERIC;
522 return VLC_SUCCESS;
525 /* Helpers */
526 static void FilterDeletePictures( picture_t *picture )
528 while( picture )
530 picture_t *next = picture->p_next;
531 picture_Release( picture );
532 picture = next;