qt: playlist: use item title if available
[vlc.git] / modules / video_chroma / chain.c
blob347c8d58da73ec7c3093ae805c8a35378470a10b
1 /*****************************************************************************
2 * chain.c : chain multiple video filter modules as a last resort solution
3 *****************************************************************************
4 * Copyright (C) 2007-2017 VLC authors and VideoLAN
6 * Authors: Antoine Cellerier <dionoea at videolan dot org>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_mouse.h>
35 #include <vlc_picture.h>
37 /*****************************************************************************
38 * Module descriptor
39 *****************************************************************************/
40 static int ActivateConverter ( filter_t * );
41 static int ActivateFilter ( filter_t * );
42 static void Destroy ( filter_t * );
44 vlc_module_begin ()
45 set_description( N_("Video filtering using a chain of video filter modules") )
46 set_callback_video_converter( ActivateConverter, 1 )
47 add_submodule ()
48 set_callback_video_filter( ActivateFilter )
49 vlc_module_end ()
51 /*****************************************************************************
52 * Local prototypes.
53 *****************************************************************************/
54 static picture_t *Chain ( filter_t *, picture_t * );
55 static void Flush ( filter_t * );
57 static int BuildTransformChain( filter_t *p_filter );
58 static int BuildChromaResize( filter_t * );
59 static int BuildChromaChain( filter_t *p_filter );
60 static int BuildFilterChain( filter_t *p_filter );
62 static int CreateChain( filter_t *p_filter, const es_format_t *p_fmt_mid );
63 static int CreateResizeChromaChain( filter_t *p_filter, const es_format_t *p_fmt_mid );
64 static filter_t * AppendTransform( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
65 const es_format_t *p_fmt_out );
66 static void EsFormatMergeSize( es_format_t *p_dst,
67 const es_format_t *p_base,
68 const es_format_t *p_size );
70 #define ALLOWED_CHROMAS_YUV10 \
71 VLC_CODEC_I420_10L, \
72 VLC_CODEC_I420_10B, \
73 VLC_CODEC_I420_16L \
75 static const vlc_fourcc_t pi_allowed_chromas_yuv[] = {
76 VLC_CODEC_I420,
77 VLC_CODEC_I422,
78 ALLOWED_CHROMAS_YUV10,
79 VLC_CODEC_RGB32,
80 VLC_CODEC_RGB24,
81 VLC_CODEC_BGRA,
85 static const vlc_fourcc_t pi_allowed_chromas_yuv10[] = {
86 ALLOWED_CHROMAS_YUV10,
87 VLC_CODEC_I420,
88 VLC_CODEC_I422,
89 VLC_CODEC_RGB32,
90 VLC_CODEC_RGB24,
91 VLC_CODEC_BGRA,
95 static const vlc_fourcc_t *get_allowed_chromas( filter_t *p_filter )
97 switch (p_filter->fmt_out.video.i_chroma)
99 case VLC_CODEC_I420_10L:
100 case VLC_CODEC_I420_10B:
101 case VLC_CODEC_I420_16L:
102 case VLC_CODEC_CVPX_P010:
103 case VLC_CODEC_D3D9_OPAQUE_10B:
104 case VLC_CODEC_D3D11_OPAQUE_10B:
105 case VLC_CODEC_VAAPI_420_10BPP:
106 return pi_allowed_chromas_yuv10;
107 default:
108 return pi_allowed_chromas_yuv;
112 typedef struct
114 filter_chain_t *p_chain;
115 filter_t *p_video_filter;
116 struct vlc_filter_operations custom_ops;
117 } filter_sys_t;
119 /* Restart filter callback */
120 static int RestartFilterCallback( vlc_object_t *obj, char const *psz_name,
121 vlc_value_t oldval, vlc_value_t newval,
122 void *p_data )
123 { VLC_UNUSED(obj); VLC_UNUSED(psz_name); VLC_UNUSED(oldval);
124 VLC_UNUSED(newval);
126 var_TriggerCallback( (vlc_object_t *)p_data, "video-filter" );
127 return VLC_SUCCESS;
130 /*****************************************************************************
131 * Buffer management
132 *****************************************************************************/
133 static picture_t *BufferChainNew( filter_t *p_filter )
135 filter_t *p_chain_parent = p_filter->owner.sys;
136 // the last filter of the internal chain gets its pictures from the original
137 // filter source
138 return filter_NewPicture( p_chain_parent );
141 #define CHAIN_LEVEL_MAX 2
143 static vlc_decoder_device * HoldChainDecoderDevice(vlc_object_t *o, void *sys)
145 VLC_UNUSED(o);
146 filter_t *p_chain_parent = sys;
147 return filter_HoldDecoderDevice( p_chain_parent );
150 static const struct filter_video_callbacks filter_video_chain_cbs =
152 BufferChainNew, HoldChainDecoderDevice,
155 static const struct vlc_filter_operations filter_ops = {
156 .filter_video = Chain, .flush = Flush, .close = Destroy,
159 /*****************************************************************************
160 * Activate: allocate a chroma function
161 *****************************************************************************
162 * This function allocates and initializes a chroma function
163 *****************************************************************************/
164 static int Activate( filter_t *p_filter, int (*pf_build)(filter_t *) )
166 filter_sys_t *p_sys;
167 int i_ret = VLC_EGENERIC;
169 p_sys = p_filter->p_sys = calloc( 1, sizeof( *p_sys ) );
170 if( !p_sys )
171 return VLC_ENOMEM;
173 filter_owner_t owner = {
174 .video = &filter_video_chain_cbs,
175 .sys = p_filter,
178 p_sys->p_chain = filter_chain_NewVideo( p_filter, p_filter->b_allow_fmt_out_change, &owner );
179 if( !p_sys->p_chain )
181 free( p_sys );
182 return VLC_EGENERIC;
185 int type = VLC_VAR_INTEGER;
186 if( var_Type( vlc_object_parent(p_filter), "chain-level" ) != 0 )
187 type |= VLC_VAR_DOINHERIT;
189 var_Create( p_filter, "chain-level", type );
190 /* Note: atomicity is not actually needed here. */
191 var_IncInteger( p_filter, "chain-level" );
193 int level = var_GetInteger( p_filter, "chain-level" );
194 if( level < 0 || level > CHAIN_LEVEL_MAX )
195 msg_Err( p_filter, "Too high level of recursion (%d)", level );
196 else
197 i_ret = pf_build( p_filter );
199 var_Destroy( p_filter, "chain-level" );
201 if( i_ret )
203 /* Hum ... looks like this really isn't going to work. Too bad. */
204 if (p_sys->p_video_filter)
205 filter_DelProxyCallbacks( p_filter, p_sys->p_video_filter,
206 RestartFilterCallback );
207 filter_chain_Delete( p_sys->p_chain );
208 free( p_sys );
209 return VLC_EGENERIC;
211 if( p_filter->b_allow_fmt_out_change )
213 es_format_Clean( &p_filter->fmt_out );
214 es_format_Copy( &p_filter->fmt_out,
215 filter_chain_GetFmtOut( p_sys->p_chain ) );
216 p_filter->vctx_out = filter_chain_GetVideoCtxOut( p_sys->p_chain );
218 assert(p_filter->vctx_out == filter_chain_GetVideoCtxOut( p_sys->p_chain ));
219 /* */
220 p_filter->ops = &filter_ops;
221 return VLC_SUCCESS;
224 static int ActivateConverter( filter_t *p_filter )
226 const bool b_chroma = p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma;
227 const bool b_resize = p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ||
228 p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height;
230 const bool b_chroma_resize = b_chroma && b_resize;
231 const bool b_transform = p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation;
233 if( !b_chroma && !b_chroma_resize && !b_transform)
234 return VLC_EGENERIC;
236 return Activate( p_filter, b_transform ? BuildTransformChain :
237 b_chroma_resize ? BuildChromaResize :
238 BuildChromaChain );
241 static int ActivateFilter( filter_t *p_filter )
243 if( !p_filter->b_allow_fmt_out_change || p_filter->psz_name == NULL )
244 return VLC_EGENERIC;
246 if( var_Type( vlc_object_parent(p_filter), "chain-filter-level" ) != 0 )
247 return VLC_EGENERIC;
249 var_Create( p_filter, "chain-filter-level", VLC_VAR_INTEGER );
250 int i_ret = Activate( p_filter, BuildFilterChain );
251 var_Destroy( p_filter, "chain-filter-level" );
253 return i_ret;
256 static void Destroy( filter_t *p_filter )
258 filter_sys_t *p_sys = p_filter->p_sys;
260 if (p_sys->p_video_filter)
261 filter_DelProxyCallbacks( p_filter, p_sys->p_video_filter,
262 RestartFilterCallback );
263 filter_chain_Delete( p_sys->p_chain );
264 free( p_sys );
267 /*****************************************************************************
268 * Chain
269 *****************************************************************************/
270 static picture_t *Chain( filter_t *p_filter, picture_t *p_pic )
272 filter_sys_t *p_sys = p_filter->p_sys;
273 return filter_chain_VideoFilter( p_sys->p_chain, p_pic );
276 static void Flush( filter_t *p_filter )
278 filter_sys_t *p_sys = p_filter->p_sys;
279 filter_chain_VideoFlush( p_sys->p_chain );
282 /*****************************************************************************
283 * Builders
284 *****************************************************************************/
286 static int BuildTransformChain( filter_t *p_filter )
289 es_format_t fmt_mid;
290 int i_ret;
292 /* Lets try transform first, then (potentially) resize+chroma */
293 msg_Dbg( p_filter, "Trying to build transform, then chroma+resize" );
294 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
295 video_format_TransformTo(&fmt_mid.video, p_filter->fmt_out.video.orientation);
296 i_ret = CreateChain( p_filter, &fmt_mid );
297 es_format_Clean( &fmt_mid );
298 if( i_ret == VLC_SUCCESS )
299 return VLC_SUCCESS;
301 /* Lets try resize+chroma first, then transform */
302 msg_Dbg( p_filter, "Trying to build chroma+resize" );
303 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
304 i_ret = CreateChain( p_filter, &fmt_mid );
305 es_format_Clean( &fmt_mid );
306 return i_ret;
309 static int BuildChromaResize( filter_t *p_filter )
311 es_format_t fmt_mid;
312 int i_ret;
314 /* Lets try resizing and then doing the chroma conversion */
315 msg_Dbg( p_filter, "Trying to build resize+chroma" );
316 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_in, &p_filter->fmt_out );
317 i_ret = CreateResizeChromaChain( p_filter, &fmt_mid );
318 es_format_Clean( &fmt_mid );
320 if( i_ret == VLC_SUCCESS )
321 return VLC_SUCCESS;
323 /* Lets try it the other way arround (chroma and then resize) */
324 msg_Dbg( p_filter, "Trying to build chroma+resize" );
325 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
326 i_ret = CreateChain( p_filter, &fmt_mid );
327 es_format_Clean( &fmt_mid );
328 if( i_ret == VLC_SUCCESS )
329 return VLC_SUCCESS;
331 return VLC_EGENERIC;
334 static int BuildChromaChain( filter_t *p_filter )
336 es_format_t fmt_mid;
337 int i_ret = VLC_EGENERIC;
339 /* Now try chroma format list */
340 const vlc_fourcc_t *pi_allowed_chromas = get_allowed_chromas( p_filter );
341 for( int i = 0; pi_allowed_chromas[i]; i++ )
343 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
344 if( i_chroma == p_filter->fmt_in.i_codec ||
345 i_chroma == p_filter->fmt_out.i_codec )
346 continue;
348 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
349 (char*)&i_chroma );
351 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
352 fmt_mid.i_codec =
353 fmt_mid.video.i_chroma = i_chroma;
354 fmt_mid.video.i_rmask = 0;
355 fmt_mid.video.i_gmask = 0;
356 fmt_mid.video.i_bmask = 0;
357 video_format_FixRgb(&fmt_mid.video);
359 i_ret = CreateChain( p_filter, &fmt_mid );
360 es_format_Clean( &fmt_mid );
362 if( i_ret == VLC_SUCCESS )
363 break;
366 return i_ret;
369 static int ChainMouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
370 const vlc_mouse_t *p_old )
372 filter_sys_t *p_sys = p_filter->p_sys;
373 return filter_chain_MouseFilter( p_sys->p_chain, p_mouse, p_old );
376 static int BuildFilterChain( filter_t *p_filter )
378 es_format_t fmt_mid;
379 int i_ret = VLC_EGENERIC;
381 filter_sys_t *p_sys = p_filter->p_sys;
383 /* Now try chroma format list */
384 const vlc_fourcc_t *pi_allowed_chromas = get_allowed_chromas( p_filter );
385 for( int i = 0; pi_allowed_chromas[i]; i++ )
387 filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, p_filter->vctx_in, &p_filter->fmt_out );
389 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
390 if( i_chroma == p_filter->fmt_in.i_codec ||
391 i_chroma == p_filter->fmt_out.i_codec )
392 continue;
394 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
395 (char*)&i_chroma );
397 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
398 fmt_mid.i_codec =
399 fmt_mid.video.i_chroma = i_chroma;
400 fmt_mid.video.i_rmask = 0;
401 fmt_mid.video.i_gmask = 0;
402 fmt_mid.video.i_bmask = 0;
403 video_format_FixRgb(&fmt_mid.video);
405 if( filter_chain_AppendConverter( p_sys->p_chain,
406 &fmt_mid ) == VLC_SUCCESS )
408 p_sys->p_video_filter =
409 filter_chain_AppendFilter( p_sys->p_chain,
410 p_filter->psz_name, p_filter->p_cfg,
411 &fmt_mid );
412 if( p_sys->p_video_filter )
414 filter_AddProxyCallbacks( p_filter,
415 p_sys->p_video_filter,
416 RestartFilterCallback );
417 if (p_sys->p_video_filter->ops->video_mouse != NULL)
419 p_sys->custom_ops = *p_sys->p_video_filter->ops;
420 p_sys->p_video_filter->ops = &p_sys->custom_ops;
421 p_sys->custom_ops.video_mouse = ChainMouse;
423 es_format_Clean( &fmt_mid );
424 i_ret = VLC_SUCCESS;
425 p_filter->vctx_out = filter_chain_GetVideoCtxOut( p_sys->p_chain );
426 break;
429 es_format_Clean( &fmt_mid );
431 if( i_ret != VLC_SUCCESS )
432 filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, p_filter->vctx_in, &p_filter->fmt_out );
434 return i_ret;
437 /*****************************************************************************
439 *****************************************************************************/
440 static int CreateChain( filter_t *p_filter, const es_format_t *p_fmt_mid )
442 filter_sys_t *p_sys = p_filter->p_sys;
443 filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, p_filter->vctx_in, &p_filter->fmt_out );
445 if( p_filter->fmt_in.video.orientation != p_fmt_mid->video.orientation)
447 filter_t *p_transform = AppendTransform( p_sys->p_chain, &p_filter->fmt_in, p_fmt_mid );
448 // Check if filter was enough:
449 if( p_transform == NULL )
450 return VLC_EGENERIC;
451 if( es_format_IsSimilar(&p_transform->fmt_out, &p_filter->fmt_out ) )
453 p_filter->vctx_out = p_transform->vctx_out;
454 return VLC_SUCCESS;
457 else
459 if( filter_chain_AppendConverter( p_sys->p_chain, p_fmt_mid ) )
460 return VLC_EGENERIC;
463 if( p_fmt_mid->video.orientation != p_filter->fmt_out.video.orientation)
465 if( AppendTransform( p_sys->p_chain, p_fmt_mid,
466 &p_filter->fmt_out ) == NULL )
467 goto error;
469 else
471 if( filter_chain_AppendConverter( p_sys->p_chain, &p_filter->fmt_out ) )
472 goto error;
474 p_filter->vctx_out = filter_chain_GetVideoCtxOut( p_sys->p_chain );
475 return VLC_SUCCESS;
476 error:
477 //Clean up.
478 filter_chain_Clear( p_sys->p_chain );
479 return VLC_EGENERIC;
482 static int CreateResizeChromaChain( filter_t *p_filter, const es_format_t *p_fmt_mid )
484 filter_sys_t *p_sys = p_filter->p_sys;
485 filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, p_filter->vctx_in, &p_filter->fmt_out );
487 int i_ret = filter_chain_AppendConverter( p_sys->p_chain, p_fmt_mid );
488 if( i_ret != VLC_SUCCESS )
489 return i_ret;
491 if( p_filter->b_allow_fmt_out_change )
493 /* XXX: Update i_sar_num/i_sar_den from last converter. Cf.
494 * p_filter->b_allow_fmt_out_change comment in video_chroma/swscale.c.
495 * */
497 es_format_t fmt_out;
498 es_format_Copy( &fmt_out,
499 filter_chain_GetFmtOut( p_sys->p_chain ) );
500 fmt_out.video.i_chroma = p_filter->fmt_out.video.i_chroma;
502 i_ret = filter_chain_AppendConverter( p_sys->p_chain, &fmt_out );
503 es_format_Clean( &fmt_out );
505 else
506 i_ret = filter_chain_AppendConverter( p_sys->p_chain, &p_filter->fmt_out );
508 if( i_ret != VLC_SUCCESS )
509 filter_chain_Clear( p_sys->p_chain );
510 else
511 p_filter->vctx_out = filter_chain_GetVideoCtxOut( p_sys->p_chain );
513 return i_ret;
516 static filter_t * AppendTransform( filter_chain_t *p_chain, const es_format_t *p_fmt1,
517 const es_format_t *p_fmt2 )
519 video_transform_t transform = video_format_GetTransform(p_fmt1->video.orientation, p_fmt2->video.orientation);
521 const char *type;
523 switch ( transform ) {
525 case TRANSFORM_R90:
526 type = "90";
527 break;
528 case TRANSFORM_R180:
529 type = "180";
530 break;
531 case TRANSFORM_R270:
532 type = "270";
533 break;
534 case TRANSFORM_HFLIP:
535 type = "hflip";
536 break;
537 case TRANSFORM_VFLIP:
538 type = "vflip";
539 break;
540 case TRANSFORM_TRANSPOSE:
541 type = "transpose";
542 break;
543 case TRANSFORM_ANTI_TRANSPOSE:
544 type = "antitranspose";
545 break;
546 default:
547 type = NULL;
548 break;
551 if( !type )
552 return NULL;
554 config_chain_t *cfg;
555 char *name;
556 char config[100];
557 snprintf( config, 100, "transform{type=%s}", type );
558 char *next = config_ChainCreate( &name, &cfg, config );
560 filter_t *p_filter = filter_chain_AppendFilter( p_chain, name, cfg, p_fmt2 );
562 config_ChainDestroy(cfg);
563 free(name);
564 free(next);
566 return p_filter;
569 static void EsFormatMergeSize( es_format_t *p_dst,
570 const es_format_t *p_base,
571 const es_format_t *p_size )
573 es_format_Copy( p_dst, p_base );
575 p_dst->video.i_width = p_size->video.i_width;
576 p_dst->video.i_height = p_size->video.i_height;
578 p_dst->video.i_visible_width = p_size->video.i_visible_width;
579 p_dst->video.i_visible_height = p_size->video.i_visible_height;
581 p_dst->video.i_x_offset = p_size->video.i_x_offset;
582 p_dst->video.i_y_offset = p_size->video.i_y_offset;
584 p_dst->video.orientation = p_size->video.orientation;
585 p_dst->video.i_sar_num = p_size->video.i_sar_num;
586 p_dst->video.i_sar_den = p_size->video.i_sar_den;