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
;
209 filter
->owner
= chain
->callbacks
;
210 filter
->owner
.sys
= chain
;
212 assert( capability
!= NULL
);
214 filter
->p_module
= module_need( filter
, capability
, name
, name
!= NULL
);
215 if( filter
->p_module
== NULL
)
218 if( filter
->b_allow_fmt_out_change
)
220 es_format_Clean( &chain
->fmt_out
);
221 es_format_Copy( &chain
->fmt_out
, &filter
->fmt_out
);
224 if( chain
->last
== NULL
)
226 assert( chain
->first
== NULL
);
227 chain
->first
= chained
;
230 chain
->last
->next
= chained
;
231 chained
->prev
= chain
->last
;
232 chain
->last
= chained
;
233 chained
->next
= NULL
;
236 vlc_mouse_t
*mouse
= malloc( sizeof(*mouse
) );
237 if( likely(mouse
!= NULL
) )
238 vlc_mouse_Init( mouse
);
239 chained
->mouse
= mouse
;
240 chained
->pending
= NULL
;
242 msg_Dbg( parent
, "Filter '%s' (%p) appended to chain",
243 (name
!= NULL
) ? name
: module_get_name(filter
->p_module
, false),
249 msg_Err( parent
, "Failed to create %s '%s'", capability
, name
);
251 msg_Err( parent
, "Failed to create %s", capability
);
252 es_format_Clean( &filter
->fmt_out
);
253 es_format_Clean( &filter
->fmt_in
);
254 vlc_object_release( filter
);
258 filter_t
*filter_chain_AppendFilter( filter_chain_t
*chain
,
259 const char *name
, config_chain_t
*cfg
,
260 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
262 return filter_chain_AppendInner( chain
, name
, chain
->filter_cap
, cfg
,
266 int filter_chain_AppendConverter( filter_chain_t
*chain
,
267 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
269 return filter_chain_AppendInner( chain
, NULL
, chain
->conv_cap
, NULL
,
270 fmt_in
, fmt_out
) != NULL
? 0 : -1;
273 void filter_chain_DeleteFilter( filter_chain_t
*chain
, filter_t
*filter
)
275 vlc_object_t
*obj
= chain
->callbacks
.sys
;
276 chained_filter_t
*chained
= (chained_filter_t
*)filter
;
278 /* Remove it from the chain */
279 if( chained
->prev
!= NULL
)
280 chained
->prev
->next
= chained
->next
;
283 assert( chained
== chain
->first
);
284 chain
->first
= chained
->next
;
287 if( chained
->next
!= NULL
)
288 chained
->next
->prev
= chained
->prev
;
291 assert( chained
== chain
->last
);
292 chain
->last
= chained
->prev
;
295 assert( chain
->length
> 0 );
298 module_unneed( filter
, filter
->p_module
);
300 msg_Dbg( obj
, "Filter %p removed from chain", (void *)filter
);
301 FilterDeletePictures( chained
->pending
);
303 free( chained
->mouse
);
304 es_format_Clean( &filter
->fmt_out
);
305 es_format_Clean( &filter
->fmt_in
);
307 vlc_object_release( filter
);
308 /* FIXME: check fmt_in/fmt_out consitency */
312 int filter_chain_AppendFromString( filter_chain_t
*chain
, const char *str
)
314 vlc_object_t
*obj
= chain
->callbacks
.sys
;
318 while( str
!= NULL
&& str
[0] != '\0' )
323 char *next
= config_ChainCreate( &name
, &cfg
, str
);
329 filter_t
*filter
= filter_chain_AppendFilter( chain
, name
, cfg
,
332 config_ChainDestroy( cfg
);
336 msg_Err( obj
, "Failed to append '%s' to chain", name
);
349 while( ret
> 0 ) /* Unwind */
351 filter_chain_DeleteFilter( chain
, &chain
->last
->filter
);
358 int filter_chain_ForEach( filter_chain_t
*chain
,
359 int (*cb
)( filter_t
*, void * ), void *opaque
)
361 for( chained_filter_t
*f
= chain
->first
; f
!= NULL
; f
= f
->next
)
363 int ret
= cb( &f
->filter
, opaque
);
370 int filter_chain_GetLength( filter_chain_t
*p_chain
)
372 return p_chain
->length
;
375 const es_format_t
*filter_chain_GetFmtOut( filter_chain_t
*p_chain
)
378 if( p_chain
->b_allow_fmt_out_change
)
379 return &p_chain
->fmt_out
;
381 if( p_chain
->last
!= NULL
)
382 return &p_chain
->last
->filter
.fmt_out
;
384 /* Unless filter_chain_Reset has been called we are doomed */
385 return &p_chain
->fmt_out
;
388 static picture_t
*FilterChainVideoFilter( chained_filter_t
*f
, picture_t
*p_pic
)
390 for( ; f
!= NULL
; f
= f
->next
)
392 filter_t
*p_filter
= &f
->filter
;
393 p_pic
= p_filter
->pf_video_filter( p_filter
, p_pic
);
398 msg_Warn( p_filter
, "dropping pictures" );
399 FilterDeletePictures( f
->pending
);
401 f
->pending
= p_pic
->p_next
;
402 p_pic
->p_next
= NULL
;
407 picture_t
*filter_chain_VideoFilter( filter_chain_t
*p_chain
, picture_t
*p_pic
)
411 p_pic
= FilterChainVideoFilter( p_chain
->first
, p_pic
);
415 for( chained_filter_t
*b
= p_chain
->last
; b
!= NULL
; b
= b
->prev
)
420 b
->pending
= p_pic
->p_next
;
421 p_pic
->p_next
= NULL
;
423 p_pic
= FilterChainVideoFilter( b
->next
, p_pic
);
430 void filter_chain_VideoFlush( filter_chain_t
*p_chain
)
432 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
434 filter_t
*p_filter
= &f
->filter
;
436 FilterDeletePictures( f
->pending
);
439 filter_Flush( p_filter
);
443 void filter_chain_SubSource( filter_chain_t
*p_chain
, spu_t
*spu
,
444 mtime_t display_date
)
446 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
448 filter_t
*p_filter
= &f
->filter
;
449 subpicture_t
*p_subpic
= p_filter
->pf_sub_source( p_filter
, display_date
);
451 spu_PutSubpicture( spu
, p_subpic
);
455 subpicture_t
*filter_chain_SubFilter( filter_chain_t
*p_chain
, subpicture_t
*p_subpic
)
457 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
459 filter_t
*p_filter
= &f
->filter
;
461 p_subpic
= p_filter
->pf_sub_filter( p_filter
, p_subpic
);
469 int filter_chain_MouseFilter( filter_chain_t
*p_chain
, vlc_mouse_t
*p_dst
, const vlc_mouse_t
*p_src
)
471 vlc_mouse_t current
= *p_src
;
473 for( chained_filter_t
*f
= p_chain
->last
; f
!= NULL
; f
= f
->prev
)
475 filter_t
*p_filter
= &f
->filter
;
476 vlc_mouse_t
*p_mouse
= f
->mouse
;
478 if( p_filter
->pf_video_mouse
&& p_mouse
)
480 vlc_mouse_t old
= *p_mouse
;
481 vlc_mouse_t filtered
;
484 if( p_filter
->pf_video_mouse( p_filter
, &filtered
, &old
, ¤t
) )
494 int filter_chain_MouseEvent( filter_chain_t
*p_chain
,
495 const vlc_mouse_t
*p_mouse
,
496 const video_format_t
*p_fmt
)
498 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
500 filter_t
*p_filter
= &f
->filter
;
502 if( p_filter
->pf_sub_mouse
)
504 vlc_mouse_t old
= *f
->mouse
;
505 *f
->mouse
= *p_mouse
;
506 if( p_filter
->pf_sub_mouse( p_filter
, &old
, p_mouse
, p_fmt
) )
515 static void FilterDeletePictures( picture_t
*picture
)
519 picture_t
*next
= picture
->p_next
;
520 picture_Release( picture
);