qt: playlist: use item title if available
[vlc.git] / src / misc / filter_chain.c
blob58ef23e027f90cbcc831730986fac5c3c8378095
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 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <vlc_filter.h>
29 #include <vlc_modules.h>
30 #include <vlc_mouse.h>
31 #include <vlc_spu.h>
32 #include <libvlc.h>
33 #include <assert.h>
35 typedef struct chained_filter_t
37 /* Public part of the filter structure */
38 filter_t filter;
39 /* Private filter chain data (shhhh!) */
40 struct chained_filter_t *prev, *next;
41 vlc_mouse_t mouse;
42 vlc_picture_chain_t pending;
43 } chained_filter_t;
45 /* */
46 struct filter_chain_t
48 vlc_object_t *obj;
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 */
61 /**
62 * Local prototypes
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) )
75 return NULL;
77 chain->obj = obj;
78 chain->first = NULL;
79 chain->last = 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;
86 return chain;
89 #undef filter_chain_NewSPU
90 /**
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 )
101 picture_t *pic;
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;
111 if( pic == NULL )
112 msg_Err( filter, "Failed to allocate picture" );
114 else
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
119 /* XXX ugly */
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;
125 return pic;
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)
134 return 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))
152 return 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;
160 else
161 chain->parent_video_owner = (filter_owner_t){0};
162 return chain;
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 );
183 free( p_chain );
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) )
213 return 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;
224 else
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;
237 filter->p_cfg = cfg;
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;
245 else
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 );
258 else
259 filter->p_module = module_need( filter, capability, name, name != NULL );
261 if( filter->p_module == NULL )
262 goto error;
263 assert( filter->ops != NULL );
265 if( chain->last == NULL )
267 assert( chain->first == NULL );
268 chain->first = chained;
270 else
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),
281 (void *)filter );
282 return filter;
284 error:
285 if( name != NULL )
286 msg_Err( chain->obj, "Failed to create %s '%s'", capability, name );
287 else
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);
292 return NULL;
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,
300 fmt_out );
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;
317 else
319 assert( chained == chain->first );
320 chain->first = chained->next;
323 if( chained->next != NULL )
324 chained->next->prev = chained->prev;
325 else
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 )
347 char *buf = NULL;
348 int ret = 0;
350 while( str != NULL && str[0] != '\0' )
352 config_chain_t *cfg;
353 char *name;
355 char *next = config_ChainCreate( &name, &cfg, str );
357 str = next;
358 free( buf );
359 buf = next;
361 filter_t *filter = filter_chain_AppendFilter( chain, name, cfg, NULL );
362 if( cfg )
363 config_ChainDestroy( cfg );
365 if( filter == NULL )
367 msg_Err( chain->obj, "Failed to append '%s' to chain", name );
368 free( name );
369 goto error;
372 free( name );
373 ret++;
376 free( buf );
377 return ret;
379 error:
380 while( ret > 0 ) /* Unwind */
382 filter_chain_DeleteFilter( chain, &chain->last->filter );
383 ret--;
385 free( buf );
386 return VLC_EGENERIC;
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 );
395 if( ret )
396 return ret;
398 return VLC_SUCCESS;
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 );
431 if( !p_pic )
432 break;
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 );
440 return p_pic;
443 picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic )
445 if( p_pic )
447 p_pic = FilterChainVideoFilter( p_chain->first, p_pic );
448 if( p_pic )
449 return p_pic;
451 for( chained_filter_t *b = p_chain->last; b != NULL; b = b->prev )
453 if( vlc_picture_chain_IsEmpty( &b->pending ) )
454 continue;
455 p_pic = vlc_picture_chain_PopFront( &b->pending );
457 p_pic = FilterChainVideoFilter( b->next, p_pic );
458 if( p_pic )
459 return p_pic;
461 return NULL;
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 );
483 if( p_subpic )
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 );
496 if( !p_subpic )
497 break;
499 return 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;
515 f->mouse = current;
516 if( p_filter->ops->video_mouse( p_filter, &filtered, &old) )
517 return VLC_EGENERIC;
518 current = filtered;
522 *p_dst = current;
523 return VLC_SUCCESS;
526 /* Helpers */
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 );