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
;
42 vlc_picture_chain_t pending
;
49 filter_owner_t parent_video_owner
; /**< Owner (downstream) callbacks */
51 chained_filter_t
*first
, *last
; /**< List of filters */
53 es_format_t fmt_in
; /**< Chain input format (constant) */
54 vlc_video_context
*vctx_in
; /**< Chain input video context (set on Reset) */
55 es_format_t fmt_out
; /**< Chain output format (constant) */
56 bool b_allow_fmt_out_change
; /**< Each filter can change the output */
57 const char *filter_cap
; /**< Filter modules capability */
58 const char *conv_cap
; /**< Converter modules capability */
64 static void FilterDeletePictures( vlc_picture_chain_t
* );
66 static filter_chain_t
*filter_chain_NewInner( vlc_object_t
*obj
,
67 const char *cap
, const char *conv_cap
, bool fmt_out_change
,
68 enum es_format_category_e cat
)
70 assert( obj
!= NULL
);
71 assert( cap
!= NULL
);
73 filter_chain_t
*chain
= malloc( sizeof (*chain
) );
74 if( unlikely(chain
== NULL
) )
80 es_format_Init( &chain
->fmt_in
, cat
, 0 );
81 chain
->vctx_in
= NULL
;
82 es_format_Init( &chain
->fmt_out
, cat
, 0 );
83 chain
->b_allow_fmt_out_change
= fmt_out_change
;
84 chain
->filter_cap
= cap
;
85 chain
->conv_cap
= conv_cap
;
89 #undef filter_chain_NewSPU
91 * Filter chain initialisation
93 filter_chain_t
*filter_chain_NewSPU( vlc_object_t
*obj
, const char *cap
)
95 return filter_chain_NewInner( obj
, cap
, NULL
, false, SPU_ES
);
98 /** Chained filter picture allocator function */
99 static picture_t
*filter_chain_VideoBufferNew( filter_t
*filter
)
102 chained_filter_t
*chained
= container_of(filter
, chained_filter_t
, filter
);
103 if( chained
->next
!= NULL
)
105 // HACK as intermediate filters may not have the same video format as
106 // the last one handled by the owner
107 filter_owner_t saved_owner
= filter
->owner
;
108 filter
->owner
= (filter_owner_t
) {0};
109 pic
= filter_NewPicture( filter
);
110 filter
->owner
= saved_owner
;
112 msg_Err( filter
, "Failed to allocate picture" );
116 filter_chain_t
*chain
= filter
->owner
.sys
;
118 // the owner of the chain requires pictures from the last filter to be grabbed from its callback
120 filter_owner_t saved_owner
= filter
->owner
;
121 filter
->owner
= chain
->parent_video_owner
;
122 pic
= filter_NewPicture( filter
);
123 filter
->owner
= saved_owner
;
128 static vlc_decoder_device
* filter_chain_HoldDecoderDevice(vlc_object_t
*o
, void *sys
)
130 filter_chain_t
*chain
= sys
;
132 if (chain
->parent_video_owner
.video
== NULL
||
133 chain
->parent_video_owner
.video
->hold_device
== NULL
)
136 return chain
->parent_video_owner
.video
->hold_device(o
, chain
->parent_video_owner
.sys
);
139 static const struct filter_video_callbacks filter_chain_video_cbs
=
141 filter_chain_VideoBufferNew
, filter_chain_HoldDecoderDevice
,
144 #undef filter_chain_NewVideo
145 filter_chain_t
*filter_chain_NewVideo( vlc_object_t
*obj
, bool allow_change
,
146 const filter_owner_t
*restrict owner
)
148 filter_chain_t
*chain
=
149 filter_chain_NewInner( obj
, "video filter",
150 "video converter", allow_change
, VIDEO_ES
);
151 if (unlikely(chain
== NULL
))
154 if( owner
!= NULL
&& owner
->video
!= NULL
)
156 // keep this to get pictures for the last filter in the chain
157 assert( owner
->video
->buffer_new
!= NULL
);
158 chain
->parent_video_owner
= *owner
;
161 chain
->parent_video_owner
= (filter_owner_t
){0};
165 void filter_chain_Clear( filter_chain_t
*p_chain
)
167 while( p_chain
->first
!= NULL
)
168 filter_chain_DeleteFilter( p_chain
, &p_chain
->first
->filter
);
172 * Filter chain destruction
174 void filter_chain_Delete( filter_chain_t
*p_chain
)
176 filter_chain_Clear( p_chain
);
178 es_format_Clean( &p_chain
->fmt_in
);
179 if ( p_chain
->vctx_in
)
180 vlc_video_context_Release( p_chain
->vctx_in
);
181 es_format_Clean( &p_chain
->fmt_out
);
186 * Filter chain reinitialisation
188 void filter_chain_Reset( filter_chain_t
*p_chain
,
189 const es_format_t
*p_fmt_in
, vlc_video_context
*vctx_in
,
190 const es_format_t
*p_fmt_out
)
192 filter_chain_Clear( p_chain
);
194 assert(p_fmt_in
!= NULL
);
195 es_format_Clean( &p_chain
->fmt_in
);
196 es_format_Copy( &p_chain
->fmt_in
, p_fmt_in
);
197 if ( p_chain
->vctx_in
)
198 vlc_video_context_Release( p_chain
->vctx_in
);
199 p_chain
->vctx_in
= vctx_in
? vlc_video_context_Hold(vctx_in
) : NULL
;
201 assert(p_fmt_out
!= NULL
);
202 es_format_Clean( &p_chain
->fmt_out
);
203 es_format_Copy( &p_chain
->fmt_out
, p_fmt_out
);
206 static filter_t
*filter_chain_AppendInner( filter_chain_t
*chain
,
207 const char *name
, const char *capability
, const config_chain_t
*cfg
,
208 const es_format_t
*fmt_out
)
210 chained_filter_t
*chained
=
211 vlc_custom_create( chain
->obj
, sizeof(*chained
), "filter" );
212 if( unlikely(chained
== NULL
) )
215 filter_t
*filter
= &chained
->filter
;
217 const es_format_t
*fmt_in
;
218 vlc_video_context
*vctx_in
;
219 if( chain
->last
!= NULL
)
221 fmt_in
= &chain
->last
->filter
.fmt_out
;
222 vctx_in
= chain
->last
->filter
.vctx_out
;
226 fmt_in
= &chain
->fmt_in
;
227 vctx_in
= chain
->vctx_in
;
230 if( fmt_out
== NULL
)
231 fmt_out
= &chain
->fmt_out
;
233 es_format_Copy( &filter
->fmt_in
, fmt_in
);
234 filter
->vctx_in
= vctx_in
;
235 es_format_Copy( &filter
->fmt_out
, fmt_out
);
236 filter
->b_allow_fmt_out_change
= chain
->b_allow_fmt_out_change
;
238 filter
->psz_name
= name
;
240 if (fmt_in
->i_cat
== VIDEO_ES
)
242 filter
->owner
.video
= &filter_chain_video_cbs
;
243 filter
->owner
.sys
= chain
;
246 filter
->owner
.sub
= NULL
;
248 assert( capability
!= NULL
);
249 if( name
!= NULL
&& chain
->b_allow_fmt_out_change
)
251 /* Append the "chain" video filter to the current list.
252 * This filter will be used if the requested filter fails to load.
253 * It will then try to add a video converter before. */
254 char name_chained
[strlen(name
) + sizeof(",chain")];
255 sprintf( name_chained
, "%s,chain", name
);
256 filter
->p_module
= module_need( filter
, capability
, name_chained
, true );
259 filter
->p_module
= module_need( filter
, capability
, name
, name
!= NULL
);
261 if( filter
->p_module
== NULL
)
263 assert( filter
->ops
!= NULL
);
265 if( chain
->last
== NULL
)
267 assert( chain
->first
== NULL
);
268 chain
->first
= chained
;
271 chain
->last
->next
= chained
;
272 chained
->prev
= chain
->last
;
273 chain
->last
= chained
;
274 chained
->next
= NULL
;
276 vlc_mouse_Init( &chained
->mouse
);
277 vlc_picture_chain_Init( &chained
->pending
);
279 msg_Dbg( chain
->obj
, "Filter '%s' (%p) appended to chain",
280 (name
!= NULL
) ? name
: module_get_name(filter
->p_module
, false),
286 msg_Err( chain
->obj
, "Failed to create %s '%s'", capability
, name
);
288 msg_Err( chain
->obj
, "Failed to create %s", capability
);
289 es_format_Clean( &filter
->fmt_out
);
290 es_format_Clean( &filter
->fmt_in
);
291 vlc_object_delete(filter
);
295 filter_t
*filter_chain_AppendFilter( filter_chain_t
*chain
,
296 const char *name
, const config_chain_t
*cfg
,
297 const es_format_t
*fmt_out
)
299 return filter_chain_AppendInner( chain
, name
, chain
->filter_cap
, cfg
,
303 int filter_chain_AppendConverter( filter_chain_t
*chain
,
304 const es_format_t
*fmt_out
)
306 return filter_chain_AppendInner( chain
, NULL
, chain
->conv_cap
, NULL
,
307 fmt_out
) != NULL
? 0 : -1;
310 void filter_chain_DeleteFilter( filter_chain_t
*chain
, filter_t
*filter
)
312 chained_filter_t
*chained
= (chained_filter_t
*)filter
;
314 /* Remove it from the chain */
315 if( chained
->prev
!= NULL
)
316 chained
->prev
->next
= chained
->next
;
319 assert( chained
== chain
->first
);
320 chain
->first
= chained
->next
;
323 if( chained
->next
!= NULL
)
324 chained
->next
->prev
= chained
->prev
;
327 assert( chained
== chain
->last
);
328 chain
->last
= chained
->prev
;
331 filter_Close( filter
);
332 module_unneed( filter
, filter
->p_module
);
334 msg_Dbg( chain
->obj
, "Filter %p removed from chain", (void *)filter
);
335 FilterDeletePictures( &chained
->pending
);
337 es_format_Clean( &filter
->fmt_out
);
338 es_format_Clean( &filter
->fmt_in
);
340 vlc_object_delete(filter
);
341 /* FIXME: check fmt_in/fmt_out consitency */
345 int filter_chain_AppendFromString( filter_chain_t
*chain
, const char *str
)
350 while( str
!= NULL
&& str
[0] != '\0' )
355 char *next
= config_ChainCreate( &name
, &cfg
, str
);
361 filter_t
*filter
= filter_chain_AppendFilter( chain
, name
, cfg
, NULL
);
363 config_ChainDestroy( cfg
);
367 msg_Err( chain
->obj
, "Failed to append '%s' to chain", name
);
380 while( ret
> 0 ) /* Unwind */
382 filter_chain_DeleteFilter( chain
, &chain
->last
->filter
);
389 int filter_chain_ForEach( filter_chain_t
*chain
,
390 int (*cb
)( filter_t
*, void * ), void *opaque
)
392 for( chained_filter_t
*f
= chain
->first
; f
!= NULL
; f
= f
->next
)
394 int ret
= cb( &f
->filter
, opaque
);
401 bool filter_chain_IsEmpty(const filter_chain_t
*chain
)
403 return chain
->first
== NULL
;
406 const es_format_t
*filter_chain_GetFmtOut( const filter_chain_t
*p_chain
)
408 if( p_chain
->last
!= NULL
)
409 return &p_chain
->last
->filter
.fmt_out
;
411 /* Unless filter_chain_Reset has been called we are doomed */
412 return &p_chain
->fmt_out
;
415 vlc_video_context
*filter_chain_GetVideoCtxOut(const filter_chain_t
*p_chain
)
417 if( p_chain
->last
!= NULL
)
418 return p_chain
->last
->filter
.vctx_out
;
420 /* No filter was added, the filter chain has no effect, make sure the chromas are compatible */
421 assert(p_chain
->fmt_in
.video
.i_chroma
== p_chain
->fmt_out
.video
.i_chroma
);
422 return p_chain
->vctx_in
;
425 static picture_t
*FilterChainVideoFilter( chained_filter_t
*f
, picture_t
*p_pic
)
427 for( ; f
!= NULL
; f
= f
->next
)
429 filter_t
*p_filter
= &f
->filter
;
430 p_pic
= p_filter
->ops
->filter_video( p_filter
, p_pic
);
433 if( !vlc_picture_chain_IsEmpty( &f
->pending
) )
435 msg_Warn( p_filter
, "dropping pictures" );
436 FilterDeletePictures( &f
->pending
);
438 f
->pending
= picture_GetAndResetChain( p_pic
);
443 picture_t
*filter_chain_VideoFilter( filter_chain_t
*p_chain
, picture_t
*p_pic
)
447 p_pic
= FilterChainVideoFilter( p_chain
->first
, p_pic
);
451 for( chained_filter_t
*b
= p_chain
->last
; b
!= NULL
; b
= b
->prev
)
453 if( vlc_picture_chain_IsEmpty( &b
->pending
) )
455 p_pic
= vlc_picture_chain_PopFront( &b
->pending
);
457 p_pic
= FilterChainVideoFilter( b
->next
, p_pic
);
464 void filter_chain_VideoFlush( filter_chain_t
*p_chain
)
466 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
468 filter_t
*p_filter
= &f
->filter
;
470 FilterDeletePictures( &f
->pending
);
472 filter_Flush( p_filter
);
476 void filter_chain_SubSource( filter_chain_t
*p_chain
, spu_t
*spu
,
477 vlc_tick_t display_date
)
479 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
481 filter_t
*p_filter
= &f
->filter
;
482 subpicture_t
*p_subpic
= p_filter
->ops
->source_sub( p_filter
, display_date
);
484 spu_PutSubpicture( spu
, p_subpic
);
488 subpicture_t
*filter_chain_SubFilter( filter_chain_t
*p_chain
, subpicture_t
*p_subpic
)
490 for( chained_filter_t
*f
= p_chain
->first
; f
!= NULL
; f
= f
->next
)
492 filter_t
*p_filter
= &f
->filter
;
494 p_subpic
= p_filter
->ops
->filter_sub( p_filter
, p_subpic
);
502 int filter_chain_MouseFilter( filter_chain_t
*p_chain
, vlc_mouse_t
*p_dst
, const vlc_mouse_t
*p_src
)
504 vlc_mouse_t current
= *p_src
;
506 for( chained_filter_t
*f
= p_chain
->last
; f
!= NULL
; f
= f
->prev
)
508 filter_t
*p_filter
= &f
->filter
;
510 if( p_filter
->ops
->video_mouse
)
512 vlc_mouse_t old
= f
->mouse
;
513 vlc_mouse_t filtered
= current
;
516 if( p_filter
->ops
->video_mouse( p_filter
, &filtered
, &old
) )
527 static void FilterDeletePictures( vlc_picture_chain_t
*pictures
)
529 while( !vlc_picture_chain_IsEmpty( pictures
) )
531 picture_t
*next
= vlc_picture_chain_PopFront( pictures
);
532 picture_Release( next
);