]> git.sesse.net Git - vlc/blob - modules/video_filter/transform.c
Fix segfault when child vout creation fails
[vlc] / modules / video_filter / transform.c
1 /*****************************************************************************
2  * transform.c : transform image module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2004 VideoLAN
5  * $Id$
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 #define TRANSFORM_MODE_HFLIP   1
36 #define TRANSFORM_MODE_VFLIP   2
37 #define TRANSFORM_MODE_90      3
38 #define TRANSFORM_MODE_180     4
39 #define TRANSFORM_MODE_270     5
40
41 /*****************************************************************************
42  * Local prototypes
43  *****************************************************************************/
44 static int  Create    ( vlc_object_t * );
45 static void Destroy   ( vlc_object_t * );
46
47 static int  Init      ( vout_thread_t * );
48 static void End       ( vout_thread_t * );
49 static void Render    ( vout_thread_t *, picture_t * );
50
51 static int  SendEvents( vlc_object_t *, char const *,
52                         vlc_value_t, vlc_value_t, void * );
53
54 /*****************************************************************************
55  * Module descriptor
56  *****************************************************************************/
57 #define TYPE_TEXT N_("Transform type")
58 #define TYPE_LONGTEXT N_("One of '90', '180', '270', 'hflip' and 'vflip'")
59
60 static char *type_list[] = { "90", "180", "270", "hflip", "vflip" };
61 static char *type_list_text[] = { N_("Rotate by 90 degrees"),
62   N_("Rotate by 180 degrees"), N_("Rotate by 270 degrees"),
63   N_("Flip horizontally"), N_("Flip vertically") };
64
65 vlc_module_begin();
66     set_description( _("Video transformation filter") );
67     set_capability( "video filter", 0 );
68
69     add_string( "transform-type", "90", NULL,
70                           TYPE_TEXT, TYPE_LONGTEXT, VLC_FALSE);
71         change_string_list( type_list, type_list_text, 0);
72
73     add_shortcut( "transform" );
74     set_callbacks( Create, Destroy );
75 vlc_module_end();
76
77 /*****************************************************************************
78  * vout_sys_t: Transform video output method descriptor
79  *****************************************************************************
80  * This structure is part of the video output thread descriptor.
81  * It describes the Transform specific properties of an output thread.
82  *****************************************************************************/
83 struct vout_sys_t
84 {
85     int i_mode;
86     vlc_bool_t b_rotation;
87     vout_thread_t *p_vout;
88 };
89
90 /*****************************************************************************
91  * Control: control facility for the vout (forwards to child vout)
92  *****************************************************************************/
93 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
94 {
95     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
96 }
97
98 /*****************************************************************************
99  * Create: allocates Transform video thread output method
100  *****************************************************************************
101  * This function allocates and initializes a Transform vout method.
102  *****************************************************************************/
103 static int Create( vlc_object_t *p_this )
104 {
105     vout_thread_t *p_vout = (vout_thread_t *)p_this;
106     char *psz_method;
107
108     /* Allocate structure */
109     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
110     if( p_vout->p_sys == NULL )
111     {
112         msg_Err( p_vout, "out of memory" );
113         return VLC_ENOMEM;
114     }
115
116     p_vout->pf_init = Init;
117     p_vout->pf_end = End;
118     p_vout->pf_manage = NULL;
119     p_vout->pf_render = Render;
120     p_vout->pf_display = NULL;
121     p_vout->pf_control = Control;
122
123     /* Look what method was requested */
124     psz_method = config_GetPsz( p_vout, "transform-type" );
125
126     if( psz_method == NULL )
127     {
128         msg_Err( p_vout, "configuration variable %s empty", "transform-type" );
129         msg_Err( p_vout, "no valid transform mode provided, using '90'" );
130         p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
131         p_vout->p_sys->b_rotation = 1;
132     }
133     else
134     {
135         if( !strcmp( psz_method, "hflip" ) )
136         {
137             p_vout->p_sys->i_mode = TRANSFORM_MODE_HFLIP;
138             p_vout->p_sys->b_rotation = 0;
139         }
140         else if( !strcmp( psz_method, "vflip" ) )
141         {
142             p_vout->p_sys->i_mode = TRANSFORM_MODE_VFLIP;
143             p_vout->p_sys->b_rotation = 0;
144         }
145         else if( !strcmp( psz_method, "90" ) )
146         {
147             p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
148             p_vout->p_sys->b_rotation = 1;
149         }
150         else if( !strcmp( psz_method, "180" ) )
151         {
152             p_vout->p_sys->i_mode = TRANSFORM_MODE_180;
153             p_vout->p_sys->b_rotation = 0;
154         }
155         else if( !strcmp( psz_method, "270" ) )
156         {
157             p_vout->p_sys->i_mode = TRANSFORM_MODE_270;
158             p_vout->p_sys->b_rotation = 1;
159         }
160         else
161         {
162             msg_Err( p_vout, "no valid transform mode provided, using '90'" );
163             p_vout->p_sys->i_mode = TRANSFORM_MODE_90;
164             p_vout->p_sys->b_rotation = 1;
165         }
166
167         free( psz_method );
168     }
169
170     return VLC_SUCCESS;
171 }
172
173 /*****************************************************************************
174  * Init: initialize Transform video thread output method
175  *****************************************************************************/
176 static int Init( vout_thread_t *p_vout )
177 {
178     int i_index;
179     picture_t *p_pic;
180
181     I_OUTPUTPICTURES = 0;
182
183     /* Initialize the output structure */
184     p_vout->output.i_chroma = p_vout->render.i_chroma;
185     p_vout->output.i_width  = p_vout->render.i_width;
186     p_vout->output.i_height = p_vout->render.i_height;
187     p_vout->output.i_aspect = p_vout->render.i_aspect;
188
189     /* Try to open the real video output */
190     msg_Dbg( p_vout, "spawning the real video output" );
191
192     if( p_vout->p_sys->b_rotation )
193     {
194         p_vout->p_sys->p_vout = vout_Create( p_vout,
195                            p_vout->render.i_height, p_vout->render.i_width,
196                            p_vout->render.i_chroma,
197                            (uint64_t)VOUT_ASPECT_FACTOR
198                             * (uint64_t)VOUT_ASPECT_FACTOR
199                             / (uint64_t)p_vout->render.i_aspect );
200     }
201     else
202     {
203         p_vout->p_sys->p_vout = vout_Create( p_vout,
204                            p_vout->render.i_width, p_vout->render.i_height,
205                            p_vout->render.i_chroma, p_vout->render.i_aspect );
206     }
207
208     /* Everything failed */
209     if( p_vout->p_sys->p_vout == NULL )
210     {
211         msg_Err( p_vout, "cannot open vout, aborting" );
212         return VLC_EGENERIC;
213     }
214
215     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
216
217     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
218
219     ADD_PARENT_CALLBACKS( SendEventsToChild );
220
221     return VLC_SUCCESS;
222 }
223
224 /*****************************************************************************
225  * End: terminate Transform video thread output method
226  *****************************************************************************/
227 static void End( vout_thread_t *p_vout )
228 {
229     int i_index;
230
231     /* Free the fake output buffers we allocated */
232     for( i_index = I_OUTPUTPICTURES ; i_index ; )
233     {
234         i_index--;
235         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
236     }
237 }
238
239 /*****************************************************************************
240  * Destroy: destroy Transform video thread output method
241  *****************************************************************************
242  * Terminate an output method created by TransformCreateOutputMethod
243  *****************************************************************************/
244 static void Destroy( vlc_object_t *p_this )
245 {
246     vout_thread_t *p_vout = (vout_thread_t *)p_this;
247
248     if( p_vout->p_sys->p_vout )
249     {
250         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
251         vlc_object_detach( p_vout->p_sys->p_vout );
252         vout_Destroy( p_vout->p_sys->p_vout );
253     }
254
255     DEL_PARENT_CALLBACKS( SendEventsToChild );
256
257     free( p_vout->p_sys );
258 }
259
260 /*****************************************************************************
261  * Render: displays previously rendered output
262  *****************************************************************************
263  * This function send the currently rendered image to Transform image, waits
264  * until it is displayed and switch the two rendering buffers, preparing next
265  * frame.
266  *****************************************************************************/
267 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
268 {
269     picture_t *p_outpic;
270     int i_index;
271
272     /* This is a new frame. Get a structure from the video_output. */
273     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
274               == NULL )
275     {
276         if( p_vout->b_die || p_vout->b_error )
277         {
278             return;
279         }
280         msleep( VOUT_OUTMEM_SLEEP );
281     }
282
283     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
284     vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic );
285
286     switch( p_vout->p_sys->i_mode )
287     {
288         case TRANSFORM_MODE_90:
289             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
290             {
291                 int i_pitch = p_pic->p[i_index].i_pitch;
292
293                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
294
295                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
296                 uint8_t *p_out_end = p_out + p_outpic->p[i_index].i_lines
297                                               * p_outpic->p[i_index].i_pitch;
298
299                 for( ; p_out < p_out_end ; )
300                 {
301                     uint8_t *p_line_end;
302
303                     p_out_end -= p_outpic->p[i_index].i_pitch
304                                   - p_outpic->p[i_index].i_visible_pitch;
305                     p_line_end = p_in + p_pic->p[i_index].i_lines * i_pitch;
306
307                     for( ; p_in < p_line_end ; )
308                     {
309                         p_line_end -= i_pitch;
310                         *(--p_out_end) = *p_line_end;
311                     }
312
313                     p_in++;
314                 }
315             }
316             break;
317
318         case TRANSFORM_MODE_180:
319             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
320             {
321                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
322                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_lines
323                                             * p_pic->p[i_index].i_pitch;
324
325                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
326
327                 for( ; p_in < p_in_end ; )
328                 {
329                     uint8_t *p_line_start = p_in_end
330                                              - p_pic->p[i_index].i_pitch;
331                     p_in_end -= p_pic->p[i_index].i_pitch
332                                  - p_pic->p[i_index].i_visible_pitch;
333
334                     for( ; p_line_start < p_in_end ; )
335                     {
336                         *p_out++ = *(--p_in_end);
337                     }
338
339                     p_out += p_outpic->p[i_index].i_pitch
340                               - p_outpic->p[i_index].i_visible_pitch;
341                 }
342             }
343             break;
344
345         case TRANSFORM_MODE_270:
346             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
347             {
348                 int i_pitch = p_pic->p[i_index].i_pitch;
349
350                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
351
352                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
353                 uint8_t *p_out_end = p_out + p_outpic->p[i_index].i_lines
354                                               * p_outpic->p[i_index].i_pitch;
355
356                 for( ; p_out < p_out_end ; )
357                 {
358                     uint8_t *p_in_end;
359
360                     p_in_end = p_in + p_pic->p[i_index].i_lines * i_pitch;
361
362                     for( ; p_in < p_in_end ; )
363                     {
364                         p_in_end -= i_pitch;
365                         *p_out++ = *p_in_end;
366                     }
367
368                     p_out += p_outpic->p[i_index].i_pitch
369                               - p_outpic->p[i_index].i_visible_pitch;
370                     p_in++;
371                 }
372             }
373             break;
374
375         case TRANSFORM_MODE_HFLIP:
376             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
377             {
378                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
379                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_lines
380                                             * p_pic->p[i_index].i_pitch;
381
382                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
383
384                 for( ; p_in < p_in_end ; )
385                 {
386                     p_in_end -= p_pic->p[i_index].i_pitch;
387                     p_vout->p_vlc->pf_memcpy( p_out, p_in_end,
388                                            p_pic->p[i_index].i_visible_pitch );
389                     p_out += p_pic->p[i_index].i_pitch;
390                 }
391             }
392             break;
393
394         case TRANSFORM_MODE_VFLIP:
395             for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
396             {
397                 uint8_t *p_in = p_pic->p[i_index].p_pixels;
398                 uint8_t *p_in_end = p_in + p_pic->p[i_index].i_lines
399                                             * p_pic->p[i_index].i_pitch;
400
401                 uint8_t *p_out = p_outpic->p[i_index].p_pixels;
402
403                 for( ; p_in < p_in_end ; )
404                 {
405                     uint8_t *p_line_end = p_in
406                                            + p_pic->p[i_index].i_visible_pitch;
407
408                     for( ; p_in < p_line_end ; )
409                     {
410                         *p_out++ = *(--p_line_end);
411                     }
412
413                     p_in += p_pic->p[i_index].i_pitch;
414                 }
415             }
416             break;
417
418         default:
419             break;
420     }
421
422     vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic );
423
424     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
425 }
426
427 /*****************************************************************************
428  * SendEvents: forward mouse and keyboard events to the parent p_vout
429  *****************************************************************************/
430 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
431                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
432 {
433     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
434     vlc_value_t sentval = newval;
435
436     /* Translate the mouse coordinates */
437     if( !strcmp( psz_var, "mouse-x" ) )
438     {
439         switch( p_vout->p_sys->i_mode )
440         {
441         case TRANSFORM_MODE_270:
442             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
443                              - sentval.i_int;
444         case TRANSFORM_MODE_90:
445             var_Set( p_vout, "mouse-y", sentval );
446             return VLC_SUCCESS;
447
448         case TRANSFORM_MODE_180:
449         case TRANSFORM_MODE_HFLIP:
450             sentval.i_int = p_vout->p_sys->p_vout->output.i_width
451                              - sentval.i_int;
452             break;
453
454         case TRANSFORM_MODE_VFLIP:
455         default:
456             break;
457         }
458     }
459     else if( !strcmp( psz_var, "mouse-y" ) )
460     {
461         switch( p_vout->p_sys->i_mode )
462         {
463         case TRANSFORM_MODE_90:
464             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
465                              - sentval.i_int;
466         case TRANSFORM_MODE_270:
467             var_Set( p_vout, "mouse-x", sentval );
468             return VLC_SUCCESS;
469
470         case TRANSFORM_MODE_180:
471         case TRANSFORM_MODE_VFLIP:
472             sentval.i_int = p_vout->p_sys->p_vout->output.i_height
473                              - sentval.i_int;
474             break;
475
476         case TRANSFORM_MODE_HFLIP:
477         default:
478             break;
479         }
480     }
481
482     var_Set( p_vout, psz_var, sentval );
483
484     return VLC_SUCCESS;
485 }
486
487 /*****************************************************************************
488  * SendEventsToChild: forward events to the child/children vout
489  *****************************************************************************/
490 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
491                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
492 {
493     vout_thread_t *p_vout = (vout_thread_t *)p_this;
494     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
495     return VLC_SUCCESS;
496 }