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
7 * Author: Antoine Cellerier <dionoea at videolan dot org>
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.
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.
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 *****************************************************************************/
28 #include <vlc_filter.h>
29 #include <vlc_modules.h>
30 #include <vlc_mouse.h>
35 typedef struct chained_filter_t
37 /* Public part of the filter structure */
39 /* Private filter chain data (shhhh!) */
40 struct chained_filter_t
*prev
, *next
;
45 /* Only use this with filter objects from _this_ C module */
46 static inline chained_filter_t
*chained (filter_t
*filter
)
48 return (chained_filter_t
*)filter
;
54 filter_owner_t callbacks
; /**< Inner callbacks */
55 filter_owner_t owner
; /**< Owner (downstream) callbacks */
57 chained_filter_t
*first
, *last
; /**< List of filters */
59 es_format_t fmt_in
; /**< Chain input format (constant) */
60 es_format_t fmt_out
; /**< Chain current output format */
61 unsigned length
; /**< Number of filters */
62 bool b_allow_fmt_out_change
; /**< Can the output format be changed? */
63 const char *filter_cap
; /**< Filter modules capability */
64 const char *conv_cap
; /**< Converter modules capability */
70 static void FilterDeletePictures( picture_t
* );
72 static filter_chain_t
*filter_chain_NewInner( const filter_owner_t
*callbacks
,
73 const char *cap
, const char *conv_cap
, bool fmt_out_change
,
74 const filter_owner_t
*owner
)
76 assert( callbacks
!= NULL
&& callbacks
->sys
!= NULL
);
77 assert( cap
!= NULL
);
79 filter_chain_t
*chain
= malloc( sizeof (*chain
) );
80 if( unlikely(chain
== NULL
) )
83 chain
->callbacks
= *callbacks
;
85 chain
->owner
= *owner
;
88 es_format_Init( &chain
->fmt_in
, UNKNOWN_ES
, 0 );
89 es_format_Init( &chain
->fmt_out
, UNKNOWN_ES
, 0 );
91 chain
->b_allow_fmt_out_change
= fmt_out_change
;
92 chain
->filter_cap
= cap
;
93 chain
->conv_cap
= conv_cap
;
97 #undef filter_chain_New
99 * Filter chain initialisation
101 filter_chain_t
*filter_chain_New( vlc_object_t
*obj
, const char *cap
)
103 filter_owner_t callbacks
= {
107 return filter_chain_NewInner( &callbacks
, cap
, NULL
, false, NULL
);
110 /** Chained filter picture allocator function */
111 static picture_t
*filter_chain_VideoBufferNew( filter_t
*filter
)
113 if( chained(filter
)->next
!= NULL
)
115 picture_t
*pic
= picture_NewFromFormat( &filter
->fmt_out
.video
);
117 msg_Err( filter
, "Failed to allocate picture" );
122 filter_chain_t
*chain
= filter
->owner
.sys
;
125 filter
->owner
.sys
= chain
->owner
.sys
;
126 picture_t
*pic
= chain
->owner
.video
.buffer_new( filter
);
127 filter
->owner
.sys
= chain
;
132 #undef filter_chain_NewVideo
133 filter_chain_t
*filter_chain_NewVideo( vlc_object_t
*obj
, bool allow_change
,
134 const filter_owner_t
*restrict owner
)
136 filter_owner_t callbacks
= {
139 .buffer_new
= filter_chain_VideoBufferNew
,
143 return filter_chain_NewInner( &callbacks
, "video filter",
144 "video converter", allow_change
, owner
);
148 * Filter chain destruction
150 void filter_chain_Delete( filter_chain_t
*p_chain
)
152 while( p_chain
->first
!= NULL
)
153 filter_chain_DeleteFilter( p_chain
, &p_chain
->first
->filter
);
155 es_format_Clean( &p_chain
->fmt_in
);
156 es_format_Clean( &p_chain
->fmt_out
);
161 * Filter chain reinitialisation
163 void filter_chain_Reset( filter_chain_t
*p_chain
, const es_format_t
*p_fmt_in
,
164 const es_format_t
*p_fmt_out
)
166 while( p_chain
->first
!= NULL
)
167 filter_chain_DeleteFilter( p_chain
, &p_chain
->first
->filter
);
171 es_format_Clean( &p_chain
->fmt_in
);
172 es_format_Copy( &p_chain
->fmt_in
, p_fmt_in
);
176 es_format_Clean( &p_chain
->fmt_out
);
177 es_format_Copy( &p_chain
->fmt_out
, p_fmt_out
);
181 static filter_t
*filter_chain_AppendInner( filter_chain_t
*chain
,
182 const char *name
, const char *capability
, config_chain_t
*cfg
,
183 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
185 vlc_object_t
*parent
= chain
->callbacks
.sys
;
186 chained_filter_t
*chained
=
187 vlc_custom_create( parent
, sizeof(*chained
), "filter" );
188 if( unlikely(chained
== NULL
) )
191 filter_t
*filter
= &chained
->filter
;
195 if( chain
->last
!= NULL
)
196 fmt_in
= &chain
->last
->filter
.fmt_out
;
198 fmt_in
= &chain
->fmt_in
;
201 if( fmt_out
== NULL
)
202 fmt_out
= &chain
->fmt_out
;
204 es_format_Copy( &filter
->fmt_in
, fmt_in
);
205 es_format_Copy( &filter
->fmt_out
, fmt_out
);
206 filter
->b_allow_fmt_out_change
= chain
->b_allow_fmt_out_change
;
208 filter
->psz_name
= name
;
210 filter
->owner
= chain
->callbacks
;
211 filter
->owner
.sys
= chain
;
213 assert( capability
!= NULL
);
214 if( name
!= NULL
&& filter
->b_allow_fmt_out_change
)
216 /* Append the "chain" video filter to the current list.
217 * This filter will be used if the requested filter fails to load.
218 * It will then try to add a video converter before. */
219 char name_chained
[strlen(name
) + sizeof(",chain")];
220 sprintf( name_chained
, "%s,chain", name
);
221 filter
->p_module
= module_need( filter
, capability
, name_chained
, true );
224 filter
->p_module
= module_need( filter
, capability
, name
, name
!= NULL
);
226 if( filter
->p_module
== NULL
)
229 if( filter
->b_allow_fmt_out_change
)
231 es_format_Clean( &chain
->fmt_out
);
232 es_format_Copy( &chain
->fmt_out
, &filter
->fmt_out
);
235 if( chain
->last
== NULL
)
237 assert( chain
->first
== NULL
);
238 chain
->first
= chained
;
241 chain
->last
->next
= chained
;
242 chained
->prev
= chain
->last
;
243 chain
->last
= chained
;
244 chained
->next
= NULL
;
247 vlc_mouse_t
*mouse
= malloc( sizeof(*mouse
) );
248 if( likely(mouse
!= NULL
) )
249 vlc_mouse_Init( mouse
);
250 chained
->mouse
= mouse
;
251 chained
->pending
= NULL
;
253 msg_Dbg( parent
, "Filter '%s' (%p) appended to chain",
254 (name
!= NULL
) ? name
: module_get_name(filter
->p_module
, false),
260 msg_Err( parent
, "Failed to create %s '%s'", capability
, name
);
262 msg_Err( parent
, "Failed to create %s", capability
);
263 es_format_Clean( &filter
->fmt_out
);
264 es_format_Clean( &filter
->fmt_in
);
265 vlc_object_release( filter
);
269 filter_t
*filter_chain_AppendFilter( filter_chain_t
*chain
,
270 const char *name
, config_chain_t
*cfg
,
271 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
273 return filter_chain_AppendInner( chain
, name
, chain
->filter_cap
, cfg
,
277 int filter_chain_AppendConverter( filter_chain_t
*chain
,
278 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
280 return filter_chain_AppendInner( chain
, NULL
, chain
->conv_cap
, NULL
,
281 fmt_in
, fmt_out
) != NULL
? 0 : -1;
284 void filter_chain_DeleteFilter( filter_chain_t
*chain
, filter_t
*filter
)
286 vlc_object_t
*obj
= chain
->callbacks
.sys
;
287 chained_filter_t
*chained
= (chained_filter_t
*)filter
;
289 /* Remove it from the chain */
290 if( chained
->prev
!= NULL
)
291 chained
->prev
->next
= chained
->next
;
294 assert( chained
== chain
->first
);
295 chain
->first
= chained
->next
;
298 if( chained
->next
!= NULL
)
299 chained
->next
->prev
= chained
->prev
;
302 assert( chained
== chain
->last
);
303 chain
->last
= chained
->prev
;
306 assert( chain
->length
> 0 );
309 module_unneed( filter
, filter
->p_module
);
311 msg_Dbg( obj
, "Filter %p removed from chain", (void *)filter
);
312 FilterDeletePictures( chained
->pending
);
314 free( chained
->mouse
);
315 es_format_Clean( &filter
->fmt_out
);
316 es_format_Clean( &filter
->fmt_in
);
318 vlc_object_release( filter
);
319 /* FIXME: check fmt_in/fmt_out consitency */
323 int filter_chain_AppendFromString( filter_chain_t
*chain
, const char *str
)
325 vlc_object_t
*obj
= chain
->callbacks
.sys
;
329 while( str
!= NULL
&& str
[0] != '\0' )
334 char *next
= config_ChainCreate( &name
, &cfg
, str
);
340 filter_t
*filter
= filter_chain_AppendFilter( chain
, name
, cfg
,
343 config_ChainDestroy( cfg
);
347 msg_Err( obj
, "Failed to append '%s' to chain", name
);
360 while( ret
> 0 ) /* Unwind */
362 filter_chain_DeleteFilter( chain
, &chain
->last
->filter
);
369 int filter_chain_ForEach( filter_chain_t
*chain
,
370 int (*cb
)( filter_t
*, void * ), void *opaque
)
372 for( chained_filter_t
*f
= chain
->first
; f
!= NULL
; f
= f
->next
)
374 int ret
= cb( &f
->filter
, opaque
);
381 int filter_chain_GetLength( filter_chain_t
*p_chain
)
383 return p_chain
->length
;
386 const es_format_t
*filter_chain_GetFmtOut( filter_chain_t
*p_chain
)
389 if( p_chain
->b_allow_fmt_out_change
)
390 return &p_chain
->fmt_out
;
392 if( p_chain
->last
!= NULL
)
393 return &p_chain
->last
->filter
.fmt_out
;
395 /* Unless filter_chain_Reset has been called we are doomed */
396 return &p_chain
->fmt_out
;
399 static picture_t
*FilterChainVideoFilter( chained_filter_t
*f
, picture_t
*p_pic
)
401 for( ; f
!= NULL
; f
= f
->next
)
403 filter_t
*p_filter
= &f
->filter
;
404 p_pic
= p_filter
->pf_video_filter( p_filter
, p_pic
);
409 msg_Warn( p_filter
, "dropping pictures" );
410 FilterDeletePictures( f
->pending
);
412 f
->pending
= p_pic
->p_next
;
413 p_pic
->p_next
= NULL
;
418 picture_t
*filter_chain_VideoFilter( filter_chain_t
*p_chain
, picture_t
*p_pic
)
422 p_pic
= FilterChainVideoFilter( p_chain
->first
, p_pic
);
426 for( chained_filter_t
*b
= p_chain
->last
; b
!= NULL
; b
= b
->prev
)
431 b
->pending
= p_pic
->p_next
;
432 p_pic
->p_next
= NULL
;
434 p_pic
= FilterChainVideoFilter( b
->next
, p_pic
);
441 void filter_chain_VideoFlush( filter_chain_t
*p_chain
)
443 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
445 filter_t
*p_filter
= &f
->filter
;
447 FilterDeletePictures( f
->pending
);
450 filter_Flush( p_filter
);
454 void filter_chain_SubSource( filter_chain_t
*p_chain
, spu_t
*spu
,
455 mtime_t display_date
)
457 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
459 filter_t
*p_filter
= &f
->filter
;
460 subpicture_t
*p_subpic
= p_filter
->pf_sub_source( p_filter
, display_date
);
462 spu_PutSubpicture( spu
, p_subpic
);
466 subpicture_t
*filter_chain_SubFilter( filter_chain_t
*p_chain
, subpicture_t
*p_subpic
)
468 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
470 filter_t
*p_filter
= &f
->filter
;
472 p_subpic
= p_filter
->pf_sub_filter( p_filter
, p_subpic
);
480 int filter_chain_MouseFilter( filter_chain_t
*p_chain
, vlc_mouse_t
*p_dst
, const vlc_mouse_t
*p_src
)
482 vlc_mouse_t current
= *p_src
;
484 for( chained_filter_t
*f
= p_chain
->last
; f
!= NULL
; f
= f
->prev
)
486 filter_t
*p_filter
= &f
->filter
;
487 vlc_mouse_t
*p_mouse
= f
->mouse
;
489 if( p_filter
->pf_video_mouse
&& p_mouse
)
491 vlc_mouse_t old
= *p_mouse
;
492 vlc_mouse_t filtered
;
495 if( p_filter
->pf_video_mouse( p_filter
, &filtered
, &old
, ¤t
) )
505 int filter_chain_MouseEvent( filter_chain_t
*p_chain
,
506 const vlc_mouse_t
*p_mouse
,
507 const video_format_t
*p_fmt
)
509 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
511 filter_t
*p_filter
= &f
->filter
;
513 if( p_filter
->pf_sub_mouse
)
515 vlc_mouse_t old
= *f
->mouse
;
516 *f
->mouse
= *p_mouse
;
517 if( p_filter
->pf_sub_mouse( p_filter
, &old
, p_mouse
, p_fmt
) )
526 static void FilterDeletePictures( picture_t
*picture
)
530 picture_t
*next
= picture
->p_next
;
531 picture_Release( picture
);