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