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 static const struct filter_video_callbacks filter_chain_video_cbs
=
133 .buffer_new
= filter_chain_VideoBufferNew
,
136 #undef filter_chain_NewVideo
137 filter_chain_t
*filter_chain_NewVideo( vlc_object_t
*obj
, bool allow_change
,
138 const filter_owner_t
*restrict owner
)
140 filter_owner_t callbacks
= {
141 .video
= &filter_chain_video_cbs
,
145 return filter_chain_NewInner( &callbacks
, "video filter",
146 "video converter", allow_change
, owner
, VIDEO_ES
);
150 * Filter chain destruction
152 void filter_chain_Delete( filter_chain_t
*p_chain
)
154 while( p_chain
->first
!= NULL
)
155 filter_chain_DeleteFilter( p_chain
, &p_chain
->first
->filter
);
157 es_format_Clean( &p_chain
->fmt_in
);
158 es_format_Clean( &p_chain
->fmt_out
);
163 * Filter chain reinitialisation
165 void filter_chain_Reset( filter_chain_t
*p_chain
, const es_format_t
*p_fmt_in
,
166 const es_format_t
*p_fmt_out
)
168 while( p_chain
->first
!= NULL
)
169 filter_chain_DeleteFilter( p_chain
, &p_chain
->first
->filter
);
173 es_format_Clean( &p_chain
->fmt_in
);
174 es_format_Copy( &p_chain
->fmt_in
, p_fmt_in
);
178 es_format_Clean( &p_chain
->fmt_out
);
179 es_format_Copy( &p_chain
->fmt_out
, p_fmt_out
);
183 static filter_t
*filter_chain_AppendInner( filter_chain_t
*chain
,
184 const char *name
, const char *capability
, config_chain_t
*cfg
,
185 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
187 vlc_object_t
*parent
= chain
->callbacks
.sys
;
188 chained_filter_t
*chained
=
189 vlc_custom_create( parent
, sizeof(*chained
), "filter" );
190 if( unlikely(chained
== NULL
) )
193 filter_t
*filter
= &chained
->filter
;
197 if( chain
->last
!= NULL
)
198 fmt_in
= &chain
->last
->filter
.fmt_out
;
200 fmt_in
= &chain
->fmt_in
;
203 if( fmt_out
== NULL
)
204 fmt_out
= &chain
->fmt_out
;
206 es_format_Copy( &filter
->fmt_in
, fmt_in
);
207 es_format_Copy( &filter
->fmt_out
, fmt_out
);
208 filter
->b_allow_fmt_out_change
= chain
->b_allow_fmt_out_change
;
210 filter
->psz_name
= name
;
212 filter
->owner
= chain
->callbacks
;
213 filter
->owner
.sys
= chain
;
215 assert( capability
!= NULL
);
216 if( name
!= NULL
&& filter
->b_allow_fmt_out_change
)
218 /* Append the "chain" video filter to the current list.
219 * This filter will be used if the requested filter fails to load.
220 * It will then try to add a video converter before. */
221 char name_chained
[strlen(name
) + sizeof(",chain")];
222 sprintf( name_chained
, "%s,chain", name
);
223 filter
->p_module
= module_need( filter
, capability
, name_chained
, true );
226 filter
->p_module
= module_need( filter
, capability
, name
, name
!= NULL
);
228 if( filter
->p_module
== NULL
)
231 if( filter
->b_allow_fmt_out_change
)
233 es_format_Clean( &chain
->fmt_out
);
234 es_format_Copy( &chain
->fmt_out
, &filter
->fmt_out
);
237 if( chain
->last
== NULL
)
239 assert( chain
->first
== NULL
);
240 chain
->first
= chained
;
243 chain
->last
->next
= chained
;
244 chained
->prev
= chain
->last
;
245 chain
->last
= chained
;
246 chained
->next
= NULL
;
248 vlc_mouse_t
*mouse
= malloc( sizeof(*mouse
) );
249 if( likely(mouse
!= NULL
) )
250 vlc_mouse_Init( mouse
);
251 chained
->mouse
= mouse
;
252 chained
->pending
= NULL
;
254 msg_Dbg( parent
, "Filter '%s' (%p) appended to chain",
255 (name
!= NULL
) ? name
: module_get_name(filter
->p_module
, false),
261 msg_Err( parent
, "Failed to create %s '%s'", capability
, name
);
263 msg_Err( parent
, "Failed to create %s", capability
);
264 es_format_Clean( &filter
->fmt_out
);
265 es_format_Clean( &filter
->fmt_in
);
266 vlc_object_release( filter
);
270 filter_t
*filter_chain_AppendFilter( filter_chain_t
*chain
,
271 const char *name
, config_chain_t
*cfg
,
272 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
274 return filter_chain_AppendInner( chain
, name
, chain
->filter_cap
, cfg
,
278 int filter_chain_AppendConverter( filter_chain_t
*chain
,
279 const es_format_t
*fmt_in
, const es_format_t
*fmt_out
)
281 return filter_chain_AppendInner( chain
, NULL
, chain
->conv_cap
, NULL
,
282 fmt_in
, fmt_out
) != NULL
? 0 : -1;
285 void filter_chain_DeleteFilter( filter_chain_t
*chain
, filter_t
*filter
)
287 vlc_object_t
*obj
= chain
->callbacks
.sys
;
288 chained_filter_t
*chained
= (chained_filter_t
*)filter
;
290 /* Remove it from the chain */
291 if( chained
->prev
!= NULL
)
292 chained
->prev
->next
= chained
->next
;
295 assert( chained
== chain
->first
);
296 chain
->first
= chained
->next
;
299 if( chained
->next
!= NULL
)
300 chained
->next
->prev
= chained
->prev
;
303 assert( chained
== chain
->last
);
304 chain
->last
= chained
->prev
;
307 module_unneed( filter
, filter
->p_module
);
309 msg_Dbg( obj
, "Filter %p removed from chain", (void *)filter
);
310 FilterDeletePictures( chained
->pending
);
312 free( chained
->mouse
);
313 es_format_Clean( &filter
->fmt_out
);
314 es_format_Clean( &filter
->fmt_in
);
316 vlc_object_release( filter
);
317 /* FIXME: check fmt_in/fmt_out consitency */
321 int filter_chain_AppendFromString( filter_chain_t
*chain
, const char *str
)
323 vlc_object_t
*obj
= chain
->callbacks
.sys
;
327 while( str
!= NULL
&& str
[0] != '\0' )
332 char *next
= config_ChainCreate( &name
, &cfg
, str
);
338 filter_t
*filter
= filter_chain_AppendFilter( chain
, name
, cfg
,
341 config_ChainDestroy( cfg
);
345 msg_Err( obj
, "Failed to append '%s' to chain", name
);
358 while( ret
> 0 ) /* Unwind */
360 filter_chain_DeleteFilter( chain
, &chain
->last
->filter
);
367 int filter_chain_ForEach( filter_chain_t
*chain
,
368 int (*cb
)( filter_t
*, void * ), void *opaque
)
370 for( chained_filter_t
*f
= chain
->first
; f
!= NULL
; f
= f
->next
)
372 int ret
= cb( &f
->filter
, opaque
);
379 bool filter_chain_IsEmpty(const filter_chain_t
*chain
)
381 return chain
->first
== NULL
;
384 const es_format_t
*filter_chain_GetFmtOut( filter_chain_t
*p_chain
)
387 if( p_chain
->b_allow_fmt_out_change
)
388 return &p_chain
->fmt_out
;
390 if( p_chain
->last
!= NULL
)
391 return &p_chain
->last
->filter
.fmt_out
;
393 /* Unless filter_chain_Reset has been called we are doomed */
394 return &p_chain
->fmt_out
;
397 static picture_t
*FilterChainVideoFilter( chained_filter_t
*f
, picture_t
*p_pic
)
399 for( ; f
!= NULL
; f
= f
->next
)
401 filter_t
*p_filter
= &f
->filter
;
402 p_pic
= p_filter
->pf_video_filter( p_filter
, p_pic
);
407 msg_Warn( p_filter
, "dropping pictures" );
408 FilterDeletePictures( f
->pending
);
410 f
->pending
= p_pic
->p_next
;
411 p_pic
->p_next
= NULL
;
416 picture_t
*filter_chain_VideoFilter( filter_chain_t
*p_chain
, picture_t
*p_pic
)
420 p_pic
= FilterChainVideoFilter( p_chain
->first
, p_pic
);
424 for( chained_filter_t
*b
= p_chain
->last
; b
!= NULL
; b
= b
->prev
)
429 b
->pending
= p_pic
->p_next
;
430 p_pic
->p_next
= NULL
;
432 p_pic
= FilterChainVideoFilter( b
->next
, p_pic
);
439 void filter_chain_VideoFlush( filter_chain_t
*p_chain
)
441 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
443 filter_t
*p_filter
= &f
->filter
;
445 FilterDeletePictures( f
->pending
);
448 filter_Flush( p_filter
);
452 void filter_chain_SubSource( filter_chain_t
*p_chain
, spu_t
*spu
,
453 vlc_tick_t display_date
)
455 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
457 filter_t
*p_filter
= &f
->filter
;
458 subpicture_t
*p_subpic
= p_filter
->pf_sub_source( p_filter
, display_date
);
460 spu_PutSubpicture( spu
, p_subpic
);
464 subpicture_t
*filter_chain_SubFilter( filter_chain_t
*p_chain
, subpicture_t
*p_subpic
)
466 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
468 filter_t
*p_filter
= &f
->filter
;
470 p_subpic
= p_filter
->pf_sub_filter( p_filter
, p_subpic
);
478 int filter_chain_MouseFilter( filter_chain_t
*p_chain
, vlc_mouse_t
*p_dst
, const vlc_mouse_t
*p_src
)
480 vlc_mouse_t current
= *p_src
;
482 for( chained_filter_t
*f
= p_chain
->last
; f
!= NULL
; f
= f
->prev
)
484 filter_t
*p_filter
= &f
->filter
;
485 vlc_mouse_t
*p_mouse
= f
->mouse
;
487 if( p_filter
->pf_video_mouse
&& p_mouse
)
489 vlc_mouse_t old
= *p_mouse
;
490 vlc_mouse_t filtered
;
493 if( p_filter
->pf_video_mouse( p_filter
, &filtered
, &old
, ¤t
) )
503 int filter_chain_MouseEvent( filter_chain_t
*p_chain
,
504 const vlc_mouse_t
*p_mouse
,
505 const video_format_t
*p_fmt
)
507 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
509 filter_t
*p_filter
= &f
->filter
;
511 if( p_filter
->pf_sub_mouse
)
513 vlc_mouse_t old
= *f
->mouse
;
514 *f
->mouse
= *p_mouse
;
515 if( p_filter
->pf_sub_mouse( p_filter
, &old
, p_mouse
, p_fmt
) )
524 static void FilterDeletePictures( picture_t
*picture
)
528 picture_t
*next
= picture
->p_next
;
529 picture_Release( picture
);