1 /*****************************************************************************
2 * wall.c : Wall video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2009 VLC authors and VideoLAN
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 /*****************************************************************************
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 */
43 /*****************************************************************************
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, " \
58 #define ASPECT_TEXT N_("Element aspect ratio")
59 #define ASPECT_LONGTEXT N_("Aspect ratio of the individual displays " \
62 #define CFG_PREFIX "wall-"
64 static int Open ( vlc_object_t
* );
65 static void Close( vlc_object_t
* );
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
,
80 add_string( CFG_PREFIX
"element-aspect", "16:9", ASPECT_TEXT
, ASPECT_LONGTEXT
, false )
82 add_shortcut( "wall" )
83 set_callbacks( Open
, Close
)
86 /*****************************************************************************
88 *****************************************************************************/
89 static const char *const ppsz_filter_options
[] = {
90 "cols", "rows", "active", "element-aspect", NULL
105 struct video_splitter_sys_t
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
*,
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 )
131 p_splitter
->p_sys
= p_sys
= malloc( sizeof(*p_sys
) );
135 config_ChainParse( p_splitter
, CFG_PREFIX
, ppsz_filter_options
,
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
);
149 char *psz_state
= var_CreateGetNonEmptyString( p_splitter
, CFG_PREFIX
"active" );
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
, ',' );
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;
172 /* Parse aspect ratio if provided */
174 char *psz_aspect
= var_CreateGetNonEmptyString( p_splitter
,
175 CFG_PREFIX
"element-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
;
186 msg_Warn( p_splitter
, "invalid aspect ratio specification" );
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
)
210 i_target_height
= h2
;
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
;
227 i_target_height
= h1
;
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
);
247 for( int y
= 0, i_top
= 0; y
< p_sys
->i_row
; y
++ )
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
)
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;
273 i_halign
= VOUT_ALIGN_BOTTOM
;
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
];
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
)
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;
305 i_valign
= VOUT_ALIGN_RIGHT
;
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
)
331 msg_Err( p_splitter
, "No active video output" );
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
)
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
)
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
;
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
;
372 p_splitter
->pf_filter
= Filter
;
373 p_splitter
->pf_mouse
= Mouse
;
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
);
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
);
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
)
408 picture_t
*p_dst
= pp_dst
[p_output
->i_output
];
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
);
428 static int Mouse( video_splitter_t
*p_splitter
, vlc_mouse_t
*p_mouse
,
430 const vlc_mouse_t
*p_old
, const vlc_mouse_t
*p_new
)
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
)
443 p_mouse
->i_x
+= p_output
->i_left
;
444 p_mouse
->i_y
+= p_output
->i_top
;
449 vlc_assert_unreachable();