1 /*****************************************************************************
2 * filter_chain.c : Handle chains of filter_t objects.
3 *****************************************************************************
4 * Copyright (C) 2008 the VideoLAN team
7 * Author: Antoine Cellerier <dionoea at videolan dot org>
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.
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.
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 *****************************************************************************/
28 #include <vlc_filter.h>
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 */
39 } filter_chain_allocator_t
;
41 typedef struct chained_filter_t
43 /* Public part of the filter structure */
45 /* Private filter chain data (shhhh!) */
46 struct chained_filter_t
*prev
, *next
;
50 /* Only use this with filter objects from _this_ C module */
51 static inline chained_filter_t
*chained (filter_t
*filter
)
53 return (chained_filter_t
*)filter
;
56 static int AllocatorInit( const filter_chain_allocator_t
*,
58 static void AllocatorClean( const filter_chain_allocator_t
*,
61 static bool IsInternalVideoAllocator( chained_filter_t
* );
63 static int InternalVideoInit( filter_t
*, void * );
64 static void InternalVideoClean( filter_t
* );
66 static const filter_chain_allocator_t internal_video_allocator
= {
67 .pf_init
= InternalVideoInit
,
68 .pf_clean
= InternalVideoClean
,
75 vlc_object_t
*p_this
; /**< Owner object */
76 filter_chain_allocator_t allocator
; /**< Owner allocation callbacks */
78 chained_filter_t
*first
, *last
; /**< List of filters */
80 es_format_t fmt_in
; /**< Chain input format (constant) */
81 es_format_t fmt_out
; /**< Chain current output format */
82 unsigned length
; /**< Number of filters */
83 bool b_allow_fmt_out_change
; /**< Can the output format be changed? */
84 char psz_capability
[1]; /**< Module capability for all chained filters */
90 static filter_t
*filter_chain_AppendFilterInternal( filter_chain_t
*,
91 const char *, config_chain_t
*,
92 const es_format_t
*, const es_format_t
* );
94 static int filter_chain_AppendFromStringInternal( filter_chain_t
*, const char * );
96 static int filter_chain_DeleteFilterInternal( filter_chain_t
*, filter_t
* );
98 static int UpdateBufferFunctions( filter_chain_t
* );
100 #undef filter_chain_New
102 * Filter chain initialisation
104 filter_chain_t
*filter_chain_New( vlc_object_t
*p_this
,
105 const char *psz_capability
,
106 bool b_allow_fmt_out_change
,
107 int (*pf_buffer_allocation_init
)( filter_t
*, void * ),
108 void (*pf_buffer_allocation_clean
)( filter_t
* ),
109 void *p_buffer_allocation_data
)
112 assert( psz_capability
);
114 size_t size
= sizeof(filter_chain_t
) + strlen(psz_capability
);
115 filter_chain_t
*p_chain
= malloc( size
);
119 p_chain
->p_this
= p_this
;
120 p_chain
->last
= p_chain
->first
= NULL
;
122 strcpy( p_chain
->psz_capability
, psz_capability
);
124 es_format_Init( &p_chain
->fmt_in
, UNKNOWN_ES
, 0 );
125 es_format_Init( &p_chain
->fmt_out
, UNKNOWN_ES
, 0 );
126 p_chain
->b_allow_fmt_out_change
= b_allow_fmt_out_change
;
128 p_chain
->allocator
.pf_init
= pf_buffer_allocation_init
;
129 p_chain
->allocator
.pf_clean
= pf_buffer_allocation_clean
;
130 p_chain
->allocator
.p_data
= p_buffer_allocation_data
;
136 * Filter chain destruction
138 void filter_chain_Delete( filter_chain_t
*p_chain
)
140 filter_chain_Reset( p_chain
, NULL
, NULL
);
142 es_format_Clean( &p_chain
->fmt_in
);
143 es_format_Clean( &p_chain
->fmt_out
);
148 * Filter chain reinitialisation
150 void filter_chain_Reset( filter_chain_t
*p_chain
, const es_format_t
*p_fmt_in
,
151 const es_format_t
*p_fmt_out
)
155 while( (p_filter
= &p_chain
->first
->filter
) != NULL
)
156 filter_chain_DeleteFilterInternal( p_chain
, p_filter
);
160 es_format_Clean( &p_chain
->fmt_in
);
161 es_format_Copy( &p_chain
->fmt_in
, p_fmt_in
);
165 es_format_Clean( &p_chain
->fmt_out
);
166 es_format_Copy( &p_chain
->fmt_out
, p_fmt_out
);
170 filter_t
*filter_chain_AppendFilter( filter_chain_t
*p_chain
,
171 const char *psz_name
,
172 config_chain_t
*p_cfg
,
173 const es_format_t
*p_fmt_in
,
174 const es_format_t
*p_fmt_out
)
176 filter_t
*p_filter
= filter_chain_AppendFilterInternal( p_chain
, psz_name
,
179 if( UpdateBufferFunctions( p_chain
) < 0 )
180 msg_Err( p_filter
, "Woah! This doesn't look good." );
184 int filter_chain_AppendFromString( filter_chain_t
*p_chain
,
185 const char *psz_string
)
187 const int i_ret
= filter_chain_AppendFromStringInternal( p_chain
, psz_string
);
191 /* FIXME That one seems bad if a error is returned */
192 return UpdateBufferFunctions( p_chain
);
195 int filter_chain_DeleteFilter( filter_chain_t
*p_chain
, filter_t
*p_filter
)
197 const int i_ret
= filter_chain_DeleteFilterInternal( p_chain
, p_filter
);
201 /* FIXME That one seems bad if a error is returned */
202 return UpdateBufferFunctions( p_chain
);
205 int filter_chain_GetLength( filter_chain_t
*p_chain
)
207 return p_chain
->length
;
210 const es_format_t
*filter_chain_GetFmtOut( filter_chain_t
*p_chain
)
213 if( p_chain
->b_allow_fmt_out_change
)
214 return &p_chain
->fmt_out
;
216 if( p_chain
->last
!= NULL
)
217 return &p_chain
->last
->filter
.fmt_out
;
219 /* Unless filter_chain_Reset has been called we are doomed */
220 return &p_chain
->fmt_out
;
223 picture_t
*filter_chain_VideoFilter( filter_chain_t
*p_chain
, picture_t
*p_pic
)
225 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
227 filter_t
*p_filter
= &f
->filter
;
229 p_pic
= p_filter
->pf_video_filter( p_filter
, p_pic
);
236 block_t
*filter_chain_AudioFilter( filter_chain_t
*p_chain
, block_t
*p_block
)
238 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
240 filter_t
*p_filter
= &f
->filter
;
242 p_block
= p_filter
->pf_audio_filter( p_filter
, p_block
);
249 void filter_chain_SubFilter( filter_chain_t
*p_chain
,
250 mtime_t display_date
)
252 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
254 filter_t
*p_filter
= &f
->filter
;
255 subpicture_t
*p_subpic
= p_filter
->pf_sub_filter( p_filter
, display_date
);
256 /* XXX I find that spu_t cast ugly */
258 spu_DisplaySubpicture( (spu_t
*)p_chain
->p_this
, p_subpic
);
262 int filter_chain_MouseFilter( filter_chain_t
*p_chain
, vlc_mouse_t
*p_dst
, const vlc_mouse_t
*p_src
)
264 vlc_mouse_t current
= *p_src
;
266 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
268 filter_t
*p_filter
= &f
->filter
;
269 vlc_mouse_t
*p_mouse
= f
->mouse
;
271 if( p_filter
->pf_video_mouse
&& p_mouse
)
273 vlc_mouse_t old
= *p_mouse
;
274 vlc_mouse_t filtered
;
277 if( p_filter
->pf_video_mouse( p_filter
, &filtered
, &old
, ¤t
) )
287 int filter_chain_MouseEvent( filter_chain_t
*p_chain
,
288 const vlc_mouse_t
*p_mouse
,
289 const video_format_t
*p_fmt
)
291 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
293 filter_t
*p_filter
= &f
->filter
;
295 if( p_filter
->pf_sub_mouse
)
297 vlc_mouse_t old
= *f
->mouse
;
298 *f
->mouse
= *p_mouse
;
299 if( p_filter
->pf_sub_mouse( p_filter
, &old
, p_mouse
, p_fmt
) )
308 static filter_t
*filter_chain_AppendFilterInternal( filter_chain_t
*p_chain
,
309 const char *psz_name
,
310 config_chain_t
*p_cfg
,
311 const es_format_t
*p_fmt_in
,
312 const es_format_t
*p_fmt_out
)
314 chained_filter_t
*p_chained
=
315 vlc_custom_create( p_chain
->p_this
, sizeof(*p_chained
),
316 VLC_OBJECT_GENERIC
, "filter" );
317 filter_t
*p_filter
= &p_chained
->filter
;
320 vlc_object_attach( p_filter
, p_chain
->p_this
);
324 if( p_chain
->last
!= NULL
)
325 p_fmt_in
= &p_chain
->last
->filter
.fmt_out
;
327 p_fmt_in
= &p_chain
->fmt_in
;
332 p_fmt_out
= &p_chain
->fmt_out
;
335 es_format_Copy( &p_filter
->fmt_in
, p_fmt_in
);
336 es_format_Copy( &p_filter
->fmt_out
, p_fmt_out
);
337 p_filter
->p_cfg
= p_cfg
;
338 p_filter
->b_allow_fmt_out_change
= p_chain
->b_allow_fmt_out_change
;
340 p_filter
->p_module
= module_need( p_filter
, p_chain
->psz_capability
,
341 psz_name
, psz_name
!= NULL
);
343 if( !p_filter
->p_module
)
346 if( p_filter
->b_allow_fmt_out_change
)
348 es_format_Clean( &p_chain
->fmt_out
);
349 es_format_Copy( &p_chain
->fmt_out
, &p_filter
->fmt_out
);
352 if( AllocatorInit( &p_chain
->allocator
, p_chained
) )
355 if( p_chain
->last
== NULL
)
357 assert( p_chain
->first
== NULL
);
358 p_chain
->first
= p_chained
;
361 p_chain
->last
->next
= p_chained
;
362 p_chained
->prev
= p_chain
->last
;
363 p_chain
->last
= p_chained
;
364 p_chained
->next
= NULL
;
367 vlc_mouse_t
*p_mouse
= malloc( sizeof(*p_mouse
) );
369 vlc_mouse_Init( p_mouse
);
370 p_chained
->mouse
= p_mouse
;
372 msg_Dbg( p_chain
->p_this
, "Filter '%s' (%p) appended to chain",
373 psz_name
? psz_name
: module_get_name(p_filter
->p_module
, false),
380 msg_Err( p_chain
->p_this
, "Failed to create %s '%s'",
381 p_chain
->psz_capability
, psz_name
);
383 msg_Err( p_chain
->p_this
, "Failed to create %s",
384 p_chain
->psz_capability
);
385 if( p_filter
->p_module
)
386 module_unneed( p_filter
, p_filter
->p_module
);
387 es_format_Clean( &p_filter
->fmt_in
);
388 es_format_Clean( &p_filter
->fmt_out
);
389 vlc_object_release( p_filter
);
394 static int filter_chain_AppendFromStringInternal( filter_chain_t
*p_chain
,
395 const char *psz_string
)
397 config_chain_t
*p_cfg
= NULL
;
398 char *psz_name
= NULL
;
399 char* psz_new_string
;
401 if( !psz_string
|| !*psz_string
)
404 psz_new_string
= config_ChainCreate( &psz_name
, &p_cfg
, psz_string
);
406 filter_t
*p_filter
= filter_chain_AppendFilterInternal( p_chain
, psz_name
,
410 msg_Err( p_chain
->p_this
, "Failed while trying to append '%s' "
411 "to filter chain", psz_name
);
414 free( psz_new_string
);
419 const int i_ret
= filter_chain_AppendFromStringInternal( p_chain
, psz_new_string
);
420 free( psz_new_string
);
423 filter_chain_DeleteFilterInternal( p_chain
, p_filter
);
429 static int filter_chain_DeleteFilterInternal( filter_chain_t
*p_chain
,
432 chained_filter_t
*p_chained
= chained( p_filter
);
434 /* Remove it from the chain */
435 if( p_chained
->prev
!= NULL
)
436 p_chained
->prev
->next
= p_chained
->next
;
439 assert( p_chained
== p_chain
->first
);
440 p_chain
->first
= p_chained
->next
;
443 if( p_chained
->next
!= NULL
)
444 p_chained
->next
->prev
= p_chained
->prev
;
447 assert( p_chained
== p_chain
->last
);
448 p_chain
->last
= p_chained
->prev
;
452 msg_Dbg( p_chain
->p_this
, "Filter %p removed from chain", p_filter
);
454 /* Destroy the filter object */
455 if( IsInternalVideoAllocator( p_chained
) )
456 AllocatorClean( &internal_video_allocator
, p_chained
);
458 AllocatorClean( &p_chain
->allocator
, p_chained
);
460 if( p_filter
->p_module
)
461 module_unneed( p_filter
, p_filter
->p_module
);
462 free( p_chained
->mouse
);
463 vlc_object_release( p_filter
);
466 /* FIXME: check fmt_in/fmt_out consitency */
471 * Internal chain buffer handling
474 static int UpdateVideoBufferFunctions( filter_chain_t
*p_chain
)
477 * Last filter uses the filter chain's parent buffer allocation
478 * functions. All the other filters use internal functions.
479 * This makes it possible to have format changes between each
480 * filter without having to worry about the parent's picture
483 /* FIXME: we should only update the last and penultimate filters */
486 for( f
= p_chain
->first
; f
!= p_chain
->last
; f
= f
->next
)
488 if( !IsInternalVideoAllocator( f
) )
490 AllocatorClean( &p_chain
->allocator
, f
);
492 AllocatorInit( &internal_video_allocator
, f
);
498 if( IsInternalVideoAllocator( f
) )
500 AllocatorClean( &internal_video_allocator
, f
);
502 if( AllocatorInit( &p_chain
->allocator
, f
) )
510 * This function should be called after every filter chain change
512 static int UpdateBufferFunctions( filter_chain_t
*p_chain
)
514 if( !strcmp( p_chain
->psz_capability
, "video filter2" ) )
515 return UpdateVideoBufferFunctions( p_chain
);
520 /* Internal video allocator functions */
521 static picture_t
*VideoBufferNew( filter_t
*p_filter
)
523 const video_format_t
*p_fmt
= &p_filter
->fmt_out
.video
;
525 picture_t
*p_picture
= picture_NewFromFormat( p_fmt
);
527 msg_Err( p_filter
, "Failed to allocate picture" );
530 static void VideoBufferDelete( filter_t
*p_filter
, picture_t
*p_picture
)
532 VLC_UNUSED( p_filter
);
533 picture_Release( p_picture
);
535 static int InternalVideoInit( filter_t
*p_filter
, void *p_data
)
539 p_filter
->pf_video_buffer_new
= VideoBufferNew
;
540 p_filter
->pf_video_buffer_del
= VideoBufferDelete
;
544 static void InternalVideoClean( filter_t
*p_filter
)
546 p_filter
->pf_video_buffer_new
= NULL
;
547 p_filter
->pf_video_buffer_del
= NULL
;
550 static bool IsInternalVideoAllocator( chained_filter_t
*p_filter
)
552 return p_filter
->filter
.pf_video_buffer_new
== VideoBufferNew
;
556 static int AllocatorInit( const filter_chain_allocator_t
*p_alloc
,
557 chained_filter_t
*p_filter
)
559 if( p_alloc
->pf_init
)
560 return p_alloc
->pf_init( &p_filter
->filter
, p_alloc
->p_data
);
564 static void AllocatorClean( const filter_chain_allocator_t
*p_alloc
,
565 chained_filter_t
*p_filter
)
567 if( p_alloc
->pf_clean
)
568 p_alloc
->pf_clean( &p_filter
->filter
);