]> git.sesse.net Git - vlc/blob - modules/video_filter/wrapper.c
99ff4276e7f409d8bcfae14a4600bcda1d7b8d94
[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(clone, false)
51 DECLARE_OPEN(wall, false)
52 DECLARE_OPEN(panoramix, false)
53
54 #undef DECLARE_OPEN
55
56 #define DECLARE_MODULE(name)                            \
57     set_description( "Video filter "#name" wrapper" )   \
58     set_shortname( "Video filter"#name" wrapper" )      \
59     set_capability( "video filter", 0 )                 \
60     set_callbacks( Open##name, Close )                  \
61     add_shortcut( #name )
62
63 vlc_module_begin()
64     set_category( CAT_VIDEO )
65     set_subcategory( SUBCAT_VIDEO_VFILTER )
66
67     DECLARE_MODULE(clone)
68
69     add_submodule()
70     DECLARE_MODULE(wall)
71
72     add_submodule()
73     DECLARE_MODULE(panoramix)
74
75 vlc_module_end()
76
77 #undef DECLARE_MODULE
78
79 /*****************************************************************************
80  * Local prototypes
81  *****************************************************************************/
82 static int  Init      ( vout_thread_t * );
83 static void End       ( vout_thread_t * );
84 static void Render    ( vout_thread_t *, picture_t * );
85 static int Control    ( vout_thread_t *, int, va_list );
86
87 struct vout_sys_t
88 {
89     int              i_vout;
90     vout_thread_t    **pp_vout;
91
92     es_format_t      fmt;
93
94     vlc_mutex_t      lock;
95     filter_chain_t   *p_chain;
96     video_splitter_t *p_splitter;
97
98     vlc_mouse_t      *p_mouse_src;
99     vlc_mouse_t      mouse;
100 };
101
102 /* */
103 static int  FilterAllocationInit ( filter_t *, void * );
104 static void FilterAllocationClean( filter_t * );
105
106 /* */
107 static int  FullscreenEventDown( vlc_object_t *, char const *,
108                                  vlc_value_t, vlc_value_t, void * );
109 static int  SplitterPictureNew( video_splitter_t *, picture_t *pp_picture[] );
110 static void SplitterPictureDel( video_splitter_t *, picture_t *pp_picture[] );
111
112 /* */
113 static int  MouseEvent( vlc_object_t *, char const *,
114                         vlc_value_t, vlc_value_t, void * );
115 static void VoutsClean( vout_thread_t *p_vout, int i_count );
116
117 /**
118  * Open our wrapper instance.
119  */
120 static int Open( vlc_object_t *p_this, const char *psz_name, bool b_filter )
121 {
122     vout_thread_t *p_vout = (vout_thread_t *)p_this;
123     vout_sys_t *p_sys;
124
125     msg_Err( p_vout, "Opening video %s wrapper for %s",
126              b_filter ? "filter" : "splitter", psz_name );
127
128     /* */
129     es_format_t fmt;
130     es_format_Init( &fmt, VIDEO_ES, p_vout->render.i_chroma );
131     video_format_Setup( &fmt.video, p_vout->render.i_chroma,
132                         p_vout->render.i_width, p_vout->render.i_height,
133                         p_vout->render.i_aspect * p_vout->render.i_height,
134                         VOUT_ASPECT_FACTOR      * p_vout->render.i_width );
135
136     /* Try to open our real module */
137     filter_chain_t   *p_chain = NULL;
138     video_splitter_t *p_splitter = NULL;
139     if( b_filter )
140     {
141         p_chain = filter_chain_New( p_vout, "video filter2", false,
142                                     FilterAllocationInit, FilterAllocationClean, p_vout );
143         if( !p_chain )
144             return VLC_ENOMEM;
145
146         filter_chain_Reset( p_chain, &fmt, &fmt );
147
148         filter_t *p_filter =
149             filter_chain_AppendFilter( p_chain, psz_name, p_vout->p_cfg, &fmt, &fmt );
150
151         if( !p_filter )
152         {
153             msg_Err( p_vout, "Failed to open filter '%s'", psz_name );
154             filter_chain_Delete( p_chain );
155             return VLC_EGENERIC;
156         }
157     }
158     else
159     {
160         p_splitter = video_splitter_New( VLC_OBJECT(p_vout), psz_name, &fmt.video );
161         if( !p_splitter )
162         {
163             msg_Err( p_vout, "Failed to open splitter '%s'", psz_name );
164             return VLC_EGENERIC;
165         }
166         assert( p_splitter->i_output > 0 );
167
168         p_splitter->p_owner = (video_splitter_owner_t*)p_vout;
169         p_splitter->pf_picture_new = SplitterPictureNew;
170         p_splitter->pf_picture_del = SplitterPictureDel;
171     }
172
173     /* */
174     p_vout->p_sys = p_sys = malloc( sizeof(*p_sys) );
175     if( !p_sys )
176         goto error;
177
178     p_sys->i_vout  = p_chain ? 1 : p_splitter->i_output;
179     p_sys->pp_vout = calloc( p_sys->i_vout, sizeof(*p_sys->pp_vout) );;
180     p_sys->p_mouse_src = calloc( p_sys->i_vout, sizeof(*p_sys->p_mouse_src) );
181
182     p_sys->fmt = fmt;
183     vlc_mutex_init( &p_sys->lock );
184     p_sys->p_chain    = p_chain;
185     p_sys->p_splitter = p_splitter;
186     vlc_mouse_Init( &p_sys->mouse );
187     for( int i = 0; i < p_sys->i_vout; i++ )
188         vlc_mouse_Init( &p_sys->p_mouse_src[i] );
189
190     p_vout->pf_init = Init;
191     p_vout->pf_end = End;
192     p_vout->pf_manage = NULL;
193     p_vout->pf_render = Render;
194     p_vout->pf_display = NULL;
195     p_vout->pf_control = Control;
196
197     return VLC_SUCCESS;
198
199 error:
200     if( p_chain )
201         filter_chain_Delete( p_chain );
202     if( p_splitter )
203         video_splitter_Delete( p_splitter );
204     return VLC_ENOMEM;
205 }
206
207 /**
208  * Close our wrapper instance
209  */
210 static void Close( vlc_object_t *p_this )
211 {
212     vout_thread_t *p_vout = (vout_thread_t *)p_this;
213     vout_sys_t *p_sys = p_vout->p_sys;
214
215     if( p_sys->p_chain )
216         filter_chain_Delete( p_sys->p_chain );
217     if( p_sys->p_splitter )
218         video_splitter_Delete( p_sys->p_splitter );
219
220     vlc_mutex_destroy( &p_sys->lock );
221     es_format_Clean( &p_sys->fmt );
222     free( p_sys->p_mouse_src );
223     free( p_sys->pp_vout );
224
225     free( p_vout->p_sys );
226 }
227
228 /**
229  * Initialise our wrapper
230  */
231 static int Init( vout_thread_t *p_vout )
232 {
233     vout_sys_t *p_sys = p_vout->p_sys;
234
235     assert( p_vout->render.i_chroma == p_sys->fmt.video.i_chroma &&
236             p_vout->render.i_width  == p_sys->fmt.video.i_width &&
237             p_vout->render.i_height == p_sys->fmt.video.i_height );
238
239     /* Initialize the output structure */
240     I_OUTPUTPICTURES = 0;
241     p_vout->output.i_chroma = p_vout->render.i_chroma;
242     p_vout->output.i_width  = p_vout->render.i_width;
243     p_vout->output.i_height = p_vout->render.i_height;
244     p_vout->output.i_aspect = p_vout->render.i_aspect;
245
246     p_vout->fmt_out = p_vout->fmt_in;
247
248     /* Try to open the real video output */
249     msg_Dbg( p_vout, "spawning the real video output(s)" );
250
251     video_format_t fmt = p_vout->fmt_out;
252
253     if( p_sys->p_chain )
254     {
255         p_sys->pp_vout[0] = vout_Create( p_vout, &fmt );
256         if( !p_sys->pp_vout[0] )
257         {
258             msg_Err( p_vout, "cannot open vout, aborting" );
259             return VLC_EGENERIC;
260         }
261         vout_filter_AddChild( p_vout, p_sys->pp_vout[0], MouseEvent );
262     }
263     else
264     {
265         video_splitter_t *p_splitter = p_sys->p_splitter;
266
267         /* */
268         const int i_org_align = var_CreateGetInteger( p_vout, "align" );
269         const int i_org_x = var_CreateGetInteger( p_vout, "video-x" );
270         const int i_org_y = var_CreateGetInteger( p_vout, "video-y" );
271         const char *psz_org_vout = var_CreateGetNonEmptyString( p_vout, "vout" );
272
273         /* */
274         for( int i = 0; i < p_splitter->i_output; i++ )
275         {
276             const video_splitter_output_t *p_cfg = &p_splitter->p_output[i];
277
278             /* */
279             var_SetInteger( p_vout, "align", p_cfg->window.i_align);
280
281             var_SetInteger( p_vout, "video-x", i_org_x + p_cfg->window.i_x );
282             var_SetInteger( p_vout, "video-y", i_org_y + p_cfg->window.i_y );
283
284             if( p_cfg->psz_module )
285                 var_SetString( p_vout, "vout", p_cfg->psz_module );
286
287             /* */
288             video_format_t fmt = p_cfg->fmt;
289             p_sys->pp_vout[i] = vout_Create( p_vout, &fmt );
290             if( !p_sys->pp_vout[i] )
291             {
292                 msg_Err( p_vout, "cannot open vout, aborting" );
293                 VoutsClean( p_vout, i );
294                 return VLC_EGENERIC;
295             }
296         }
297
298         /* Attach once pp_vout is completly filed to avoid race conditions */
299         for( int i = 0; i < p_splitter->i_output; i++ )
300             vout_filter_SetupChild( p_vout, p_sys->pp_vout[i],
301                                     MouseEvent,
302                                     FullscreenEventDown, true );
303         /* Restore settings */
304         var_SetInteger( p_vout, "align", i_org_align );
305         var_SetInteger( p_vout, "video-x", i_org_x );
306         var_SetInteger( p_vout, "video-y", i_org_y );
307         var_SetString( p_vout, "vout", psz_org_vout ? psz_org_vout : "" );
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     /* Detach all vouts before destroying them */
379     for( int i = 0; i < i_count; i++ )
380     {
381         if( p_sys->p_chain )
382             vout_filter_DelChild( p_vout, p_sys->pp_vout[i], MouseEvent );
383         else
384              vout_filter_SetupChild( p_vout, p_sys->pp_vout[i],
385                                      MouseEvent,
386                                      FullscreenEventDown, false );
387     }
388
389     for( int i = 0; i < i_count; i++ )
390         vout_CloseAndRelease( p_sys->pp_vout[i] );
391 }
392 static int VoutsNewPicture( vout_thread_t *p_vout, picture_t *pp_dst[] )
393 {
394     vout_sys_t *p_sys = p_vout->p_sys;
395
396     for( int i = 0; i < p_sys->i_vout; i++ )
397     {
398         picture_t *p_picture = NULL;
399         for( ;; )
400         {
401             p_picture = vout_CreatePicture( p_sys->pp_vout[i], 0, 0, 0 );
402             if( p_picture )
403                 break;
404
405             if( !vlc_object_alive( p_vout ) || p_vout->b_error )
406                 break;
407             msleep( VOUT_OUTMEM_SLEEP );
408         }
409         /* FIXME what to do with the allocated picture ? */
410         if( !p_picture )
411             return VLC_EGENERIC;
412
413         pp_dst[i] = p_picture;
414     }
415     return VLC_SUCCESS;
416 }
417
418 /**
419  * Callback for mouse events
420  */
421 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
422                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
423 {
424     VLC_UNUSED(psz_var); VLC_UNUSED(oldval); VLC_UNUSED(newval);
425     vout_thread_t *p_vout = p_data;
426     vout_sys_t *p_sys = p_vout->p_sys;
427     int i_index;
428
429     for( i_index = 0; i_index < p_sys->i_vout; i_index++ )
430     {
431         if( p_this == VLC_OBJECT(p_sys->pp_vout[i_index]) )
432             break;
433     }
434     if( i_index >= p_sys->i_vout )
435     {
436         msg_Err( p_vout, "Failed to find vout source in MouseEvent" );
437         return VLC_SUCCESS;
438     }
439
440     vout_thread_t *p_vout_src = p_sys->pp_vout[i_index];
441
442     vlc_mouse_t m;
443     vlc_mouse_Init( &m );
444     var_GetCoords( p_vout_src, "mouse-moved", &m.i_x, &m.i_y );
445     m.i_pressed = var_GetInteger( p_vout_src, "mouse-button-down" );
446
447     vlc_mutex_lock( &p_sys->lock );
448
449     vlc_mouse_t nmouse;
450     vlc_mouse_t omouse = p_sys->mouse;
451
452     int i_ret;
453     if( p_sys->p_chain )
454     {
455         i_ret = filter_chain_MouseFilter( p_sys->p_chain, &nmouse, &m );
456     }
457     else
458     {
459         vlc_mouse_t *p_mouse_src = &p_sys->p_mouse_src[i_index];
460
461         i_ret = video_splitter_Mouse( p_sys->p_splitter, &nmouse, i_index, p_mouse_src, &m );
462         *p_mouse_src = m;
463     }
464
465     if( !i_ret )
466         p_sys->mouse = nmouse;
467     vlc_mutex_unlock( &p_sys->lock );
468
469     if( i_ret )
470         return VLC_EGENERIC;
471
472     if( vlc_mouse_HasMoved( &omouse, &nmouse ) )
473     {
474         var_SetCoords( p_vout, "mouse-moved", nmouse.i_x, nmouse.i_y );
475     }
476     if( vlc_mouse_HasButton( &omouse, &nmouse ) )
477     {
478         var_SetInteger( p_vout, "mouse-button-down", nmouse.i_pressed );
479         if( vlc_mouse_HasPressed( &omouse, &nmouse, MOUSE_BUTTON_LEFT ) )
480             var_SetCoords( p_vout, "mouse-clicked", nmouse.i_x, nmouse.i_y );
481     }
482     if( m.b_double_click )
483     {
484         /* Nothing with current API */
485         msg_Warn( p_vout, "Ignoring double click" );
486     }
487     return VLC_SUCCESS;
488 }
489
490 /* -- Filter callbacks -- */
491
492 static picture_t *VideoBufferNew( filter_t *p_filter )
493 {
494     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
495
496     picture_t *pp_picture[1];
497     if( VoutsNewPicture( p_vout, pp_picture ) )
498         return NULL;
499     return pp_picture[0];
500 }
501 static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
502 {
503     VLC_UNUSED(p_filter); VLC_UNUSED(p_picture);
504     /* FIXME is there anything to do ? */
505 }
506
507 static int FilterAllocationInit( filter_t *p_filter, void *p_data )
508 {
509     VLC_UNUSED( p_data );
510
511     p_filter->pf_video_buffer_new = VideoBufferNew;
512     p_filter->pf_video_buffer_del = VideoBufferDelete;
513     p_filter->p_owner = p_data;
514
515     return VLC_SUCCESS;
516 }
517 static void FilterAllocationClean( filter_t *p_filter )
518 {
519     p_filter->pf_video_buffer_new = NULL;
520     p_filter->pf_video_buffer_del = NULL;
521 }
522
523 /* -- Splitter callbacks -- */
524
525 static int FullscreenEventDown( vlc_object_t *p_this, char const *psz_var,
526                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
527 {
528     vout_thread_t *p_vout = (vout_thread_t*)p_this;
529     vout_sys_t *p_sys = p_vout->p_sys;
530     VLC_UNUSED(oldval); VLC_UNUSED(p_data); VLC_UNUSED(psz_var);
531
532     for( int i = 0; i < p_sys->i_vout; i++ )
533     {
534         vout_thread_t *p_child = p_sys->pp_vout[i];
535         var_SetBool( p_child, "fullscreen", newval.b_bool );
536     }
537     return VLC_SUCCESS;
538 }
539
540 static int  SplitterPictureNew( video_splitter_t *p_splitter, picture_t *pp_picture[] )
541 {
542     vout_thread_t *p_vout = (vout_thread_t*)p_splitter->p_owner;
543
544     return VoutsNewPicture( p_vout, pp_picture );
545 }
546 static void SplitterPictureDel( video_splitter_t *p_splitter, picture_t *pp_picture[] )
547 {
548     VLC_UNUSED(p_splitter); VLC_UNUSED(pp_picture);
549     /* FIXME is there anything to do ? */
550 }
551