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