1 /*****************************************************************************
2 * chain.c : chain multiple video filter modules as a last resort solution
3 *****************************************************************************
4 * Copyright (C) 2007-2017 VLC authors and VideoLAN
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 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
35 #include <vlc_picture.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 static int ActivateConverter ( vlc_object_t
* );
41 static int ActivateFilter ( vlc_object_t
* );
42 static void Destroy ( vlc_object_t
* );
45 set_description( N_("Video filtering using a chain of video filter modules") )
46 set_capability( "video converter", 1 )
47 set_callbacks( ActivateConverter
, Destroy
)
49 set_capability( "video filter", 0 )
50 set_callbacks( ActivateFilter
, Destroy
)
53 /*****************************************************************************
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
[] = {
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
,
89 { VLC_UNUSED(obj
); VLC_UNUSED(psz_name
); VLC_UNUSED(oldval
);
92 var_TriggerCallback( (vlc_object_t
*)p_data
, "video-filter" );
96 /*****************************************************************************
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
*) )
116 int i_ret
= VLC_EGENERIC
;
118 p_sys
= p_filter
->p_sys
= calloc( 1, sizeof( *p_sys
) );
122 filter_owner_t owner
= {
125 .buffer_new
= BufferNew
,
129 p_sys
->p_chain
= filter_chain_NewVideo( p_filter
, false, &owner
);
130 if( !p_sys
->p_chain
)
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
);
148 i_ret
= pf_build( p_filter
);
150 var_Destroy( p_filter
, MODULE_STRING
"-level" );
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
);
164 p_filter
->pf_video_filter
= Chain
;
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
)
182 return Activate( p_filter
, b_transform
? BuildTransformChain
:
183 b_chroma_resize
? BuildChromaResize
:
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
)
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 /*****************************************************************************
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 /*****************************************************************************
220 *****************************************************************************/
222 static int BuildTransformChain( filter_t
*p_filter
)
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
)
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
)
248 static int BuildChromaResize( filter_t
*p_filter
)
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
)
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
)
272 static int BuildChromaChain( filter_t
*p_filter
)
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
)
285 msg_Dbg( p_filter
, "Trying to use chroma %4.4s as middle man",
288 es_format_Copy( &fmt_mid
, &p_filter
->fmt_in
);
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
)
306 static int BuildFilterChain( filter_t
*p_filter
)
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
)
321 msg_Dbg( p_filter
, "Trying to use chroma %4.4s as middle man",
324 es_format_Copy( &fmt_mid
, &p_filter
->fmt_in
);
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
,
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
);
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
) );
358 filter_chain_Reset( p_filter
->p_sys
->p_chain
, &p_filter
->fmt_in
, &p_filter
->fmt_out
);
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
);
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
)
378 if( es_format_IsSimilar(&p_filter
->fmt_out
, &p_parent
->fmt_out
))
383 if( !filter_chain_AppendConverter( p_parent
->p_sys
->p_chain
,
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
)
396 if( !filter_chain_AppendConverter( p_parent
->p_sys
->p_chain
,
403 filter_chain_Reset( p_parent
->p_sys
->p_chain
, NULL
, NULL
);
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
);
413 switch ( transform
) {
424 case TRANSFORM_HFLIP
:
427 case TRANSFORM_VFLIP
:
430 case TRANSFORM_TRANSPOSE
:
433 case TRANSFORM_ANTI_TRANSPOSE
:
434 type
= "antitranspose";
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
);
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
;