7248e21f03efd56a9241ef78a81f088aad3da291
[vlc.git] / modules / video_splitter / wall.c
1 /*****************************************************************************
2  * wall.c : Wall video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2009 VLC authors and VideoLAN
5  *
6  * Authors: Samuel Hocevar <sam@zoy.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <assert.h>
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_video_splitter.h>
35 #include <vlc_vout_window.h>
36
37 #define ROW_MAX (15)
38 #define COL_MAX (15)
39
40 /*****************************************************************************
41  * Module descriptor
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.")
46
47 #define ROWS_TEXT N_("Number of rows")
48 #define ROWS_LONGTEXT N_("Number of vertical windows in " \
49     "which to split the video.")
50
51 #define ACTIVE_TEXT N_("Active windows")
52 #define ACTIVE_LONGTEXT N_("Comma-separated list of active windows, " \
53     "defaults to all")
54
55 #define CFG_PREFIX "wall-"
56
57 static int  Open ( vlc_object_t * );
58 static void Close( vlc_object_t * );
59
60 vlc_module_begin()
61     set_description( N_("Wall video filter") )
62     set_shortname( N_("Image wall" ))
63     set_capability( "video splitter", 0 )
64     set_category( CAT_VIDEO )
65     set_subcategory( SUBCAT_VIDEO_SPLITTER )
66
67     add_integer( CFG_PREFIX "cols", 3, COLS_TEXT, COLS_LONGTEXT, false )
68     change_integer_range( 1, COL_MAX )
69     add_integer( CFG_PREFIX "rows", 3, ROWS_TEXT, ROWS_LONGTEXT, false )
70     change_integer_range( 1, ROW_MAX )
71     add_string( CFG_PREFIX "active", NULL, ACTIVE_TEXT, ACTIVE_LONGTEXT,
72                  true )
73     add_obsolete_string( CFG_PREFIX "element-aspect" ) /* since 4.0.0 */
74
75     add_shortcut( "wall" )
76     set_callbacks( Open, Close )
77 vlc_module_end()
78
79 /*****************************************************************************
80  * Local prototypes
81  *****************************************************************************/
82 static const char *const ppsz_filter_options[] = {
83     "cols", "rows", "active", NULL
84 };
85
86 /* */
87 typedef struct
88 {
89     bool b_active;
90     int  i_output;
91     int  i_width;
92     int  i_height;
93     int  i_left;
94     int  i_top;
95 } wall_output_t;
96
97 typedef struct
98 {
99     int           i_col;
100     int           i_row;
101     int           i_output;
102     wall_output_t pp_output[COL_MAX][ROW_MAX]; /* [x][y] */
103 } video_splitter_sys_t;
104
105 static int Filter( video_splitter_t *, picture_t *pp_dst[], picture_t * );
106 static int Mouse( video_splitter_t *, int, vout_window_mouse_event_t * );
107
108 /**
109  * This function allocates and initializes a Wall splitter module.
110  */
111 static int Open( vlc_object_t *p_this )
112 {
113     video_splitter_t *p_splitter = (video_splitter_t*)p_this;
114     video_splitter_sys_t *p_sys;
115
116     const vlc_chroma_description_t *p_chroma =
117         vlc_fourcc_GetChromaDescription( p_splitter->fmt.i_chroma );
118     if( p_chroma == NULL || p_chroma->plane_count == 0 )
119         return VLC_EGENERIC;
120
121     p_splitter->p_sys = p_sys = malloc( sizeof(*p_sys) );
122     if( !p_sys )
123         return VLC_ENOMEM;
124
125     config_ChainParse( p_splitter, CFG_PREFIX, ppsz_filter_options,
126                        p_splitter->p_cfg );
127
128     /* */
129     p_sys->i_col = var_CreateGetInteger( p_splitter, CFG_PREFIX "cols" );
130     p_sys->i_col = VLC_CLIP( p_sys->i_col, 1, COL_MAX );
131
132     p_sys->i_row = var_CreateGetInteger( p_splitter, CFG_PREFIX "rows" );
133     p_sys->i_row = VLC_CLIP( p_sys->i_row, 1, ROW_MAX );
134
135     msg_Dbg( p_splitter, "opening a %i x %i wall",
136              p_sys->i_col, p_sys->i_row );
137
138     /* */
139     char *psz_state = var_CreateGetNonEmptyString( p_splitter, CFG_PREFIX "active" );
140
141     /* */
142     bool pb_active[COL_MAX*ROW_MAX];
143     for( int i = 0; i < COL_MAX*ROW_MAX; i++ )
144         pb_active[i] = psz_state == NULL;
145
146     /* Parse active list if provided */
147     char *psz_tmp = psz_state;
148     while( psz_tmp && *psz_tmp )
149     {
150         char *psz_next = strchr( psz_tmp, ',' );
151         if( psz_next )
152             *psz_next++ = '\0';
153
154         const int i_index = atoi( psz_tmp );
155         if( i_index >= 0 && i_index < COL_MAX*ROW_MAX )
156             pb_active[i_index] = true;
157
158         psz_tmp = psz_next;
159     }
160     free( psz_state );
161
162     /* Compute placements/size of the windows */
163     int i_active = 0;
164     for( int y = 0, i_top = 0; y < p_sys->i_row; y++ )
165     {
166         unsigned i_height = ((p_splitter->fmt.i_visible_height - i_top)
167                              / (p_sys->i_row - y)) & ~1;
168
169         for( int x = 0, i_left = 0; x < p_sys->i_col; x++ )
170         {
171             wall_output_t *p_output = &p_sys->pp_output[x][y];
172             unsigned i_width = ((p_splitter->fmt.i_visible_width - i_left)
173                                 / (p_sys->i_col - x)) & ~1;
174
175             /* */
176             p_output->b_active = pb_active[y * p_sys->i_col + x];
177             p_output->i_output = -1;
178             p_output->i_width = i_width;
179             p_output->i_height = i_height;
180             p_output->i_left = i_left;
181             p_output->i_top = i_top;
182
183             msg_Dbg( p_splitter, "window %dx%d at %d:%d size %dx%d", 
184                      x, y, i_left, i_top, i_width, i_height );
185
186             if( p_output->b_active )
187                 i_active++;
188
189             i_left += i_width;
190         }
191         i_top += i_height;
192     }
193     if( i_active <= 0 )
194     {
195         msg_Err( p_splitter, "No active video output" );
196         free( p_sys );
197         return VLC_EGENERIC;
198     }
199
200     /* Setup output configuration */
201     p_splitter->i_output = i_active;
202     p_splitter->p_output = calloc( p_splitter->i_output,
203                                    sizeof(*p_splitter->p_output) );
204     if( !p_splitter->p_output )
205     {
206         free( p_sys );
207         return VLC_ENOMEM;
208     }
209     for( int y = 0, i_output = 0; y < p_sys->i_row; y++ )
210     {
211         for( int x = 0; x < p_sys->i_col; x++ )
212         {
213             wall_output_t *p_output = &p_sys->pp_output[x][y];
214             if( !p_output->b_active )
215                 continue;
216
217             p_output->i_output = i_output++;
218
219             video_splitter_output_t *p_cfg = &p_splitter->p_output[p_output->i_output];
220
221             video_format_Copy( &p_cfg->fmt, &p_splitter->fmt );
222             p_cfg->fmt.i_visible_width  =
223             p_cfg->fmt.i_width          = p_output->i_width;
224             p_cfg->fmt.i_visible_height =
225             p_cfg->fmt.i_height         = p_output->i_height;
226             p_cfg->fmt.i_sar_num        = p_splitter->fmt.i_sar_num;
227             p_cfg->fmt.i_sar_den        = p_splitter->fmt.i_sar_den;
228             p_cfg->psz_module = NULL;
229         }
230     }
231
232     /* */
233     p_splitter->pf_filter = Filter;
234     p_splitter->mouse = Mouse;
235
236     return VLC_SUCCESS;
237 }
238
239 /**
240  * Terminate a splitter module.
241  */
242 static void Close( vlc_object_t *p_this )
243 {
244     video_splitter_t *p_splitter = (video_splitter_t*)p_this;
245     video_splitter_sys_t *p_sys = p_splitter->p_sys;
246
247     free( p_splitter->p_output );
248     free( p_sys );
249 }
250
251 static int Filter( video_splitter_t *p_splitter, picture_t *pp_dst[], picture_t *p_src )
252 {
253     video_splitter_sys_t *p_sys = p_splitter->p_sys;
254
255     if( video_splitter_NewPicture( p_splitter, pp_dst ) )
256     {
257         picture_Release( p_src );
258         return VLC_EGENERIC;
259     }
260
261     for( int y = 0; y < p_sys->i_row; y++ )
262     {
263         for( int x = 0; x < p_sys->i_col; x++ )
264         {
265             wall_output_t *p_output = &p_sys->pp_output[x][y];
266             if( !p_output->b_active )
267                 continue;
268
269             picture_t *p_dst = pp_dst[p_output->i_output];
270
271             /* */
272             picture_t tmp = *p_src;
273             for( int i = 0; i < tmp.i_planes; i++ )
274             {
275                 plane_t *p0 = &tmp.p[0];
276                 plane_t *p = &tmp.p[i];
277                 const int i_y = p_output->i_top  * p->i_visible_pitch / p0->i_visible_pitch;
278                 const int i_x = p_output->i_left * p->i_visible_lines / p0->i_visible_lines;
279
280                 p->p_pixels += i_y * p->i_pitch + ( i_x - (i_x % p->i_pixel_pitch));
281             }
282             picture_Copy( p_dst, &tmp );
283         }
284     }
285
286     picture_Release( p_src );
287     return VLC_SUCCESS;
288 }
289 static int Mouse( video_splitter_t *p_splitter, int i_index,
290                   vout_window_mouse_event_t *restrict ev )
291 {
292     video_splitter_sys_t *p_sys = p_splitter->p_sys;
293
294     for( int y = 0; y < p_sys->i_row; y++ )
295     {
296         for( int x = 0; x < p_sys->i_col; x++ )
297         {
298             wall_output_t *p_output = &p_sys->pp_output[x][y];
299
300             if( p_output->b_active && p_output->i_output == i_index )
301             {
302                 ev->x += p_output->i_left;
303                 ev->y += p_output->i_top;
304                 return VLC_SUCCESS;
305             }
306         }
307     }
308     vlc_assert_unreachable();
309     return VLC_EGENERIC;
310 }
311