]> git.sesse.net Git - vlc/blob - modules/video_filter/wrapper.c
Cosmetics.
[vlc] / modules / video_filter / wrapper.c
1 /*****************************************************************************
2  * wrapper.c: a "video filter2/splitter" with mouse to "video filter" wrapper.
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout.h>
36 #include <vlc_filter.h>
37 #include <vlc_video_splitter.h>
38
39 #include "filter_common.h"
40
41 /*****************************************************************************
42  * Module descriptor
43  *****************************************************************************/
44 static int  Open ( vlc_object_t *, const char *psz_name, bool b_filter );
45 static void Close( vlc_object_t * );
46
47 #define DECLARE_OPEN(name,filter) \
48     static int  Open##name ( vlc_object_t *p_this ) { return Open( p_this, #name, filter ); }
49
50 DECLARE_OPEN(magnify, true)
51 DECLARE_OPEN(puzzle, true)
52 DECLARE_OPEN(logo, true)
53
54 DECLARE_OPEN(clone, false)
55 DECLARE_OPEN(wall, false)
56
57 #undef DECLARE_OPEN
58
59 #define DECLARE_MODULE(name)                            \
60     set_description( "Video filter "#name" wrapper" )   \
61     set_shortname( "Video filter"#name" wrapper" )      \
62     set_capability( "video filter", 0 )                 \
63     set_callbacks( Open##name, Close )                  \
64     add_shortcut( #name )
65
66 vlc_module_begin()
67     set_category( CAT_VIDEO )
68     set_subcategory( SUBCAT_VIDEO_VFILTER )
69
70     DECLARE_MODULE(magnify)
71
72     add_submodule()
73     DECLARE_MODULE(puzzle)
74
75     add_submodule()
76     DECLARE_MODULE(logo)
77
78     add_submodule()
79     DECLARE_MODULE(clone)
80
81     add_submodule()
82     DECLARE_MODULE(wall)
83
84 vlc_module_end()
85
86 #undef DECLARE_MODULE
87
88 /*****************************************************************************
89  * Local prototypes
90  *****************************************************************************/
91 static int  Init      ( vout_thread_t * );
92 static void End       ( vout_thread_t * );
93 static void Render    ( vout_thread_t *, picture_t * );
94 static int Control    ( vout_thread_t *, int, va_list );
95
96 struct vout_sys_t
97 {
98     int              i_vout;
99     vout_thread_t    **pp_vout;
100
101     es_format_t      fmt;
102
103     vlc_mutex_t      lock;
104     filter_chain_t   *p_chain;
105     video_splitter_t *p_splitter;
106
107     vlc_mouse_t      *p_mouse_src;
108     vlc_mouse_t      mouse;
109 };
110
111 /* */
112 static int  FilterAllocationInit ( filter_t *, void * );
113 static void FilterAllocationClean( filter_t * );
114
115 /* */
116 static int  FullscreenEventUp( vlc_object_t *, char const *,
117                                vlc_value_t, vlc_value_t, void * );
118 static int  FullscreenEventDown( vlc_object_t *, char const *,
119                                  vlc_value_t, vlc_value_t, void * );
120 static int  SplitterPictureNew( video_splitter_t *, picture_t *pp_picture[] );
121 static void SplitterPictureDel( video_splitter_t *, picture_t *pp_picture[] );
122
123 /* */
124 static int  MouseEvent( vlc_object_t *, char const *,
125                         vlc_value_t, vlc_value_t, void * );
126 static void VoutsClean( vout_thread_t *p_vout, int i_count );
127
128 /**
129  * Open our wrapper instance.
130  */
131 static int Open( vlc_object_t *p_this, const char *psz_name, bool b_filter )
132 {
133     vout_thread_t *p_vout = (vout_thread_t *)p_this;
134     vout_sys_t *p_sys;
135
136     msg_Err( p_vout, "Opening video %s wrapper for %s",
137              b_filter ? "filter" : "splitter", psz_name );
138
139     /* */
140     es_format_t fmt;
141     es_format_Init( &fmt, VIDEO_ES, p_vout->render.i_chroma );
142     video_format_Setup( &fmt.video, p_vout->render.i_chroma,
143                         p_vout->render.i_width, p_vout->render.i_height,
144                         p_vout->render.i_aspect );
145     if( fmt.video.i_sar_num <= 0 || fmt.video.i_sar_den <= 0 )
146     {
147         fmt.video.i_sar_num = fmt.video.i_aspect * fmt.video.i_visible_height;
148         fmt.video.i_sar_den = VOUT_ASPECT_FACTOR * fmt.video.i_visible_width;
149     }
150
151     /* Try to open our real module */
152     filter_chain_t   *p_chain = NULL;
153     video_splitter_t *p_splitter = NULL;
154     if( b_filter )
155     {
156         p_chain = filter_chain_New( p_vout, "video filter2", false,
157                                     FilterAllocationInit, FilterAllocationClean, p_vout );
158         if( !p_chain )
159             return VLC_ENOMEM;
160
161         filter_chain_Reset( p_chain, &fmt, &fmt );
162
163         filter_t *p_filter =
164             filter_chain_AppendFilter( p_chain, psz_name, p_vout->p_cfg, &fmt, &fmt );
165
166         if( !p_filter )
167         {
168             msg_Err( p_vout, "Failed to open filter '%s'", psz_name );
169             filter_chain_Delete( p_chain );
170             return VLC_EGENERIC;
171         }
172     }
173     else
174     {
175         p_splitter = video_splitter_New( VLC_OBJECT(p_vout), psz_name, &fmt.video );
176         if( !p_splitter )
177         {
178             msg_Err( p_vout, "Failed to open splitter '%s'", psz_name );
179             return VLC_EGENERIC;
180         }
181         assert( p_splitter->i_output > 0 );
182
183         p_splitter->p_owner = (video_splitter_owner_t*)p_vout;
184         p_splitter->pf_picture_new = SplitterPictureNew;
185         p_splitter->pf_picture_del = SplitterPictureDel;
186     }
187
188     /* */
189     p_vout->p_sys = p_sys = malloc( sizeof(*p_sys) );
190     if( !p_sys )
191         goto error;
192
193     p_sys->i_vout  = p_chain ? 1 : p_splitter->i_output;
194     p_sys->pp_vout = calloc( p_sys->i_vout, sizeof(*p_sys->pp_vout) );;
195     p_sys->p_mouse_src = calloc( p_sys->i_vout, sizeof(*p_sys->p_mouse_src) );
196
197     p_sys->fmt = fmt;
198     vlc_mutex_init( &p_sys->lock );
199     p_sys->p_chain    = p_chain;
200     p_sys->p_splitter = p_splitter;
201     vlc_mouse_Init( &p_sys->mouse );
202     for( int i = 0; i < p_sys->i_vout; i++ )
203         vlc_mouse_Init( &p_sys->p_mouse_src[i] );
204
205     p_vout->pf_init = Init;
206     p_vout->pf_end = End;
207     p_vout->pf_manage = NULL;
208     p_vout->pf_render = Render;
209     p_vout->pf_display = NULL;
210     p_vout->pf_control = Control;
211
212     return VLC_SUCCESS;
213
214 error:
215     if( p_chain )
216         filter_chain_Delete( p_chain );
217     if( p_splitter )
218         video_splitter_Delete( p_splitter );
219     return VLC_ENOMEM;
220 }
221
222 /**
223  * Close our wrapper instance
224  */
225 static void Close( vlc_object_t *p_this )
226 {
227     vout_thread_t *p_vout = (vout_thread_t *)p_this;
228     vout_sys_t *p_sys = p_vout->p_sys;
229
230     if( p_sys->p_chain )
231         filter_chain_Delete( p_sys->p_chain );
232     if( p_sys->p_splitter )
233         video_splitter_Delete( p_sys->p_splitter );
234
235     vlc_mutex_destroy( &p_sys->lock );
236     es_format_Clean( &p_sys->fmt );
237     free( p_sys->p_mouse_src );
238     free( p_sys->pp_vout );
239
240     free( p_vout->p_sys );
241 }
242
243 /**
244  * Initialise our wrapper
245  */
246 static int Init( vout_thread_t *p_vout )
247 {
248     vout_sys_t *p_sys = p_vout->p_sys;
249
250     assert( p_vout->render.i_chroma == p_sys->fmt.video.i_chroma &&
251             p_vout->render.i_width  == p_sys->fmt.video.i_width &&
252             p_vout->render.i_height == p_sys->fmt.video.i_height );
253
254     /* Initialize the output structure */
255     I_OUTPUTPICTURES = 0;
256     p_vout->output.i_chroma = p_vout->render.i_chroma;
257     p_vout->output.i_width  = p_vout->render.i_width;
258     p_vout->output.i_height = p_vout->render.i_height;
259     p_vout->output.i_aspect = p_vout->render.i_aspect;
260
261     p_vout->fmt_out = p_vout->fmt_in;
262
263     /* Try to open the real video output */
264     msg_Dbg( p_vout, "spawning the real video output(s)" );
265
266     video_format_t fmt = p_vout->fmt_out;
267
268     if( p_sys->p_chain )
269     {
270         p_sys->pp_vout[0] = vout_Create( p_vout, &fmt );
271         if( !p_sys->pp_vout[0] )
272         {
273             msg_Err( p_vout, "cannot open vout, aborting" );
274             return VLC_EGENERIC;
275         }
276         vout_filter_AddChild( p_vout, p_sys->pp_vout[0], MouseEvent );
277     }
278     else
279     {
280         video_splitter_t *p_splitter = p_sys->p_splitter;
281
282         for( int i = 0; i < p_splitter->i_output; i++ )
283         {
284             const video_splitter_output_t *p_cfg = &p_splitter->p_output[i];
285
286             /* */
287             var_Create( p_vout, "align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
288             var_SetInteger( p_vout, "align", p_cfg->window.i_align);
289
290             var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
291             var_SetInteger( p_vout, "video-x", p_cfg->window.i_x );
292
293             var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
294             var_SetInteger( p_vout, "video-y", p_cfg->window.i_y );
295
296             var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
297             var_SetString( p_vout, "vout", p_cfg->psz_module ? p_cfg->psz_module : "" );
298
299             /* */
300             video_format_t fmt = p_cfg->fmt;
301             p_sys->pp_vout[i] = vout_Create( p_vout, &fmt );
302             if( !p_sys->pp_vout[i] )
303             {
304                 msg_Err( p_vout, "cannot open vout, aborting" );
305                 VoutsClean( p_vout, i );
306                 return VLC_EGENERIC;
307             }
308         }
309
310         /* Attach once pp_vout is completly field to avoid race conditions */
311         for( int i = 0; i < p_splitter->i_output; i++ )
312             vout_filter_SetupChild( p_vout, p_sys->pp_vout[i],
313                                     MouseEvent,
314                                     FullscreenEventUp, FullscreenEventDown, true );
315     }
316
317     vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
318
319     return VLC_SUCCESS;
320 }
321
322 /**
323  * Clean up our wrapper
324  */
325 static void End( vout_thread_t *p_vout )
326 {
327     vout_sys_t *p_sys = p_vout->p_sys;
328
329     VoutsClean( p_vout, p_sys->i_vout );
330
331     vout_filter_ReleaseDirectBuffers( p_vout );
332 }
333
334 /**
335  * Control the real vout
336  */
337 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
338 {
339     vout_sys_t *p_sys = p_vout->p_sys;
340     int i_ret = VLC_SUCCESS;
341
342     for( int i = 0; i < p_sys->i_vout; i++ )
343         i_ret = vout_vaControl( p_sys->pp_vout[i], i_query, args );
344     return i_ret;
345 }
346
347 /**
348  * Filter a picture
349  */
350 static void Render( vout_thread_t *p_vout, picture_t *p_src )
351 {
352     vout_sys_t *p_sys = p_vout->p_sys;
353
354     vlc_mutex_lock( &p_sys->lock );
355
356     picture_t *pp_dst[p_sys->i_vout];
357
358     if( p_sys->p_chain )
359     {
360         pp_dst[0] = filter_chain_VideoFilter( p_sys->p_chain, p_src );
361     }
362     else
363     {
364         if( video_splitter_Filter( p_sys->p_splitter, pp_dst, p_src ) )
365         {
366             for( int i = 0; i < p_sys->i_vout; i++ )
367                 pp_dst[i] = NULL;
368         }
369     }
370     for( int i = 0; i < p_sys->i_vout; i++ )
371     {
372         picture_t *p_dst = pp_dst[i];
373         if( p_dst )
374             vout_DisplayPicture( p_sys->pp_vout[i], p_dst );
375     }
376
377     vlc_mutex_unlock( &p_sys->lock );
378 }
379
380 /* */
381 static void VoutsClean( vout_thread_t *p_vout, int i_count )
382 {
383     vout_sys_t *p_sys = p_vout->p_sys;
384
385     /* Detach all vouts before destroying them */
386     for( int i = 0; i < i_count; i++ )
387     {
388         if( p_sys->p_chain )
389             vout_filter_DelChild( p_vout, p_sys->pp_vout[i], MouseEvent );
390         else
391              vout_filter_SetupChild( p_vout, p_sys->pp_vout[i],
392                                      MouseEvent,
393                                      FullscreenEventUp, FullscreenEventDown, false );
394     }
395
396     for( int i = 0; i < i_count; i++ )
397         vout_CloseAndRelease( p_sys->pp_vout[i] );
398 }
399 static int VoutsNewPicture( vout_thread_t *p_vout, picture_t *pp_dst[] )
400 {
401     vout_sys_t *p_sys = p_vout->p_sys;
402
403     for( int i = 0; i < p_sys->i_vout; i++ )
404     {
405         picture_t *p_picture = NULL;
406         for( ;; )
407         {
408             p_picture = vout_CreatePicture( p_sys->pp_vout[i], 0, 0, 0 );
409             if( p_picture )
410                 break;
411
412             if( !vlc_object_alive( p_vout ) || p_vout->b_error )
413                 break;
414             msleep( VOUT_OUTMEM_SLEEP );
415         }
416         /* FIXME what to do with the allocated picture ? */
417         if( !p_picture )
418             return VLC_EGENERIC;
419
420         pp_dst[i] = p_picture;
421     }
422     return VLC_SUCCESS;
423 }
424
425 /**
426  * Callback for mouse events
427  */
428 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
429                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
430 {
431     VLC_UNUSED(psz_var); VLC_UNUSED(oldval); VLC_UNUSED(newval);
432     vout_thread_t *p_vout = p_data;
433     vout_sys_t *p_sys = p_vout->p_sys;
434     int i_index;
435
436     for( i_index = 0; i_index < p_sys->i_vout; i_index++ )
437     {
438         if( p_this == VLC_OBJECT(p_sys->pp_vout[i_index]) )
439             break;
440     }
441     if( i_index >= p_sys->i_vout )
442     {
443         msg_Err( p_vout, "Failed to find vout source in MouseEvent" );
444         return VLC_SUCCESS;
445     }
446
447     vout_thread_t *p_vout_src = p_sys->pp_vout[i_index];
448
449     vlc_mouse_t m;
450     vlc_mouse_Init( &m );
451     m.i_x = var_GetInteger( p_vout_src, "mouse-x" );
452     m.i_y = var_GetInteger( p_vout_src, "mouse-y" );
453     m.i_pressed = var_GetInteger( p_vout_src, "mouse-button-down" );
454
455     vlc_mutex_lock( &p_sys->lock );
456
457     vlc_mouse_t nmouse;
458     vlc_mouse_t omouse = p_sys->mouse;
459
460     int i_ret;
461     if( p_sys->p_chain )
462     {
463         i_ret = filter_chain_MouseFilter( p_sys->p_chain, &nmouse, &m );
464     }
465     else
466     {
467         vlc_mouse_t *p_mouse_src = &p_sys->p_mouse_src[i_index];
468
469         i_ret = video_splitter_Mouse( p_sys->p_splitter, &nmouse, i_index, p_mouse_src, &m );
470         *p_mouse_src = m;
471     }
472
473     if( !i_ret )
474         p_sys->mouse = nmouse;
475     vlc_mutex_unlock( &p_sys->lock );
476
477     if( i_ret )
478         return VLC_EGENERIC;
479
480     if( vlc_mouse_HasMoved( &omouse, &nmouse ) )
481     {
482         var_SetInteger( p_vout, "mouse-x", nmouse.i_x );
483         var_SetInteger( p_vout, "mouse-y", nmouse.i_y );
484         var_SetBool( p_vout, "mouse-moved", true );
485     }
486     if( vlc_mouse_HasButton( &omouse, &nmouse ) )
487     {
488         var_SetInteger( p_vout, "mouse-button-down", nmouse.i_pressed );
489         if( vlc_mouse_HasPressed( &omouse, &nmouse, MOUSE_BUTTON_LEFT ) )
490             var_SetBool( p_vout, "mouse-clicked", true );
491     }
492     if( m.b_double_click )
493     {
494         /* Nothing with current API */
495         msg_Warn( p_vout, "Ignoring double click" );
496     }
497     return VLC_SUCCESS;
498 }
499
500 /* -- Filter callbacks -- */
501
502 static picture_t *VideoBufferNew( filter_t *p_filter )
503 {
504     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
505
506     picture_t *pp_picture[1];
507     if( VoutsNewPicture( p_vout, pp_picture ) )
508         return NULL;
509     return pp_picture[0];
510 }
511 static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
512 {
513     VLC_UNUSED(p_filter); VLC_UNUSED(p_picture);
514     /* FIXME is there anything to do ? */
515 }
516
517 static int FilterAllocationInit( filter_t *p_filter, void *p_data )
518 {
519     VLC_UNUSED( p_data );
520
521     p_filter->pf_vout_buffer_new = VideoBufferNew;
522     p_filter->pf_vout_buffer_del = VideoBufferDelete;
523     p_filter->p_owner = p_data;
524
525     return VLC_SUCCESS;
526 }
527 static void FilterAllocationClean( filter_t *p_filter )
528 {
529     p_filter->pf_vout_buffer_new = NULL;
530     p_filter->pf_vout_buffer_del = NULL;
531 }
532
533 /* -- Splitter callbacks -- */
534
535 /**
536  * Forward fullscreen event to/from the childrens.
537  *
538  * FIXME probably unsafe (pp_vout[] content)
539  */
540 static bool IsFullscreenActive( vout_thread_t *p_vout )
541 {
542     vout_sys_t *p_sys = p_vout->p_sys;
543     for( int i = 0; i < p_sys->i_vout; i++ )
544     {
545         if( var_GetBool( p_sys->pp_vout[i], "fullscreen" ) )
546             return true;
547     }
548     return false;
549 }
550 static int FullscreenEventUp( vlc_object_t *p_this, char const *psz_var,
551                               vlc_value_t oldval, vlc_value_t newval, void *p_data )
552 {
553     vout_thread_t *p_vout = p_data;
554     VLC_UNUSED(oldval); VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(newval);
555
556     const bool b_fullscreen = IsFullscreenActive( p_vout );
557     if( !var_GetBool( p_vout, "fullscreen" ) != !b_fullscreen )
558         return var_SetBool( p_vout, "fullscreen", b_fullscreen );
559     return VLC_SUCCESS;
560 }
561 static int FullscreenEventDown( vlc_object_t *p_this, char const *psz_var,
562                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
563 {
564     vout_thread_t *p_vout = (vout_thread_t*)p_this;
565     vout_sys_t *p_sys = p_vout->p_sys;
566     VLC_UNUSED(oldval); VLC_UNUSED(p_data); VLC_UNUSED(psz_var);
567
568     const bool b_fullscreen = IsFullscreenActive( p_vout );
569     if( !b_fullscreen != !newval.b_bool )
570     {
571         for( int i = 0; i < p_sys->i_vout; i++ )
572         {
573             vout_thread_t *p_child = p_sys->pp_vout[i];
574             if( !var_GetBool( p_child, "fullscreen" ) != !newval.b_bool )
575             {
576                 var_SetBool( p_child, "fullscreen", newval.b_bool );
577                 if( newval.b_bool )
578                     return VLC_SUCCESS;
579             }
580         }
581     }
582     return VLC_SUCCESS;
583 }
584
585 static int  SplitterPictureNew( video_splitter_t *p_splitter, picture_t *pp_picture[] )
586 {
587     vout_thread_t *p_vout = (vout_thread_t*)p_splitter->p_owner;
588
589     return VoutsNewPicture( p_vout, pp_picture );
590 }
591 static void SplitterPictureDel( video_splitter_t *p_splitter, picture_t *pp_picture[] )
592 {
593     VLC_UNUSED(p_splitter); VLC_UNUSED(pp_picture);
594     /* FIXME is there anything to do ? */
595 }
596