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