]> git.sesse.net Git - vlc/blob - src/misc/filter_chain.c
LGPL
[vlc] / 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  * $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 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_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_SubSource( 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_source( p_filter, display_date );
303         /* XXX I find that spu_t cast ugly */
304         if( p_subpic )
305             spu_PutSubpicture( (spu_t*)p_chain->p_this, p_subpic );
306     }
307 }
308
309 subpicture_t *filter_chain_SubFilter( filter_chain_t *p_chain, subpicture_t *p_subpic )
310 {
311     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
312     {
313         filter_t *p_filter = &f->filter;
314
315         p_subpic = p_filter->pf_sub_filter( p_filter, p_subpic );
316
317         if( !p_subpic )
318             break;
319     }
320     return p_subpic;
321 }
322
323 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
324 {
325     vlc_mouse_t current = *p_src;
326
327     for( chained_filter_t *f = p_chain->last; f != NULL; f = f->prev )
328     {
329         filter_t *p_filter = &f->filter;
330         vlc_mouse_t *p_mouse = f->mouse;
331
332         if( p_filter->pf_video_mouse && p_mouse )
333         {
334             vlc_mouse_t old = *p_mouse;
335             vlc_mouse_t filtered;
336
337             *p_mouse = current;
338             if( p_filter->pf_video_mouse( p_filter, &filtered, &old, &current ) )
339                 return VLC_EGENERIC;
340             current = filtered;
341         }
342     }
343
344     *p_dst = current;
345     return VLC_SUCCESS;
346 }
347
348 int filter_chain_MouseEvent( filter_chain_t *p_chain,
349                              const vlc_mouse_t *p_mouse,
350                              const video_format_t *p_fmt )
351 {
352     for( chained_filter_t *f = p_chain->first; f != NULL; f = f->next )
353     {
354         filter_t *p_filter = &f->filter;
355
356         if( p_filter->pf_sub_mouse )
357         {
358             vlc_mouse_t old = *f->mouse;
359             *f->mouse = *p_mouse;
360             if( p_filter->pf_sub_mouse( p_filter, &old, p_mouse, p_fmt ) )
361                 return VLC_EGENERIC;
362         }
363     }
364
365     return VLC_SUCCESS;
366 }
367
368 /* Helpers */
369 static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *p_chain,
370                                                     const char *psz_name,
371                                                     config_chain_t *p_cfg,
372                                                     const es_format_t *p_fmt_in,
373                                                     const es_format_t *p_fmt_out )
374 {
375     chained_filter_t *p_chained =
376         vlc_custom_create( p_chain->p_this, sizeof(*p_chained), "filter" );
377     filter_t *p_filter = &p_chained->filter;
378     if( !p_filter )
379         return NULL;
380
381     if( !p_fmt_in )
382     {
383         if( p_chain->last != NULL )
384             p_fmt_in = &p_chain->last->filter.fmt_out;
385         else
386             p_fmt_in = &p_chain->fmt_in;
387     }
388
389     if( !p_fmt_out )
390     {
391         p_fmt_out = &p_chain->fmt_out;
392     }
393
394     es_format_Copy( &p_filter->fmt_in, p_fmt_in );
395     es_format_Copy( &p_filter->fmt_out, p_fmt_out );
396     p_filter->p_cfg = p_cfg;
397     p_filter->b_allow_fmt_out_change = p_chain->b_allow_fmt_out_change;
398
399     p_filter->p_module = module_need( p_filter, p_chain->psz_capability,
400                                       psz_name, psz_name != NULL );
401
402     if( !p_filter->p_module )
403         goto error;
404
405     if( p_filter->b_allow_fmt_out_change )
406     {
407         es_format_Clean( &p_chain->fmt_out );
408         es_format_Copy( &p_chain->fmt_out, &p_filter->fmt_out );
409     }
410
411     if( AllocatorInit( &p_chain->allocator, p_chained ) )
412         goto error;
413
414     if( p_chain->last == NULL )
415     {
416         assert( p_chain->first == NULL );
417         p_chain->first = p_chained;
418     }
419     else
420         p_chain->last->next = p_chained;
421     p_chained->prev = p_chain->last;
422     p_chain->last = p_chained;
423     p_chained->next = NULL;
424     p_chain->length++;
425
426     vlc_mouse_t *p_mouse = malloc( sizeof(*p_mouse) );
427     if( p_mouse )
428         vlc_mouse_Init( p_mouse );
429     p_chained->mouse = p_mouse;
430     p_chained->pending = NULL;
431
432     msg_Dbg( p_chain->p_this, "Filter '%s' (%p) appended to chain",
433              psz_name ? psz_name : module_get_name(p_filter->p_module, false),
434              p_filter );
435
436     return p_filter;
437
438 error:
439     if( psz_name )
440         msg_Err( p_chain->p_this, "Failed to create %s '%s'",
441                  p_chain->psz_capability, psz_name );
442     else
443         msg_Err( p_chain->p_this, "Failed to create %s",
444                  p_chain->psz_capability );
445     if( p_filter->p_module )
446         module_unneed( p_filter, p_filter->p_module );
447     es_format_Clean( &p_filter->fmt_in );
448     es_format_Clean( &p_filter->fmt_out );
449     vlc_object_release( p_filter );
450     return NULL;
451 }
452
453
454 static int filter_chain_AppendFromStringInternal( filter_chain_t *p_chain,
455                                                   const char *psz_string )
456 {
457     config_chain_t *p_cfg = NULL;
458     char *psz_name = NULL;
459     char* psz_new_string;
460
461     if( !psz_string || !*psz_string )
462         return 0;
463
464     psz_new_string = config_ChainCreate( &psz_name, &p_cfg, psz_string );
465
466     filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
467                                                             p_cfg, NULL, NULL );
468     if( !p_filter )
469     {
470         msg_Err( p_chain->p_this, "Failed while trying to append '%s' "
471                  "to filter chain", psz_name );
472         free( psz_name );
473         free( p_cfg );
474         free( psz_new_string );
475         return -1;
476     }
477     free( psz_name );
478
479     const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_new_string );
480     free( psz_new_string );
481     if( i_ret < 0 )
482     {
483         filter_chain_DeleteFilterInternal( p_chain, p_filter );
484         return i_ret;
485     }
486     return 1 + i_ret;
487 }
488
489 static int filter_chain_DeleteFilterInternal( filter_chain_t *p_chain,
490                                               filter_t *p_filter )
491 {
492     chained_filter_t *p_chained = chained( p_filter );
493
494     /* Remove it from the chain */
495     if( p_chained->prev != NULL )
496         p_chained->prev->next = p_chained->next;
497     else
498     {
499         assert( p_chained == p_chain->first );
500         p_chain->first = p_chained->next;
501     }
502
503     if( p_chained->next != NULL )
504         p_chained->next->prev = p_chained->prev;
505     else
506     {
507         assert( p_chained == p_chain->last );
508         p_chain->last = p_chained->prev;
509     }
510     p_chain->length--;
511
512     msg_Dbg( p_chain->p_this, "Filter %p removed from chain", p_filter );
513
514     FilterDeletePictures( &p_chained->filter, p_chained->pending );
515
516     /* Destroy the filter object */
517     if( IsInternalVideoAllocator( p_chained ) )
518         AllocatorClean( &internal_video_allocator, p_chained );
519     else
520         AllocatorClean( &p_chain->allocator, p_chained );
521
522     if( p_filter->p_module )
523         module_unneed( p_filter, p_filter->p_module );
524     free( p_chained->mouse );
525     vlc_object_release( p_filter );
526
527
528     /* FIXME: check fmt_in/fmt_out consitency */
529     return VLC_SUCCESS;
530 }
531
532 static void FilterDeletePictures( filter_t *filter, picture_t *picture )
533 {
534     while( picture )
535     {
536         picture_t *next = picture->p_next;
537         filter_DeletePicture( filter, picture );
538         picture = next;
539     }
540 }
541
542 /**
543  * Internal chain buffer handling
544  */
545
546 static int UpdateVideoBufferFunctions( filter_chain_t *p_chain )
547 {
548     /**
549      * Last filter uses the filter chain's parent buffer allocation
550      * functions. All the other filters use internal functions.
551      * This makes it possible to have format changes between each
552      * filter without having to worry about the parent's picture
553      * heap format.
554      */
555     /* FIXME: we should only update the last and penultimate filters */
556     chained_filter_t *f;
557
558     for( f = p_chain->first; f != p_chain->last; f = f->next )
559     {
560         if( !IsInternalVideoAllocator( f ) )
561         {
562             AllocatorClean( &p_chain->allocator, f );
563
564             AllocatorInit( &internal_video_allocator, f );
565         }
566     }
567
568     if( f != NULL )
569     {
570         if( IsInternalVideoAllocator( f ) )
571         {
572             AllocatorClean( &internal_video_allocator, f );
573
574             if( AllocatorInit( &p_chain->allocator, f ) )
575                 return VLC_EGENERIC;
576         }
577     }
578     return VLC_SUCCESS;
579 }
580
581 /**
582  * This function should be called after every filter chain change
583  */
584 static int UpdateBufferFunctions( filter_chain_t *p_chain )
585 {
586     if( !strcmp( p_chain->psz_capability, "video filter2" ) )
587         return UpdateVideoBufferFunctions( p_chain );
588
589     return VLC_SUCCESS;
590 }
591
592 /* Internal video allocator functions */
593 static picture_t *VideoBufferNew( filter_t *p_filter )
594 {
595     const video_format_t *p_fmt = &p_filter->fmt_out.video;
596
597     picture_t *p_picture = picture_NewFromFormat( p_fmt );
598     if( !p_picture )
599         msg_Err( p_filter, "Failed to allocate picture" );
600     return p_picture;
601 }
602 static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
603 {
604     VLC_UNUSED( p_filter );
605     picture_Release( p_picture );
606 }
607 static int InternalVideoInit( filter_t *p_filter, void *p_data )
608 {
609     VLC_UNUSED(p_data);
610
611     p_filter->pf_video_buffer_new = VideoBufferNew;
612     p_filter->pf_video_buffer_del = VideoBufferDelete;
613
614     return VLC_SUCCESS;
615 }
616 static void InternalVideoClean( filter_t *p_filter )
617 {
618     p_filter->pf_video_buffer_new = NULL;
619     p_filter->pf_video_buffer_del = NULL;
620 }
621
622 static bool IsInternalVideoAllocator( chained_filter_t *p_filter )
623 {
624     return p_filter->filter.pf_video_buffer_new == VideoBufferNew;
625 }
626
627 /* */
628 static int AllocatorInit( const filter_chain_allocator_t *p_alloc,
629                           chained_filter_t *p_filter )
630 {
631     if( p_alloc->pf_init )
632         return p_alloc->pf_init( &p_filter->filter, p_alloc->p_data );
633     return VLC_SUCCESS;
634 }
635
636 static void AllocatorClean( const filter_chain_allocator_t *p_alloc,
637                             chained_filter_t *p_filter )
638 {
639     if( p_alloc->pf_clean )
640         p_alloc->pf_clean( &p_filter->filter );
641 }
642