1 /*****************************************************************************
2 * wrapper.c: a "video filter2/splitter" with mouse to "video filter" wrapper.
3 *****************************************************************************
4 * Copyright (C) 2009 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_video_splitter.h>
39 #include "filter_common.h"
41 /*****************************************************************************
43 *****************************************************************************/
44 static int Open ( vlc_object_t *, const char *psz_name, bool b_filter );
45 static void Close( vlc_object_t * );
47 #define DECLARE_OPEN(name,filter) \
48 static int Open##name ( vlc_object_t *p_this ) { return Open( p_this, #name, filter ); }
50 DECLARE_OPEN(clone, false)
51 DECLARE_OPEN(wall, false)
52 DECLARE_OPEN(panoramix, false)
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 ) \
64 set_category( CAT_VIDEO )
65 set_subcategory( SUBCAT_VIDEO_VFILTER )
73 DECLARE_MODULE(panoramix)
79 /*****************************************************************************
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 );
90 vout_thread_t **pp_vout;
95 filter_chain_t *p_chain;
96 video_splitter_t *p_splitter;
98 vlc_mouse_t *p_mouse_src;
103 static int FilterAllocationInit ( filter_t *, void * );
104 static void FilterAllocationClean( filter_t * );
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[] );
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 );
118 * Open our wrapper instance.
120 static int Open( vlc_object_t *p_this, const char *psz_name, bool b_filter )
122 vout_thread_t *p_vout = (vout_thread_t *)p_this;
125 msg_Err( p_vout, "Opening video %s wrapper for %s",
126 b_filter ? "filter" : "splitter", psz_name );
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 );
136 /* Try to open our real module */
137 filter_chain_t *p_chain = NULL;
138 video_splitter_t *p_splitter = NULL;
141 p_chain = filter_chain_New( p_vout, "video filter2", false,
142 FilterAllocationInit, FilterAllocationClean, p_vout );
146 filter_chain_Reset( p_chain, &fmt, &fmt );
149 filter_chain_AppendFilter( p_chain, psz_name, p_vout->p_cfg, &fmt, &fmt );
153 msg_Err( p_vout, "Failed to open filter '%s'", psz_name );
154 filter_chain_Delete( p_chain );
160 p_splitter = video_splitter_New( VLC_OBJECT(p_vout), psz_name, &fmt.video );
163 msg_Err( p_vout, "Failed to open splitter '%s'", psz_name );
166 assert( p_splitter->i_output > 0 );
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;
174 p_vout->p_sys = p_sys = malloc( sizeof(*p_sys) );
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) );
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] );
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;
201 filter_chain_Delete( p_chain );
203 video_splitter_Delete( p_splitter );
208 * Close our wrapper instance
210 static void Close( vlc_object_t *p_this )
212 vout_thread_t *p_vout = (vout_thread_t *)p_this;
213 vout_sys_t *p_sys = p_vout->p_sys;
216 filter_chain_Delete( p_sys->p_chain );
217 if( p_sys->p_splitter )
218 video_splitter_Delete( p_sys->p_splitter );
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 );
225 free( p_vout->p_sys );
229 * Initialise our wrapper
231 static int Init( vout_thread_t *p_vout )
233 vout_sys_t *p_sys = p_vout->p_sys;
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 );
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;
246 p_vout->fmt_out = p_vout->fmt_in;
248 /* Try to open the real video output */
249 msg_Dbg( p_vout, "spawning the real video output(s)" );
251 video_format_t fmt = p_vout->fmt_out;
255 p_sys->pp_vout[0] = vout_Create( p_vout, &fmt );
256 if( !p_sys->pp_vout[0] )
258 msg_Err( p_vout, "cannot open vout, aborting" );
261 vout_filter_AddChild( p_vout, p_sys->pp_vout[0], MouseEvent );
265 video_splitter_t *p_splitter = p_sys->p_splitter;
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" );
274 for( int i = 0; i < p_splitter->i_output; i++ )
276 const video_splitter_output_t *p_cfg = &p_splitter->p_output[i];
279 var_SetInteger( p_vout, "align", p_cfg->window.i_align);
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 );
284 if( p_cfg->psz_module )
285 var_SetString( p_vout, "vout", p_cfg->psz_module );
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] )
292 msg_Err( p_vout, "cannot open vout, aborting" );
293 VoutsClean( p_vout, i );
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],
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 : "" );
310 vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
316 * Clean up our wrapper
318 static void End( vout_thread_t *p_vout )
320 vout_sys_t *p_sys = p_vout->p_sys;
322 VoutsClean( p_vout, p_sys->i_vout );
324 vout_filter_ReleaseDirectBuffers( p_vout );
328 * Control the real vout
330 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
332 vout_sys_t *p_sys = p_vout->p_sys;
333 int i_ret = VLC_SUCCESS;
335 for( int i = 0; i < p_sys->i_vout; i++ )
336 i_ret = vout_vaControl( p_sys->pp_vout[i], i_query, args );
343 static void Render( vout_thread_t *p_vout, picture_t *p_src )
345 vout_sys_t *p_sys = p_vout->p_sys;
347 vlc_mutex_lock( &p_sys->lock );
349 picture_t *pp_dst[p_sys->i_vout];
353 pp_dst[0] = filter_chain_VideoFilter( p_sys->p_chain, p_src );
357 if( video_splitter_Filter( p_sys->p_splitter, pp_dst, p_src ) )
359 for( int i = 0; i < p_sys->i_vout; i++ )
363 for( int i = 0; i < p_sys->i_vout; i++ )
365 picture_t *p_dst = pp_dst[i];
367 vout_DisplayPicture( p_sys->pp_vout[i], p_dst );
370 vlc_mutex_unlock( &p_sys->lock );
374 static void VoutsClean( vout_thread_t *p_vout, int i_count )
376 vout_sys_t *p_sys = p_vout->p_sys;
378 /* Detach all vouts before destroying them */
379 for( int i = 0; i < i_count; i++ )
382 vout_filter_DelChild( p_vout, p_sys->pp_vout[i], MouseEvent );
384 vout_filter_SetupChild( p_vout, p_sys->pp_vout[i],
386 FullscreenEventDown, false );
389 for( int i = 0; i < i_count; i++ )
390 vout_CloseAndRelease( p_sys->pp_vout[i] );
392 static int VoutsNewPicture( vout_thread_t *p_vout, picture_t *pp_dst[] )
394 vout_sys_t *p_sys = p_vout->p_sys;
396 for( int i = 0; i < p_sys->i_vout; i++ )
398 picture_t *p_picture = NULL;
401 p_picture = vout_CreatePicture( p_sys->pp_vout[i], 0, 0, 0 );
405 if( !vlc_object_alive( p_vout ) || p_vout->b_error )
407 msleep( VOUT_OUTMEM_SLEEP );
409 /* FIXME what to do with the allocated picture ? */
413 pp_dst[i] = p_picture;
419 * Callback for mouse events
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 )
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;
429 for( i_index = 0; i_index < p_sys->i_vout; i_index++ )
431 if( p_this == VLC_OBJECT(p_sys->pp_vout[i_index]) )
434 if( i_index >= p_sys->i_vout )
436 msg_Err( p_vout, "Failed to find vout source in MouseEvent" );
440 vout_thread_t *p_vout_src = p_sys->pp_vout[i_index];
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" );
447 vlc_mutex_lock( &p_sys->lock );
450 vlc_mouse_t omouse = p_sys->mouse;
455 i_ret = filter_chain_MouseFilter( p_sys->p_chain, &nmouse, &m );
459 vlc_mouse_t *p_mouse_src = &p_sys->p_mouse_src[i_index];
461 i_ret = video_splitter_Mouse( p_sys->p_splitter, &nmouse, i_index, p_mouse_src, &m );
466 p_sys->mouse = nmouse;
467 vlc_mutex_unlock( &p_sys->lock );
472 if( vlc_mouse_HasMoved( &omouse, &nmouse ) )
474 var_SetCoords( p_vout, "mouse-moved", nmouse.i_x, nmouse.i_y );
476 if( vlc_mouse_HasButton( &omouse, &nmouse ) )
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 );
482 if( m.b_double_click )
484 /* Nothing with current API */
485 msg_Warn( p_vout, "Ignoring double click" );
490 /* -- Filter callbacks -- */
492 static picture_t *VideoBufferNew( filter_t *p_filter )
494 vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
496 picture_t *pp_picture[1];
497 if( VoutsNewPicture( p_vout, pp_picture ) )
499 return pp_picture[0];
501 static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
503 VLC_UNUSED(p_filter); VLC_UNUSED(p_picture);
504 /* FIXME is there anything to do ? */
507 static int FilterAllocationInit( filter_t *p_filter, void *p_data )
509 VLC_UNUSED( p_data );
511 p_filter->pf_video_buffer_new = VideoBufferNew;
512 p_filter->pf_video_buffer_del = VideoBufferDelete;
513 p_filter->p_owner = p_data;
517 static void FilterAllocationClean( filter_t *p_filter )
519 p_filter->pf_video_buffer_new = NULL;
520 p_filter->pf_video_buffer_del = NULL;
523 /* -- Splitter callbacks -- */
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 )
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);
532 for( int i = 0; i < p_sys->i_vout; i++ )
534 vout_thread_t *p_child = p_sys->pp_vout[i];
535 var_SetBool( p_child, "fullscreen", newval.b_bool );
540 static int SplitterPictureNew( video_splitter_t *p_splitter, picture_t *pp_picture[] )
542 vout_thread_t *p_vout = (vout_thread_t*)p_splitter->p_owner;
544 return VoutsNewPicture( p_vout, pp_picture );
546 static void SplitterPictureDel( video_splitter_t *p_splitter, picture_t *pp_picture[] )
548 VLC_UNUSED(p_splitter); VLC_UNUSED(pp_picture);
549 /* FIXME is there anything to do ? */