Change the gmp download URL to https://gmplib.org/download
[vlc/gmpfix.git] / modules / video_splitter / wall.c
blob5be60dcf313233e8b61a0b6ea5541cf953cecf4c
1 /*****************************************************************************
2 * wall.c : Wall video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2009 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Samuel Hocevar <sam@zoy.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
31 #include <assert.h>
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_video_splitter.h>
37 /* FIXME it is needed for VOUT_ALIGN_* only */
38 #include <vlc_vout.h>
40 #define ROW_MAX (15)
41 #define COL_MAX (15)
43 /*****************************************************************************
44 * Module descriptor
45 *****************************************************************************/
46 #define COLS_TEXT N_("Number of columns")
47 #define COLS_LONGTEXT N_("Number of horizontal windows in " \
48 "which to split the video.")
50 #define ROWS_TEXT N_("Number of rows")
51 #define ROWS_LONGTEXT N_("Number of vertical windows in " \
52 "which to split the video.")
54 #define ACTIVE_TEXT N_("Active windows")
55 #define ACTIVE_LONGTEXT N_("Comma-separated list of active windows, " \
56 "defaults to all")
58 #define ASPECT_TEXT N_("Element aspect ratio")
59 #define ASPECT_LONGTEXT N_("Aspect ratio of the individual displays " \
60 "building the wall.")
62 #define CFG_PREFIX "wall-"
64 static int Open ( vlc_object_t * );
65 static void Close( vlc_object_t * );
67 vlc_module_begin()
68 set_description( N_("Wall video filter") )
69 set_shortname( N_("Image wall" ))
70 set_capability( "video splitter", 0 )
71 set_category( CAT_VIDEO )
72 set_subcategory( SUBCAT_VIDEO_VFILTER )
74 add_integer( CFG_PREFIX "cols", 3, COLS_TEXT, COLS_LONGTEXT, false )
75 change_integer_range( 1, COL_MAX )
76 add_integer( CFG_PREFIX "rows", 3, ROWS_TEXT, ROWS_LONGTEXT, false )
77 change_integer_range( 1, ROW_MAX )
78 add_string( CFG_PREFIX "active", NULL, ACTIVE_TEXT, ACTIVE_LONGTEXT,
79 true )
80 add_string( CFG_PREFIX "element-aspect", "16:9", ASPECT_TEXT, ASPECT_LONGTEXT, false )
82 add_shortcut( "wall" )
83 set_callbacks( Open, Close )
84 vlc_module_end()
86 /*****************************************************************************
87 * Local prototypes
88 *****************************************************************************/
89 static const char *const ppsz_filter_options[] = {
90 "cols", "rows", "active", "element-aspect", NULL
93 /* */
94 typedef struct
96 bool b_active;
97 int i_output;
98 int i_width;
99 int i_height;
100 int i_align;
101 int i_left;
102 int i_top;
103 } wall_output_t;
105 struct video_splitter_sys_t
107 int i_col;
108 int i_row;
109 int i_output;
110 wall_output_t pp_output[COL_MAX][ROW_MAX]; /* [x][y] */
113 static int Filter( video_splitter_t *, picture_t *pp_dst[], picture_t * );
114 static int Mouse( video_splitter_t *, vlc_mouse_t *,
115 int i_index,
116 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new );
119 * This function allocates and initializes a Wall splitter module.
121 static int Open( vlc_object_t *p_this )
123 video_splitter_t *p_splitter = (video_splitter_t*)p_this;
124 video_splitter_sys_t *p_sys;
126 const vlc_chroma_description_t *p_chroma =
127 vlc_fourcc_GetChromaDescription( p_splitter->fmt.i_chroma );
128 if( p_chroma == NULL || p_chroma->plane_count == 0 )
129 return VLC_EGENERIC;
131 p_splitter->p_sys = p_sys = malloc( sizeof(*p_sys) );
132 if( !p_sys )
133 return VLC_ENOMEM;
135 config_ChainParse( p_splitter, CFG_PREFIX, ppsz_filter_options,
136 p_splitter->p_cfg );
138 /* */
139 p_sys->i_col = var_CreateGetInteger( p_splitter, CFG_PREFIX "cols" );
140 p_sys->i_col = VLC_CLIP( p_sys->i_col, 1, COL_MAX );
142 p_sys->i_row = var_CreateGetInteger( p_splitter, CFG_PREFIX "rows" );
143 p_sys->i_row = VLC_CLIP( p_sys->i_row, 1, ROW_MAX );
145 msg_Dbg( p_splitter, "opening a %i x %i wall",
146 p_sys->i_col, p_sys->i_row );
148 /* */
149 char *psz_state = var_CreateGetNonEmptyString( p_splitter, CFG_PREFIX "active" );
151 /* */
152 bool pb_active[COL_MAX*ROW_MAX];
153 for( int i = 0; i < COL_MAX*ROW_MAX; i++ )
154 pb_active[i] = psz_state == NULL;
156 /* Parse active list if provided */
157 char *psz_tmp = psz_state;
158 while( psz_tmp && *psz_tmp )
160 char *psz_next = strchr( psz_tmp, ',' );
161 if( psz_next )
162 *psz_next++ = '\0';
164 const int i_index = atoi( psz_tmp );
165 if( i_index >= 0 && i_index < COL_MAX*ROW_MAX )
166 pb_active[i_index] = true;
168 psz_tmp = psz_next;
170 free( psz_state );
172 /* Parse aspect ratio if provided */
173 int i_aspect = 0;
174 char *psz_aspect = var_CreateGetNonEmptyString( p_splitter,
175 CFG_PREFIX "element-aspect" );
176 if( psz_aspect )
178 int i_ar_num, i_ar_den;
179 if( sscanf( psz_aspect, "%d:%d", &i_ar_num, &i_ar_den ) == 2 &&
180 i_ar_num > 0 && i_ar_den > 0 )
182 i_aspect = i_ar_num * VOUT_ASPECT_FACTOR / i_ar_den;
184 else
186 msg_Warn( p_splitter, "invalid aspect ratio specification" );
188 free( psz_aspect );
190 if( i_aspect <= 0 )
191 i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
193 /* Compute placements/size of the windows */
194 const unsigned w1 = ( p_splitter->fmt.i_width / p_sys->i_col ) & ~1;
195 const unsigned h1 = ( w1 * VOUT_ASPECT_FACTOR / i_aspect ) & ~1;
197 const unsigned h2 = ( p_splitter->fmt.i_height / p_sys->i_row ) & ~1;
198 const unsigned w2 = ( h2 * i_aspect / VOUT_ASPECT_FACTOR ) & ~1;
200 unsigned i_target_width;
201 unsigned i_target_height;
202 unsigned i_hstart, i_hend;
203 unsigned i_vstart, i_vend;
204 bool b_vstart_rounded;
205 bool b_hstart_rounded;
207 if( h1 * p_sys->i_row < p_splitter->fmt.i_height )
209 i_target_width = w2;
210 i_target_height = h2;
212 i_vstart = 0;
213 b_vstart_rounded = false;
214 i_vend = p_splitter->fmt.i_height;
216 unsigned i_tmp = i_target_width * p_sys->i_col;
217 while( i_tmp < p_splitter->fmt.i_width )
218 i_tmp += p_sys->i_col;
220 i_hstart = (( i_tmp - p_splitter->fmt.i_width ) / 2)&~1;
221 b_hstart_rounded = ( ( i_tmp - p_splitter->fmt.i_width ) % 2 ) ||
222 ( ( ( i_tmp - p_splitter->fmt.i_width ) / 2 ) & 1 );
223 i_hend = i_hstart + p_splitter->fmt.i_width;
225 else
227 i_target_height = h1;
228 i_target_width = w1;
230 i_hstart = 0;
231 b_hstart_rounded = false;
232 i_hend = p_splitter->fmt.i_width;
234 unsigned i_tmp = i_target_height * p_sys->i_row;
235 while( i_tmp < p_splitter->fmt.i_height )
236 i_tmp += p_sys->i_row;
238 i_vstart = ( ( i_tmp - p_splitter->fmt.i_height ) / 2 ) & ~1;
239 b_vstart_rounded = ( ( i_tmp - p_splitter->fmt.i_height ) % 2 ) ||
240 ( ( ( i_tmp - p_splitter->fmt.i_height ) / 2 ) & 1 );
241 i_vend = i_vstart + p_splitter->fmt.i_height;
243 msg_Dbg( p_splitter, "target resolution %dx%d", i_target_width, i_target_height );
244 msg_Dbg( p_splitter, "target window (%d,%d)-(%d,%d)", i_hstart,i_vstart,i_hend,i_vend );
246 int i_active = 0;
247 for( int y = 0, i_top = 0; y < p_sys->i_row; y++ )
249 /* */
250 int i_height = 0;
251 int i_halign = 0;
252 if( y * i_target_height >= i_vstart &&
253 ( y + 1 ) * i_target_height <= i_vend )
255 i_height = i_target_height;
257 else if( ( y + 1 ) * i_target_height < i_vstart ||
258 ( y * i_target_height ) > i_vend )
260 i_height = 0;
262 else
264 i_height = ( i_target_height -
265 i_vstart%i_target_height );
266 if( y >= ( p_sys->i_row / 2 ) )
268 i_halign = VOUT_ALIGN_TOP;
269 i_height -= b_vstart_rounded ? 2: 0;
271 else
273 i_halign = VOUT_ALIGN_BOTTOM;
277 /* */
278 for( int x = 0, i_left = 0; x < p_sys->i_col; x++ )
280 wall_output_t *p_output = &p_sys->pp_output[x][y];
282 /* */
283 int i_width;
284 int i_valign = 0;
285 if( x*i_target_width >= i_hstart &&
286 (x+1)*i_target_width <= i_hend )
288 i_width = i_target_width;
290 else if( ( x + 1 ) * i_target_width < i_hstart ||
291 ( x * i_target_width ) > i_hend )
293 i_width = 0;
295 else
297 i_width = ( i_target_width - i_hstart % i_target_width );
298 if( x >= ( p_sys->i_col / 2 ) )
300 i_valign = VOUT_ALIGN_LEFT;
301 i_width -= b_hstart_rounded ? 2: 0;
303 else
305 i_valign = VOUT_ALIGN_RIGHT;
309 /* */
310 p_output->b_active = pb_active[y * p_sys->i_col + x] &&
311 i_height > 0 && i_width > 0;
312 p_output->i_output = -1;
313 p_output->i_align = i_valign | i_halign;
314 p_output->i_width = i_width;
315 p_output->i_height = i_height;
316 p_output->i_left = i_left;
317 p_output->i_top = i_top;
319 msg_Dbg( p_splitter, "window %dx%d at %d:%d size %dx%d",
320 x, y, i_left, i_top, i_width, i_height );
322 if( p_output->b_active )
323 i_active++;
325 i_left += i_width;
327 i_top += i_height;
329 if( i_active <= 0 )
331 msg_Err( p_splitter, "No active video output" );
332 free( p_sys );
333 return VLC_EGENERIC;
336 /* Setup output configuration */
337 p_splitter->i_output = i_active;
338 p_splitter->p_output = calloc( p_splitter->i_output,
339 sizeof(*p_splitter->p_output) );
340 if( !p_splitter->p_output )
342 free( p_sys );
343 return VLC_ENOMEM;
345 for( int y = 0, i_output = 0; y < p_sys->i_row; y++ )
347 for( int x = 0; x < p_sys->i_col; x++ )
349 wall_output_t *p_output = &p_sys->pp_output[x][y];
350 if( !p_output->b_active )
351 continue;
353 p_output->i_output = i_output++;
355 video_splitter_output_t *p_cfg = &p_splitter->p_output[p_output->i_output];
357 video_format_Copy( &p_cfg->fmt, &p_splitter->fmt );
358 p_cfg->fmt.i_visible_width =
359 p_cfg->fmt.i_width = p_output->i_width;
360 p_cfg->fmt.i_visible_height =
361 p_cfg->fmt.i_height = p_output->i_height;
362 p_cfg->fmt.i_sar_num = (int64_t)i_aspect * i_target_height;
363 p_cfg->fmt.i_sar_den = VOUT_ASPECT_FACTOR * i_target_width;
364 p_cfg->window.i_x = p_output->i_left; /* FIXME relative to video-x/y (TODO in wrapper.c) ? */
365 p_cfg->window.i_y = p_output->i_top;
366 p_cfg->window.i_align = p_output->i_align;
367 p_cfg->psz_module = NULL;
371 /* */
372 p_splitter->pf_filter = Filter;
373 p_splitter->pf_mouse = Mouse;
375 return VLC_SUCCESS;
379 * Terminate a splitter module.
381 static void Close( vlc_object_t *p_this )
383 video_splitter_t *p_splitter = (video_splitter_t*)p_this;
384 video_splitter_sys_t *p_sys = p_splitter->p_sys;
386 free( p_splitter->p_output );
387 free( p_sys );
390 static int Filter( video_splitter_t *p_splitter, picture_t *pp_dst[], picture_t *p_src )
392 video_splitter_sys_t *p_sys = p_splitter->p_sys;
394 if( video_splitter_NewPicture( p_splitter, pp_dst ) )
396 picture_Release( p_src );
397 return VLC_EGENERIC;
400 for( int y = 0; y < p_sys->i_row; y++ )
402 for( int x = 0; x < p_sys->i_col; x++ )
404 wall_output_t *p_output = &p_sys->pp_output[x][y];
405 if( !p_output->b_active )
406 continue;
408 picture_t *p_dst = pp_dst[p_output->i_output];
410 /* */
411 picture_t tmp = *p_src;
412 for( int i = 0; i < tmp.i_planes; i++ )
414 plane_t *p0 = &tmp.p[0];
415 plane_t *p = &tmp.p[i];
416 const int i_y = p_output->i_top * p->i_visible_pitch / p0->i_visible_pitch;
417 const int i_x = p_output->i_left * p->i_visible_lines / p0->i_visible_lines;
419 p->p_pixels += i_y * p->i_pitch + ( i_x - (i_x % p->i_pixel_pitch));
421 picture_Copy( p_dst, &tmp );
425 picture_Release( p_src );
426 return VLC_SUCCESS;
428 static int Mouse( video_splitter_t *p_splitter, vlc_mouse_t *p_mouse,
429 int i_index,
430 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
432 VLC_UNUSED(p_old);
433 video_splitter_sys_t *p_sys = p_splitter->p_sys;
435 for( int y = 0; y < p_sys->i_row; y++ )
437 for( int x = 0; x < p_sys->i_col; x++ )
439 wall_output_t *p_output = &p_sys->pp_output[x][y];
440 if( p_output->b_active && p_output->i_output == i_index )
442 *p_mouse = *p_new;
443 p_mouse->i_x += p_output->i_left;
444 p_mouse->i_y += p_output->i_top;
445 return VLC_SUCCESS;
449 assert(0);
450 return VLC_EGENERIC;