d3d11: handle VLC_CODEC_D3D11_OPAQUE_10B upload/download
[vlc.git] / modules / video_chroma / chain.c
blobcc8b0c349681fe09f89b980aad23b335fe917f5a
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, es_format_t *p_fmt_mid );
65 static filter_t * AppendTransform( filter_chain_t *p_chain, es_format_t *p_fmt_in, 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 struct filter_sys_t
114 filter_chain_t *p_chain;
115 filter_t *p_video_filter;
118 /* Restart filter callback */
119 static int RestartFilterCallback( vlc_object_t *obj, char const *psz_name,
120 vlc_value_t oldval, vlc_value_t newval,
121 void *p_data )
122 { VLC_UNUSED(obj); VLC_UNUSED(psz_name); VLC_UNUSED(oldval);
123 VLC_UNUSED(newval);
125 var_TriggerCallback( (vlc_object_t *)p_data, "video-filter" );
126 return VLC_SUCCESS;
129 /*****************************************************************************
130 * Buffer management
131 *****************************************************************************/
132 static picture_t *BufferNew( filter_t *p_filter )
134 filter_t *p_parent = p_filter->owner.sys;
136 return filter_NewPicture( p_parent );
139 #define CHAIN_LEVEL_MAX 2
141 /*****************************************************************************
142 * Activate: allocate a chroma function
143 *****************************************************************************
144 * This function allocates and initializes a chroma function
145 *****************************************************************************/
146 static int Activate( filter_t *p_filter, int (*pf_build)(filter_t *) )
148 filter_sys_t *p_sys;
149 int i_ret = VLC_EGENERIC;
151 p_sys = p_filter->p_sys = calloc( 1, sizeof( *p_sys ) );
152 if( !p_sys )
153 return VLC_ENOMEM;
155 filter_owner_t owner = {
156 .sys = p_filter,
157 .video = {
158 .buffer_new = BufferNew,
162 p_sys->p_chain = filter_chain_NewVideo( p_filter, false, &owner );
163 if( !p_sys->p_chain )
165 free( p_sys );
166 return VLC_EGENERIC;
169 int type = VLC_VAR_INTEGER;
170 if( var_Type( p_filter->obj.parent, "chain-level" ) != 0 )
171 type |= VLC_VAR_DOINHERIT;
173 var_Create( p_filter, "chain-level", type );
174 /* Note: atomicity is not actually needed here. */
175 var_IncInteger( p_filter, "chain-level" );
177 int level = var_GetInteger( p_filter, "chain-level" );
178 if( level < 0 || level > CHAIN_LEVEL_MAX )
179 msg_Err( p_filter, "Too high level of recursion (%d)", level );
180 else
181 i_ret = pf_build( p_filter );
183 var_Destroy( p_filter, "chain-level" );
185 if( i_ret )
187 /* Hum ... looks like this really isn't going to work. Too bad. */
188 if (p_sys->p_video_filter)
189 filter_DelProxyCallbacks( p_filter, p_sys->p_video_filter,
190 RestartFilterCallback );
191 filter_chain_Delete( p_sys->p_chain );
192 free( p_sys );
193 return VLC_EGENERIC;
195 /* */
196 p_filter->pf_video_filter = Chain;
197 return VLC_SUCCESS;
200 static int ActivateConverter( vlc_object_t *p_this )
202 filter_t *p_filter = (filter_t *)p_this;
204 const bool b_chroma = p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma;
205 const bool b_resize = p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ||
206 p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height;
208 const bool b_chroma_resize = b_chroma && b_resize;
209 const bool b_transform = p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation;
211 if( !b_chroma && !b_chroma_resize && !b_transform)
212 return VLC_EGENERIC;
214 return Activate( p_filter, b_transform ? BuildTransformChain :
215 b_chroma_resize ? BuildChromaResize :
216 BuildChromaChain );
219 static int ActivateFilter( vlc_object_t *p_this )
221 filter_t *p_filter = (filter_t *)p_this;
223 if( !p_filter->b_allow_fmt_out_change || p_filter->psz_name == NULL )
224 return VLC_EGENERIC;
226 if( var_Type( p_filter->obj.parent, "chain-filter-level" ) != 0 )
227 return VLC_EGENERIC;
229 var_Create( p_filter, "chain-filter-level", VLC_VAR_INTEGER );
230 int i_ret = Activate( p_filter, BuildFilterChain );
231 var_Destroy( p_filter, "chain-filter-level" );
233 return i_ret;
236 static void Destroy( vlc_object_t *p_this )
238 filter_t *p_filter = (filter_t *)p_this;
240 if (p_filter->p_sys->p_video_filter)
241 filter_DelProxyCallbacks( p_filter, p_filter->p_sys->p_video_filter,
242 RestartFilterCallback );
243 filter_chain_Delete( p_filter->p_sys->p_chain );
244 free( p_filter->p_sys );
247 /*****************************************************************************
248 * Chain
249 *****************************************************************************/
250 static picture_t *Chain( filter_t *p_filter, picture_t *p_pic )
252 return filter_chain_VideoFilter( p_filter->p_sys->p_chain, p_pic );
255 /*****************************************************************************
256 * Builders
257 *****************************************************************************/
259 static int BuildTransformChain( filter_t *p_filter )
262 es_format_t fmt_mid;
263 int i_ret;
265 /* Lets try transform first, then (potentially) resize+chroma */
266 msg_Dbg( p_filter, "Trying to build transform, then chroma+resize" );
267 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
268 video_format_TransformTo(&fmt_mid.video, p_filter->fmt_out.video.orientation);
269 i_ret = CreateChain( p_filter, &fmt_mid );
270 es_format_Clean( &fmt_mid );
271 if( i_ret == VLC_SUCCESS )
272 return VLC_SUCCESS;
274 /* Lets try resize+chroma first, then transform */
275 msg_Dbg( p_filter, "Trying to build chroma+resize" );
276 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
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 return VLC_EGENERIC;
285 static int BuildChromaResize( filter_t *p_filter )
287 es_format_t fmt_mid;
288 int i_ret;
290 /* Lets try resizing and then doing the chroma conversion */
291 msg_Dbg( p_filter, "Trying to build resize+chroma" );
292 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_in, &p_filter->fmt_out );
293 i_ret = CreateChain( p_filter, &fmt_mid );
294 es_format_Clean( &fmt_mid );
295 if( i_ret == VLC_SUCCESS )
296 return VLC_SUCCESS;
298 /* Lets try it the other way arround (chroma and then resize) */
299 msg_Dbg( p_filter, "Trying to build chroma+resize" );
300 EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
301 i_ret = CreateChain( p_filter, &fmt_mid );
302 es_format_Clean( &fmt_mid );
303 if( i_ret == VLC_SUCCESS )
304 return VLC_SUCCESS;
306 return VLC_EGENERIC;
309 static int BuildChromaChain( filter_t *p_filter )
311 es_format_t fmt_mid;
312 int i_ret = VLC_EGENERIC;
314 /* Now try chroma format list */
315 const vlc_fourcc_t *pi_allowed_chromas = get_allowed_chromas( p_filter );
316 for( int i = 0; pi_allowed_chromas[i]; i++ )
318 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
319 if( i_chroma == p_filter->fmt_in.i_codec ||
320 i_chroma == p_filter->fmt_out.i_codec )
321 continue;
323 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
324 (char*)&i_chroma );
326 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
327 fmt_mid.i_codec =
328 fmt_mid.video.i_chroma = i_chroma;
329 fmt_mid.video.i_rmask = 0;
330 fmt_mid.video.i_gmask = 0;
331 fmt_mid.video.i_bmask = 0;
332 video_format_FixRgb(&fmt_mid.video);
334 i_ret = CreateChain( p_filter, &fmt_mid );
335 es_format_Clean( &fmt_mid );
337 if( i_ret == VLC_SUCCESS )
338 break;
341 return i_ret;
344 static int ChainMouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
345 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
347 (void) p_old;
348 return filter_chain_MouseFilter( p_filter->p_sys->p_chain, p_mouse, p_new );
351 static int BuildFilterChain( filter_t *p_filter )
353 es_format_t fmt_mid;
354 int i_ret = VLC_EGENERIC;
356 /* Now try chroma format list */
357 const vlc_fourcc_t *pi_allowed_chromas = get_allowed_chromas( p_filter );
358 for( int i = 0; pi_allowed_chromas[i]; i++ )
360 filter_chain_Reset( p_filter->p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out );
362 const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
363 if( i_chroma == p_filter->fmt_in.i_codec ||
364 i_chroma == p_filter->fmt_out.i_codec )
365 continue;
367 msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
368 (char*)&i_chroma );
370 es_format_Copy( &fmt_mid, &p_filter->fmt_in );
371 fmt_mid.i_codec =
372 fmt_mid.video.i_chroma = i_chroma;
373 fmt_mid.video.i_rmask = 0;
374 fmt_mid.video.i_gmask = 0;
375 fmt_mid.video.i_bmask = 0;
376 video_format_FixRgb(&fmt_mid.video);
378 if( filter_chain_AppendConverter( p_filter->p_sys->p_chain,
379 NULL, &fmt_mid ) == VLC_SUCCESS )
381 p_filter->p_sys->p_video_filter =
382 filter_chain_AppendFilter( p_filter->p_sys->p_chain,
383 p_filter->psz_name, p_filter->p_cfg,
384 &fmt_mid, &fmt_mid );
385 if( p_filter->p_sys->p_video_filter )
387 filter_AddProxyCallbacks( p_filter,
388 p_filter->p_sys->p_video_filter,
389 RestartFilterCallback );
390 if (p_filter->p_sys->p_video_filter->pf_video_mouse != NULL)
391 p_filter->pf_video_mouse = ChainMouse;
392 es_format_Clean( &fmt_mid );
393 i_ret = VLC_SUCCESS;
394 break;
397 es_format_Clean( &fmt_mid );
399 if (i_ret == VLC_SUCCESS)
401 es_format_Clean( &p_filter->fmt_out );
402 es_format_Copy( &p_filter->fmt_out,
403 filter_chain_GetFmtOut( p_filter->p_sys->p_chain ) );
405 else
406 filter_chain_Reset( p_filter->p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out );
408 return i_ret;
411 /*****************************************************************************
413 *****************************************************************************/
414 static int CreateChain( filter_t *p_parent, es_format_t *p_fmt_mid )
416 filter_chain_Reset( p_parent->p_sys->p_chain, &p_parent->fmt_in, &p_parent->fmt_out );
418 filter_t *p_filter;
420 if( p_parent->fmt_in.video.orientation != p_fmt_mid->video.orientation)
422 p_filter = AppendTransform( p_parent->p_sys->p_chain, &p_parent->fmt_in, p_fmt_mid );
423 // Check if filter was enough:
424 if( p_filter == NULL )
425 return VLC_EGENERIC;
426 if( es_format_IsSimilar(&p_filter->fmt_out, &p_parent->fmt_out ))
427 return VLC_SUCCESS;
429 else
431 if( filter_chain_AppendConverter( p_parent->p_sys->p_chain,
432 NULL, p_fmt_mid ) )
433 return VLC_EGENERIC;
436 if( p_fmt_mid->video.orientation != p_parent->fmt_out.video.orientation)
438 if( AppendTransform( p_parent->p_sys->p_chain, p_fmt_mid,
439 &p_parent->fmt_out ) == NULL )
440 goto error;
442 else
444 if( filter_chain_AppendConverter( p_parent->p_sys->p_chain,
445 p_fmt_mid, NULL ) )
446 goto error;
448 return VLC_SUCCESS;
449 error:
450 //Clean up.
451 filter_chain_Reset( p_parent->p_sys->p_chain, NULL, NULL );
452 return VLC_EGENERIC;
455 static filter_t * AppendTransform( filter_chain_t *p_chain, es_format_t *p_fmt1, es_format_t *p_fmt2 )
457 video_transform_t transform = video_format_GetTransform(p_fmt1->video.orientation, p_fmt2->video.orientation);
459 const char *type;
461 switch ( transform ) {
463 case TRANSFORM_R90:
464 type = "90";
465 break;
466 case TRANSFORM_R180:
467 type = "180";
468 break;
469 case TRANSFORM_R270:
470 type = "270";
471 break;
472 case TRANSFORM_HFLIP:
473 type = "hflip";
474 break;
475 case TRANSFORM_VFLIP:
476 type = "vflip";
477 break;
478 case TRANSFORM_TRANSPOSE:
479 type = "transpose";
480 break;
481 case TRANSFORM_ANTI_TRANSPOSE:
482 type = "antitranspose";
483 break;
484 default:
485 type = NULL;
486 break;
489 if( !type )
490 return NULL;
492 config_chain_t *cfg;
493 char *name;
494 char config[100];
495 snprintf( config, 100, "transform{type=%s}", type );
496 char *next = config_ChainCreate( &name, &cfg, config );
498 filter_t *p_filter = filter_chain_AppendFilter( p_chain, name, cfg, p_fmt1, p_fmt2 );
500 config_ChainDestroy(cfg);
501 free(name);
502 free(next);
504 return p_filter;
507 static void EsFormatMergeSize( es_format_t *p_dst,
508 const es_format_t *p_base,
509 const es_format_t *p_size )
511 es_format_Copy( p_dst, p_base );
513 p_dst->video.i_width = p_size->video.i_width;
514 p_dst->video.i_height = p_size->video.i_height;
516 p_dst->video.i_visible_width = p_size->video.i_visible_width;
517 p_dst->video.i_visible_height = p_size->video.i_visible_height;
519 p_dst->video.i_x_offset = p_size->video.i_x_offset;
520 p_dst->video.i_y_offset = p_size->video.i_y_offset;
522 p_dst->video.orientation = p_size->video.orientation;
523 p_dst->video.i_sar_num = p_size->video.i_sar_num;
524 p_dst->video.i_sar_den = p_size->video.i_sar_den;