]> git.sesse.net Git - vlc/blob - src/misc/filter_chain.c
Use vlc_object_set_name in some places
[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_arrays.h>
30 #include <vlc_osd.h>
31 #include <libvlc.h>
32
33 typedef struct
34 {
35     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. */
36     void (* pf_clean)( filter_t * ); /* Callback called on filter removal from chain to clean up buffer allocation callbacks data (ie p_owner) */
37     void *p_data; /* Data for pf_buffer_allocation_init */
38
39 } filter_chain_allocator_t;
40
41 static int  AllocatorInit( const filter_chain_allocator_t *, filter_t * );
42 static void AllocatorClean( const filter_chain_allocator_t *, filter_t * );
43
44 static bool IsInternalVideoAllocator( filter_t * );
45
46 static int  InternalVideoInit( filter_t *, void * );
47 static void InternalVideoClean( filter_t * );
48
49 static const filter_chain_allocator_t internal_video_allocator = {
50     .pf_init = InternalVideoInit,
51     .pf_clean = InternalVideoClean,
52     .p_data = NULL,
53 };
54
55 /* */
56 struct filter_chain_t
57 {
58     /* Parent object */
59     vlc_object_t *p_this;
60
61     /* List of filters */
62     vlc_array_t filters;
63     vlc_array_t mouses;
64
65     /* Capability of all the filters in the chain */
66     char *psz_capability;
67
68     /* Input format (read only) */
69     es_format_t fmt_in;
70
71     /* allow changing fmt_out if true */
72     bool b_allow_fmt_out_change;
73
74     /* Output format (writable depending on ... */
75     es_format_t fmt_out;
76
77     /* User provided allocator */
78     filter_chain_allocator_t allocator;
79 };
80
81 /**
82  * Local prototypes
83  */
84 static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *,
85                                                     const char *, config_chain_t *,
86                                                     const es_format_t *, const es_format_t * );
87
88 static int filter_chain_AppendFromStringInternal( filter_chain_t *, const char * );
89
90 static int filter_chain_DeleteFilterInternal( filter_chain_t *, filter_t * );
91
92 static int UpdateBufferFunctions( filter_chain_t * );
93
94 /**
95  * Filter chain initialisation
96  */
97 filter_chain_t *__filter_chain_New( vlc_object_t *p_this,
98                                     const char *psz_capability,
99                                     bool b_allow_fmt_out_change,
100                                     int  (*pf_buffer_allocation_init)( filter_t *, void * ),
101                                     void (*pf_buffer_allocation_clean)( filter_t * ),
102                                     void *p_buffer_allocation_data )
103 {
104     filter_chain_t *p_chain = malloc( sizeof( *p_chain ) );
105     if( !p_chain )
106         return NULL;
107
108     p_chain->p_this = p_this;
109     vlc_array_init( &p_chain->filters );
110     vlc_array_init( &p_chain->mouses );
111     p_chain->psz_capability = strdup( psz_capability );
112     if( !p_chain->psz_capability )
113     {
114         vlc_array_clear( &p_chain->mouses );
115         vlc_array_clear( &p_chain->filters );
116         free( p_chain );
117         return NULL;
118     }
119     es_format_Init( &p_chain->fmt_in, UNKNOWN_ES, 0 );
120     es_format_Init( &p_chain->fmt_out, UNKNOWN_ES, 0 );
121     p_chain->b_allow_fmt_out_change = b_allow_fmt_out_change;
122
123     p_chain->allocator.pf_init = pf_buffer_allocation_init;
124     p_chain->allocator.pf_clean = pf_buffer_allocation_clean;
125     p_chain->allocator.p_data = p_buffer_allocation_data;
126
127     return p_chain;
128 }
129
130 /**
131  * Filter chain destruction
132  */
133 void filter_chain_Delete( filter_chain_t *p_chain )
134 {
135     filter_chain_Reset( p_chain, NULL, NULL );
136
137     es_format_Clean( &p_chain->fmt_in );
138     es_format_Clean( &p_chain->fmt_out );
139
140     free( p_chain->psz_capability );
141     vlc_array_clear( &p_chain->mouses );
142     vlc_array_clear( &p_chain->filters );
143     free( p_chain );
144 }
145 /**
146  * Filter chain reinitialisation
147  */
148 void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
149                          const es_format_t *p_fmt_out )
150 {
151     while( vlc_array_count( &p_chain->filters ) > 0 )
152     {
153         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, 0 );
154
155         filter_chain_DeleteFilterInternal( p_chain, p_filter );
156     }
157     if( p_fmt_in )
158     {
159         es_format_Clean( &p_chain->fmt_in );
160         es_format_Copy( &p_chain->fmt_in, p_fmt_in );
161     }
162     if( p_fmt_out )
163     {
164         es_format_Clean( &p_chain->fmt_out );
165         es_format_Copy( &p_chain->fmt_out, p_fmt_out );
166     }
167 }
168
169 filter_t *filter_chain_AppendFilter( filter_chain_t *p_chain,
170                                      const char *psz_name,
171                                      config_chain_t *p_cfg,
172                                      const es_format_t *p_fmt_in,
173                                      const es_format_t *p_fmt_out )
174 {
175     filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
176                                                             p_cfg, p_fmt_in,
177                                                             p_fmt_out );
178     if( UpdateBufferFunctions( p_chain ) < 0 )
179         msg_Err( p_filter, "Woah! This doesn't look good." );
180     return p_filter;
181 }
182
183 int filter_chain_AppendFromString( filter_chain_t *p_chain,
184                                    const char *psz_string )
185 {
186     const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_string );
187     if( i_ret < 0 )
188         return i_ret;
189
190     /* FIXME That one seems bad if a error is returned */
191     return UpdateBufferFunctions( p_chain );
192 }
193
194 int filter_chain_DeleteFilter( filter_chain_t *p_chain, filter_t *p_filter )
195 {
196     const int i_ret = filter_chain_DeleteFilterInternal( p_chain, p_filter );
197     if( i_ret < 0 )
198         return i_ret;
199
200     /* FIXME That one seems bad if a error is returned */
201     return UpdateBufferFunctions( p_chain );
202 }
203
204 int filter_chain_GetLength( filter_chain_t *p_chain )
205 {
206     return vlc_array_count( &p_chain->filters );
207 }
208
209 const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain )
210 {
211
212     if( p_chain->b_allow_fmt_out_change )
213         return &p_chain->fmt_out;
214
215     const int i_count = vlc_array_count( &p_chain->filters );
216     if( i_count > 0 )
217     {
218         filter_t *p_last = vlc_array_item_at_index( &p_chain->filters, i_count - 1 );
219         return &p_last->fmt_out;
220     }
221
222     /* Unless filter_chain_Reset has been called we are doomed */
223     return &p_chain->fmt_out;
224 }
225
226 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
227 {
228     for( int i = 0; i < vlc_array_count( &p_chain->filters ); i++ )
229     {
230         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
231
232         p_pic = p_filter->pf_video_filter( p_filter, p_pic );
233         if( !p_pic )
234             break;
235     }
236     return p_pic;
237 }
238
239 block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block )
240 {
241     for( int i = 0; i < vlc_array_count( &p_chain->filters ); i++ )
242     {
243         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
244
245         p_block = p_filter->pf_audio_filter( p_filter, p_block );
246         if( !p_block )
247             break;
248     }
249     return p_block;
250 }
251
252 void filter_chain_SubFilter( filter_chain_t *p_chain,
253                              mtime_t display_date )
254 {
255     for( int i = 0; i < vlc_array_count( &p_chain->filters ); i++ )
256     {
257         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
258
259         subpicture_t *p_subpic = p_filter->pf_sub_filter( p_filter, display_date );
260         /* XXX I find that spu_t cast ugly */
261         if( p_subpic )
262             spu_DisplaySubpicture( (spu_t*)p_chain->p_this, p_subpic );
263     }
264 }
265
266 int filter_chain_MouseFilter( filter_chain_t *p_chain, vlc_mouse_t *p_dst, const vlc_mouse_t *p_src )
267 {
268     vlc_mouse_t current = *p_src;
269
270     for( int i = vlc_array_count( &p_chain->filters ) - 1; i >= 0; i-- )
271     {
272         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
273         vlc_mouse_t *p_mouse = vlc_array_item_at_index( &p_chain->mouses, i );
274
275         if( p_filter->pf_mouse && p_mouse )
276         {
277             vlc_mouse_t old = *p_mouse;
278             vlc_mouse_t filtered;
279
280             *p_mouse = current;
281             if( p_filter->pf_mouse( p_filter, &filtered, &old, &current ) )
282                 return VLC_EGENERIC;
283             current = filtered;
284         }
285     }
286
287     *p_dst = current;
288     return VLC_SUCCESS;
289 }
290
291
292 /* Helpers */
293 static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *p_chain,
294                                                     const char *psz_name,
295                                                     config_chain_t *p_cfg,
296                                                     const es_format_t *p_fmt_in,
297                                                     const es_format_t *p_fmt_out )
298 {
299     filter_t *p_filter = vlc_custom_create( p_chain->p_this, sizeof(*p_filter),
300                                             VLC_OBJECT_GENERIC, "filter" );
301     if( !p_filter )
302         return NULL;
303     vlc_object_set_name( p_filter, psz_name );
304     vlc_object_attach( p_filter, p_chain->p_this );
305
306     if( !p_fmt_in )
307     {
308         p_fmt_in = &p_chain->fmt_in;
309
310         const int i_count = vlc_array_count( &p_chain->filters );
311         if( i_count > 0 )
312         {
313             filter_t *p_last = vlc_array_item_at_index( &p_chain->filters, i_count - 1 );
314             p_fmt_in = &p_last->fmt_out;
315         }
316     }
317
318     if( !p_fmt_out )
319     {
320         p_fmt_out = &p_chain->fmt_out;
321     }
322
323     es_format_Copy( &p_filter->fmt_in, p_fmt_in );
324     es_format_Copy( &p_filter->fmt_out, p_fmt_out );
325     p_filter->p_cfg = p_cfg;
326     p_filter->b_allow_fmt_out_change = p_chain->b_allow_fmt_out_change;
327
328     p_filter->p_module = module_need( p_filter, p_chain->psz_capability,
329                                       psz_name, psz_name != NULL );
330
331     if( !p_filter->p_module )
332         goto error;
333
334     if( p_filter->b_allow_fmt_out_change )
335     {
336         es_format_Clean( &p_chain->fmt_out );
337         es_format_Copy( &p_chain->fmt_out, &p_filter->fmt_out );
338     }
339
340     if( AllocatorInit( &p_chain->allocator, p_filter ) )
341         goto error;
342
343     vlc_array_append( &p_chain->filters, p_filter );
344
345     vlc_mouse_t *p_mouse = malloc( sizeof(*p_mouse) );
346     if( p_mouse )
347         vlc_mouse_Init( p_mouse );
348     vlc_array_append( &p_chain->mouses, p_mouse );
349
350     msg_Dbg( p_chain->p_this, "Filter '%s' (%p) appended to chain",
351              psz_name ? psz_name : module_get_name(p_filter->p_module, false),
352              p_filter );
353
354     return p_filter;
355
356 error:
357     if( psz_name )
358         msg_Err( p_chain->p_this, "Failed to create %s '%s'",
359                  p_chain->psz_capability, psz_name );
360     else
361         msg_Err( p_chain->p_this, "Failed to create %s",
362                  p_chain->psz_capability );
363     if( p_filter->p_module )
364         module_unneed( p_filter, p_filter->p_module );
365     es_format_Clean( &p_filter->fmt_in );
366     es_format_Clean( &p_filter->fmt_out );
367     vlc_object_detach( p_filter );
368     vlc_object_release( p_filter );
369     return NULL;
370 }
371
372
373 static int filter_chain_AppendFromStringInternal( filter_chain_t *p_chain,
374                                                   const char *psz_string )
375 {
376     config_chain_t *p_cfg = NULL;
377     char *psz_name = NULL;
378     char* psz_new_string;
379
380     if( !psz_string || !*psz_string )
381         return 0;
382
383     psz_new_string = config_ChainCreate( &psz_name, &p_cfg, psz_string );
384
385     filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
386                                                             p_cfg, NULL, NULL );
387     if( !p_filter )
388     {
389         msg_Err( p_chain->p_this, "Failed while trying to append '%s' "
390                  "to filter chain", psz_name );
391         free( psz_name );
392         free( p_cfg );
393         free( psz_new_string );
394         return -1;
395     }
396     free( psz_name );
397
398     const int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_new_string );
399     free( psz_new_string );
400     if( i_ret < 0 )
401     {
402         filter_chain_DeleteFilterInternal( p_chain, p_filter );
403         return i_ret;
404     }
405     return 1 + i_ret;
406 }
407
408 static int filter_chain_DeleteFilterInternal( filter_chain_t *p_chain,
409                                               filter_t *p_filter )
410 {
411     const int i_filter_idx = vlc_array_index_of_item( &p_chain->filters, p_filter );
412     if( i_filter_idx < 0 )
413     {
414         /* Oops, filter wasn't found
415          * FIXME shoulnd't it be an assert instead ? */
416         msg_Err( p_chain->p_this,
417                  "Couldn't find filter %p when trying to remove it from chain",
418                  p_filter );
419         return VLC_EGENERIC;
420     }
421
422     /* Remove it from the chain */
423     vlc_array_remove( &p_chain->filters, i_filter_idx );
424
425     msg_Dbg( p_chain->p_this, "Filter %p removed from chain", p_filter );
426
427     /* Destroy the filter object */
428     if( IsInternalVideoAllocator( p_filter ) )
429         AllocatorClean( &internal_video_allocator, p_filter );
430     else
431         AllocatorClean( &p_chain->allocator, p_filter );
432
433     vlc_object_detach( p_filter );
434     if( p_filter->p_module )
435         module_unneed( p_filter, p_filter->p_module );
436     vlc_object_release( p_filter );
437
438     vlc_mouse_t *p_mouse = vlc_array_item_at_index( &p_chain->mouses, i_filter_idx );
439     free( p_mouse );
440     vlc_array_remove( &p_chain->mouses, i_filter_idx );
441
442     /* FIXME: check fmt_in/fmt_out consitency */
443     return VLC_SUCCESS;
444 }
445
446 /**
447  * Internal chain buffer handling
448  */
449
450 static int UpdateVideoBufferFunctions( filter_chain_t *p_chain )
451 {
452     /**
453      * Last filter uses the filter chain's parent buffer allocation
454      * functions. All the other filters use internal functions.
455      * This makes it possible to have format changes between each
456      * filter without having to worry about the parent's picture
457      * heap format.
458      */
459     const int i_count = vlc_array_count( &p_chain->filters );
460     for( int i = 0; i < i_count - 1; i++ )
461     {
462         filter_t *p_filter = vlc_array_item_at_index( &p_chain->filters, i );
463
464         if( !IsInternalVideoAllocator( p_filter ) )
465         {
466             AllocatorClean( &p_chain->allocator, p_filter );
467
468             AllocatorInit( &internal_video_allocator, p_filter );
469         }
470     }
471     if( i_count >= 1 )
472     {
473         filter_t * p_filter = vlc_array_item_at_index( &p_chain->filters, i_count - 1 );
474         if( IsInternalVideoAllocator( p_filter ) )
475         {
476             AllocatorClean( &internal_video_allocator, p_filter );
477
478             if( AllocatorInit( &p_chain->allocator, p_filter ) )
479                 return VLC_EGENERIC;
480         }
481     }
482     return VLC_SUCCESS;
483 }
484 /**
485  * This function should be called after every filter chain change
486  */
487 static int UpdateBufferFunctions( filter_chain_t *p_chain )
488 {
489     if( !strcmp( p_chain->psz_capability, "video filter2" ) )
490         return UpdateVideoBufferFunctions( p_chain );
491
492     return VLC_SUCCESS;
493 }
494
495 /* Internal video allocator functions */
496 static picture_t *VideoBufferNew( filter_t *p_filter )
497 {
498     const video_format_t *p_fmt = &p_filter->fmt_out.video;
499
500     picture_t *p_picture = picture_New( p_fmt->i_chroma,
501                                         p_fmt->i_width, p_fmt->i_height,
502                                         p_fmt->i_aspect );
503     if( !p_picture )
504         msg_Err( p_filter, "Failed to allocate picture" );
505     return p_picture;
506 }
507 static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
508 {
509     VLC_UNUSED( p_filter );
510     picture_Release( p_picture );
511 }
512 static int InternalVideoInit( filter_t *p_filter, void *p_data )
513 {
514     VLC_UNUSED(p_data);
515
516     p_filter->pf_vout_buffer_new = VideoBufferNew;
517     p_filter->pf_vout_buffer_del = VideoBufferDelete;
518
519     return VLC_SUCCESS;
520 }
521 static void InternalVideoClean( filter_t *p_filter )
522 {
523     p_filter->pf_vout_buffer_new = NULL;
524     p_filter->pf_vout_buffer_del = NULL;
525 }
526 static bool IsInternalVideoAllocator( filter_t *p_filter )
527 {
528     return p_filter->pf_vout_buffer_new == VideoBufferNew;
529 }
530
531 /* */
532 static int AllocatorInit( const filter_chain_allocator_t *p_alloc, filter_t *p_filter )
533 {
534     if( p_alloc->pf_init )
535         return p_alloc->pf_init( p_filter, p_alloc->p_data );
536     return VLC_SUCCESS;
537 }
538 static void AllocatorClean( const filter_chain_allocator_t *p_alloc, filter_t *p_filter )
539 {
540     if( p_alloc->pf_clean )
541         p_alloc->pf_clean( p_filter );
542 }
543