chroma: chain: handle resize converter fmt changes
[vlc.git] / modules / video_chroma / chain.c
blobbfd8f50e8adf2c38f897279436b5396294d1e420
1 /*****************************************************************************
2 * chain.c : chain multiple video filter modules as a last resort solution
3 *****************************************************************************
4 * Copyright (C) 2007-2017 VLC authors and VideoLAN
5 * $Id$
7 * Authors: 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 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
35 #include <vlc_mouse.h>
36 #include <vlc_picture.h>
38 /*****************************************************************************
39 * Module descriptor
40 *****************************************************************************/
41 static int ActivateConverter ( vlc_object_t * );
42 static int ActivateFilter ( vlc_object_t * );
43 static void Destroy ( vlc_object_t * );
45 vlc_module_begin ()
46 set_description( N_("Video filtering using a chain of video filter modules") )
47 set_capability( "video converter", 1 )
48 set_callbacks( ActivateConverter, Destroy )
49 add_submodule ()
50 set_capability( "video filter", 0 )
51 set_callbacks( ActivateFilter, Destroy )
52 vlc_module_end ()
54 /*****************************************************************************
55 * Local prototypes.
56 *****************************************************************************/
57 static picture_t *Chain ( filter_t *, picture_t * );
59 static int BuildTransformChain( filter_t *p_filter );
60 static int BuildChromaResize( filter_t * );
61 static int BuildChromaChain( filter_t *p_filter );
62 static int BuildFilterChain( filter_t *p_filter );
64 static int CreateChain( filter_t *p_parent, const es_format_t *p_fmt_mid );
65 static int CreateResizeChromaChain( filter_t *p_parent, const es_format_t *p_fmt_mid );
66 static filter_t * AppendTransform( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
67 const es_format_t *p_fmt_out );
68 static void EsFormatMergeSize( es_format_t *p_dst,
69 const es_format_t *p_base,
70 const es_format_t *p_size );
72 #define ALLOWED_CHROMAS_YUV10 \
73 VLC_CODEC_I420_10L, \
74 VLC_CODEC_I420_10B, \
75 VLC_CODEC_I420_16L \
77 static const vlc_fourcc_t pi_allowed_chromas_yuv[] = {
78 VLC_CODEC_I420,
79 VLC_CODEC_I422,
80 ALLOWED_CHROMAS_YUV10,
81 VLC_CODEC_RGB32,
82 VLC_CODEC_RGB24,
83 VLC_CODEC_BGRA,
87 static const vlc_fourcc_t pi_allowed_chromas_yuv10[] = {
88 ALLOWED_CHROMAS_YUV10,
89 VLC_CODEC_I420,
90 VLC_CODEC_I422,
91 VLC_CODEC_RGB32,
92 VLC_CODEC_RGB24,
93 VLC_CODEC_BGRA,
97 static const vlc_fourcc_t *get_allowed_chromas( filter_t *p_filter )
99 switch (p_filter->fmt_out.video.i_chroma)
101 case VLC_CODEC_I420_10L:
102 case VLC_CODEC_I420_10B:
103 case VLC_CODEC_I420_16L:
104 case VLC_CODEC_CVPX_P010:
105 case VLC_CODEC_D3D9_OPAQUE_10B:
106 case VLC_CODEC_D3D11_OPAQUE_10B:
107 case VLC_CODEC_VAAPI_420_10BPP:
108 return pi_allowed_chromas_yuv10;
109 default:
110 return pi_allowed_chromas_yuv;
114 struct filter_sys_t
116 filter_chain_t *p_chain;
117 filter_t *p_video_filter;
120 /* Restart filter callback */
121 static int RestartFilterCallback( vlc_object_t *obj, char const *psz_name,
122 vlc_value_t oldval, vlc_value_t newval,
123 void *p_data )
124 { VLC_UNUSED(obj); VLC_UNUSED(psz_name); VLC_UNUSED(oldval);
125 VLC_UNUSED(newval);
127 var_TriggerCallback( (vlc_object_t *)p_data, "video-filter" );
128 return VLC_SUCCESS;
131 /*****************************************************************************
132 * Buffer management
133 *****************************************************************************/
134 static picture_t *BufferNew( filter_t *p_filter )
136 filter_t *p_parent = p_filter->owner.sys;
138 return filter_NewPicture( p_parent );
141 #define CHAIN_LEVEL_MAX 2
143 /*****************************************************************************
144 * Activate: allocate a chroma function
145 *****************************************************************************
146 * This function allocates and initializes a chroma function
147 *****************************************************************************/
148 static int Activate( filter_t *p_filter, int (*pf_build)(filter_t *) )
150 filter_sys_t *p_sys;
151 int i_ret = VLC_EGENERIC;
153 p_sys = p_filter->p_sys = calloc( 1, sizeof( *p_sys ) );
154 if( !p_sys )
155 return VLC_ENOMEM;
157 filter_owner_t owner = {
158 .sys = p_filter,
159 .video = {
160 .buffer_new = BufferNew,
164 p_sys->p_chain = filter_chain_NewVideo( p_filter, p_filter->b_allow_fmt_out_change, &owner );
165 if( !p_sys->p_chain )
167 free( p_sys );
168 return VLC_EGENERIC;
171 int type = VLC_VAR_INTEGER;
172 if( var_Type( p_filter->obj.parent, "chain-level" ) != 0 )
173 type |= VLC_VAR_DOINHERIT;
175 var_Create( p_filter, "chain-level", type );
176 /* Note: atomicity is not actually needed here. */
177 var_IncInteger( p_filter, "chain-level" );
179 int level = var_GetInteger( p_filter, "chain-level" );
180 if( level < 0 || level > CHAIN_LEVEL_MAX )
181 msg_Err( p_filter, "Too high level of recursion (%d)", level );
182 else
183 i_ret = pf_build( p_filter );
185 var_Destroy( p_filter, "chain-level" );
187 if( i_ret )
189 /* Hum ... looks like this really isn't going to work. Too bad. */
190 if (p_sys->p_video_filter)
191 filter_DelProxyCallbacks( p_filter, p_sys->p_video_filter,
192 RestartFilterCallback );
193 filter_chain_Delete( p_sys->p_chain );
194 free( p_sys );
195 return VLC_EGENERIC;
197 else if( p_filter->b_allow_fmt_out_change )
199 es_format_Clean( &p_filter->fmt_out );
200 es_format_Copy( &p_filter->fmt_out,
201 filter_chain_GetFmtOut( p_filter->p_sys->p_chain ) );
203 /* */
204 p_filter->pf_video_filter = Chain;
205 return VLC_SUCCESS;
208 static int ActivateConverter( vlc_object_t *p_this )
210 filter_t *p_filter = (filter_t *)p_this;
212 const bool b_chroma = p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma;
213 const bool b_resize = p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ||
214 p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height;
216 const bool b_chroma_resize = b_chroma && b_resize;
217 const bool b_transform = p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation;
219 if( !b_chroma && !b_chroma_resize && !b_transform)
220 return VLC_EGENERIC;
222 return Activate( p_filter, b_transform ? BuildTransformChain :
223 b_chroma_resize ? BuildChromaResize :
224 BuildChromaChain );
227 static int ActivateFilter( vlc_object_t *p_this )
229 filter_t *p_filter = (filter_t *)p_this;
231 if( !p_filter->b_allow_fmt_out_change || p_filter->psz_name == NULL )
232 return VLC_EGENERIC;
234 if( var_Type( p_filter->obj.parent, "chain-filter-level" ) != 0 )
235 return VLC_EGENERIC;
237 var_Create( p_filter, "chain-filter-level", VLC_VAR_INTEGER );
238 int i_ret = Activate( p_filter, BuildFilterChain );
239 var_Destroy( p_filter, "chain-filter-level" );
241 return i_ret;
244 static void Destroy( vlc_object_t *p_this )
246 filter_t *p_filter = (filter_t *)p_this;
248 if (p_filter->p_sys->p_video_filter)
249 filter_DelProxyCallbacks( p_filter, p_filter->p_sys->p_video_filter,
250 RestartFilterCallback );
251 filter_chain_Delete( p_filter->p_sys->p_chain );
252 free( p_filter->p_sys );
255 /*****************************************************************************
256 * Chain
257 *****************************************************************************/
258 static picture_t *Chain( filter_t *p_filter, picture_t *p_pic )
260 return filter_chain_VideoFilter( p_filter->p_sys->p_chain, p_pic );
263 /*****************************************************************************
264 * Builders
265 *****************************************************************************/
267 static int BuildTransformChain( filter_t *p_filter )
270 es_format_t fmt_mid;
271 int i_ret;
273 /* Lets try transform first, then (potentially) resize+chroma */
274 msg_Dbg( p_filter, "Trying to build transform, then chroma+resize" );
275 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
276 video_format_TransformTo(&fmt_mid.video, p_filter->fmt_out.video.orientation);
277 i_ret = CreateChain( p_filter, &fmt_mid );
278 es_format_Clean( &fmt_mid );
279 if( i_ret == VLC_SUCCESS )
280 return VLC_SUCCESS;
282 /* Lets try resize+chroma first, then transform */
283 msg_Dbg( p_filter, "Trying to build chroma+resize" );
284 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
285 i_ret = CreateChain( p_filter, &fmt_mid );
286 es_format_Clean( &fmt_mid );
287 if( i_ret == VLC_SUCCESS )
288 return VLC_SUCCESS;
290 return VLC_EGENERIC;
293 static int BuildChromaResize( filter_t *p_filter )
295 es_format_t fmt_mid;
296 int i_ret;
298 /* Lets try resizing and then doing the chroma conversion */
299 msg_Dbg( p_filter, "Trying to build resize+chroma" );
300 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_in, &p_filter->fmt_out );
301 i_ret = CreateResizeChromaChain( p_filter, &fmt_mid );
302 es_format_Clean( &fmt_mid );
304 if( i_ret == VLC_SUCCESS )
305 return VLC_SUCCESS;
307 /* Lets try it the other way arround (chroma and then resize) */
308 msg_Dbg( p_filter, "Trying to build chroma+resize" );
309 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
310 i_ret = CreateChain( p_filter, &fmt_mid );
311 es_format_Clean( &fmt_mid );
312 if( i_ret == VLC_SUCCESS )
313 return VLC_SUCCESS;
315 return VLC_EGENERIC;
318 static int BuildChromaChain( filter_t *p_filter )
320 es_format_t fmt_mid;
321 int i_ret = VLC_EGENERIC;
323 /* Now try chroma format list */
324 const vlc_fourcc_t *pi_allowed_chromas = get_allowed_chromas( p_filter );
325 for( int i = 0; pi_allowed_chromas[i]; i++ )
327 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
328 if( i_chroma == p_filter->fmt_in.i_codec ||
329 i_chroma == p_filter->fmt_out.i_codec )
330 continue;
332 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
333 (char*)&i_chroma );
335 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
336 fmt_mid.i_codec =
337 fmt_mid.video.i_chroma = i_chroma;
338 fmt_mid.video.i_rmask = 0;
339 fmt_mid.video.i_gmask = 0;
340 fmt_mid.video.i_bmask = 0;
341 video_format_FixRgb(&fmt_mid.video);
343 i_ret = CreateChain( p_filter, &fmt_mid );
344 es_format_Clean( &fmt_mid );
346 if( i_ret == VLC_SUCCESS )
347 break;
350 return i_ret;
353 static int ChainMouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
354 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
356 (void) p_old;
357 return filter_chain_MouseFilter( p_filter->p_sys->p_chain, p_mouse, p_new );
360 static int BuildFilterChain( filter_t *p_filter )
362 es_format_t fmt_mid;
363 int i_ret = VLC_EGENERIC;
365 /* Now try chroma format list */
366 const vlc_fourcc_t *pi_allowed_chromas = get_allowed_chromas( p_filter );
367 for( int i = 0; pi_allowed_chromas[i]; i++ )
369 filter_chain_Reset( p_filter->p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out );
371 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
372 if( i_chroma == p_filter->fmt_in.i_codec ||
373 i_chroma == p_filter->fmt_out.i_codec )
374 continue;
376 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
377 (char*)&i_chroma );
379 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
380 fmt_mid.i_codec =
381 fmt_mid.video.i_chroma = i_chroma;
382 fmt_mid.video.i_rmask = 0;
383 fmt_mid.video.i_gmask = 0;
384 fmt_mid.video.i_bmask = 0;
385 video_format_FixRgb(&fmt_mid.video);
387 if( filter_chain_AppendConverter( p_filter->p_sys->p_chain,
388 NULL, &fmt_mid ) == VLC_SUCCESS )
390 p_filter->p_sys->p_video_filter =
391 filter_chain_AppendFilter( p_filter->p_sys->p_chain,
392 p_filter->psz_name, p_filter->p_cfg,
393 &fmt_mid, &fmt_mid );
394 if( p_filter->p_sys->p_video_filter )
396 filter_AddProxyCallbacks( p_filter,
397 p_filter->p_sys->p_video_filter,
398 RestartFilterCallback );
399 if (p_filter->p_sys->p_video_filter->pf_video_mouse != NULL)
400 p_filter->pf_video_mouse = ChainMouse;
401 es_format_Clean( &fmt_mid );
402 i_ret = VLC_SUCCESS;
403 break;
406 es_format_Clean( &fmt_mid );
408 if( i_ret != VLC_SUCCESS )
409 filter_chain_Reset( p_filter->p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out );
411 return i_ret;
414 /*****************************************************************************
416 *****************************************************************************/
417 static int CreateChain( filter_t *p_parent, const es_format_t *p_fmt_mid )
419 filter_chain_Reset( p_parent->p_sys->p_chain, &p_parent->fmt_in, &p_parent->fmt_out );
421 filter_t *p_filter;
423 if( p_parent->fmt_in.video.orientation != p_fmt_mid->video.orientation)
425 p_filter = AppendTransform( p_parent->p_sys->p_chain, &p_parent->fmt_in, p_fmt_mid );
426 // Check if filter was enough:
427 if( p_filter == NULL )
428 return VLC_EGENERIC;
429 if( es_format_IsSimilar(&p_filter->fmt_out, &p_parent->fmt_out ))
430 return VLC_SUCCESS;
432 else
434 if( filter_chain_AppendConverter( p_parent->p_sys->p_chain,
435 NULL, p_fmt_mid ) )
436 return VLC_EGENERIC;
439 if( p_fmt_mid->video.orientation != p_parent->fmt_out.video.orientation)
441 if( AppendTransform( p_parent->p_sys->p_chain, p_fmt_mid,
442 &p_parent->fmt_out ) == NULL )
443 goto error;
445 else
447 if( filter_chain_AppendConverter( p_parent->p_sys->p_chain,
448 p_fmt_mid, NULL ) )
449 goto error;
451 return VLC_SUCCESS;
452 error:
453 //Clean up.
454 filter_chain_Reset( p_parent->p_sys->p_chain, NULL, NULL );
455 return VLC_EGENERIC;
458 static int CreateResizeChromaChain( filter_t *p_parent, const es_format_t *p_fmt_mid )
460 filter_chain_Reset( p_parent->p_sys->p_chain, &p_parent->fmt_in, &p_parent->fmt_out );
462 int i_ret = filter_chain_AppendConverter( p_parent->p_sys->p_chain,
463 NULL, p_fmt_mid );
464 if( i_ret != VLC_SUCCESS )
465 return i_ret;
467 if( p_parent->b_allow_fmt_out_change )
469 /* XXX: Update i_sar_num/i_sar_den from last converter. Cf.
470 * p_filter->b_allow_fmt_out_change comment in video_chroma/swscale.c.
471 * */
473 es_format_t fmt_out;
474 es_format_Copy( &fmt_out,
475 filter_chain_GetFmtOut( p_parent->p_sys->p_chain ) );
476 fmt_out.video.i_chroma = p_parent->fmt_out.video.i_chroma;
478 i_ret = filter_chain_AppendConverter( p_parent->p_sys->p_chain,
479 NULL, &fmt_out );
480 es_format_Clean( &fmt_out );
482 else
483 i_ret = filter_chain_AppendConverter( p_parent->p_sys->p_chain,
484 NULL, &p_parent->fmt_out );
486 if( i_ret != VLC_SUCCESS )
487 filter_chain_Reset( p_parent->p_sys->p_chain, NULL, NULL );
488 return i_ret;
491 static filter_t * AppendTransform( filter_chain_t *p_chain, const es_format_t *p_fmt1,
492 const es_format_t *p_fmt2 )
494 video_transform_t transform = video_format_GetTransform(p_fmt1->video.orientation, p_fmt2->video.orientation);
496 const char *type;
498 switch ( transform ) {
500 case TRANSFORM_R90:
501 type = "90";
502 break;
503 case TRANSFORM_R180:
504 type = "180";
505 break;
506 case TRANSFORM_R270:
507 type = "270";
508 break;
509 case TRANSFORM_HFLIP:
510 type = "hflip";
511 break;
512 case TRANSFORM_VFLIP:
513 type = "vflip";
514 break;
515 case TRANSFORM_TRANSPOSE:
516 type = "transpose";
517 break;
518 case TRANSFORM_ANTI_TRANSPOSE:
519 type = "antitranspose";
520 break;
521 default:
522 type = NULL;
523 break;
526 if( !type )
527 return NULL;
529 config_chain_t *cfg;
530 char *name;
531 char config[100];
532 snprintf( config, 100, "transform{type=%s}", type );
533 char *next = config_ChainCreate( &name, &cfg, config );
535 filter_t *p_filter = filter_chain_AppendFilter( p_chain, name, cfg, p_fmt1, p_fmt2 );
537 config_ChainDestroy(cfg);
538 free(name);
539 free(next);
541 return p_filter;
544 static void EsFormatMergeSize( es_format_t *p_dst,
545 const es_format_t *p_base,
546 const es_format_t *p_size )
548 es_format_Copy( p_dst, p_base );
550 p_dst->video.i_width = p_size->video.i_width;
551 p_dst->video.i_height = p_size->video.i_height;
553 p_dst->video.i_visible_width = p_size->video.i_visible_width;
554 p_dst->video.i_visible_height = p_size->video.i_visible_height;
556 p_dst->video.i_x_offset = p_size->video.i_x_offset;
557 p_dst->video.i_y_offset = p_size->video.i_y_offset;
559 p_dst->video.orientation = p_size->video.orientation;
560 p_dst->video.i_sar_num = p_size->video.i_sar_num;
561 p_dst->video.i_sar_den = p_size->video.i_sar_den;