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