chain: add filter proxy callbacks
[vlc.git] / modules / video_chroma / chain.c
blobbf4f426a8f6ecbc0f48369aac69c97499f18fad0
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_picture.h>
37 /*****************************************************************************
38 * Module descriptor
39 *****************************************************************************/
40 static int ActivateConverter ( vlc_object_t * );
41 static int ActivateFilter ( vlc_object_t * );
42 static void Destroy ( vlc_object_t * );
44 vlc_module_begin ()
45 set_description( N_("Video filtering using a chain of video filter modules") )
46 set_capability( "video converter", 1 )
47 set_callbacks( ActivateConverter, Destroy )
48 add_submodule ()
49 set_capability( "video filter", 0 )
50 set_callbacks( ActivateFilter, Destroy )
51 vlc_module_end ()
53 /*****************************************************************************
54 * Local prototypes.
55 *****************************************************************************/
56 static picture_t *Chain ( filter_t *, picture_t * );
58 static int BuildTransformChain( filter_t *p_filter );
59 static int BuildChromaResize( filter_t * );
60 static int BuildChromaChain( filter_t *p_filter );
61 static int BuildFilterChain( filter_t *p_filter );
63 static int CreateChain( filter_t *p_parent, es_format_t *p_fmt_mid );
64 static filter_t * AppendTransform( filter_chain_t *p_chain, es_format_t *p_fmt_in, es_format_t *p_fmt_out );
65 static void EsFormatMergeSize( es_format_t *p_dst,
66 const es_format_t *p_base,
67 const es_format_t *p_size );
69 static const vlc_fourcc_t pi_allowed_chromas[] = {
70 VLC_CODEC_I420,
71 VLC_CODEC_I422,
72 VLC_CODEC_I420_10L,
73 VLC_CODEC_I420_16L,
74 VLC_CODEC_RGB32,
75 VLC_CODEC_RGB24,
79 struct filter_sys_t
81 filter_chain_t *p_chain;
82 filter_t *p_video_filter;
85 /* Restart filter callback */
86 static int RestartFilterCallback( vlc_object_t *obj, char const *psz_name,
87 vlc_value_t oldval, vlc_value_t newval,
88 void *p_data )
89 { VLC_UNUSED(obj); VLC_UNUSED(psz_name); VLC_UNUSED(oldval);
90 VLC_UNUSED(newval);
92 var_TriggerCallback( (vlc_object_t *)p_data, "video-filter" );
93 return VLC_SUCCESS;
96 /*****************************************************************************
97 * Buffer management
98 *****************************************************************************/
99 static picture_t *BufferNew( filter_t *p_filter )
101 filter_t *p_parent = p_filter->owner.sys;
103 return filter_NewPicture( p_parent );
106 #define CHAIN_LEVEL_MAX 2
108 /*****************************************************************************
109 * Activate: allocate a chroma function
110 *****************************************************************************
111 * This function allocates and initializes a chroma function
112 *****************************************************************************/
113 static int Activate( filter_t *p_filter, int (*pf_build)(filter_t *) )
115 filter_sys_t *p_sys;
116 int i_ret = VLC_EGENERIC;
118 p_sys = p_filter->p_sys = calloc( 1, sizeof( *p_sys ) );
119 if( !p_sys )
120 return VLC_ENOMEM;
122 filter_owner_t owner = {
123 .sys = p_filter,
124 .video = {
125 .buffer_new = BufferNew,
129 p_sys->p_chain = filter_chain_NewVideo( p_filter, false, &owner );
130 if( !p_sys->p_chain )
132 free( p_sys );
133 return VLC_EGENERIC;
136 int type = VLC_VAR_INTEGER;
137 if( var_Type( p_filter->obj.parent, MODULE_STRING "-level" ) != 0 )
138 type |= VLC_VAR_DOINHERIT;
140 var_Create( p_filter, MODULE_STRING "-level", type );
141 /* Note: atomicity is not actually needed here. */
142 var_IncInteger( p_filter, MODULE_STRING "-level" );
144 int level = var_GetInteger( p_filter, MODULE_STRING "-level" );
145 if( level < 0 || level > CHAIN_LEVEL_MAX )
146 msg_Err( p_filter, "Too high level of recursion (%d)", level );
147 else
148 i_ret = pf_build( p_filter );
150 var_Destroy( p_filter, MODULE_STRING "-level" );
152 if( i_ret )
154 /* Hum ... looks like this really isn't going to work. Too bad. */
155 if (p_sys->p_video_filter)
156 filter_DelProxyCallbacks( p_filter->obj.parent,
157 p_sys->p_video_filter,
158 RestartFilterCallback );
159 filter_chain_Delete( p_sys->p_chain );
160 free( p_sys );
161 return VLC_EGENERIC;
163 /* */
164 p_filter->pf_video_filter = Chain;
165 return VLC_SUCCESS;
168 static int ActivateConverter( vlc_object_t *p_this )
170 filter_t *p_filter = (filter_t *)p_this;
172 const bool b_chroma = p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma;
173 const bool b_resize = p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ||
174 p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height;
176 const bool b_chroma_resize = b_chroma && b_resize;
177 const bool b_transform = p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation;
179 if( !b_chroma && !b_chroma_resize && !b_transform)
180 return VLC_EGENERIC;
182 return Activate( p_filter, b_transform ? BuildTransformChain :
183 b_chroma_resize ? BuildChromaResize :
184 BuildChromaChain );
187 static int ActivateFilter( vlc_object_t *p_this )
189 filter_t *p_filter = (filter_t *)p_this;
191 if( !p_filter->b_allow_fmt_out_change || p_filter->psz_name == NULL )
192 return VLC_EGENERIC;
194 /* Try to add a converter before the requested filter */
195 return Activate( p_filter, BuildFilterChain );
198 static void Destroy( vlc_object_t *p_this )
200 filter_t *p_filter = (filter_t *)p_this;
202 if (p_filter->p_sys->p_video_filter)
203 filter_DelProxyCallbacks( p_filter->obj.parent,
204 p_filter->p_sys->p_video_filter,
205 RestartFilterCallback );
206 filter_chain_Delete( p_filter->p_sys->p_chain );
207 free( p_filter->p_sys );
210 /*****************************************************************************
211 * Chain
212 *****************************************************************************/
213 static picture_t *Chain( filter_t *p_filter, picture_t *p_pic )
215 return filter_chain_VideoFilter( p_filter->p_sys->p_chain, p_pic );
218 /*****************************************************************************
219 * Builders
220 *****************************************************************************/
222 static int BuildTransformChain( filter_t *p_filter )
225 es_format_t fmt_mid;
226 int i_ret;
228 /* Lets try transform first, then (potentially) resize+chroma */
229 msg_Dbg( p_filter, "Trying to build transform, then chroma+resize" );
230 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
231 video_format_TransformTo(&fmt_mid.video, p_filter->fmt_out.video.orientation);
232 i_ret = CreateChain( p_filter, &fmt_mid );
233 es_format_Clean( &fmt_mid );
234 if( i_ret == VLC_SUCCESS )
235 return VLC_SUCCESS;
237 /* Lets try resize+chroma first, then transform */
238 msg_Dbg( p_filter, "Trying to build chroma+resize" );
239 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
240 i_ret = CreateChain( p_filter, &fmt_mid );
241 es_format_Clean( &fmt_mid );
242 if( i_ret == VLC_SUCCESS )
243 return VLC_SUCCESS;
245 return VLC_EGENERIC;
248 static int BuildChromaResize( filter_t *p_filter )
250 es_format_t fmt_mid;
251 int i_ret;
253 /* Lets try resizing and then doing the chroma conversion */
254 msg_Dbg( p_filter, "Trying to build resize+chroma" );
255 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_in, &p_filter->fmt_out );
256 i_ret = CreateChain( p_filter, &fmt_mid );
257 es_format_Clean( &fmt_mid );
258 if( i_ret == VLC_SUCCESS )
259 return VLC_SUCCESS;
261 /* Lets try it the other way arround (chroma and then resize) */
262 msg_Dbg( p_filter, "Trying to build chroma+resize" );
263 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
264 i_ret = CreateChain( p_filter, &fmt_mid );
265 es_format_Clean( &fmt_mid );
266 if( i_ret == VLC_SUCCESS )
267 return VLC_SUCCESS;
269 return VLC_EGENERIC;
272 static int BuildChromaChain( filter_t *p_filter )
274 es_format_t fmt_mid;
275 int i_ret = VLC_EGENERIC;
277 /* Now try chroma format list */
278 for( int i = 0; pi_allowed_chromas[i]; i++ )
280 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
281 if( i_chroma == p_filter->fmt_in.i_codec ||
282 i_chroma == p_filter->fmt_out.i_codec )
283 continue;
285 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
286 (char*)&i_chroma );
288 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
289 fmt_mid.i_codec =
290 fmt_mid.video.i_chroma = i_chroma;
291 fmt_mid.video.i_rmask = 0;
292 fmt_mid.video.i_gmask = 0;
293 fmt_mid.video.i_bmask = 0;
294 video_format_FixRgb(&fmt_mid.video);
296 i_ret = CreateChain( p_filter, &fmt_mid );
297 es_format_Clean( &fmt_mid );
299 if( i_ret == VLC_SUCCESS )
300 break;
303 return i_ret;
306 static int BuildFilterChain( filter_t *p_filter )
308 es_format_t fmt_mid;
309 int i_ret = VLC_EGENERIC;
311 /* Now try chroma format list */
312 for( int i = 0; pi_allowed_chromas[i]; i++ )
314 filter_chain_Reset( p_filter->p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out );
316 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
317 if( i_chroma == p_filter->fmt_in.i_codec ||
318 i_chroma == p_filter->fmt_out.i_codec )
319 continue;
321 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
322 (char*)&i_chroma );
324 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
325 fmt_mid.i_codec =
326 fmt_mid.video.i_chroma = i_chroma;
327 fmt_mid.video.i_rmask = 0;
328 fmt_mid.video.i_gmask = 0;
329 fmt_mid.video.i_bmask = 0;
330 video_format_FixRgb(&fmt_mid.video);
332 if( filter_chain_AppendConverter( p_filter->p_sys->p_chain,
333 NULL, &fmt_mid ) )
335 p_filter->p_sys->p_video_filter =
336 filter_chain_AppendFilter( p_filter->p_sys->p_chain,
337 p_filter->psz_name, p_filter->p_cfg,
338 &fmt_mid, &fmt_mid );
339 if( p_filter->p_sys->p_video_filter )
341 filter_AddProxyCallbacks( p_filter->obj.parent,
342 p_filter->p_sys->p_video_filter,
343 RestartFilterCallback );
344 es_format_Clean( &fmt_mid );
345 i_ret = VLC_SUCCESS;
346 break;
349 es_format_Clean( &fmt_mid );
351 if (i_ret == VLC_SUCCESS)
353 es_format_Clean( &p_filter->fmt_out );
354 es_format_Copy( &p_filter->fmt_out,
355 filter_chain_GetFmtOut( p_filter->p_sys->p_chain ) );
357 else
358 filter_chain_Reset( p_filter->p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out );
360 return i_ret;
363 /*****************************************************************************
365 *****************************************************************************/
366 static int CreateChain( filter_t *p_parent, es_format_t *p_fmt_mid )
368 filter_chain_Reset( p_parent->p_sys->p_chain, &p_parent->fmt_in, &p_parent->fmt_out );
370 filter_t *p_filter;
372 if( p_parent->fmt_in.video.orientation != p_fmt_mid->video.orientation)
374 p_filter = AppendTransform( p_parent->p_sys->p_chain, &p_parent->fmt_in, p_fmt_mid );
375 // Check if filter was enough:
376 if( p_filter == NULL )
377 return VLC_EGENERIC;
378 if( es_format_IsSimilar(&p_filter->fmt_out, &p_parent->fmt_out ))
379 return VLC_SUCCESS;
381 else
383 if( !filter_chain_AppendConverter( p_parent->p_sys->p_chain,
384 NULL, p_fmt_mid ) )
385 return VLC_EGENERIC;
388 if( p_fmt_mid->video.orientation != p_parent->fmt_out.video.orientation)
390 if( AppendTransform( p_parent->p_sys->p_chain, p_fmt_mid,
391 &p_parent->fmt_out ) == NULL )
392 goto error;
394 else
396 if( !filter_chain_AppendConverter( p_parent->p_sys->p_chain,
397 p_fmt_mid, NULL ) )
398 goto error;
400 return VLC_SUCCESS;
401 error:
402 //Clean up.
403 filter_chain_Reset( p_parent->p_sys->p_chain, NULL, NULL );
404 return VLC_EGENERIC;
407 static filter_t * AppendTransform( filter_chain_t *p_chain, es_format_t *p_fmt1, es_format_t *p_fmt2 )
409 video_transform_t transform = video_format_GetTransform(p_fmt1->video.orientation, p_fmt2->video.orientation);
411 const char *type;
413 switch ( transform ) {
415 case TRANSFORM_R90:
416 type = "90";
417 break;
418 case TRANSFORM_R180:
419 type = "180";
420 break;
421 case TRANSFORM_R270:
422 type = "270";
423 break;
424 case TRANSFORM_HFLIP:
425 type = "hflip";
426 break;
427 case TRANSFORM_VFLIP:
428 type = "vflip";
429 break;
430 case TRANSFORM_TRANSPOSE:
431 type = "transpose";
432 break;
433 case TRANSFORM_ANTI_TRANSPOSE:
434 type = "antitranspose";
435 break;
436 default:
437 type = NULL;
438 break;
441 if( !type )
442 return NULL;
444 config_chain_t *cfg;
445 char *name;
446 char config[100];
447 snprintf( config, 100, "transform{type=%s}", type );
448 char *next = config_ChainCreate( &name, &cfg, config );
450 filter_t *p_filter = filter_chain_AppendFilter( p_chain, name, cfg, p_fmt1, p_fmt2 );
452 config_ChainDestroy(cfg);
453 free(name);
454 free(next);
456 return p_filter;
459 static void EsFormatMergeSize( es_format_t *p_dst,
460 const es_format_t *p_base,
461 const es_format_t *p_size )
463 es_format_Copy( p_dst, p_base );
465 p_dst->video.i_width = p_size->video.i_width;
466 p_dst->video.i_height = p_size->video.i_height;
468 p_dst->video.i_visible_width = p_size->video.i_visible_width;
469 p_dst->video.i_visible_height = p_size->video.i_visible_height;
471 p_dst->video.i_x_offset = p_size->video.i_x_offset;
472 p_dst->video.i_y_offset = p_size->video.i_y_offset;
474 p_dst->video.orientation = p_size->video.orientation;