]> git.sesse.net Git - vlc/blob - modules/video_chroma/chain.c
mediacodec: skip prerolled frames
[vlc] / modules / video_chroma / chain.c
1 /*****************************************************************************
2  * chain.c : chain multiple video filter modules as a last resort solution
3  *****************************************************************************
4  * Copyright (C) 2007-2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan dot org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
35
36 /*****************************************************************************
37  * Module descriptor
38  *****************************************************************************/
39 static int       Activate   ( vlc_object_t * );
40 static void      Destroy    ( vlc_object_t * );
41
42 vlc_module_begin ()
43     set_description( N_("Video filtering using a chain of video filter modules") )
44     set_capability( "video filter2", 1 )
45     set_callbacks( Activate, Destroy )
46 vlc_module_end ()
47
48 /*****************************************************************************
49  * Local prototypes.
50  *****************************************************************************/
51 static picture_t *Chain         ( filter_t *, picture_t * );
52
53 static int BuildTransformChain( filter_t *p_filter );
54 static int BuildChromaResize( filter_t * );
55 static int BuildChromaChain( filter_t *p_filter );
56
57 static int CreateChain( filter_t *p_parent, es_format_t *p_fmt_mid, config_chain_t * );
58 static filter_t * AppendTransform( filter_chain_t *p_chain, es_format_t *p_fmt_in, es_format_t *p_fmt_out );
59 static void EsFormatMergeSize( es_format_t *p_dst,
60                                const es_format_t *p_base,
61                                const es_format_t *p_size );
62
63 static const vlc_fourcc_t pi_allowed_chromas[] = {
64     VLC_CODEC_I420,
65     VLC_CODEC_I422,
66     VLC_CODEC_RGB32,
67     VLC_CODEC_RGB24,
68     0
69 };
70
71 struct filter_sys_t
72 {
73     filter_chain_t *p_chain;
74 };
75
76 /*****************************************************************************
77  * Buffer management
78  *****************************************************************************/
79 static picture_t *BufferNew( filter_t *p_filter )
80 {
81     filter_t *p_parent = p_filter->owner.sys;
82
83     return filter_NewPicture( p_parent );
84 }
85
86 #define CHAIN_LEVEL_MAX 1
87
88 /*****************************************************************************
89  * Activate: allocate a chroma function
90  *****************************************************************************
91  * This function allocates and initializes a chroma function
92  *****************************************************************************/
93 static int Activate( vlc_object_t *p_this )
94 {
95     filter_t *p_filter = (filter_t *)p_this;
96     filter_sys_t *p_sys;
97     int i_ret;
98
99     const bool b_chroma = p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma;
100     const bool b_resize = p_filter->fmt_in.video.i_width  != p_filter->fmt_out.video.i_width ||
101                           p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height;
102     const bool b_transform = p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation;
103
104     if( !b_chroma && !b_resize && !b_transform)
105         return VLC_EGENERIC;
106
107     p_sys = p_filter->p_sys = calloc( 1, sizeof( *p_sys ) );
108     if( !p_sys )
109         return VLC_ENOMEM;
110
111     filter_owner_t owner = {
112         .sys = p_filter,
113         .video = {
114             .buffer_new = BufferNew,
115         },
116     };
117
118     p_sys->p_chain = filter_chain_NewVideo( p_filter, false, &owner );
119     if( !p_sys->p_chain )
120     {
121         free( p_sys );
122         return VLC_EGENERIC;
123     }
124
125     if( b_transform )
126         i_ret = BuildTransformChain( p_filter );
127     else if( b_chroma && b_resize )
128         i_ret = BuildChromaResize( p_filter );
129     else if( b_chroma )
130         i_ret = BuildChromaChain( p_filter );
131     else
132         i_ret = VLC_EGENERIC;
133
134     if( i_ret )
135     {
136         /* Hum ... looks like this really isn't going to work. Too bad. */
137         filter_chain_Delete( p_sys->p_chain );
138         free( p_sys );
139         return VLC_EGENERIC;
140     }
141     /* */
142     p_filter->pf_video_filter = Chain;
143     return VLC_SUCCESS;
144 }
145
146 static void Destroy( vlc_object_t *p_this )
147 {
148     filter_t *p_filter = (filter_t *)p_this;
149     filter_chain_Delete( p_filter->p_sys->p_chain );
150     free( p_filter->p_sys );
151 }
152
153 /*****************************************************************************
154  * Chain
155  *****************************************************************************/
156 static picture_t *Chain( filter_t *p_filter, picture_t *p_pic )
157 {
158     return filter_chain_VideoFilter( p_filter->p_sys->p_chain, p_pic );
159 }
160
161 /*****************************************************************************
162  * Builders
163  *****************************************************************************/
164
165 static int BuildTransformChain( filter_t *p_filter )
166 {
167
168     es_format_t fmt_mid;
169     int i_ret;
170
171     /* Lets try transform first, then (potentially) resize+chroma */
172     msg_Dbg( p_filter, "Trying to build transform, then chroma+resize" );
173     es_format_Copy( &fmt_mid, &p_filter->fmt_in );
174     video_format_TransformTo(&fmt_mid.video, p_filter->fmt_out.video.orientation);
175     i_ret = CreateChain( p_filter, &fmt_mid, NULL );
176     es_format_Clean( &fmt_mid );
177     if( i_ret == VLC_SUCCESS )
178         return VLC_SUCCESS;
179
180     /* Lets try resize+chroma first, then transform */
181     msg_Dbg( p_filter, "Trying to build chroma+resize" );
182     EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
183     i_ret = CreateChain( p_filter, &fmt_mid, NULL );
184     es_format_Clean( &fmt_mid );
185     if( i_ret == VLC_SUCCESS )
186         return VLC_SUCCESS;
187
188     return VLC_EGENERIC;
189 }
190
191 static int BuildChromaResize( filter_t *p_filter )
192 {
193     es_format_t fmt_mid;
194     int i_ret;
195
196     /* Lets try resizing and then doing the chroma conversion */
197     msg_Dbg( p_filter, "Trying to build resize+chroma" );
198     EsFormatMergeSize( &fmt_mid, &p_filter->fmt_in, &p_filter->fmt_out );
199     i_ret = CreateChain( p_filter, &fmt_mid, NULL );
200     es_format_Clean( &fmt_mid );
201     if( i_ret == VLC_SUCCESS )
202         return VLC_SUCCESS;
203
204     /* Lets try it the other way arround (chroma and then resize) */
205     msg_Dbg( p_filter, "Trying to build chroma+resize" );
206     EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in );
207     i_ret = CreateChain( p_filter, &fmt_mid, NULL );
208     es_format_Clean( &fmt_mid );
209     if( i_ret == VLC_SUCCESS )
210         return VLC_SUCCESS;
211
212     return VLC_EGENERIC;
213 }
214
215 static int BuildChromaChain( filter_t *p_filter )
216 {
217     es_format_t fmt_mid;
218
219     /* We have to protect ourself against a too high recursion */
220     const char *psz_option = MODULE_STRING"-level";
221     int i_level = 0;
222     for( const config_chain_t *c = p_filter->p_cfg; c != NULL; c = c->p_next)
223     {
224         if( c->psz_name && c->psz_value && !strcmp(c->psz_name, psz_option) )
225         {
226             i_level = atoi(c->psz_value);
227             if( i_level < 0 || i_level > CHAIN_LEVEL_MAX )
228             {
229                 msg_Err( p_filter, "Too high level of recursion (%d)", i_level );
230                 return VLC_EGENERIC;
231             }
232             break;
233         }
234     }
235
236     /* */
237     int i_ret = VLC_EGENERIC;
238
239     /* */
240     config_chain_t cfg_level;
241     memset(&cfg_level, 0, sizeof(cfg_level));
242     cfg_level.psz_name = strdup(psz_option);
243     if( asprintf( &cfg_level.psz_value, "%d", i_level + 1) < 0 )
244         cfg_level.psz_value = NULL;
245     if( !cfg_level.psz_name || !cfg_level.psz_value )
246         goto exit;
247
248     /* Now try chroma format list */
249     for( int i = 0; pi_allowed_chromas[i]; i++ )
250     {
251         const vlc_fourcc_t i_chroma = pi_allowed_chromas[i];
252         if( i_chroma == p_filter->fmt_in.i_codec ||
253             i_chroma == p_filter->fmt_out.i_codec )
254             continue;
255
256         msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man",
257                  (char*)&i_chroma );
258
259         es_format_Copy( &fmt_mid, &p_filter->fmt_in );
260         fmt_mid.i_codec        =
261         fmt_mid.video.i_chroma = i_chroma;
262         fmt_mid.video.i_rmask  = 0;
263         fmt_mid.video.i_gmask  = 0;
264         fmt_mid.video.i_bmask  = 0;
265         video_format_FixRgb(&fmt_mid.video);
266
267         i_ret = CreateChain( p_filter, &fmt_mid, &cfg_level );
268         es_format_Clean( &fmt_mid );
269
270         if( i_ret == VLC_SUCCESS )
271             break;
272     }
273
274 exit:
275     free( cfg_level.psz_name );
276     free( cfg_level.psz_value );
277     return i_ret;
278 }
279
280 /*****************************************************************************
281  *
282  *****************************************************************************/
283 static int CreateChain( filter_t *p_parent, es_format_t *p_fmt_mid, config_chain_t *p_cfg )
284 {
285     filter_chain_Reset( p_parent->p_sys->p_chain, &p_parent->fmt_in, &p_parent->fmt_out );
286
287     filter_t *p_filter;
288
289     if( p_parent->fmt_in.video.orientation != p_fmt_mid->video.orientation)
290     {
291         p_filter = AppendTransform( p_parent->p_sys->p_chain, &p_parent->fmt_in, p_fmt_mid );
292     }
293     else
294     {
295         p_filter = filter_chain_AppendFilter( p_parent->p_sys->p_chain, NULL, p_cfg, NULL, p_fmt_mid );
296     }
297
298     if( !p_filter )
299         return VLC_EGENERIC;
300
301     //Check if first filter was enough (transform filter most likely):
302     if( es_format_IsSimilar(&p_filter->fmt_out, &p_parent->fmt_out ))
303        return VLC_SUCCESS;
304
305     if( p_fmt_mid->video.orientation != p_parent->fmt_out.video.orientation)
306     {
307         p_filter = AppendTransform( p_parent->p_sys->p_chain, p_fmt_mid, &p_parent->fmt_out );
308     }
309     else
310     {
311         p_filter = filter_chain_AppendFilter( p_parent->p_sys->p_chain, NULL, p_cfg, p_fmt_mid, NULL );
312     }
313
314     if( !p_filter )
315     {
316         //Clean up.
317         filter_chain_Reset( p_parent->p_sys->p_chain, NULL, NULL );
318         return VLC_EGENERIC;
319     }
320
321     return VLC_SUCCESS;
322 }
323
324 static filter_t * AppendTransform( filter_chain_t *p_chain, es_format_t *p_fmt1, es_format_t *p_fmt2 )
325 {
326     video_transform_t transform = video_format_GetTransform(p_fmt1->video.orientation, p_fmt2->video.orientation);
327
328     const char *type;
329
330     switch ( transform ) {
331
332         case TRANSFORM_R90:
333             type = "90";
334             break;
335         case TRANSFORM_R180:
336             type = "180";
337             break;
338         case TRANSFORM_R270:
339             type = "270";
340             break;
341         case TRANSFORM_HFLIP:
342             type = "hflip";
343             break;
344         case TRANSFORM_VFLIP:
345             type = "vflip";
346             break;
347         case TRANSFORM_TRANSPOSE:
348             type = "transpose";
349             break;
350         case TRANSFORM_ANTI_TRANSPOSE:
351             type = "antitranspose";
352             break;
353         default:
354             type = NULL;
355             break;
356     }
357
358     if( !type )
359         return NULL;
360
361     config_chain_t *cfg;
362     char *name;
363     char config[100];
364     snprintf( config, 100, "transform{type=%s}", type );
365     char *next = config_ChainCreate( &name, &cfg, config );
366
367     filter_t *p_filter = filter_chain_AppendFilter( p_chain, name, cfg, p_fmt1, p_fmt2 );
368
369     config_ChainDestroy(cfg);
370     free(name);
371     free(next);
372
373     return p_filter;
374 }
375
376 static void EsFormatMergeSize( es_format_t *p_dst,
377                                const es_format_t *p_base,
378                                const es_format_t *p_size )
379 {
380     es_format_Copy( p_dst, p_base );
381
382     p_dst->video.i_width  = p_size->video.i_width;
383     p_dst->video.i_height = p_size->video.i_height;
384
385     p_dst->video.i_visible_width  = p_size->video.i_visible_width;
386     p_dst->video.i_visible_height = p_size->video.i_visible_height;
387
388     p_dst->video.i_x_offset = p_size->video.i_x_offset;
389     p_dst->video.i_y_offset = p_size->video.i_y_offset;
390
391     p_dst->video.orientation = p_size->video.orientation;
392 }
393