]> git.sesse.net Git - vlc/blob - modules/video_filter/mosaic.c
Replace argument = realloc( argument, size ); with realloc_or_free() in modules/...
[vlc] / modules / video_filter / mosaic.c
1 /*****************************************************************************
2  * mosaic.c : Mosaic video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2004-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan dot org>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34
35 #include <math.h>
36 #include <limits.h> /* INT_MAX */
37 #include <assert.h>
38
39 #include <vlc_filter.h>
40 #include <vlc_image.h>
41
42 #include <vlc_memory.h>
43
44 #include "mosaic.h"
45
46 #define BLANK_DELAY INT64_C(1000000)
47
48 /*****************************************************************************
49  * Local prototypes
50  *****************************************************************************/
51 static int  CreateFilter    ( vlc_object_t * );
52 static void DestroyFilter   ( vlc_object_t * );
53 static subpicture_t *Filter ( filter_t *, mtime_t );
54
55 static int MosaicCallback   ( vlc_object_t *, char const *, vlc_value_t,
56                               vlc_value_t, void * );
57
58 /*****************************************************************************
59  * filter_sys_t : filter descriptor
60  *****************************************************************************/
61 struct filter_sys_t
62 {
63     vlc_mutex_t lock;         /* Internal filter lock */
64     vlc_mutex_t *p_lock;      /* Pointer to mosaic bridge lock */
65
66     image_handler_t *p_image;
67
68     int i_position;           /* Mosaic positioning method */
69     bool b_ar;          /* Do we keep the aspect ratio ? */
70     bool b_keep;        /* Do we keep the original picture format ? */
71     int i_width, i_height;    /* Mosaic height and width */
72     int i_cols, i_rows;       /* Mosaic rows and cols */
73     int i_align;              /* Mosaic alignment in background video */
74     int i_xoffset, i_yoffset; /* Top left corner offset */
75     int i_borderw, i_borderh; /* Border width/height between miniatures */
76     int i_alpha;              /* Subfilter alpha blending */
77
78     char **ppsz_order;        /* List of picture-ids */
79     int i_order_length;
80
81     int *pi_x_offsets;        /* List of substreams x offsets */
82     int *pi_y_offsets;        /* List of substreams y offsets */
83     int i_offsets_length;
84
85     mtime_t i_delay;
86 };
87
88 /*****************************************************************************
89  * Module descriptor
90  *****************************************************************************/
91 #define ALPHA_TEXT N_("Transparency")
92 #define ALPHA_LONGTEXT N_( \
93         "Transparency of the mosaic foreground pictures. " \
94         "0 means transparent, 255 opaque (default)." )
95
96 #define HEIGHT_TEXT N_("Height")
97 #define HEIGHT_LONGTEXT N_( "Total height of the mosaic, in pixels." )
98 #define WIDTH_TEXT N_("Width")
99 #define WIDTH_LONGTEXT N_( "Total width of the mosaic, in pixels." )
100
101 #define XOFFSET_TEXT N_("Top left corner X coordinate")
102 #define XOFFSET_LONGTEXT N_( \
103         "X Coordinate of the top-left corner of the mosaic.")
104 #define YOFFSET_TEXT N_("Top left corner Y coordinate")
105 #define YOFFSET_LONGTEXT N_( \
106         "Y Coordinate of the top-left corner of the mosaic.")
107
108 #define BORDERW_TEXT N_("Border width")
109 #define BORDERW_LONGTEXT N_( \
110         "Width in pixels of the border between miniatures." )
111 #define BORDERH_TEXT N_("Border height")
112 #define BORDERH_LONGTEXT N_( \
113         "Height in pixels of the border between miniatures." )
114
115 #define ALIGN_TEXT N_("Mosaic alignment" )
116 #define ALIGN_LONGTEXT N_( \
117         "You can enforce the mosaic alignment on the video " \
118         "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
119         "also use combinations of these values, eg 6 = top-right).")
120
121 #define POS_TEXT N_("Positioning method")
122 #define POS_LONGTEXT N_( \
123         "Positioning method for the mosaic. auto: " \
124         "automatically choose the best number of rows and columns. " \
125         "fixed: use the user-defined number of rows and columns. " \
126         "offsets: use the user-defined offsets for each image." )
127
128 #define ROWS_TEXT N_("Number of rows")
129 #define ROWS_LONGTEXT N_( \
130         "Number of image rows in the mosaic (only used if " \
131         "positionning method is set to \"fixed\")." )
132
133 #define COLS_TEXT N_("Number of columns")
134 #define COLS_LONGTEXT N_( \
135         "Number of image columns in the mosaic (only used if " \
136         "positionning method is set to \"fixed\"." )
137
138 #define AR_TEXT N_("Keep aspect ratio")
139 #define AR_LONGTEXT N_( \
140         "Keep the original aspect ratio when resizing " \
141         "mosaic elements." )
142 #define KEEP_TEXT N_("Keep original size")
143 #define KEEP_LONGTEXT N_( \
144         "Keep the original size of mosaic elements." )
145
146 #define ORDER_TEXT N_("Elements order" )
147 #define ORDER_LONGTEXT N_( \
148         "You can enforce the order of the elements on " \
149         "the mosaic. You must give a comma-separated list of picture ID(s)." \
150         "These IDs are assigned in the \"mosaic-bridge\" module." )
151
152 #define OFFSETS_TEXT N_("Offsets in order" )
153 #define OFFSETS_LONGTEXT N_( \
154         "You can enforce the (x,y) offsets of the elements on the mosaic " \
155         "(only used if positioning method is set to \"offsets\"). You " \
156         "must give a comma-separated list of coordinates (eg: 10,10,150,10)." )
157
158 #define DELAY_TEXT N_("Delay")
159 #define DELAY_LONGTEXT N_( \
160         "Pictures coming from the mosaic elements will be delayed " \
161         "according to this value (in milliseconds). For high " \
162         "values you will need to raise caching at input.")
163
164 enum
165 {
166     position_auto = 0, position_fixed = 1, position_offsets = 2
167 };
168 static const int pi_pos_values[] = { 0, 1, 2 };
169 static const char *const ppsz_pos_descriptions[] =
170     { N_("auto"), N_("fixed"), N_("offsets") };
171
172 static const int pi_align_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
173 static const char *const ppsz_align_descriptions[] =
174      { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
175      N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
176
177 #define CFG_PREFIX "mosaic-"
178
179 vlc_module_begin ()
180     set_description( N_("Mosaic video sub filter") )
181     set_shortname( N_("Mosaic") )
182     set_category( CAT_VIDEO )
183     set_subcategory( SUBCAT_VIDEO_SUBPIC)
184     set_capability( "sub filter", 0 )
185     set_callbacks( CreateFilter, DestroyFilter )
186
187     add_integer_with_range( CFG_PREFIX "alpha", 255, 0, 255, NULL,
188                             ALPHA_TEXT, ALPHA_LONGTEXT, false )
189
190     add_integer( CFG_PREFIX "height", 100, NULL,
191                  HEIGHT_TEXT, HEIGHT_LONGTEXT, false )
192     add_integer( CFG_PREFIX "width", 100, NULL,
193                  WIDTH_TEXT, WIDTH_LONGTEXT, false )
194
195     add_integer( CFG_PREFIX "align", 5, NULL,
196                  ALIGN_TEXT, ALIGN_LONGTEXT, true)
197         change_integer_list( pi_align_values, ppsz_align_descriptions, NULL )
198
199     add_integer( CFG_PREFIX "xoffset", 0, NULL,
200                  XOFFSET_TEXT, XOFFSET_LONGTEXT, true )
201     add_integer( CFG_PREFIX "yoffset", 0, NULL,
202                  YOFFSET_TEXT, YOFFSET_LONGTEXT, true )
203
204     add_integer( CFG_PREFIX "borderw", 0, NULL,
205                  BORDERW_TEXT, BORDERW_LONGTEXT, true )
206         add_deprecated_alias( CFG_PREFIX "vborder" )
207     add_integer( CFG_PREFIX "borderh", 0, NULL,
208                  BORDERH_TEXT, BORDERH_LONGTEXT, true )
209         add_deprecated_alias( CFG_PREFIX "hborder" )
210
211     add_integer( CFG_PREFIX "position", 0, NULL,
212                  POS_TEXT, POS_LONGTEXT, false )
213         change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
214     add_integer( CFG_PREFIX "rows", 2, NULL,
215                  ROWS_TEXT, ROWS_LONGTEXT, false )
216     add_integer( CFG_PREFIX "cols", 2, NULL,
217                  COLS_TEXT, COLS_LONGTEXT, false )
218
219     add_bool( CFG_PREFIX "keep-aspect-ratio", false, NULL,
220               AR_TEXT, AR_LONGTEXT, false )
221     add_bool( CFG_PREFIX "keep-picture", false, NULL,
222               KEEP_TEXT, KEEP_LONGTEXT, false )
223
224     add_string( CFG_PREFIX "order", "", NULL,
225                 ORDER_TEXT, ORDER_LONGTEXT, false )
226
227     add_string( CFG_PREFIX "offsets", "", NULL,
228                 OFFSETS_TEXT, OFFSETS_LONGTEXT, false )
229
230     add_integer( CFG_PREFIX "delay", 0, NULL, DELAY_TEXT, DELAY_LONGTEXT,
231                  false )
232 vlc_module_end ()
233
234 static const char *const ppsz_filter_options[] = {
235     "alpha", "height", "width", "align", "xoffset", "yoffset",
236     "borderw", "borderh", "position", "rows", "cols",
237     "keep-aspect-ratio", "keep-picture", "order", "offsets",
238     "delay", NULL
239 };
240
241 /*****************************************************************************
242  * mosaic_ParseSetOffsets:
243  * parse the "--mosaic-offsets x1,y1,x2,y2,x3,y3" parameter
244  * and set the corresponding struct filter_sys_t entries.
245  *****************************************************************************/
246 #define mosaic_ParseSetOffsets( a, b, c ) \
247       __mosaic_ParseSetOffsets( VLC_OBJECT( a ), b, c )
248 static void __mosaic_ParseSetOffsets( vlc_object_t *p_this,
249                                       filter_sys_t *p_sys,
250                                       char *psz_offsets )
251 {
252     if( *psz_offsets )
253     {
254         char *psz_end = NULL;
255         int i_index = 0;
256         do
257         {
258             i_index++;
259
260             p_sys->pi_x_offsets = realloc_or_free( p_sys->pi_x_offsets,
261                                                    i_index * sizeof(int) );
262             assert( p_sys->pi_x_offsets );
263             p_sys->pi_x_offsets[i_index - 1] = atoi( psz_offsets );
264             psz_end = strchr( psz_offsets, ',' );
265             psz_offsets = psz_end + 1;
266
267             p_sys->pi_y_offsets = realloc_or_free( p_sys->pi_y_offsets,
268                                                    i_index * sizeof(int) );
269             assert( p_sys->pi_y_offsets );
270             p_sys->pi_y_offsets[i_index - 1] = atoi( psz_offsets );
271             psz_end = strchr( psz_offsets, ',' );
272             psz_offsets = psz_end + 1;
273
274             msg_Dbg( p_this, CFG_PREFIX "offset: id %d, x=%d, y=%d",
275                      i_index, p_sys->pi_x_offsets[i_index - 1],
276                               p_sys->pi_y_offsets[i_index - 1]  );
277
278         } while( psz_end );
279         p_sys->i_offsets_length = i_index;
280     }
281 }
282
283 /*****************************************************************************
284  * CreateFiler: allocate mosaic video filter
285  *****************************************************************************/
286 static int CreateFilter( vlc_object_t *p_this )
287 {
288     filter_t *p_filter = (filter_t *)p_this;
289     filter_sys_t *p_sys;
290     vlc_object_t *p_libvlc = VLC_OBJECT( p_filter->p_libvlc );
291     char *psz_order, *_psz_order;
292     char *psz_offsets;
293     int i_index;
294     vlc_value_t val;
295     int i_command;
296
297     /* The mosaic thread is more important than the decoder threads */
298     vlc_thread_set_priority( p_this, VLC_THREAD_PRIORITY_OUTPUT );
299
300     /* Allocate structure */
301     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
302     if( p_sys == NULL )
303         return VLC_ENOMEM;
304
305     p_filter->pf_sub_filter = Filter;
306
307     vlc_mutex_init( &p_sys->lock );
308     vlc_mutex_lock( &p_sys->lock );
309
310     var_Create( p_libvlc, "mosaic-lock", VLC_VAR_MUTEX );
311     var_Get( p_libvlc, "mosaic-lock", &val );
312     p_sys->p_lock = val.p_address;
313
314     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
315                        p_filter->p_cfg );
316
317 #define GET_VAR( name, min, max )                                           \
318     i_command = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX #name );  \
319     p_sys->i_##name = __MIN( max, __MAX( min, i_command ) );                \
320     var_AddCallback( p_filter, CFG_PREFIX #name, MosaicCallback, p_sys );
321
322     GET_VAR( width, 0, INT_MAX );
323     GET_VAR( height, 0, INT_MAX );
324     GET_VAR( xoffset, 0, INT_MAX );
325     GET_VAR( yoffset, 0, INT_MAX );
326
327     GET_VAR( align, 0, 10 );
328     if( p_sys->i_align == 3 || p_sys->i_align == 7 )
329         p_sys->i_align = 5;
330
331     GET_VAR( borderw, 0, INT_MAX );
332     GET_VAR( borderh, 0, INT_MAX );
333     GET_VAR( rows, 1, INT_MAX );
334     GET_VAR( cols, 1, INT_MAX );
335     GET_VAR( alpha, 0, 255 );
336     GET_VAR( position, 0, 2 );
337     GET_VAR( delay, 100, INT_MAX );
338 #undef GET_VAR
339     p_sys->i_delay *= 1000;
340
341     p_sys->b_ar = var_CreateGetBoolCommand( p_filter,
342                                             CFG_PREFIX "keep-aspect-ratio" );
343     var_AddCallback( p_filter, CFG_PREFIX "keep-aspect-ratio", MosaicCallback,
344                      p_sys );
345
346     p_sys->b_keep = var_CreateGetBoolCommand( p_filter,
347                                               CFG_PREFIX "keep-picture" );
348     if ( !p_sys->b_keep )
349     {
350         p_sys->p_image = image_HandlerCreate( p_filter );
351     }
352
353     p_sys->i_order_length = 0;
354     p_sys->ppsz_order = NULL;
355     psz_order = var_CreateGetStringCommand( p_filter, CFG_PREFIX "order" );
356     _psz_order = psz_order;
357     var_AddCallback( p_filter, CFG_PREFIX "order", MosaicCallback, p_sys );
358
359     if( *psz_order )
360     {
361         char *psz_end = NULL;
362         i_index = 0;
363         do
364         {
365             psz_end = strchr( psz_order, ',' );
366             i_index++;
367             p_sys->ppsz_order = realloc_or_free( p_sys->ppsz_order,
368                                                  i_index * sizeof(char *) );
369             assert( p_sys->ppsz_order );
370             p_sys->ppsz_order[i_index - 1] = strndup( psz_order,
371                                            psz_end - psz_order );
372             psz_order = psz_end+1;
373         } while( psz_end );
374         p_sys->i_order_length = i_index;
375     }
376
377     free( _psz_order );
378
379     /* Manage specific offsets for substreams */
380     psz_offsets = var_CreateGetStringCommand( p_filter, CFG_PREFIX "offsets" );
381     p_sys->i_offsets_length = 0;
382     p_sys->pi_x_offsets = NULL;
383     p_sys->pi_y_offsets = NULL;
384     mosaic_ParseSetOffsets( p_filter, p_sys, psz_offsets );
385     free( psz_offsets );
386     var_AddCallback( p_filter, CFG_PREFIX "offsets", MosaicCallback, p_sys );
387
388     vlc_mutex_unlock( &p_sys->lock );
389
390     return VLC_SUCCESS;
391 }
392
393 /*****************************************************************************
394  * DestroyFilter: destroy mosaic video filter
395  *****************************************************************************/
396 static void DestroyFilter( vlc_object_t *p_this )
397 {
398     filter_t *p_filter = (filter_t*)p_this;
399     filter_sys_t *p_sys = p_filter->p_sys;
400
401 #define DEL_CB( name ) \
402     var_DelCallback( p_filter, CFG_PREFIX #name, MosaicCallback, p_sys )
403     DEL_CB( width );
404     DEL_CB( height );
405     DEL_CB( xoffset );
406     DEL_CB( yoffset );
407
408     DEL_CB( align );
409
410     DEL_CB( borderw );
411     DEL_CB( borderh );
412     DEL_CB( rows );
413     DEL_CB( cols );
414     DEL_CB( alpha );
415     DEL_CB( position );
416     DEL_CB( delay );
417
418     DEL_CB( keep-aspect-ratio );
419     DEL_CB( order );
420 #undef DEL_CB
421
422     if( !p_sys->b_keep )
423     {
424         image_HandlerDelete( p_sys->p_image );
425     }
426
427     if( p_sys->i_order_length )
428     {
429         for( int i_index = 0; i_index < p_sys->i_order_length; i_index++ )
430         {
431             free( p_sys->ppsz_order[i_index] );
432         }
433         free( p_sys->ppsz_order );
434     }
435     if( p_sys->i_offsets_length )
436     {
437         free( p_sys->pi_x_offsets );
438         free( p_sys->pi_y_offsets );
439         p_sys->i_offsets_length = 0;
440     }
441
442     vlc_mutex_destroy( &p_sys->lock );
443     free( p_sys );
444 }
445
446 /*****************************************************************************
447  * Filter
448  *****************************************************************************/
449 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
450 {
451     filter_sys_t *p_sys = p_filter->p_sys;
452     bridge_t *p_bridge;
453
454     subpicture_t *p_spu;
455
456     int i_index, i_real_index, i_row, i_col;
457     int i_greatest_real_index_used = p_sys->i_order_length - 1;
458
459     unsigned int col_inner_width, row_inner_height;
460
461     subpicture_region_t *p_region;
462     subpicture_region_t *p_region_prev = NULL;
463
464     /* Allocate the subpicture internal data. */
465     p_spu = filter_NewSubpicture( p_filter );
466     if( !p_spu )
467         return NULL;
468
469     /* Initialize subpicture */
470     p_spu->i_channel = 0;
471     p_spu->i_start  = date;
472     p_spu->i_stop = 0;
473     p_spu->b_ephemer = true;
474     p_spu->i_alpha = p_sys->i_alpha;
475     p_spu->b_absolute = false;
476
477     vlc_mutex_lock( &p_sys->lock );
478     vlc_mutex_lock( p_sys->p_lock );
479
480     p_bridge = GetBridge( p_filter );
481     if ( p_bridge == NULL )
482     {
483         vlc_mutex_unlock( p_sys->p_lock );
484         vlc_mutex_unlock( &p_sys->lock );
485         return p_spu;
486     }
487
488     if ( p_sys->i_position == position_offsets )
489     {
490         /* If we have either too much or not enough offsets, fall-back
491          * to automatic positioning. */
492         if ( p_sys->i_offsets_length != p_sys->i_order_length )
493         {
494             msg_Err( p_filter,
495                      "Number of specified offsets (%d) does not match number "
496                      "of input substreams in mosaic-order (%d), falling back "
497                      "to mosaic-position=0",
498                      p_sys->i_offsets_length, p_sys->i_order_length );
499             p_sys->i_position = position_auto;
500         }
501     }
502
503     if ( p_sys->i_position == position_auto )
504     {
505         int i_numpics = p_sys->i_order_length; /* keep slots and all */
506         for ( i_index = 0; i_index < p_bridge->i_es_num; i_index++ )
507         {
508             bridged_es_t *p_es = p_bridge->pp_es[i_index];
509             if ( !p_es->b_empty )
510             {
511                 i_numpics ++;
512                 if( p_sys->i_order_length && p_es->psz_id != NULL )
513                 {
514                     /* We also want to leave slots for images given in
515                      * mosaic-order that are not available in p_vout_picture */
516                     int i;
517                     for( i = 0; i < p_sys->i_order_length ; i++ )
518                     {
519                         if( !strcmp( p_sys->ppsz_order[i], p_es->psz_id ) )
520                         {
521                             i_numpics--;
522                             break;
523                         }
524                     }
525
526                 }
527             }
528         }
529         p_sys->i_rows = ceil(sqrt( (double)i_numpics ));
530         p_sys->i_cols = ( i_numpics % p_sys->i_rows == 0 ?
531                             i_numpics / p_sys->i_rows :
532                             i_numpics / p_sys->i_rows + 1 );
533     }
534
535     col_inner_width  = ( ( p_sys->i_width - ( p_sys->i_cols - 1 )
536                        * p_sys->i_borderw ) / p_sys->i_cols );
537     row_inner_height = ( ( p_sys->i_height - ( p_sys->i_rows - 1 )
538                        * p_sys->i_borderh ) / p_sys->i_rows );
539
540     i_real_index = 0;
541
542     for ( i_index = 0; i_index < p_bridge->i_es_num; i_index++ )
543     {
544         bridged_es_t *p_es = p_bridge->pp_es[i_index];
545         video_format_t fmt_in, fmt_out;
546         picture_t *p_converted;
547
548         memset( &fmt_in, 0, sizeof( video_format_t ) );
549         memset( &fmt_out, 0, sizeof( video_format_t ) );
550
551         if ( p_es->b_empty )
552             continue;
553
554         while ( p_es->p_picture != NULL
555                  && p_es->p_picture->date + p_sys->i_delay < date )
556         {
557             if ( p_es->p_picture->p_next != NULL )
558             {
559                 picture_t *p_next = p_es->p_picture->p_next;
560                 picture_Release( p_es->p_picture );
561                 p_es->p_picture = p_next;
562             }
563             else if ( p_es->p_picture->date + p_sys->i_delay + BLANK_DELAY <
564                         date )
565             {
566                 /* Display blank */
567                 picture_Release( p_es->p_picture );
568                 p_es->p_picture = NULL;
569                 p_es->pp_last = &p_es->p_picture;
570                 break;
571             }
572             else
573             {
574                 msg_Dbg( p_filter, "too late picture for %s (%"PRId64 ")",
575                          p_es->psz_id,
576                          date - p_es->p_picture->date - p_sys->i_delay );
577                 break;
578             }
579         }
580
581         if ( p_es->p_picture == NULL )
582             continue;
583
584         if ( p_sys->i_order_length == 0 )
585         {
586             i_real_index++;
587         }
588         else
589         {
590             int i;
591             for ( i = 0; i <= p_sys->i_order_length; i++ )
592             {
593                 if ( i == p_sys->i_order_length ) break;
594                 if ( strcmp( p_es->psz_id, p_sys->ppsz_order[i] ) == 0 )
595                 {
596                     i_real_index = i;
597                     break;
598                 }
599             }
600             if ( i == p_sys->i_order_length )
601                 i_real_index = ++i_greatest_real_index_used;
602         }
603         i_row = ( i_real_index / p_sys->i_cols ) % p_sys->i_rows;
604         i_col = i_real_index % p_sys->i_cols ;
605
606         if ( !p_sys->b_keep )
607         {
608             /* Convert the images */
609             fmt_in.i_chroma = p_es->p_picture->format.i_chroma;
610             fmt_in.i_height = p_es->p_picture->format.i_height;
611             fmt_in.i_width = p_es->p_picture->format.i_width;
612
613             if( fmt_in.i_chroma == VLC_CODEC_YUVA ||
614                 fmt_in.i_chroma == VLC_CODEC_RGBA )
615                 fmt_out.i_chroma = VLC_CODEC_YUVA;
616             else
617                 fmt_out.i_chroma = VLC_CODEC_I420;
618             fmt_out.i_width = col_inner_width;
619             fmt_out.i_height = row_inner_height;
620
621             if( p_sys->b_ar ) /* keep aspect ratio */
622             {
623                 if( (float)fmt_out.i_width / (float)fmt_out.i_height
624                       > (float)fmt_in.i_width / (float)fmt_in.i_height )
625                 {
626                     fmt_out.i_width = ( fmt_out.i_height * fmt_in.i_width )
627                                          / fmt_in.i_height;
628                 }
629                 else
630                 {
631                     fmt_out.i_height = ( fmt_out.i_width * fmt_in.i_height )
632                                         / fmt_in.i_width;
633                 }
634              }
635
636             fmt_out.i_visible_width = fmt_out.i_width;
637             fmt_out.i_visible_height = fmt_out.i_height;
638
639             p_converted = image_Convert( p_sys->p_image, p_es->p_picture,
640                                          &fmt_in, &fmt_out );
641             if( !p_converted )
642             {
643                 msg_Warn( p_filter,
644                            "image resizing and chroma conversion failed" );
645                 continue;
646             }
647         }
648         else
649         {
650             p_converted = p_es->p_picture;
651             fmt_in.i_width = fmt_out.i_width = p_converted->format.i_width;
652             fmt_in.i_height = fmt_out.i_height = p_converted->format.i_height;
653             fmt_in.i_chroma = fmt_out.i_chroma = p_converted->format.i_chroma;
654             fmt_out.i_visible_width = fmt_out.i_width;
655             fmt_out.i_visible_height = fmt_out.i_height;
656         }
657
658         p_region = subpicture_region_New( &fmt_out );
659         /* FIXME the copy is probably not needed anymore */
660         if( p_region )
661             picture_Copy( p_region->p_picture, p_converted );
662         if( !p_sys->b_keep )
663             picture_Release( p_converted );
664
665         if( !p_region )
666         {
667             msg_Err( p_filter, "cannot allocate SPU region" );
668             p_filter->pf_sub_buffer_del( p_filter, p_spu );
669             vlc_mutex_unlock( p_sys->p_lock );
670             vlc_mutex_unlock( &p_sys->lock );
671             return p_spu;
672         }
673
674         if( p_es->i_x >= 0 && p_es->i_y >= 0 )
675         {
676             p_region->i_x = p_es->i_x;
677             p_region->i_y = p_es->i_y;
678         }
679         else if( p_sys->i_position == position_offsets )
680         {
681             p_region->i_x = p_sys->pi_x_offsets[i_real_index];
682             p_region->i_y = p_sys->pi_y_offsets[i_real_index];
683         }
684         else
685         {
686             if( fmt_out.i_width > col_inner_width ||
687                 p_sys->b_ar || p_sys->b_keep )
688             {
689                 /* we don't have to center the video since it takes the
690                 whole rectangle area or it's larger than the rectangle */
691                 p_region->i_x = p_sys->i_xoffset
692                             + i_col * ( p_sys->i_width / p_sys->i_cols )
693                             + ( i_col * p_sys->i_borderw ) / p_sys->i_cols;
694             }
695             else
696             {
697                 /* center the video in the dedicated rectangle */
698                 p_region->i_x = p_sys->i_xoffset
699                         + i_col * ( p_sys->i_width / p_sys->i_cols )
700                         + ( i_col * p_sys->i_borderw ) / p_sys->i_cols
701                         + ( col_inner_width - fmt_out.i_width ) / 2;
702             }
703
704             if( fmt_out.i_height > row_inner_height
705                 || p_sys->b_ar || p_sys->b_keep )
706             {
707                 /* we don't have to center the video since it takes the
708                 whole rectangle area or it's taller than the rectangle */
709                 p_region->i_y = p_sys->i_yoffset
710                         + i_row * ( p_sys->i_height / p_sys->i_rows )
711                         + ( i_row * p_sys->i_borderh ) / p_sys->i_rows;
712             }
713             else
714             {
715                 /* center the video in the dedicated rectangle */
716                 p_region->i_y = p_sys->i_yoffset
717                         + i_row * ( p_sys->i_height / p_sys->i_rows )
718                         + ( i_row * p_sys->i_borderh ) / p_sys->i_rows
719                         + ( row_inner_height - fmt_out.i_height ) / 2;
720             }
721         }
722         p_region->i_align = p_sys->i_align;
723         p_region->i_alpha = p_es->i_alpha;
724
725         if( p_region_prev == NULL )
726         {
727             p_spu->p_region = p_region;
728         }
729         else
730         {
731             p_region_prev->p_next = p_region;
732         }
733
734         p_region_prev = p_region;
735     }
736
737     vlc_mutex_unlock( p_sys->p_lock );
738     vlc_mutex_unlock( &p_sys->lock );
739
740     return p_spu;
741 }
742
743 /*****************************************************************************
744 * Callback to update params on the fly
745 *****************************************************************************/
746 static int MosaicCallback( vlc_object_t *p_this, char const *psz_var,
747                             vlc_value_t oldval, vlc_value_t newval,
748                             void *p_data )
749 {
750     VLC_UNUSED(oldval);
751     filter_sys_t *p_sys = (filter_sys_t *) p_data;
752
753 #define VAR_IS( a ) !strcmp( psz_var, CFG_PREFIX a )
754     if( VAR_IS( "alpha" ) )
755     {
756         vlc_mutex_lock( &p_sys->lock );
757         msg_Dbg( p_this, "changing alpha from %d/255 to %d/255",
758                          p_sys->i_alpha, newval.i_int);
759         p_sys->i_alpha = __MIN( __MAX( newval.i_int, 0 ), 255 );
760         vlc_mutex_unlock( &p_sys->lock );
761     }
762     else if( VAR_IS( "height" ) )
763     {
764         vlc_mutex_lock( &p_sys->lock );
765         msg_Dbg( p_this, "changing height from %dpx to %dpx",
766                           p_sys->i_height, newval.i_int );
767         p_sys->i_height = __MAX( newval.i_int, 0 );
768         vlc_mutex_unlock( &p_sys->lock );
769     }
770     else if( VAR_IS( "width" ) )
771     {
772         vlc_mutex_lock( &p_sys->lock );
773         msg_Dbg( p_this, "changing width from %dpx to %dpx",
774                          p_sys->i_width, newval.i_int );
775         p_sys->i_width = __MAX( newval.i_int, 0 );
776         vlc_mutex_unlock( &p_sys->lock );
777     }
778     else if( VAR_IS( "xoffset" ) )
779     {
780         vlc_mutex_lock( &p_sys->lock );
781         msg_Dbg( p_this, "changing x offset from %dpx to %dpx",
782                          p_sys->i_xoffset, newval.i_int );
783         p_sys->i_xoffset = __MAX( newval.i_int, 0 );
784         vlc_mutex_unlock( &p_sys->lock );
785     }
786     else if( VAR_IS( "yoffset" ) )
787     {
788         vlc_mutex_lock( &p_sys->lock );
789         msg_Dbg( p_this, "changing y offset from %dpx to %dpx",
790                          p_sys->i_yoffset, newval.i_int );
791         p_sys->i_yoffset = __MAX( newval.i_int, 0 );
792         vlc_mutex_unlock( &p_sys->lock );
793     }
794     else if( VAR_IS( "align" ) )
795     {
796         int i_old = 0, i_new = 0;
797         vlc_mutex_lock( &p_sys->lock );
798         newval.i_int = __MIN( __MAX( newval.i_int, 0 ), 10 );
799         if( newval.i_int == 3 || newval.i_int == 7 )
800             newval.i_int = 5;
801         while( pi_align_values[i_old] != p_sys->i_align ) i_old++;
802         while( pi_align_values[i_new] != newval.i_int ) i_new++;
803         msg_Dbg( p_this, "changing alignment from %d (%s) to %d (%s)",
804                      p_sys->i_align, ppsz_align_descriptions[i_old],
805                      newval.i_int, ppsz_align_descriptions[i_new] );
806         p_sys->i_align = newval.i_int;
807         vlc_mutex_unlock( &p_sys->lock );
808     }
809     else if( VAR_IS( "borderw" ) )
810     {
811         vlc_mutex_lock( &p_sys->lock );
812         msg_Dbg( p_this, "changing border width from %dpx to %dpx",
813                          p_sys->i_borderw, newval.i_int );
814         p_sys->i_borderw = __MAX( newval.i_int, 0 );
815         vlc_mutex_unlock( &p_sys->lock );
816     }
817     else if( VAR_IS( "borderh" ) )
818     {
819         vlc_mutex_lock( &p_sys->lock );
820         msg_Dbg( p_this, "changing border height from %dpx to %dpx",
821                          p_sys->i_borderh, newval.i_int );
822         p_sys->i_borderh = __MAX( newval.i_int, 0 );
823         vlc_mutex_unlock( &p_sys->lock );
824     }
825     else if( VAR_IS( "position" ) )
826     {
827         if( newval.i_int > 2 || newval.i_int < 0 )
828         {
829             msg_Err( p_this,
830                      "Position is either 0 (%s), 1 (%s) or 2 (%s)",
831                      ppsz_pos_descriptions[0],
832                      ppsz_pos_descriptions[1],
833                      ppsz_pos_descriptions[2] );
834         }
835         else
836         {
837             vlc_mutex_lock( &p_sys->lock );
838             msg_Dbg( p_this, "changing position method from %d (%s) to %d (%s)",
839                     p_sys->i_position, ppsz_pos_descriptions[p_sys->i_position],
840                     newval.i_int, ppsz_pos_descriptions[newval.i_int]);
841             p_sys->i_position = newval.i_int;
842             vlc_mutex_unlock( &p_sys->lock );
843         }
844     }
845     else if( VAR_IS( "rows" ) )
846     {
847         vlc_mutex_lock( &p_sys->lock );
848         msg_Dbg( p_this, "changing number of rows from %d to %d",
849                          p_sys->i_rows, newval.i_int );
850         p_sys->i_rows = __MAX( newval.i_int, 1 );
851         vlc_mutex_unlock( &p_sys->lock );
852     }
853     else if( VAR_IS( "cols" ) )
854     {
855         vlc_mutex_lock( &p_sys->lock );
856         msg_Dbg( p_this, "changing number of columns from %d to %d",
857                          p_sys->i_cols, newval.i_int );
858         p_sys->i_cols = __MAX( newval.i_int, 1 );
859         vlc_mutex_unlock( &p_sys->lock );
860     }
861     else if( VAR_IS( "order" ) )
862     {
863         char *psz_order;
864         int i_index;
865         vlc_mutex_lock( &p_sys->lock );
866         msg_Dbg( p_this, "Changing mosaic order to %s", newval.psz_string );
867
868         psz_order = newval.psz_string;
869
870         while( p_sys->i_order_length-- )
871         {
872             free( p_sys->ppsz_order[p_sys->i_order_length] );
873         }
874         free( p_sys->ppsz_order );
875         p_sys->ppsz_order = NULL;
876
877         if( *psz_order )
878         {
879             char *psz_end = NULL;
880             i_index = 0;
881             do
882             {
883                 psz_end = strchr( psz_order, ',' );
884                 i_index++;
885                 p_sys->ppsz_order = realloc_or_free( p_sys->ppsz_order,
886                                                    i_index * sizeof(char *) );
887                 assert( p_sys->ppsz_order );
888                 p_sys->ppsz_order[i_index - 1] = strndup( psz_order,
889                                            psz_end - psz_order );
890                 psz_order = psz_end+1;
891             } while( psz_end );
892             p_sys->i_order_length = i_index;
893         }
894
895         vlc_mutex_unlock( &p_sys->lock );
896     }
897     else if( VAR_IS( "offsets" ) )
898     {
899         vlc_mutex_lock( &p_sys->lock );
900         msg_Info( p_this, "Changing mosaic-offsets to %s", newval.psz_string );
901
902         if( p_sys->i_offsets_length != 0 )
903         {
904             p_sys->i_offsets_length = 0;
905             free( p_sys->pi_x_offsets );
906             free( p_sys->pi_y_offsets );
907             p_sys->pi_x_offsets = NULL;
908             p_sys->pi_y_offsets = NULL;
909         }
910
911         mosaic_ParseSetOffsets( p_this, p_sys, newval.psz_string );
912
913         vlc_mutex_unlock( &p_sys->lock );
914     }
915     else if( VAR_IS( "keep-aspect-ratio" ) )
916     {
917         vlc_mutex_lock( &p_sys->lock );
918         if( newval.i_int )
919         {
920             msg_Dbg( p_this, "keeping aspect ratio" );
921             p_sys->b_ar = 1;
922         }
923         else
924         {
925             msg_Dbg( p_this, "won't keep aspect ratio" );
926             p_sys->b_ar = 0;
927         }
928         vlc_mutex_unlock( &p_sys->lock );
929     }
930     else if( VAR_IS( "keep-picture" ) )
931     {
932         vlc_mutex_lock( &p_sys->lock );
933         p_sys->b_keep = newval.b_bool;
934         if ( !p_sys->b_keep && !p_sys->p_image )
935         {
936             p_sys->p_image = image_HandlerCreate( p_this );
937         }
938         vlc_mutex_unlock( &p_sys->lock );
939     }
940
941     return VLC_SUCCESS;
942 }