]> git.sesse.net Git - vlc/blob - src/misc/filter_chain.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / src / misc / filter_chain.c
1 /*****************************************************************************
2  * filter_chain.c : Handle chains of filter_t objects.
3  *****************************************************************************
4  * Copyright (C) 2008 the VideoLAN team
5  * $Id$
6  *
7  * Author: Antoine Cellerier <dionoea at videolan dot org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, 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_osd.h>
30 #include <vlc_modules.h>
31 #include <libvlc.h>
32 #include <assert.h>
33
34 typedef struct
35 {
36     int (*pf_init)( filter_t *, void *p_data ); /* Callback called once filter allocation has succeeded to initialize the filter's buffer allocation callbacks. This function is responsible for setting p_owner if needed. */
37     void (* pf_clean)( filter_t * ); /* Callback called on filter removal from chain to clean up buffer allocation callbacks data (ie p_owner) */
38     void *p_data; /* Data for pf_buffer_allocation_init */
39
40 } filter_chain_allocator_t;
41
42 typedef struct chained_filter_t
43 {
44     /* Public part of the filter structure */
45     filter_t filter;
46     /* Private filter chain data (shhhh!) */
47     struct chained_filter_t *prev, *next;
48     vlc_mouse_t *mouse;
49     picture_t *pending;
50 } chained_filter_t;
51
52 /* Only use this with filter objects from _this_ C module */
53 static inline chained_filter_t *chained (filter_t *filter)
54 {
55     return (chained_filter_t *)filter;
56 }
57
58 static int  AllocatorInit( const filter_chain_allocator_t *,
59                            chained_filter_t * );
60 static void AllocatorClean( const filter_chain_allocator_t *,
61                             chained_filter_t * );
62
63 static bool IsInternalVideoAllocator( chained_filter_t * );
64
65 static int  InternalVideoInit( filter_t *, void * );
66 static void InternalVideoClean( filter_t * );
67
68 static const filter_chain_allocator_t internal_video_allocator = {
69     .pf_init = InternalVideoInit,
70     .pf_clean = InternalVideoClean,
71     .p_data = NULL,
72 };
73
74 /* */
75 struct filter_chain_t
76 {
77     vlc_object_t *p_this; /**< Owner object */
78     filter_chain_allocator_t allocator; /**< Owner allocation callbacks */
79
80     chained_filter_t *first, *last; /**< List of filters */
81
82     es_format_t fmt_in; /**< Chain input format (constant) */
83     es_format_t fmt_out; /**< Chain current output format */
84     unsigned length; /**< Number of filters */
85     bool b_allow_fmt_out_change; /**< Can the output format be changed? */
86     char psz_capability[1]; /**< Module capability for all chained filters */
87 };
88
89 /**
90  * Local prototypes
91  */
92 static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *,
93                                                     const char *, config_chain_t *,
94                                                     const es_format_t *, const es_format_t * );
95
96 static int filter_chain_AppendFromStringInternal( filter_chain_t *, const char * );
97
98 static int filter_chain_DeleteFilterInternal( filter_chain_t *, filter_t * );
99
100 static int UpdateBufferFunctions( filter_chain_t * );
101
102 static void FilterDeletePictures( filter_t *, picture_t * );
103
104 #undef filter_chain_New
105 /**
106  * Filter chain initialisation
107  */
108 filter_chain_t *filter_chain_New( vlc_object_t *p_this,
109                                   const char *psz_capability,
110                                   bool b_allow_fmt_out_change,
111                                   int  (*pf_buffer_allocation_init)( filter_t *, void * ),
112                                   void (*pf_buffer_allocation_clean)( filter_t * ),
113                                   void *p_buffer_allocation_data )
114 {
115     assert( p_this );
116     assert( psz_capability );
117
118     size_t size = sizeof(filter_chain_t) + strlen(psz_capability);
119     filter_chain_t *p_chain = malloc( size );
120     if( !p_chain )
121         return NULL;
122
123     p_chain->p_this = p_this;
124     p_chain->last = p_chain->first = NULL;
125     p_chain->length = 0;
126     strcpy( p_chain->psz_capability, psz_capability );
127
128     es_format_Init( &p_chain->fmt_in, UNKNOWN_ES, 0 );
129     es_format_Init( &p_chain->fmt_out, UNKNOWN_ES, 0 );
130     p_chain->b_allow_fmt_out_change = b_allow_fmt_out_change;
131
132     p_chain->allocator.pf_init = pf_buffer_allocation_init;
133     p_chain->allocator.pf_clean = pf_buffer_allocation_clean;
134     p_chain->allocator.p_data = p_buffer_allocation_data;
135
136     return p_chain;
137 }
138
139 /**
140  * Filter chain destruction
141  */
142 void filter_chain_Delete( filter_chain_t *p_chain )
143 {
144     filter_chain_Reset( p_chain, NULL, NULL );
145
146     es_format_Clean( &p_chain->fmt_in );
147     es_format_Clean( &p_chain->fmt_out );
148
149     free( p_chain );
150 }
151 /**
152  * Filter chain reinitialisation
153  */
154 void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
155                          const es_format_t *p_fmt_out )
156 {
157     filter_t *p_filter;
158
159     while( (p_filter = &p_chain->first->filter) != NULL )
160         filter_chain_DeleteFilterInternal( p_chain, p_filter );
161
162     if( p_fmt_in )
163     {
164         es_format_Clean( &p_chain->fmt_in );
165         es_format_Copy( &p_chain->fmt_in, p_fmt_in );
166     }
167     if( p_fmt_out )
168     {
169         es_format_Clean( &p_chain->fmt_out );
170         es_format_Copy( &p_chain->fmt_out, p_fmt_out );
171     }
172 }
173
174 filter_t *filter_chain_AppendFilter( filter_chain_t *p_chain,
175                                      const char *psz_name,
176                                      config_chain_t *p_cfg,
177                                      const es_format_t *p_fmt_in,
178                                      const es_format_t *p_fmt_out )
179 {
180     filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
181                                                             p_cfg, p_fmt_in,
182                                                             p_fmt_out );
183     if( UpdateBufferFunctions( p_chain ) < 0 )
184         msg_Err( p_filter, "Woah! This doesn't look good." );
185     return p_filter;
186 }
187
188 int filter_chain_AppendFromString( filter_chain_t *p_chain,
189                                    const char *psz_string )
190 {
191     const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_string );
192     if( i_ret < 0 )
193         return i_ret;
194
195     /* FIXME That one seems bad if a error is returned */
196     return UpdateBufferFunctions( p_chain );
197 }
198
199 int filter_chain_DeleteFilter( filter_chain_t *p_chain, filter_t *p_filter )
200 {
201     const int i_ret = filter_chain_DeleteFilterInternal( p_chain, p_filter );
202     if( i_ret < 0 )
203         return i_ret;
204
205     /* FIXME That one seems bad if a error is returned */
206     return UpdateBufferFunctions( p_chain );
207 }
208
209 int filter_chain_GetLength( filter_chain_t *p_chain )
210 {
211     return p_chain->length;
212 }
213
214 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
215 {
216
217     if( p_chain->b_allow_fmt_out_change )
218         return &p_chain->fmt_out;
219
220     if( p_chain->last != NULL )
221         return &p_chain->last->filter.fmt_out;
222
223     /* Unless filter_chain_Reset has been called we are doomed */
224     return &p_chain->fmt_out;
225 }
226
227 static picture_t *FilterChainVideoFilter( chained_filter_t *f, picture_t *p_pic )
228 {
229     for( ; f != NULL; f = f->next )
230     {
231         filter_t *p_filter = &f->filter;
232         p_pic = p_filter->pf_video_filter( p_filter, p_pic );
233         if( !p_pic )
234             break;
235         if( f->pending )
236         {
237             msg_Warn( p_filter, "dropping pictures" );
238             FilterDeletePictures( p_filter, f->pending );
239         }
240         f->pending = p_pic->p_next;
241         p_pic->p_next = NULL;
242     }
243     return p_pic;
244 }
245
246 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
247 {
248     if( p_pic )
249     {
250         p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
251         if( p_pic )
252             return p_pic;
253     }
254     for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
255     {
256         p_pic = b->pending;
257         if( !p_pic )
258             continue;
259         b->pending = p_pic->p_next;
260         p_pic->p_next = NULL;
261
262         p_pic = FilterChainVideoFilter( b->next, p_pic );
263         if( p_pic )
264             return p_pic;
265     }
266     return NULL;
267 }
268
269 void filter_chain_VideoFlush( filter_chain_t *p_chain )
270 {
271     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
272     {
273         filter_t *p_filter = &f->filter;
274
275         FilterDeletePictures( p_filter, f->pending );
276         f->pending = NULL;
277
278         filter_FlushPictures( p_filter );
279     }
280 }
281
282
283 block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block )
284 {
285     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
286     {
287         filter_t *p_filter = &f->filter;
288
289         p_block = p_filter->pf_audio_filter( p_filter, p_block );
290         if( !p_block )
291             break;
292     }
293     return p_block;
294 }
295
296 void filter_chain_SubFilter( filter_chain_t *p_chain,
297                              mtime_t display_date )
298 {
299     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
300     {
301         filter_t *p_filter = &f->filter;
302         subpicture_t *p_subpic = p_filter->pf_sub_filter( p_filter, display_date );
303         /* XXX I find that spu_t cast ugly */
304         if( p_subpic )
305             spu_DisplaySubpicture( (spu_t*)p_chain->p_this, p_subpic );
306     }
307 }
308
309 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
310 {
311     vlc_mouse_t current = *p_src;
312
313     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
314     {
315         filter_t *p_filter = &f->filter;
316         vlc_mouse_t *p_mouse = f->mouse;
317
318         if( p_filter->pf_video_mouse && p_mouse )
319         {
320             vlc_mouse_t old = *p_mouse;
321             vlc_mouse_t filtered;
322
323             *p_mouse = current;
324             if( p_filter->pf_video_mouse( p_filter, &filtered, &old, &current ) )
325                 return VLC_EGENERIC;
326             current = filtered;
327         }
328     }
329
330     *p_dst = current;
331     return VLC_SUCCESS;
332 }
333
334 int filter_chain_MouseEvent( filter_chain_t *p_chain,
335                              const vlc_mouse_t *p_mouse,
336                              const video_format_t *p_fmt )
337 {
338     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
339     {
340         filter_t *p_filter = &f->filter;
341
342         if( p_filter->pf_sub_mouse )
343         {
344             vlc_mouse_t old = *f->mouse;
345             *f->mouse = *p_mouse;
346             if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
347                 return VLC_EGENERIC;
348         }
349     }
350
351     return VLC_SUCCESS;
352 }
353
354 /* Helpers */
355 static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *p_chain,
356                                                     const char *psz_name,
357                                                     config_chain_t *p_cfg,
358                                                     const es_format_t *p_fmt_in,
359                                                     const es_format_t *p_fmt_out )
360 {
361     chained_filter_t *p_chained =
362         vlc_custom_create( p_chain->p_this, sizeof(*p_chained),
363                            VLC_OBJECT_GENERIC, "filter" );
364     filter_t *p_filter = &p_chained->filter;
365     if( !p_filter )
366         return NULL;
367     vlc_object_attach( p_filter, p_chain->p_this );
368
369     if( !p_fmt_in )
370     {
371         if( p_chain->last != NULL )
372             p_fmt_in = &p_chain->last->filter.fmt_out;
373         else
374             p_fmt_in = &p_chain->fmt_in;
375     }
376
377     if( !p_fmt_out )
378     {
379         p_fmt_out = &p_chain->fmt_out;
380     }
381
382     es_format_Copy( &p_filter->fmt_in, p_fmt_in );
383     es_format_Copy( &p_filter->fmt_out, p_fmt_out );
384     p_filter->p_cfg = p_cfg;
385     p_filter->b_allow_fmt_out_change = p_chain->b_allow_fmt_out_change;
386
387     p_filter->p_module = module_need( p_filter, p_chain->psz_capability,
388                                       psz_name, psz_name != NULL );
389
390     if( !p_filter->p_module )
391         goto error;
392
393     if( p_filter->b_allow_fmt_out_change )
394     {
395         es_format_Clean( &p_chain->fmt_out );
396         es_format_Copy( &p_chain->fmt_out, &p_filter->fmt_out );
397     }
398
399     if( AllocatorInit( &p_chain->allocator, p_chained ) )
400         goto error;
401
402     if( p_chain->last == NULL )
403     {
404         assert( p_chain->first == NULL );
405         p_chain->first = p_chained;
406     }
407     else
408         p_chain->last->next = p_chained;
409     p_chained->prev = p_chain->last;
410     p_chain->last = p_chained;
411     p_chained->next = NULL;
412     p_chain->length++;
413
414     vlc_mouse_t *p_mouse = malloc( sizeof(*p_mouse) );
415     if( p_mouse )
416         vlc_mouse_Init( p_mouse );
417     p_chained->mouse = p_mouse;
418     p_chained->pending = NULL;
419
420     msg_Dbg( p_chain->p_this, "Filter '%s' (%p) appended to chain",
421              psz_name ? psz_name : module_get_name(p_filter->p_module, false),
422              p_filter );
423
424     return p_filter;
425
426 error:
427     if( psz_name )
428         msg_Err( p_chain->p_this, "Failed to create %s '%s'",
429                  p_chain->psz_capability, psz_name );
430     else
431         msg_Err( p_chain->p_this, "Failed to create %s",
432                  p_chain->psz_capability );
433     if( p_filter->p_module )
434         module_unneed( p_filter, p_filter->p_module );
435     es_format_Clean( &p_filter->fmt_in );
436     es_format_Clean( &p_filter->fmt_out );
437     vlc_object_release( p_filter );
438     return NULL;
439 }
440
441
442 static int filter_chain_AppendFromStringInternal( filter_chain_t *p_chain,
443                                                   const char *psz_string )
444 {
445     config_chain_t *p_cfg = NULL;
446     char *psz_name = NULL;
447     char* psz_new_string;
448
449     if( !psz_string || !*psz_string )
450         return 0;
451
452     psz_new_string = config_ChainCreate( &psz_name, &p_cfg, psz_string );
453
454     filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
455                                                             p_cfg, NULL, NULL );
456     if( !p_filter )
457     {
458         msg_Err( p_chain->p_this, "Failed while trying to append '%s' "
459                  "to filter chain", psz_name );
460         free( psz_name );
461         free( p_cfg );
462         free( psz_new_string );
463         return -1;
464     }
465     free( psz_name );
466
467     const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_new_string );
468     free( psz_new_string );
469     if( i_ret < 0 )
470     {
471         filter_chain_DeleteFilterInternal( p_chain, p_filter );
472         return i_ret;
473     }
474     return 1 + i_ret;
475 }
476
477 static int filter_chain_DeleteFilterInternal( filter_chain_t *p_chain,
478                                               filter_t *p_filter )
479 {
480     chained_filter_t *p_chained = chained( p_filter );
481
482     /* Remove it from the chain */
483     if( p_chained->prev != NULL )
484         p_chained->prev->next = p_chained->next;
485     else
486     {
487         assert( p_chained == p_chain->first );
488         p_chain->first = p_chained->next;
489     }
490
491     if( p_chained->next != NULL )
492         p_chained->next->prev = p_chained->prev;
493     else
494     {
495         assert( p_chained == p_chain->last );
496         p_chain->last = p_chained->prev;
497     }
498     p_chain->length--;
499
500     msg_Dbg( p_chain->p_this, "Filter %p removed from chain", p_filter );
501
502     FilterDeletePictures( &p_chained->filter, p_chained->pending );
503
504     /* Destroy the filter object */
505     if( IsInternalVideoAllocator( p_chained ) )
506         AllocatorClean( &internal_video_allocator, p_chained );
507     else
508         AllocatorClean( &p_chain->allocator, p_chained );
509
510     if( p_filter->p_module )
511         module_unneed( p_filter, p_filter->p_module );
512     free( p_chained->mouse );
513     vlc_object_release( p_filter );
514
515
516     /* FIXME: check fmt_in/fmt_out consitency */
517     return VLC_SUCCESS;
518 }
519
520 static void FilterDeletePictures( filter_t *filter, picture_t *picture )
521 {
522     while( picture )
523     {
524         picture_t *next = picture->p_next;
525         filter_DeletePicture( filter, picture );
526         picture = next;
527     }
528 }
529
530 /**
531  * Internal chain buffer handling
532  */
533
534 static int UpdateVideoBufferFunctions( filter_chain_t *p_chain )
535 {
536     /**
537      * Last filter uses the filter chain's parent buffer allocation
538      * functions. All the other filters use internal functions.
539      * This makes it possible to have format changes between each
540      * filter without having to worry about the parent's picture
541      * heap format.
542      */
543     /* FIXME: we should only update the last and penultimate filters */
544     chained_filter_t *f;
545
546     for( f = p_chain->first; f != p_chain->last; f = f->next )
547     {
548         if( !IsInternalVideoAllocator( f ) )
549         {
550             AllocatorClean( &p_chain->allocator, f );
551
552             AllocatorInit( &internal_video_allocator, f );
553         }
554     }
555
556     if( f != NULL )
557     {
558         if( IsInternalVideoAllocator( f ) )
559         {
560             AllocatorClean( &internal_video_allocator, f );
561
562             if( AllocatorInit( &p_chain->allocator, f ) )
563                 return VLC_EGENERIC;
564         }
565     }
566     return VLC_SUCCESS;
567 }
568
569 /**
570  * This function should be called after every filter chain change
571  */
572 static int UpdateBufferFunctions( filter_chain_t *p_chain )
573 {
574     if( !strcmp( p_chain->psz_capability, "video filter2" ) )
575         return UpdateVideoBufferFunctions( p_chain );
576
577     return VLC_SUCCESS;
578 }
579
580 /* Internal video allocator functions */
581 static picture_t *VideoBufferNew( filter_t *p_filter )
582 {
583     const video_format_t *p_fmt = &p_filter->fmt_out.video;
584
585     picture_t *p_picture = picture_NewFromFormat( p_fmt );
586     if( !p_picture )
587         msg_Err( p_filter, "Failed to allocate picture" );
588     return p_picture;
589 }
590 static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
591 {
592     VLC_UNUSED( p_filter );
593     picture_Release( p_picture );
594 }
595 static int InternalVideoInit( filter_t *p_filter, void *p_data )
596 {
597     VLC_UNUSED(p_data);
598
599     p_filter->pf_video_buffer_new = VideoBufferNew;
600     p_filter->pf_video_buffer_del = VideoBufferDelete;
601
602     return VLC_SUCCESS;
603 }
604 static void InternalVideoClean( filter_t *p_filter )
605 {
606     p_filter->pf_video_buffer_new = NULL;
607     p_filter->pf_video_buffer_del = NULL;
608 }
609
610 static bool IsInternalVideoAllocator( chained_filter_t *p_filter )
611 {
612     return p_filter->filter.pf_video_buffer_new == VideoBufferNew;
613 }
614
615 /* */
616 static int AllocatorInit( const filter_chain_allocator_t *p_alloc,
617                           chained_filter_t *p_filter )
618 {
619     if( p_alloc->pf_init )
620         return p_alloc->pf_init( &p_filter->filter, p_alloc->p_data );
621     return VLC_SUCCESS;
622 }
623
624 static void AllocatorClean( const filter_chain_allocator_t *p_alloc,
625                             chained_filter_t *p_filter )
626 {
627     if( p_alloc->pf_clean )
628         p_alloc->pf_clean( &p_filter->filter );
629 }
630