1 /*****************************************************************************
2 * wall.c : Wall video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2009 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
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 */
40 /*****************************************************************************
42 *****************************************************************************/
43 #define COLS_TEXT N_("Number of columns")
44 #define COLS_LONGTEXT N_("Number of horizontal windows in " \
45 "which to split the video.")
47 #define ROWS_TEXT N_("Number of rows")
48 #define ROWS_LONGTEXT N_("Number of vertical windows in " \
49 "which to split the video.")
51 #define ACTIVE_TEXT N_("Active windows")
52 #define ACTIVE_LONGTEXT N_("Comma-separated list of active windows, " \
55 #define ASPECT_TEXT N_("Element aspect ratio")
56 #define ASPECT_LONGTEXT N_("Aspect ratio of the individual displays " \
59 #define CFG_PREFIX "wall-"
61 static int Open ( vlc_object_t
* );
62 static void Close( vlc_object_t
* );
65 set_description( N_("Wall video filter") )
66 set_shortname( N_("Image wall" ))
67 set_capability( "video splitter", 0 )
68 set_category( CAT_VIDEO
)
69 set_subcategory( SUBCAT_VIDEO_VFILTER
)
71 add_integer( CFG_PREFIX
"cols", 3, NULL
, COLS_TEXT
, COLS_LONGTEXT
, false )
72 add_integer( CFG_PREFIX
"rows", 3, NULL
, ROWS_TEXT
, ROWS_LONGTEXT
, false )
73 add_string( CFG_PREFIX
"active", NULL
, NULL
, ACTIVE_TEXT
, ACTIVE_LONGTEXT
,
75 add_string( CFG_PREFIX
"element-aspect", "4:3", NULL
, ASPECT_TEXT
, ASPECT_LONGTEXT
, false )
77 add_shortcut( "wall" )
78 set_callbacks( Open
, Close
)
81 /*****************************************************************************
83 *****************************************************************************/
84 static const char *const ppsz_filter_options
[] = {
85 "cols", "rows", "active", "element-aspect", NULL
102 struct video_splitter_sys_t
107 wall_output_t pp_output
[COL_MAX
][ROW_MAX
]; /* [x][y] */
110 static int Filter( video_splitter_t
*, picture_t
*pp_dst
[], picture_t
* );
111 static int Mouse( video_splitter_t
*, vlc_mouse_t
*,
113 const vlc_mouse_t
*p_old
, const vlc_mouse_t
*p_new
);
116 * This function allocates and initializes a Wall splitter module.
118 static int Open( vlc_object_t
*p_this
)
120 video_splitter_t
*p_splitter
= (video_splitter_t
*)p_this
;
121 video_splitter_sys_t
*p_sys
;
123 p_splitter
->p_sys
= p_sys
= malloc( sizeof(*p_sys
) );
127 config_ChainParse( p_splitter
, CFG_PREFIX
, ppsz_filter_options
,
131 p_sys
->i_col
= var_CreateGetInteger( p_splitter
, CFG_PREFIX
"cols" );
132 p_sys
->i_col
= __MAX( 1, __MIN( COL_MAX
, p_sys
->i_col
) );
134 p_sys
->i_row
= var_CreateGetInteger( p_splitter
, CFG_PREFIX
"rows" );
135 p_sys
->i_row
= __MAX( 1, __MIN( ROW_MAX
, p_sys
->i_row
) );
137 msg_Dbg( p_splitter
, "opening a %i x %i wall",
138 p_sys
->i_col
, p_sys
->i_row
);
141 char *psz_state
= var_CreateGetNonEmptyString( p_splitter
, CFG_PREFIX
"active" );
144 bool pb_active
[COL_MAX
*ROW_MAX
];
145 for( int i
= 0; i
< COL_MAX
*ROW_MAX
; i
++ )
146 pb_active
[i
] = psz_state
== NULL
;
148 /* Parse active list if provided */
149 char *psz_tmp
= psz_state
;
150 while( psz_tmp
&& *psz_tmp
)
152 char *psz_next
= strchr( psz_tmp
, ',' );
156 const int i_index
= atoi( psz_tmp
);
157 if( i_index
>= 0 && i_index
< COL_MAX
*ROW_MAX
)
158 pb_active
[i_index
] = true;
164 /* Parse aspect ratio if provided */
166 char *psz_aspect
= var_CreateGetNonEmptyString( p_splitter
,
167 CFG_PREFIX
"element-aspect" );
170 int i_ar_num
, i_ar_den
;
171 if( sscanf( psz_aspect
, "%d:%d", &i_ar_num
, &i_ar_den
) == 2 &&
172 i_ar_num
> 0 && i_ar_den
> 0 )
174 i_aspect
= i_ar_num
* VOUT_ASPECT_FACTOR
/ i_ar_den
;
178 msg_Warn( p_splitter
, "invalid aspect ratio specification" );
183 i_aspect
= 4 * VOUT_ASPECT_FACTOR
/ 3;
185 /* Compute placements/size of the windows */
186 const unsigned w1
= ( p_splitter
->fmt
.i_width
/ p_sys
->i_col
) & ~1;
187 const unsigned h1
= ( w1
* VOUT_ASPECT_FACTOR
/ i_aspect
) & ~1;
189 const unsigned h2
= ( p_splitter
->fmt
.i_height
/ p_sys
->i_row
) & ~1;
190 const unsigned w2
= ( h2
* i_aspect
/ VOUT_ASPECT_FACTOR
) & ~1;
192 unsigned i_target_width
;
193 unsigned i_target_height
;
194 unsigned i_hstart
, i_hend
;
195 unsigned i_vstart
, i_vend
;
196 bool b_vstart_rounded
;
197 bool b_hstart_rounded
;
199 if( h1
* p_sys
->i_row
< p_splitter
->fmt
.i_height
)
202 i_target_height
= h2
;
205 b_vstart_rounded
= false;
206 i_vend
= p_splitter
->fmt
.i_height
;
208 unsigned i_tmp
= i_target_width
* p_sys
->i_col
;
209 while( i_tmp
< p_splitter
->fmt
.i_width
)
210 i_tmp
+= p_sys
->i_col
;
212 i_hstart
= (( i_tmp
- p_splitter
->fmt
.i_width
) / 2)&~1;
213 b_hstart_rounded
= ( ( i_tmp
- p_splitter
->fmt
.i_width
) % 2 ) ||
214 ( ( ( i_tmp
- p_splitter
->fmt
.i_width
) / 2 ) & 1 );
215 i_hend
= i_hstart
+ p_splitter
->fmt
.i_width
;
219 i_target_height
= h1
;
223 b_hstart_rounded
= false;
224 i_hend
= p_splitter
->fmt
.i_width
;
226 unsigned i_tmp
= i_target_height
* p_sys
->i_row
;
227 while( i_tmp
< p_splitter
->fmt
.i_height
)
228 i_tmp
+= p_sys
->i_row
;
230 i_vstart
= ( ( i_tmp
- p_splitter
->fmt
.i_height
) / 2 ) & ~1;
231 b_vstart_rounded
= ( ( i_tmp
- p_splitter
->fmt
.i_height
) % 2 ) ||
232 ( ( ( i_tmp
- p_splitter
->fmt
.i_height
) / 2 ) & 1 );
233 i_vend
= i_vstart
+ p_splitter
->fmt
.i_height
;
235 msg_Dbg( p_splitter
, "target resolution %dx%d", i_target_width
, i_target_height
);
236 msg_Dbg( p_splitter
, "target window (%d,%d)-(%d,%d)", i_hstart
,i_vstart
,i_hend
,i_vend
);
239 for( int y
= 0, i_top
= 0; y
< p_sys
->i_row
; y
++ )
244 if( y
* i_target_height
>= i_vstart
&&
245 ( y
+ 1 ) * i_target_height
<= i_vend
)
247 i_height
= i_target_height
;
249 else if( ( y
+ 1 ) * i_target_height
< i_vstart
||
250 ( y
* i_target_height
) > i_vend
)
256 i_height
= ( i_target_height
-
257 i_vstart
%i_target_height
);
258 if( y
>= ( p_sys
->i_row
/ 2 ) )
260 i_halign
= VOUT_ALIGN_TOP
;
261 i_height
-= b_vstart_rounded
? 2: 0;
265 i_halign
= VOUT_ALIGN_BOTTOM
;
270 for( int x
= 0, i_left
= 0; x
< p_sys
->i_col
; x
++ )
272 wall_output_t
*p_output
= &p_sys
->pp_output
[x
][y
];
277 if( x
*i_target_width
>= i_hstart
&&
278 (x
+1)*i_target_width
<= i_hend
)
280 i_width
= i_target_width
;
282 else if( ( x
+ 1 ) * i_target_width
< i_hstart
||
283 ( x
* i_target_width
) > i_hend
)
289 i_width
= ( i_target_width
- i_hstart
% i_target_width
);
290 if( x
>= ( p_sys
->i_col
/ 2 ) )
292 i_valign
= VOUT_ALIGN_LEFT
;
293 i_width
-= b_hstart_rounded
? 2: 0;
297 i_valign
= VOUT_ALIGN_RIGHT
;
302 p_output
->b_active
= pb_active
[y
* p_sys
->i_col
+ x
] &&
303 i_height
> 0 && i_width
> 0;
304 p_output
->i_output
= -1;
305 p_output
->i_align
= i_valign
| i_halign
;
306 p_output
->i_width
= i_width
;
307 p_output
->i_height
= i_height
;
308 p_output
->i_left
= i_left
;
309 p_output
->i_top
= i_top
;
311 msg_Dbg( p_splitter
, "window %dx%d at %d:%d size %dx%d",
312 x
, y
, i_left
, i_top
, i_width
, i_height
);
314 if( p_output
->b_active
)
323 msg_Err( p_splitter
, "No active video output" );
328 /* Setup output configuration */
329 p_splitter
->i_output
= i_active
;
330 p_splitter
->p_output
= calloc( p_splitter
->i_output
,
331 sizeof(*p_splitter
->p_output
) );
332 if( !p_splitter
->p_output
)
337 for( int y
= 0, i_output
= 0; y
< p_sys
->i_row
; y
++ )
339 for( int x
= 0; x
< p_sys
->i_col
; x
++ )
341 wall_output_t
*p_output
= &p_sys
->pp_output
[x
][y
];
342 if( !p_output
->b_active
)
345 p_output
->i_output
= i_output
++;
347 video_splitter_output_t
*p_cfg
= &p_splitter
->p_output
[p_output
->i_output
];
349 video_format_Copy( &p_cfg
->fmt
, &p_splitter
->fmt
);
350 p_cfg
->fmt
.i_visible_width
=
351 p_cfg
->fmt
.i_width
= p_output
->i_width
;
352 p_cfg
->fmt
.i_visible_height
=
353 p_cfg
->fmt
.i_height
= p_output
->i_height
;
354 p_cfg
->fmt
.i_sar_num
= (int64_t)i_aspect
* i_target_height
;
355 p_cfg
->fmt
.i_sar_den
= VOUT_ASPECT_FACTOR
* i_target_width
;
356 p_cfg
->window
.i_x
= p_output
->i_left
; /* FIXME relative to video-x/y (TODO in wrapper.c) ? */
357 p_cfg
->window
.i_y
= p_output
->i_top
;
358 p_cfg
->window
.i_align
= p_output
->i_align
;
359 p_cfg
->psz_module
= NULL
;
364 p_splitter
->pf_filter
= Filter
;
365 p_splitter
->pf_mouse
= Mouse
;
371 * Terminate a splitter module.
373 static void Close( vlc_object_t
*p_this
)
375 video_splitter_t
*p_splitter
= (video_splitter_t
*)p_this
;
376 video_splitter_sys_t
*p_sys
= p_splitter
->p_sys
;
378 free( p_splitter
->p_output
);
382 static int Filter( video_splitter_t
*p_splitter
, picture_t
*pp_dst
[], picture_t
*p_src
)
384 video_splitter_sys_t
*p_sys
= p_splitter
->p_sys
;
386 if( video_splitter_NewPicture( p_splitter
, pp_dst
) )
388 picture_Release( p_src
);
392 for( int y
= 0; y
< p_sys
->i_row
; y
++ )
394 for( int x
= 0; x
< p_sys
->i_col
; x
++ )
396 wall_output_t
*p_output
= &p_sys
->pp_output
[x
][y
];
397 if( !p_output
->b_active
)
400 picture_t
*p_dst
= pp_dst
[p_output
->i_output
];
403 picture_t tmp
= *p_src
;
404 for( int i
= 0; i
< tmp
.i_planes
; i
++ )
406 plane_t
*p0
= &tmp
.p
[0];
407 plane_t
*p
= &tmp
.p
[i
];
408 const int i_y
= p_output
->i_top
* p
->i_visible_pitch
/ p0
->i_visible_pitch
;
409 const int i_x
= p_output
->i_left
* p
->i_visible_lines
/ p0
->i_visible_lines
;
411 p
->p_pixels
+= i_y
* p
->i_pitch
+ ( i_x
- (i_x
% p
->i_pixel_pitch
));
413 picture_Copy( p_dst
, &tmp
);
417 picture_Release( p_src
);
420 static int Mouse( video_splitter_t
*p_splitter
, vlc_mouse_t
*p_mouse
,
422 const vlc_mouse_t
*p_old
, const vlc_mouse_t
*p_new
)
425 video_splitter_sys_t
*p_sys
= p_splitter
->p_sys
;
427 for( int y
= 0; y
< p_sys
->i_row
; y
++ )
429 for( int x
= 0; x
< p_sys
->i_col
; x
++ )
431 wall_output_t
*p_output
= &p_sys
->pp_output
[x
][y
];
432 if( p_output
->b_active
&& p_output
->i_output
== i_index
)
435 p_mouse
->i_x
+= p_output
->i_left
;
436 p_mouse
->i_y
+= p_output
->i_top
;