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(magnify, true)
51 DECLARE_OPEN(puzzle, true)
52 DECLARE_OPEN(logo, true)
53 DECLARE_OPEN(clone, false)
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 ) \
65 set_category( CAT_VIDEO )
66 set_subcategory( SUBCAT_VIDEO_VFILTER )
68 DECLARE_MODULE(magnify)
71 DECLARE_MODULE(puzzle)
83 /*****************************************************************************
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 );
94 vout_thread_t **pp_vout;
99 filter_chain_t *p_chain;
100 video_splitter_t *p_splitter;
102 vlc_mouse_t *p_mouse_src;
107 static int FilterAllocationInit ( filter_t *, void * );
108 static void FilterAllocationClean( filter_t * );
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[] );
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 );
124 * Open our wrapper instance.
126 static int Open( vlc_object_t *p_this, const char *psz_name, bool b_filter )
128 vout_thread_t *p_vout = (vout_thread_t *)p_this;
131 msg_Err( p_vout, "Opening video %s wrapper for %s",
132 b_filter ? "filter" : "splitter", psz_name );
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 )
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;
146 /* Try to open our real module */
147 filter_chain_t *p_chain = NULL;
148 video_splitter_t *p_splitter = NULL;
151 p_chain = filter_chain_New( p_vout, "video filter2", false,
152 FilterAllocationInit, FilterAllocationClean, p_vout );
156 filter_chain_Reset( p_chain, &fmt, &fmt );
159 filter_chain_AppendFilter( p_chain, psz_name, p_vout->p_cfg, &fmt, &fmt );
163 msg_Err( p_vout, "Failed to open filter '%s'", psz_name );
164 filter_chain_Delete( p_chain );
170 p_splitter = video_splitter_New( VLC_OBJECT(p_vout), psz_name, &fmt.video );
173 msg_Err( p_vout, "Failed to open splitter '%s'", psz_name );
176 assert( p_splitter->i_output > 0 );
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;
184 p_vout->p_sys = p_sys = malloc( sizeof(*p_sys) );
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) );
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] );
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;
211 filter_chain_Delete( p_chain );
213 video_splitter_Delete( p_splitter );
218 * Close our wrapper instance
220 static void Close( vlc_object_t *p_this )
222 vout_thread_t *p_vout = (vout_thread_t *)p_this;
223 vout_sys_t *p_sys = p_vout->p_sys;
226 filter_chain_Delete( p_sys->p_chain );
227 if( p_sys->p_splitter )
228 video_splitter_Delete( p_sys->p_splitter );
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 );
235 free( p_vout->p_sys );
239 * Initialise our wrapper
241 static int Init( vout_thread_t *p_vout )
243 vout_sys_t *p_sys = p_vout->p_sys;
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 );
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;
256 p_vout->fmt_out = p_vout->fmt_in;
258 /* Try to open the real video output */
259 msg_Dbg( p_vout, "spawning the real video output(s)" );
261 video_format_t fmt = p_vout->fmt_out;
265 p_sys->pp_vout[0] = vout_Create( p_vout, &fmt );
266 if( !p_sys->pp_vout[0] )
268 msg_Err( p_vout, "cannot open vout, aborting" );
271 vout_filter_AddChild( p_vout, p_sys->pp_vout[0], MouseEvent );
275 video_splitter_t *p_splitter = p_sys->p_splitter;
277 for( int i = 0; i < p_splitter->i_output; i++ )
279 const video_splitter_output_t *p_cfg = &p_splitter->p_output[i];
282 var_Create( p_vout, "align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
283 var_SetInteger( p_vout, "align", p_cfg->window.i_align);
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 );
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 );
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 : "" );
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] )
299 msg_Err( p_vout, "cannot open vout, aborting" );
300 VoutsClean( p_vout, i );
304 vout_filter_SetupChild( p_vout, p_sys->pp_vout[i],
306 FullscreenEventUp, FullscreenEventDown, true );
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 for( int i = 0; i < i_count; i++ )
383 vout_filter_DelChild( p_vout, p_sys->pp_vout[i], MouseEvent );
387 vout_filter_SetupChild( p_vout, p_sys->pp_vout[i],
389 FullscreenEventUp, FullscreenEventDown, false );
391 vout_CloseAndRelease( p_sys->pp_vout[i] );
394 static int VoutsNewPicture( vout_thread_t *p_vout, picture_t *pp_dst[] )
396 vout_sys_t *p_sys = p_vout->p_sys;
398 for( int i = 0; i < p_sys->i_vout; i++ )
400 picture_t *p_picture = NULL;
403 p_picture = vout_CreatePicture( p_sys->pp_vout[i], 0, 0, 0 );
407 if( !vlc_object_alive( p_vout ) || p_vout->b_error )
409 msleep( VOUT_OUTMEM_SLEEP );
411 /* FIXME what to do with the allocated picture ? */
415 pp_dst[i] = p_picture;
421 * Callback for mouse events
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 )
426 vout_thread_t *p_vout = p_data;
427 vout_sys_t *p_sys = p_vout->p_sys;
430 for( i_index = 0; i_index < p_sys->i_vout; i_index++ )
432 if( p_this == VLC_OBJECT(p_sys->pp_vout[i_index]) )
435 if( i_index >= p_sys->i_vout )
437 msg_Err( p_vout, "Failed to find vout source in MouseEvent" );
441 vout_thread_t *p_vout_src = p_sys->pp_vout[i_index];
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" );
449 vlc_mutex_lock( &p_sys->lock );
452 vlc_mouse_t omouse = p_sys->mouse;
457 i_ret = filter_chain_MouseFilter( p_sys->p_chain, &nmouse, &m );
461 vlc_mouse_t *p_mouse_src = &p_sys->p_mouse_src[i_index];
463 i_ret = video_splitter_Mouse( p_sys->p_splitter, &nmouse, i_index, p_mouse_src, &m );
468 p_sys->mouse = nmouse;
469 vlc_mutex_unlock( &p_sys->lock );
474 if( vlc_mouse_HasMoved( &omouse, &nmouse ) )
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 );
480 if( vlc_mouse_HasButton( &omouse, &nmouse ) )
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 );
486 if( m.b_double_click )
488 /* Nothing with current API */
489 msg_Warn( p_vout, "Ignoring double click" );
494 /* -- Filter callbacks -- */
496 static picture_t *VideoBufferNew( filter_t *p_filter )
498 vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
500 picture_t *pp_picture[1];
501 if( VoutsNewPicture( p_vout, pp_picture ) )
503 return pp_picture[0];
505 static void VideoBufferDelete( filter_t *p_filter, picture_t *p_picture )
507 VLC_UNUSED(p_filter); VLC_UNUSED(p_picture);
508 /* FIXME is there anything to do ? */
511 static int FilterAllocationInit( filter_t *p_filter, void *p_data )
513 VLC_UNUSED( p_data );
515 p_filter->pf_vout_buffer_new = VideoBufferNew;
516 p_filter->pf_vout_buffer_del = VideoBufferDelete;
517 p_filter->p_owner = p_data;
521 static void FilterAllocationClean( filter_t *p_filter )
523 p_filter->pf_vout_buffer_new = NULL;
524 p_filter->pf_vout_buffer_del = NULL;
527 /* -- Splitter callbacks -- */
530 * Forward fullscreen event to/from the childrens.
532 * FIXME probably unsafe (pp_vout[] content)
534 static bool IsFullscreenActive( vout_thread_t *p_vout )
536 vout_sys_t *p_sys = p_vout->p_sys;
537 for( int i = 0; i < p_sys->i_vout; i++ )
539 if( var_GetBool( p_sys->pp_vout[i], "fullscreen" ) )
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 )
547 vout_thread_t *p_vout = p_data;
548 VLC_UNUSED(oldval); VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(newval);
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 );
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 )
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);
562 const bool b_fullscreen = IsFullscreenActive( p_vout );
563 if( !b_fullscreen != !newval.b_bool )
565 for( int i = 0; i < p_sys->i_vout; i++ )
567 vout_thread_t *p_child = p_sys->pp_vout[i];
568 if( !var_GetBool( p_child, "fullscreen" ) != !newval.b_bool )
570 var_SetBool( p_child, "fullscreen", newval.b_bool );
579 static int SplitterPictureNew( video_splitter_t *p_splitter, picture_t *pp_picture[] )
581 vout_thread_t *p_vout = (vout_thread_t*)p_splitter->p_owner;
583 return VoutsNewPicture( p_vout, pp_picture );
585 static void SplitterPictureDel( video_splitter_t *p_splitter, picture_t *pp_picture[] )
587 /* FIXME is there anything to do ? */