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