* ./modules/video_filter/*.c: all filters now properly use i_visible_pitch
[vlc.git] / modules / video_filter / wall.c
1 /*****************************************************************************
2  * wall.c : Wall video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: wall.c,v 1.5 2003/01/09 17:47:05 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
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.
13  *
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.
18  *
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/vout.h>
32
33 #include "filter_common.h"
34
35 /*****************************************************************************
36  * Local prototypes
37  *****************************************************************************/
38 static int  Create    ( vlc_object_t * );
39 static void Destroy   ( vlc_object_t * );
40
41 static int  Init      ( vout_thread_t * );
42 static void End       ( vout_thread_t * );
43 static void Render    ( vout_thread_t *, picture_t * );
44
45 static void RemoveAllVout  ( vout_thread_t *p_vout );
46
47 /*****************************************************************************
48  * Module descriptor
49  *****************************************************************************/
50 #define COLS_TEXT N_("number of columns")
51 #define COLS_LONGTEXT N_("Select the number of horizontal videowindows in " \
52     "which to split the video")
53
54 #define ROWS_TEXT N_("number of rows")
55 #define ROWS_LONGTEXT N_("Select the number of vertical videowindows in " \
56     "which to split the video")
57
58 #define ACTIVE_TEXT N_("active windows")
59 #define ACTIVE_LONGTEXT N_("comma separated list of active windows, " \
60     "defaults to all")
61
62 vlc_module_begin();
63     add_category_hint( N_("Miscellaneous"), NULL );
64     add_integer( "wall-cols", 3, NULL, COLS_TEXT, COLS_LONGTEXT );
65     add_integer( "wall-rows", 3, NULL, ROWS_TEXT, ROWS_LONGTEXT );
66     add_string( "wall-active", NULL, NULL, ACTIVE_TEXT, ACTIVE_LONGTEXT );
67     set_description( _("image wall video module") );
68     set_capability( "video filter", 0 );
69     add_shortcut( "wall" );
70     set_callbacks( Create, Destroy );
71 vlc_module_end();
72
73 /*****************************************************************************
74  * vout_sys_t: Wall video output method descriptor
75  *****************************************************************************
76  * This structure is part of the video output thread descriptor.
77  * It describes the Wall specific properties of an output thread.
78  *****************************************************************************/
79 struct vout_sys_t
80 {
81     int    i_col;
82     int    i_row;
83     int    i_vout;
84     struct vout_list_t
85     {
86         vlc_bool_t b_active;
87         int i_width;
88         int i_height;
89         vout_thread_t *p_vout;
90     } *pp_vout;
91 };
92
93 /*****************************************************************************
94  * Create: allocates Wall video thread output method
95  *****************************************************************************
96  * This function allocates and initializes a Wall vout method.
97  *****************************************************************************/
98 static int Create( vlc_object_t *p_this )
99 {
100     vout_thread_t *p_vout = (vout_thread_t *)p_this;
101     char *psz_method, *psz_tmp, *psz_method_tmp;
102     int i_vout;
103
104     /* Allocate structure */
105     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
106     if( p_vout->p_sys == NULL )
107     {
108         msg_Err( p_vout, "out of memory" );
109         return VLC_ENOMEM;
110     }
111
112     p_vout->pf_init = Init;
113     p_vout->pf_end = End;
114     p_vout->pf_manage = NULL;
115     p_vout->pf_render = Render;
116     p_vout->pf_display = NULL;
117
118     /* Look what method was requested */
119     p_vout->p_sys->i_col = config_GetInt( p_vout, "wall-cols" );
120     p_vout->p_sys->i_row = config_GetInt( p_vout, "wall-rows" );
121
122     p_vout->p_sys->i_col = __MAX( 1, __MIN( 15, p_vout->p_sys->i_col ) );
123     p_vout->p_sys->i_row = __MAX( 1, __MIN( 15, p_vout->p_sys->i_row ) );
124
125     msg_Dbg( p_vout, "opening a %i x %i wall",
126              p_vout->p_sys->i_col, p_vout->p_sys->i_row );
127
128     p_vout->p_sys->pp_vout = malloc( p_vout->p_sys->i_row *
129                                      p_vout->p_sys->i_col *
130                                      sizeof(struct vout_list_t) );
131     if( p_vout->p_sys->pp_vout == NULL )
132     {
133         msg_Err( p_vout, "out of memory" );
134         free( p_vout->p_sys );
135         return VLC_ENOMEM;
136     }
137
138     psz_method_tmp = psz_method = config_GetPsz( p_vout, "wall-active" );
139
140     /* If no trailing vout are specified, take them all */
141     if( psz_method == NULL )
142     {
143         for( i_vout = p_vout->p_sys->i_row * p_vout->p_sys->i_col;
144              i_vout--; )
145         {
146             p_vout->p_sys->pp_vout[i_vout].b_active = 1;
147         }
148     }
149     /* If trailing vout are specified, activate only the requested ones */
150     else
151     {
152         for( i_vout = p_vout->p_sys->i_row * p_vout->p_sys->i_col;
153              i_vout--; )
154         {
155             p_vout->p_sys->pp_vout[i_vout].b_active = 0;
156         }
157
158         while( *psz_method )
159         {
160             psz_tmp = psz_method;
161             while( *psz_tmp && *psz_tmp != ',' )
162             {
163                 psz_tmp++;
164             }
165
166             if( *psz_tmp )
167             {
168                 *psz_tmp = '\0';
169                 i_vout = atoi( psz_method );
170                 psz_method = psz_tmp + 1;
171             }
172             else
173             {
174                 i_vout = atoi( psz_method );
175                 psz_method = psz_tmp;
176             }
177
178             if( i_vout >= 0 &&
179                 i_vout < p_vout->p_sys->i_row * p_vout->p_sys->i_col )
180             {
181                 p_vout->p_sys->pp_vout[i_vout].b_active = 1;
182             }
183         }
184     }
185
186     free( psz_method_tmp );
187
188     return VLC_SUCCESS;
189 }
190
191 /*****************************************************************************
192  * Init: initialize Wall video thread output method
193  *****************************************************************************/
194 static int Init( vout_thread_t *p_vout )
195 {
196     int i_index, i_row, i_col, i_width, i_height;
197     picture_t *p_pic;
198
199     I_OUTPUTPICTURES = 0;
200
201     /* Initialize the output structure */
202     p_vout->output.i_chroma = p_vout->render.i_chroma;
203     p_vout->output.i_width  = p_vout->render.i_width;
204     p_vout->output.i_height = p_vout->render.i_height;
205     p_vout->output.i_aspect = p_vout->render.i_aspect;
206
207     /* Try to open the real video output */
208     msg_Dbg( p_vout, "spawning the real video outputs" );
209
210     p_vout->p_sys->i_vout = 0;
211
212     /* FIXME: use bresenham instead of those ugly divisions */
213     for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
214     {
215         for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
216         {
217             if( i_col + 1 < p_vout->p_sys->i_col )
218             {
219                 i_width = ( p_vout->render.i_width
220                              / p_vout->p_sys->i_col ) & ~0x1;
221             }
222             else
223             {
224                 i_width = p_vout->render.i_width
225                            - ( ( p_vout->render.i_width
226                                   / p_vout->p_sys->i_col ) & ~0x1 ) * i_col;
227             }
228
229             if( i_row + 1 < p_vout->p_sys->i_row )
230             {
231                 i_height = ( p_vout->render.i_height
232                               / p_vout->p_sys->i_row ) & ~0x3;
233             }
234             else
235             {
236                 i_height = p_vout->render.i_height
237                             - ( ( p_vout->render.i_height
238                                    / p_vout->p_sys->i_row ) & ~0x3 ) * i_row;
239             }
240
241             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_width = i_width;
242             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_height = i_height;
243
244             if( !p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active )
245             {
246                 p_vout->p_sys->i_vout++;
247                 continue;
248             }
249
250             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout =
251                 vout_Create( p_vout, i_width, i_height,
252                              p_vout->render.i_chroma,
253                              p_vout->render.i_aspect
254                               * p_vout->render.i_height / i_height
255                               * i_width / p_vout->render.i_width );
256             if( p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout == NULL )
257             {
258                 msg_Err( p_vout, "failed to get %ix%i vout threads",
259                                  p_vout->p_sys->i_col, p_vout->p_sys->i_row );
260                 RemoveAllVout( p_vout );
261                 return VLC_EGENERIC;
262             }
263
264             p_vout->p_sys->i_vout++;
265         }
266     }
267
268     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
269
270     return VLC_SUCCESS;
271 }
272
273 /*****************************************************************************
274  * End: terminate Wall video thread output method
275  *****************************************************************************/
276 static void End( vout_thread_t *p_vout )
277 {
278     int i_index;
279
280     /* Free the fake output buffers we allocated */
281     for( i_index = I_OUTPUTPICTURES ; i_index ; )
282     {
283         i_index--;
284         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
285     }
286 }
287
288 /*****************************************************************************
289  * Destroy: destroy Wall video thread output method
290  *****************************************************************************
291  * Terminate an output method created by WallCreateOutputMethod
292  *****************************************************************************/
293 static void Destroy( vlc_object_t *p_this )
294 {
295     vout_thread_t *p_vout = (vout_thread_t *)p_this;
296
297     RemoveAllVout( p_vout );
298
299     free( p_vout->p_sys->pp_vout );
300     free( p_vout->p_sys );
301 }
302
303 /*****************************************************************************
304  * Render: displays previously rendered output
305  *****************************************************************************
306  * This function send the currently rendered image to Wall image, waits
307  * until it is displayed and switch the two rendering buffers, preparing next
308  * frame.
309  *****************************************************************************/
310 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
311 {
312     picture_t *p_outpic = NULL;
313     int i_col, i_row, i_vout, i_plane;
314     int pi_left_skip[VOUT_MAX_PLANES], pi_top_skip[VOUT_MAX_PLANES];
315
316     i_vout = 0;
317
318     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
319     {
320         pi_top_skip[i_plane] = 0;
321     }
322
323     for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
324     {
325         for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
326         {
327             pi_left_skip[i_plane] = 0;
328         }
329
330         for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
331         {
332             if( !p_vout->p_sys->pp_vout[ i_vout ].b_active )
333             {
334                 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
335                 {
336                     pi_left_skip[i_plane] +=
337                         p_vout->p_sys->pp_vout[ i_vout ].i_width
338                          * p_pic->p[i_plane].i_pitch / p_vout->output.i_width;
339                 }
340                 i_vout++;
341                 continue;
342             }
343
344             while( ( p_outpic =
345                 vout_CreatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
346                                     0, 0, 0 )
347                    ) == NULL )
348             {
349                 if( p_vout->b_die || p_vout->b_error )
350                 {
351                     vout_DestroyPicture(
352                         p_vout->p_sys->pp_vout[ i_vout ].p_vout, p_outpic );
353                     return;
354                 }
355
356                 msleep( VOUT_OUTMEM_SLEEP );
357             }
358
359             vout_DatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
360                               p_outpic, p_pic->date );
361             vout_LinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
362                               p_outpic );
363
364             for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
365             {
366                 uint8_t *p_in, *p_in_end, *p_out;
367                 int i_in_pitch = p_pic->p[i_plane].i_pitch;
368                 int i_out_pitch = p_outpic->p[i_plane].i_pitch;
369                 int i_copy_pitch = p_outpic->p[i_plane].i_visible_pitch;
370
371                 p_in = p_pic->p[i_plane].p_pixels
372                         + pi_top_skip[i_plane] + pi_left_skip[i_plane];
373
374                 p_in_end = p_in + p_outpic->p[i_plane].i_lines
375                                    * p_pic->p[i_plane].i_pitch;
376
377                 p_out = p_outpic->p[i_plane].p_pixels;
378
379                 while( p_in < p_in_end )
380                 {
381                     p_vout->p_vlc->pf_memcpy( p_out, p_in, i_copy_pitch );
382                     p_in += i_in_pitch;
383                     p_out += i_out_pitch;
384                 }
385
386                 pi_left_skip[i_plane] += i_out_pitch;
387             }
388
389             vout_UnlinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
390                                 p_outpic );
391             vout_DisplayPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
392                                  p_outpic );
393
394             i_vout++;
395         }
396
397         for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
398         {
399             pi_top_skip[i_plane] += p_vout->p_sys->pp_vout[ i_vout ].i_height
400                                      * p_pic->p[i_plane].i_lines
401                                      / p_vout->output.i_height
402                                      * p_pic->p[i_plane].i_pitch;
403         }
404     }
405 }
406
407 /*****************************************************************************
408  * RemoveAllVout: destroy all the child video output threads
409  *****************************************************************************/
410 static void RemoveAllVout( vout_thread_t *p_vout )
411 {
412     while( p_vout->p_sys->i_vout )
413     {
414          --p_vout->p_sys->i_vout;
415          if( p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active )
416          {
417              vout_Destroy(
418                p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout );
419          }
420     }
421 }