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