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 container_of(filter
, 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 bool b_allow_fmt_out_change
; /**< Can the output format be changed? */
62 const char *filter_cap
; /**< Filter modules capability */
63 const char *conv_cap
; /**< Converter modules capability */
69 static void FilterDeletePictures( picture_t
* );
71 static filter_chain_t
*filter_chain_NewInner( const filter_owner_t
*callbacks
,
72 const char *cap
, const char *conv_cap
, bool fmt_out_change
,
73 const filter_owner_t
*owner
, enum es_format_category_e cat
)
75 assert( callbacks
!= NULL
&& callbacks
->sys
!= NULL
);
76 assert( cap
!= NULL
);
78 filter_chain_t
*chain
= malloc( sizeof (*chain
) );
79 if( unlikely(chain
== NULL
) )
82 chain
->callbacks
= *callbacks
;
84 chain
->owner
= *owner
;
87 es_format_Init( &chain
->fmt_in
, cat
, 0 );
88 es_format_Init( &chain
->fmt_out
, cat
, 0 );
89 chain
->b_allow_fmt_out_change
= fmt_out_change
;
90 chain
->filter_cap
= cap
;
91 chain
->conv_cap
= conv_cap
;
95 #undef filter_chain_New
97 * Filter chain initialisation
99 filter_chain_t
*filter_chain_New( vlc_object_t
*obj
, const char *cap
,
100 enum es_format_category_e cat
)
102 filter_owner_t callbacks
= {
106 return filter_chain_NewInner( &callbacks
, cap
, NULL
, false, NULL
, cat
);
109 /** Chained filter picture allocator function */
110 static picture_t
*filter_chain_VideoBufferNew( filter_t
*filter
)
112 if( chained(filter
)->next
!= NULL
)
114 picture_t
*pic
= picture_NewFromFormat( &filter
->fmt_out
.video
);
116 msg_Err( filter
, "Failed to allocate picture" );
121 filter_chain_t
*chain
= filter
->owner
.sys
;
124 filter
->owner
.sys
= chain
->owner
.sys
;
125 picture_t
*pic
= chain
->owner
.video
.buffer_new( filter
);
126 filter
->owner
.sys
= chain
;
131 #undef filter_chain_NewVideo
132 filter_chain_t
*filter_chain_NewVideo( vlc_object_t
*obj
, bool allow_change
,
133 const filter_owner_t
*restrict owner
)
135 filter_owner_t callbacks
= {
138 .buffer_new
= filter_chain_VideoBufferNew
,
142 return filter_chain_NewInner( &callbacks
, "video filter",
143 "video converter", allow_change
, owner
, VIDEO_ES
);
147 * Filter chain destruction
149 void filter_chain_Delete( filter_chain_t
*p_chain
)
151 while( p_chain
->first
!= NULL
)
152 filter_chain_DeleteFilter( p_chain
, &p_chain
->first
->filter
);
154 es_format_Clean( &p_chain
->fmt_in
);
155 es_format_Clean( &p_chain
->fmt_out
);
160 * Filter chain reinitialisation
162 void filter_chain_Reset( filter_chain_t
*p_chain
, const es_format_t
*p_fmt_in
,
163 const es_format_t
*p_fmt_out
)
165 while( p_chain
->first
!= NULL
)
166 filter_chain_DeleteFilter( p_chain
, &p_chain
->first
->filter
);
170 es_format_Clean( &p_chain
->fmt_in
);
171 es_format_Copy( &p_chain
->fmt_in
, p_fmt_in
);
175 es_format_Clean( &p_chain
->fmt_out
);
176 es_format_Copy( &p_chain
->fmt_out
, p_fmt_out
);
180 static filter_t
*filter_chain_AppendInner( filter_chain_t
*chain
,
181 const char *name
, const char *capability
, config_chain_t
*cfg
,
182 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
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
) )
190 filter_t
*filter
= &chained
->filter
;
194 if( chain
->last
!= NULL
)
195 fmt_in
= &chain
->last
->filter
.fmt_out
;
197 fmt_in
= &chain
->fmt_in
;
200 if( fmt_out
== NULL
)
201 fmt_out
= &chain
->fmt_out
;
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
;
207 filter
->psz_name
= name
;
209 filter
->owner
= chain
->callbacks
;
210 filter
->owner
.sys
= chain
;
212 assert( capability
!= NULL
);
213 if( name
!= NULL
&& filter
->b_allow_fmt_out_change
)
215 /* Append the "chain" video filter to the current list.
216 * This filter will be used if the requested filter fails to load.
217 * It will then try to add a video converter before. */
218 char name_chained
[strlen(name
) + sizeof(",chain")];
219 sprintf( name_chained
, "%s,chain", name
);
220 filter
->p_module
= module_need( filter
, capability
, name_chained
, true );
223 filter
->p_module
= module_need( filter
, capability
, name
, name
!= NULL
);
225 if( filter
->p_module
== NULL
)
228 if( filter
->b_allow_fmt_out_change
)
230 es_format_Clean( &chain
->fmt_out
);
231 es_format_Copy( &chain
->fmt_out
, &filter
->fmt_out
);
234 if( chain
->last
== NULL
)
236 assert( chain
->first
== NULL
);
237 chain
->first
= chained
;
240 chain
->last
->next
= chained
;
241 chained
->prev
= chain
->last
;
242 chain
->last
= chained
;
243 chained
->next
= NULL
;
245 vlc_mouse_t
*mouse
= malloc( sizeof(*mouse
) );
246 if( likely(mouse
!= NULL
) )
247 vlc_mouse_Init( mouse
);
248 chained
->mouse
= mouse
;
249 chained
->pending
= NULL
;
251 msg_Dbg( parent
, "Filter '%s' (%p) appended to chain",
252 (name
!= NULL
) ? name
: module_get_name(filter
->p_module
, false),
258 msg_Err( parent
, "Failed to create %s '%s'", capability
, name
);
260 msg_Err( parent
, "Failed to create %s", capability
);
261 es_format_Clean( &filter
->fmt_out
);
262 es_format_Clean( &filter
->fmt_in
);
263 vlc_object_release( filter
);
267 filter_t
*filter_chain_AppendFilter( filter_chain_t
*chain
,
268 const char *name
, config_chain_t
*cfg
,
269 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
271 return filter_chain_AppendInner( chain
, name
, chain
->filter_cap
, cfg
,
275 int filter_chain_AppendConverter( filter_chain_t
*chain
,
276 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
278 return filter_chain_AppendInner( chain
, NULL
, chain
->conv_cap
, NULL
,
279 fmt_in
, fmt_out
) != NULL
? 0 : -1;
282 void filter_chain_DeleteFilter( filter_chain_t
*chain
, filter_t
*filter
)
284 vlc_object_t
*obj
= chain
->callbacks
.sys
;
285 chained_filter_t
*chained
= (chained_filter_t
*)filter
;
287 /* Remove it from the chain */
288 if( chained
->prev
!= NULL
)
289 chained
->prev
->next
= chained
->next
;
292 assert( chained
== chain
->first
);
293 chain
->first
= chained
->next
;
296 if( chained
->next
!= NULL
)
297 chained
->next
->prev
= chained
->prev
;
300 assert( chained
== chain
->last
);
301 chain
->last
= chained
->prev
;
304 module_unneed( filter
, filter
->p_module
);
306 msg_Dbg( obj
, "Filter %p removed from chain", (void *)filter
);
307 FilterDeletePictures( chained
->pending
);
309 free( chained
->mouse
);
310 es_format_Clean( &filter
->fmt_out
);
311 es_format_Clean( &filter
->fmt_in
);
313 vlc_object_release( filter
);
314 /* FIXME: check fmt_in/fmt_out consitency */
318 int filter_chain_AppendFromString( filter_chain_t
*chain
, const char *str
)
320 vlc_object_t
*obj
= chain
->callbacks
.sys
;
324 while( str
!= NULL
&& str
[0] != '\0' )
329 char *next
= config_ChainCreate( &name
, &cfg
, str
);
335 filter_t
*filter
= filter_chain_AppendFilter( chain
, name
, cfg
,
338 config_ChainDestroy( cfg
);
342 msg_Err( obj
, "Failed to append '%s' to chain", name
);
355 while( ret
> 0 ) /* Unwind */
357 filter_chain_DeleteFilter( chain
, &chain
->last
->filter
);
364 int filter_chain_ForEach( filter_chain_t
*chain
,
365 int (*cb
)( filter_t
*, void * ), void *opaque
)
367 for( chained_filter_t
*f
= chain
->first
; f
!= NULL
; f
= f
->next
)
369 int ret
= cb( &f
->filter
, opaque
);
376 bool filter_chain_IsEmpty(const filter_chain_t
*chain
)
378 return chain
->first
== NULL
;
381 const es_format_t
*filter_chain_GetFmtOut( filter_chain_t
*p_chain
)
384 if( p_chain
->b_allow_fmt_out_change
)
385 return &p_chain
->fmt_out
;
387 if( p_chain
->last
!= NULL
)
388 return &p_chain
->last
->filter
.fmt_out
;
390 /* Unless filter_chain_Reset has been called we are doomed */
391 return &p_chain
->fmt_out
;
394 static picture_t
*FilterChainVideoFilter( chained_filter_t
*f
, picture_t
*p_pic
)
396 for( ; f
!= NULL
; f
= f
->next
)
398 filter_t
*p_filter
= &f
->filter
;
399 p_pic
= p_filter
->pf_video_filter( p_filter
, p_pic
);
404 msg_Warn( p_filter
, "dropping pictures" );
405 FilterDeletePictures( f
->pending
);
407 f
->pending
= p_pic
->p_next
;
408 p_pic
->p_next
= NULL
;
413 picture_t
*filter_chain_VideoFilter( filter_chain_t
*p_chain
, picture_t
*p_pic
)
417 p_pic
= FilterChainVideoFilter( p_chain
->first
, p_pic
);
421 for( chained_filter_t
*b
= p_chain
->last
; b
!= NULL
; b
= b
->prev
)
426 b
->pending
= p_pic
->p_next
;
427 p_pic
->p_next
= NULL
;
429 p_pic
= FilterChainVideoFilter( b
->next
, p_pic
);
436 void filter_chain_VideoFlush( filter_chain_t
*p_chain
)
438 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
440 filter_t
*p_filter
= &f
->filter
;
442 FilterDeletePictures( f
->pending
);
445 filter_Flush( p_filter
);
449 void filter_chain_SubSource( filter_chain_t
*p_chain
, spu_t
*spu
,
450 mtime_t display_date
)
452 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
454 filter_t
*p_filter
= &f
->filter
;
455 subpicture_t
*p_subpic
= p_filter
->pf_sub_source( p_filter
, display_date
);
457 spu_PutSubpicture( spu
, p_subpic
);
461 subpicture_t
*filter_chain_SubFilter( filter_chain_t
*p_chain
, subpicture_t
*p_subpic
)
463 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
465 filter_t
*p_filter
= &f
->filter
;
467 p_subpic
= p_filter
->pf_sub_filter( p_filter
, p_subpic
);
475 int filter_chain_MouseFilter( filter_chain_t
*p_chain
, vlc_mouse_t
*p_dst
, const vlc_mouse_t
*p_src
)
477 vlc_mouse_t current
= *p_src
;
479 for( chained_filter_t
*f
= p_chain
->last
; f
!= NULL
; f
= f
->prev
)
481 filter_t
*p_filter
= &f
->filter
;
482 vlc_mouse_t
*p_mouse
= f
->mouse
;
484 if( p_filter
->pf_video_mouse
&& p_mouse
)
486 vlc_mouse_t old
= *p_mouse
;
487 vlc_mouse_t filtered
;
490 if( p_filter
->pf_video_mouse( p_filter
, &filtered
, &old
, ¤t
) )
500 int filter_chain_MouseEvent( filter_chain_t
*p_chain
,
501 const vlc_mouse_t
*p_mouse
,
502 const video_format_t
*p_fmt
)
504 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
506 filter_t
*p_filter
= &f
->filter
;
508 if( p_filter
->pf_sub_mouse
)
510 vlc_mouse_t old
= *f
->mouse
;
511 *f
->mouse
= *p_mouse
;
512 if( p_filter
->pf_sub_mouse( p_filter
, &old
, p_mouse
, p_fmt
) )
521 static void FilterDeletePictures( picture_t
*picture
)
525 picture_t
*next
= picture
->p_next
;
526 picture_Release( picture
);