filter_chain: inline filter_chain_DeleteFilter() and drop return value
[vlc.git] / src / misc / filter_chain.c
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
6  *
7  * Author: Antoine Cellerier <dionoea at videolan dot org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_filter.h>
29 #include <vlc_modules.h>
30 #include <vlc_spu.h>
31 #include <libvlc.h>
32 #include <assert.h>
33
34 typedef struct chained_filter_t
35 {
36     /* Public part of the filter structure */
37     filter_t filter;
38     /* Private filter chain data (shhhh!) */
39     struct chained_filter_t *prev, *next;
40     vlc_mouse_t *mouse;
41     picture_t *pending;
42 } chained_filter_t;
43
44 /* Only use this with filter objects from _this_ C module */
45 static inline chained_filter_t *chained (filter_t *filter)
46 {
47     return (chained_filter_t *)filter;
48 }
49
50 /* */
51 struct filter_chain_t
52 {
53     filter_owner_t callbacks; /**< Inner callbacks */
54     filter_owner_t owner; /**< Owner (downstream) callbacks */
55
56     chained_filter_t *first, *last; /**< List of filters */
57
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 */
63 };
64
65 /**
66  * Local prototypes
67  */
68 static void FilterDeletePictures( filter_t *, picture_t * );
69
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 )
72 {
73     assert( callbacks != NULL && callbacks->sys != NULL );
74     assert( cap != NULL );
75
76     filter_chain_t *chain = malloc( sizeof(*chain) + strlen( cap ) );
77     if( unlikely(chain == NULL) )
78         return NULL;
79
80     chain->callbacks = *callbacks;
81     if( owner != NULL )
82         chain->owner = *owner;
83     chain->first = NULL;
84     chain->last = NULL;
85     es_format_Init( &chain->fmt_in, UNKNOWN_ES, 0 );
86     es_format_Init( &chain->fmt_out, UNKNOWN_ES, 0 );
87     chain->length = 0;
88     chain->b_allow_fmt_out_change = fmt_out_change;
89     strcpy( chain->psz_capability, cap );
90
91     return chain;
92 }
93
94 #undef filter_chain_New
95 /**
96  * Filter chain initialisation
97  */
98 filter_chain_t *filter_chain_New( vlc_object_t *obj, const char *cap,
99                                   bool fmt_out_change )
100 {
101     filter_owner_t callbacks = {
102         .sys = obj,
103     };
104
105     return filter_chain_NewInner( &callbacks, cap, fmt_out_change, NULL );
106 }
107
108 /** Chained filter picture allocator function */
109 static picture_t *filter_chain_VideoBufferNew( filter_t *filter )
110 {
111     if( chained(filter)->next != NULL )
112     {
113         picture_t *pic = picture_NewFromFormat( &filter->fmt_out.video );
114         if( pic == NULL )
115             msg_Err( filter, "Failed to allocate picture" );
116         return pic;
117     }
118     else
119     {
120         filter_chain_t *chain = filter->owner.sys;
121
122         /* XXX ugly */
123         filter->owner.sys = chain->owner.sys;
124         picture_t *pic = chain->owner.video.buffer_new( filter );
125         filter->owner.sys = chain;
126         return pic;
127     }
128 }
129
130 static void filter_chain_VideoBufferDelete( filter_t *filter, picture_t *pic )
131 {
132     if( chained(filter)->next != NULL )
133         picture_Release( pic );
134     else
135     {
136         filter_chain_t *chain = filter->owner.sys;
137
138         chain->owner.video.buffer_del( filter, pic );
139     }
140 }
141
142 #undef filter_chain_NewVideo
143 filter_chain_t *filter_chain_NewVideo( vlc_object_t *obj, bool allow_change,
144                                        const filter_owner_t *restrict owner )
145 {
146     filter_owner_t callbacks = {
147         .sys = obj,
148         .video = {
149             .buffer_new = filter_chain_VideoBufferNew,
150             .buffer_del = filter_chain_VideoBufferDelete,
151         },
152     };
153
154     return filter_chain_NewInner( &callbacks, "video filter2", allow_change,
155                                   owner );
156 }
157
158 /**
159  * Filter chain destruction
160  */
161 void filter_chain_Delete( filter_chain_t *p_chain )
162 {
163     while( p_chain->first != NULL )
164         filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
165
166     es_format_Clean( &p_chain->fmt_in );
167     es_format_Clean( &p_chain->fmt_out );
168
169     free( p_chain );
170 }
171 /**
172  * Filter chain reinitialisation
173  */
174 void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
175                          const es_format_t *p_fmt_out )
176 {
177     while( p_chain->first != NULL )
178         filter_chain_DeleteFilter( p_chain, &p_chain->first->filter );
179
180     if( p_fmt_in )
181     {
182         es_format_Clean( &p_chain->fmt_in );
183         es_format_Copy( &p_chain->fmt_in, p_fmt_in );
184     }
185     if( p_fmt_out )
186     {
187         es_format_Clean( &p_chain->fmt_out );
188         es_format_Copy( &p_chain->fmt_out, p_fmt_out );
189     }
190 }
191
192 filter_t *filter_chain_AppendFilter( filter_chain_t *chain, const char *name,
193                                      config_chain_t *cfg,
194                                      const es_format_t *fmt_in,
195                                      const es_format_t *fmt_out )
196 {
197     vlc_object_t *parent = chain->callbacks.sys;
198     chained_filter_t *chained =
199         vlc_custom_create( parent, sizeof(*chained), "filter" );
200     if( unlikely(chained == NULL) )
201         return NULL;
202
203     filter_t *filter = &chained->filter;
204
205     if( fmt_in == NULL )
206     {
207         if( chain->last != NULL )
208             fmt_in = &chain->last->filter.fmt_out;
209         else
210             fmt_in = &chain->fmt_in;
211     }
212
213     if( fmt_out == NULL )
214         fmt_out = &chain->fmt_out;
215
216     es_format_Copy( &filter->fmt_in, fmt_in );
217     es_format_Copy( &filter->fmt_out, fmt_out );
218     filter->b_allow_fmt_out_change = chain->b_allow_fmt_out_change;
219     filter->p_cfg = cfg;
220
221     filter->owner = chain->callbacks;
222     filter->owner.sys = chain;
223
224     filter->p_module = module_need( filter, chain->psz_capability, name,
225                                     name != NULL );
226     if( filter->p_module == NULL )
227         goto error;
228
229     if( filter->b_allow_fmt_out_change )
230     {
231         es_format_Clean( &chain->fmt_out );
232         es_format_Copy( &chain->fmt_out, &filter->fmt_out );
233     }
234
235     if( chain->last == NULL )
236     {
237         assert( chain->first == NULL );
238         chain->first = chained;
239     }
240     else
241         chain->last->next = chained;
242     chained->prev = chain->last;
243     chain->last = chained;
244     chained->next = NULL;
245     chain->length++;
246
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;
252
253     msg_Dbg( parent, "Filter '%s' (%p) appended to chain",
254              (name != NULL) ? name : module_get_name(filter->p_module, false),
255              filter );
256     return filter;
257
258 error:
259     if( name != NULL )
260         msg_Err( parent, "Failed to create %s '%s'", chain->psz_capability,
261                  name );
262     else
263         msg_Err( parent, "Failed to create %s", chain->psz_capability );
264     es_format_Clean( &filter->fmt_out );
265     es_format_Clean( &filter->fmt_in );
266     vlc_object_release( filter );
267     return NULL;
268 }
269
270 void filter_chain_DeleteFilter( filter_chain_t *chain, filter_t *filter )
271 {
272     vlc_object_t *obj = chain->callbacks.sys;
273     chained_filter_t *chained = (chained_filter_t *)filter;
274
275     /* Remove it from the chain */
276     if( chained->prev != NULL )
277         chained->prev->next = chained->next;
278     else
279     {
280         assert( chained == chain->first );
281         chain->first = chained->next;
282     }
283
284     if( chained->next != NULL )
285         chained->next->prev = chained->prev;
286     else
287     {
288         assert( chained == chain->last );
289         chain->last = chained->prev;
290     }
291
292     assert( chain->length > 0 );
293     chain->length--;
294
295     module_unneed( filter, filter->p_module );
296
297     msg_Dbg( obj, "Filter %p removed from chain", filter );
298     FilterDeletePictures( &chained->filter, chained->pending );
299
300     free( chained->mouse );
301     vlc_object_release( filter );
302     /* FIXME: check fmt_in/fmt_out consitency */
303 }
304
305
306 int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
307 {
308     vlc_object_t *obj = chain->callbacks.sys;
309     char *buf = NULL;
310     int ret = 0;
311
312     while( str != NULL && str[0] != '\0' )
313     {
314         config_chain_t *cfg;
315         char *name;
316
317         char *next = config_ChainCreate( &name, &cfg, str );
318
319         str = next;
320         free( buf );
321         buf = next;
322
323         filter_t *filter = filter_chain_AppendFilter( chain, name, cfg,
324                                                       NULL, NULL );
325         if( filter == NULL )
326         {
327             msg_Err( obj, "Failed to append '%s' to chain", name );
328             free( name );
329             free( cfg );
330             goto error;
331         }
332
333         free( name );
334         ret++;
335     }
336
337     free( buf );
338     return ret;
339
340 error:
341     while( ret > 0 ) /* Unwind */
342     {
343         filter_chain_DeleteFilter( chain, &chain->last->filter );
344         ret--;
345     }
346     free( buf );
347     return -1;
348 }
349
350 int filter_chain_ForEach( filter_chain_t *chain,
351                           int (*cb)( filter_t *, void * ), void *opaque )
352 {
353     for( chained_filter_t *f = chain->first; f != NULL; f = f->next )
354     {
355         int ret = cb( &f->filter, opaque );
356         if( ret )
357             return ret;
358     }
359     return VLC_SUCCESS;
360 }
361
362 int filter_chain_GetLength( filter_chain_t *p_chain )
363 {
364     return p_chain->length;
365 }
366
367 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
368 {
369
370     if( p_chain->b_allow_fmt_out_change )
371         return &p_chain->fmt_out;
372
373     if( p_chain->last != NULL )
374         return &p_chain->last->filter.fmt_out;
375
376     /* Unless filter_chain_Reset has been called we are doomed */
377     return &p_chain->fmt_out;
378 }
379
380 static picture_t *FilterChainVideoFilter( chained_filter_t *f, picture_t *p_pic )
381 {
382     for( ; f != NULL; f = f->next )
383     {
384         filter_t *p_filter = &f->filter;
385         p_pic = p_filter->pf_video_filter( p_filter, p_pic );
386         if( !p_pic )
387             break;
388         if( f->pending )
389         {
390             msg_Warn( p_filter, "dropping pictures" );
391             FilterDeletePictures( p_filter, f->pending );
392         }
393         f->pending = p_pic->p_next;
394         p_pic->p_next = NULL;
395     }
396     return p_pic;
397 }
398
399 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
400 {
401     if( p_pic )
402     {
403         p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
404         if( p_pic )
405             return p_pic;
406     }
407     for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
408     {
409         p_pic = b->pending;
410         if( !p_pic )
411             continue;
412         b->pending = p_pic->p_next;
413         p_pic->p_next = NULL;
414
415         p_pic = FilterChainVideoFilter( b->next, p_pic );
416         if( p_pic )
417             return p_pic;
418     }
419     return NULL;
420 }
421
422 void filter_chain_VideoFlush( filter_chain_t *p_chain )
423 {
424     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
425     {
426         filter_t *p_filter = &f->filter;
427
428         FilterDeletePictures( p_filter, f->pending );
429         f->pending = NULL;
430
431         filter_FlushPictures( p_filter );
432     }
433 }
434
435
436 block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block )
437 {
438     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
439     {
440         filter_t *p_filter = &f->filter;
441
442         p_block = p_filter->pf_audio_filter( p_filter, p_block );
443         if( !p_block )
444             break;
445     }
446     return p_block;
447 }
448
449 void filter_chain_SubSource( filter_chain_t *p_chain, spu_t *spu,
450                              mtime_t display_date )
451 {
452     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
453     {
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 );
458     }
459 }
460
461 subpicture_t *filter_chain_SubFilter( filter_chain_t *p_chain, subpicture_t *p_subpic )
462 {
463     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
464     {
465         filter_t *p_filter = &f->filter;
466
467         p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
468
469         if( !p_subpic )
470             break;
471     }
472     return p_subpic;
473 }
474
475 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
476 {
477     vlc_mouse_t current = *p_src;
478
479     for( chained_filter_t *f = p_chain->last; f != NULL; f = f->prev )
480     {
481         filter_t *p_filter = &f->filter;
482         vlc_mouse_t *p_mouse = f->mouse;
483
484         if( p_filter->pf_video_mouse && p_mouse )
485         {
486             vlc_mouse_t old = *p_mouse;
487             vlc_mouse_t filtered;
488
489             *p_mouse = current;
490             if( p_filter->pf_video_mouse( p_filter, &filtered, &old, &current ) )
491                 return VLC_EGENERIC;
492             current = filtered;
493         }
494     }
495
496     *p_dst = current;
497     return VLC_SUCCESS;
498 }
499
500 int filter_chain_MouseEvent( filter_chain_t *p_chain,
501                              const vlc_mouse_t *p_mouse,
502                              const video_format_t *p_fmt )
503 {
504     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
505     {
506         filter_t *p_filter = &f->filter;
507
508         if( p_filter->pf_sub_mouse )
509         {
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;
514         }
515     }
516
517     return VLC_SUCCESS;
518 }
519
520 /* Helpers */
521 static void FilterDeletePictures( filter_t *filter, picture_t *picture )
522 {
523     while( picture )
524     {
525         picture_t *next = picture->p_next;
526         filter_DeletePicture( filter, picture );
527         picture = next;
528     }
529 }